diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml index e56d2b92a..3964989bb 100644 --- a/app/src/main/AndroidManifest.xml +++ b/app/src/main/AndroidManifest.xml @@ -60,8 +60,15 @@ android:windowSoftInputMode="stateAlwaysHidden" /> + android:label="@string/favourites_categories" + android:windowSoftInputMode="stateAlwaysHidden" /> + + + + + - - - - - diff --git a/app/src/main/java/org/koitharu/kotatsu/core/db/FavouritesDao.kt b/app/src/main/java/org/koitharu/kotatsu/core/db/FavouritesDao.kt index 88f2a6997..b1f93dea3 100644 --- a/app/src/main/java/org/koitharu/kotatsu/core/db/FavouritesDao.kt +++ b/app/src/main/java/org/koitharu/kotatsu/core/db/FavouritesDao.kt @@ -9,8 +9,12 @@ import org.koitharu.kotatsu.core.db.entity.MangaEntity abstract class FavouritesDao { @Transaction - @Query("SELECT * FROM favourites GROUP BY manga_id ORDER BY :orderBy LIMIT :limit OFFSET :offset") - abstract suspend fun findAll(offset: Int, limit: Int, orderBy: String): List + @Query("SELECT * FROM favourites GROUP BY manga_id ORDER BY created_at LIMIT :limit OFFSET :offset") + abstract suspend fun findAll(offset: Int, limit: Int): List + + @Transaction + @Query("SELECT * FROM favourites WHERE category_id = :categoryId GROUP BY manga_id ORDER BY created_at LIMIT :limit OFFSET :offset") + abstract suspend fun findAll(categoryId: Long, offset: Int, limit: Int): List @Query("SELECT * FROM manga WHERE manga_id IN (SELECT manga_id FROM favourites)") abstract suspend fun findAllManga(): List diff --git a/app/src/main/java/org/koitharu/kotatsu/core/prefs/AppWidgetConfig.kt b/app/src/main/java/org/koitharu/kotatsu/core/prefs/AppWidgetConfig.kt new file mode 100644 index 000000000..fa4d577e9 --- /dev/null +++ b/app/src/main/java/org/koitharu/kotatsu/core/prefs/AppWidgetConfig.kt @@ -0,0 +1,26 @@ +package org.koitharu.kotatsu.core.prefs + +import android.content.Context +import android.content.SharedPreferences +import org.koitharu.kotatsu.utils.delegates.prefs.LongPreferenceDelegate + +class AppWidgetConfig private constructor( + private val prefs: SharedPreferences, + val widgetId: Int +) : SharedPreferences by prefs { + + var categoryId by LongPreferenceDelegate(CATEGORY_ID, 0L) + + companion object { + + private const val CATEGORY_ID = "cat_id" + + fun getInstance(context: Context, widgetId: Int) = AppWidgetConfig( + context.getSharedPreferences( + "appwidget_$widgetId", + Context.MODE_PRIVATE + ), widgetId + ) + } + +} \ No newline at end of file diff --git a/app/src/main/java/org/koitharu/kotatsu/domain/favourites/FavouritesRepository.kt b/app/src/main/java/org/koitharu/kotatsu/domain/favourites/FavouritesRepository.kt index 5a29f4218..56be7dc1a 100644 --- a/app/src/main/java/org/koitharu/kotatsu/domain/favourites/FavouritesRepository.kt +++ b/app/src/main/java/org/koitharu/kotatsu/domain/favourites/FavouritesRepository.kt @@ -19,7 +19,12 @@ class FavouritesRepository : KoinComponent { private val db: MangaDatabase by inject() suspend fun getAllManga(offset: Int): List { - val entities = db.favouritesDao.findAll(offset, 20, "created_at") + val entities = db.favouritesDao.findAll(offset, 20) + return entities.map { it.manga.toManga(it.tags.map(TagEntity::toMangaTag).toSet()) } + } + + suspend fun getManga(categoryId: Long, offset: Int): List { + val entities = db.favouritesDao.findAll(categoryId, offset, 20) return entities.map { it.manga.toManga(it.tags.map(TagEntity::toMangaTag).toSet()) } } diff --git a/app/src/main/java/org/koitharu/kotatsu/ui/widget/shelf/CategorySelectAdapter.kt b/app/src/main/java/org/koitharu/kotatsu/ui/widget/shelf/CategorySelectAdapter.kt new file mode 100644 index 000000000..16b366fda --- /dev/null +++ b/app/src/main/java/org/koitharu/kotatsu/ui/widget/shelf/CategorySelectAdapter.kt @@ -0,0 +1,35 @@ +package org.koitharu.kotatsu.ui.widget.shelf + +import android.view.ViewGroup +import org.koitharu.kotatsu.core.model.FavouriteCategory +import org.koitharu.kotatsu.ui.common.list.BaseRecyclerAdapter +import org.koitharu.kotatsu.ui.common.list.OnRecyclerItemClickListener + +class CategorySelectAdapter(onItemClickListener: OnRecyclerItemClickListener? = null) : + BaseRecyclerAdapter(onItemClickListener) { + + var checkedItemId = 0L + private set + + fun setCheckedId(id: Long) { + val oldId = checkedItemId + checkedItemId = id + val oldPos = findItemPositionById(oldId) + val newPos = findItemPositionById(id) + if (newPos != -1) { + notifyItemChanged(newPos) + } + if (oldPos != -1) { + notifyItemChanged(oldPos) + } + } + + override fun getExtra(item: FavouriteCategory, position: Int) = + checkedItemId == item.id + + override fun onCreateViewHolder(parent: ViewGroup) = CategorySelectHolder( + parent + ) + + override fun onGetItemId(item: FavouriteCategory) = item.id +} \ No newline at end of file diff --git a/app/src/main/java/org/koitharu/kotatsu/ui/widget/shelf/CategorySelectHolder.kt b/app/src/main/java/org/koitharu/kotatsu/ui/widget/shelf/CategorySelectHolder.kt new file mode 100644 index 000000000..3dc667da9 --- /dev/null +++ b/app/src/main/java/org/koitharu/kotatsu/ui/widget/shelf/CategorySelectHolder.kt @@ -0,0 +1,16 @@ +package org.koitharu.kotatsu.ui.widget.shelf + +import android.view.ViewGroup +import kotlinx.android.synthetic.main.item_category_checkable.* +import org.koitharu.kotatsu.R +import org.koitharu.kotatsu.core.model.FavouriteCategory +import org.koitharu.kotatsu.ui.common.list.BaseViewHolder + +class CategorySelectHolder(parent: ViewGroup) : + BaseViewHolder(parent, R.layout.item_category_checkable_single) { + + override fun onBind(data: FavouriteCategory, extra: Boolean) { + checkedTextView.text = data.title + checkedTextView.isChecked = extra + } +} \ No newline at end of file diff --git a/app/src/main/java/org/koitharu/kotatsu/ui/widget/shelf/ShelfConfigActivity.kt b/app/src/main/java/org/koitharu/kotatsu/ui/widget/shelf/ShelfConfigActivity.kt new file mode 100644 index 000000000..2aa0964f4 --- /dev/null +++ b/app/src/main/java/org/koitharu/kotatsu/ui/widget/shelf/ShelfConfigActivity.kt @@ -0,0 +1,102 @@ +package org.koitharu.kotatsu.ui.widget.shelf + +import android.app.Activity +import android.appwidget.AppWidgetManager +import android.content.Intent +import android.content.res.ColorStateList +import android.graphics.Color +import android.os.Bundle +import android.view.Menu +import android.view.MenuItem +import android.view.View +import androidx.core.view.isVisible +import androidx.recyclerview.widget.DividerItemDecoration +import androidx.recyclerview.widget.RecyclerView +import com.google.android.material.snackbar.Snackbar +import kotlinx.android.synthetic.main.activity_categories.* +import moxy.ktx.moxyPresenter +import org.koitharu.kotatsu.R +import org.koitharu.kotatsu.core.model.FavouriteCategory +import org.koitharu.kotatsu.core.prefs.AppWidgetConfig +import org.koitharu.kotatsu.ui.common.BaseActivity +import org.koitharu.kotatsu.ui.common.list.OnRecyclerItemClickListener +import org.koitharu.kotatsu.ui.main.list.favourites.categories.FavouriteCategoriesPresenter +import org.koitharu.kotatsu.ui.main.list.favourites.categories.FavouriteCategoriesView +import org.koitharu.kotatsu.utils.ext.getDisplayMessage +import java.util.* +import kotlin.collections.ArrayList + +class ShelfConfigActivity : BaseActivity(), FavouriteCategoriesView, + OnRecyclerItemClickListener { + + private val presenter by moxyPresenter(factory = ::FavouriteCategoriesPresenter) + + private lateinit var adapter: CategorySelectAdapter + private lateinit var config: AppWidgetConfig + + override fun onCreate(savedInstanceState: Bundle?) { + super.onCreate(savedInstanceState) + setContentView(R.layout.activity_categories) + supportActionBar?.setDisplayHomeAsUpEnabled(true) + fab_add.imageTintList = ColorStateList.valueOf(Color.WHITE) + adapter = CategorySelectAdapter(this) + recyclerView.addItemDecoration(DividerItemDecoration(this, RecyclerView.VERTICAL)) + recyclerView.adapter = adapter + fab_add.isVisible = false + val appWidgetId = intent?.getIntExtra( + AppWidgetManager.EXTRA_APPWIDGET_ID, + AppWidgetManager.INVALID_APPWIDGET_ID + ) ?: AppWidgetManager.INVALID_APPWIDGET_ID + if (appWidgetId == AppWidgetManager.INVALID_APPWIDGET_ID) { + finish() + return + } + config = AppWidgetConfig.getInstance(this, appWidgetId) + adapter.setCheckedId(config.categoryId) + } + + override fun onCreateOptionsMenu(menu: Menu?): Boolean { + menuInflater.inflate(R.menu.opt_config, menu) + return super.onCreateOptionsMenu(menu) + } + + override fun onOptionsItemSelected(item: MenuItem) = when (item.itemId) { + R.id.action_done -> { + config.categoryId = adapter.checkedItemId + updateWidget() + setResult( + Activity.RESULT_OK, + Intent().putExtra(AppWidgetManager.EXTRA_APPWIDGET_ID, config.widgetId) + ) + finish() + true + } + else -> super.onOptionsItemSelected(item) + } + + override fun onItemClick(item: FavouriteCategory, position: Int, view: View) { + adapter.setCheckedId(item.id) + } + + override fun onCategoriesChanged(categories: List) { + val data = ArrayList(categories.size + 1) + data += FavouriteCategory(0L, getString(R.string.favourites), Date()) + data += categories + adapter.replaceData(data) + } + + override fun onCheckedCategoriesChanged(checkedIds: Set) = Unit + + override fun onError(e: Throwable) { + Snackbar.make(recyclerView, e.getDisplayMessage(resources), Snackbar.LENGTH_LONG) + .show() + } + + private fun updateWidget() { + val intent = Intent(this, ShelfWidgetProvider::class.java) + intent.action = AppWidgetManager.ACTION_APPWIDGET_UPDATE + val ids = intArrayOf(config.widgetId) + intent.putExtra(AppWidgetManager.EXTRA_APPWIDGET_IDS, ids) + sendBroadcast(intent) + } +} \ No newline at end of file diff --git a/app/src/main/java/org/koitharu/kotatsu/ui/widget/shelf/ShelfListFactory.kt b/app/src/main/java/org/koitharu/kotatsu/ui/widget/shelf/ShelfListFactory.kt index 7e0aa5d04..429d13816 100644 --- a/app/src/main/java/org/koitharu/kotatsu/ui/widget/shelf/ShelfListFactory.kt +++ b/app/src/main/java/org/koitharu/kotatsu/ui/widget/shelf/ShelfListFactory.kt @@ -9,14 +9,16 @@ import coil.request.GetRequestBuilder import kotlinx.coroutines.runBlocking import org.koitharu.kotatsu.R import org.koitharu.kotatsu.core.model.Manga +import org.koitharu.kotatsu.core.prefs.AppWidgetConfig import org.koitharu.kotatsu.domain.favourites.FavouritesRepository import org.koitharu.kotatsu.ui.details.MangaDetailsActivity import org.koitharu.kotatsu.utils.ext.requireBitmap import java.io.IOException -class ShelfListFactory(private val context: Context) : RemoteViewsService.RemoteViewsFactory { +class ShelfListFactory(private val context: Context, widgetId: Int) : RemoteViewsService.RemoteViewsFactory { private val dataSet = ArrayList() + private val config = AppWidgetConfig.getInstance(context, widgetId) override fun onCreate() { } @@ -27,7 +29,9 @@ class ShelfListFactory(private val context: Context) : RemoteViewsService.Remote override fun onDataSetChanged() { dataSet.clear() - val data = runBlocking { FavouritesRepository().getAllManga(0) } + val data = runBlocking { + FavouritesRepository().getManga(config.categoryId, 0) + } dataSet.addAll(data) } diff --git a/app/src/main/java/org/koitharu/kotatsu/ui/widget/shelf/ShelfWidgetService.kt b/app/src/main/java/org/koitharu/kotatsu/ui/widget/shelf/ShelfWidgetService.kt index 945c80d91..f2d6a63de 100644 --- a/app/src/main/java/org/koitharu/kotatsu/ui/widget/shelf/ShelfWidgetService.kt +++ b/app/src/main/java/org/koitharu/kotatsu/ui/widget/shelf/ShelfWidgetService.kt @@ -1,11 +1,14 @@ package org.koitharu.kotatsu.ui.widget.shelf +import android.appwidget.AppWidgetManager import android.content.Intent import android.widget.RemoteViewsService class ShelfWidgetService : RemoteViewsService() { override fun onGetViewFactory(intent: Intent): RemoteViewsFactory { - return ShelfListFactory(this) + val widgetId = intent.getIntExtra(AppWidgetManager.EXTRA_APPWIDGET_ID, + AppWidgetManager.INVALID_APPWIDGET_ID) + return ShelfListFactory(this, widgetId) } } \ No newline at end of file diff --git a/app/src/main/res/drawable/ic_done.xml b/app/src/main/res/drawable/ic_done.xml new file mode 100644 index 000000000..b4f50b70c --- /dev/null +++ b/app/src/main/res/drawable/ic_done.xml @@ -0,0 +1,11 @@ + + + diff --git a/app/src/main/res/layout/item_category_checkable_single.xml b/app/src/main/res/layout/item_category_checkable_single.xml new file mode 100644 index 000000000..7bf1ca556 --- /dev/null +++ b/app/src/main/res/layout/item_category_checkable_single.xml @@ -0,0 +1,16 @@ + + \ No newline at end of file diff --git a/app/src/main/res/layout/item_shelf.xml b/app/src/main/res/layout/item_shelf.xml index e9d5575a9..657613c36 100644 --- a/app/src/main/res/layout/item_shelf.xml +++ b/app/src/main/res/layout/item_shelf.xml @@ -28,7 +28,9 @@ android:ellipsize="end" android:gravity="center" android:lines="2" - android:textColor="?android:textColorPrimary" /> + android:shadowColor="@android:color/black" + android:shadowRadius="1" + android:textColor="@android:color/white" /> diff --git a/app/src/main/res/layout/widget_shelf.xml b/app/src/main/res/layout/widget_shelf.xml index 0a5661fba..e4f118684 100644 --- a/app/src/main/res/layout/widget_shelf.xml +++ b/app/src/main/res/layout/widget_shelf.xml @@ -21,7 +21,9 @@ android:layout_height="wrap_content" android:layout_gravity="center" android:gravity="center" + android:shadowColor="@android:color/black" + android:shadowRadius="1" android:text="@string/you_have_not_favourites_yet" - android:textColor="?android:textColorPrimary" /> + android:textColor="@android:color/white" /> \ No newline at end of file diff --git a/app/src/main/res/menu/opt_config.xml b/app/src/main/res/menu/opt_config.xml new file mode 100644 index 000000000..425f8e7dc --- /dev/null +++ b/app/src/main/res/menu/opt_config.xml @@ -0,0 +1,12 @@ + + + + + \ No newline at end of file diff --git a/app/src/main/res/values-ru/strings.xml b/app/src/main/res/values-ru/strings.xml index 51f62d15e..9a4a99844 100644 --- a/app/src/main/res/values-ru/strings.xml +++ b/app/src/main/res/values-ru/strings.xml @@ -131,4 +131,5 @@ Не удалось найти ни одного доступного хранилища Другое хранилище Защищённое соединение (HTTPS) + Готово \ 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 0dfb3988b..13e7eff61 100644 --- a/app/src/main/res/values/strings.xml +++ b/app/src/main/res/values/strings.xml @@ -132,4 +132,5 @@ Cannot find any available storage Other storage Use secure connection (HTTPS) + Done \ No newline at end of file diff --git a/app/src/main/res/xml/widget_shelf.xml b/app/src/main/res/xml/widget_shelf.xml index 2dab58bf4..41c026cac 100644 --- a/app/src/main/res/xml/widget_shelf.xml +++ b/app/src/main/res/xml/widget_shelf.xml @@ -1,6 +1,7 @@