- Rewrote Category to Kotlin
- Moved category to ui - Reworked Animation (smoother) - Updated TextDrawable
|
@ -2,6 +2,7 @@ import java.text.SimpleDateFormat
|
|||
|
||||
apply plugin: 'com.android.application'
|
||||
apply plugin: 'kotlin-android'
|
||||
apply plugin: 'kotlin-android-extensions'
|
||||
apply plugin: 'com.neenbedankt.android-apt'
|
||||
apply plugin: 'me.tatarka.retrolambda'
|
||||
|
||||
|
|
|
@ -40,11 +40,10 @@
|
|||
android:parentActivityName=".ui.main.MainActivity" >
|
||||
</activity>
|
||||
<activity
|
||||
android:name=".ui.library.category.CategoryActivity"
|
||||
android:name=".ui.category.CategoryActivity"
|
||||
android:label="@string/label_categories"
|
||||
android:parentActivityName=".ui.main.MainActivity">
|
||||
</activity>
|
||||
|
||||
<activity
|
||||
android:name=".ui.setting.SettingsDownloadsFragment$CustomLayoutPickerActivity"
|
||||
android:label="@string/app_name"
|
||||
|
|
|
@ -6,17 +6,17 @@ import javax.inject.Singleton;
|
|||
|
||||
import dagger.Component;
|
||||
import eu.kanade.tachiyomi.data.download.DownloadService;
|
||||
import eu.kanade.tachiyomi.data.mangasync.base.MangaSyncService;
|
||||
import eu.kanade.tachiyomi.data.source.base.Source;
|
||||
import eu.kanade.tachiyomi.data.library.LibraryUpdateService;
|
||||
import eu.kanade.tachiyomi.data.mangasync.UpdateMangaSyncService;
|
||||
import eu.kanade.tachiyomi.data.mangasync.base.MangaSyncService;
|
||||
import eu.kanade.tachiyomi.data.source.base.Source;
|
||||
import eu.kanade.tachiyomi.data.updater.UpdateDownloader;
|
||||
import eu.kanade.tachiyomi.injection.module.AppModule;
|
||||
import eu.kanade.tachiyomi.injection.module.DataModule;
|
||||
import eu.kanade.tachiyomi.ui.catalogue.CataloguePresenter;
|
||||
import eu.kanade.tachiyomi.ui.category.CategoryPresenter;
|
||||
import eu.kanade.tachiyomi.ui.download.DownloadPresenter;
|
||||
import eu.kanade.tachiyomi.ui.library.LibraryPresenter;
|
||||
import eu.kanade.tachiyomi.ui.library.category.CategoryPresenter;
|
||||
import eu.kanade.tachiyomi.ui.manga.MangaActivity;
|
||||
import eu.kanade.tachiyomi.ui.manga.MangaPresenter;
|
||||
import eu.kanade.tachiyomi.ui.manga.chapter.ChaptersPresenter;
|
||||
|
|
|
@ -28,6 +28,7 @@ public class SimpleItemTouchHelperCallback extends ItemTouchHelper.Callback {
|
|||
return makeMovementFlags(dragFlags, swipeFlags);
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public boolean onMove(RecyclerView recyclerView, RecyclerView.ViewHolder viewHolder,
|
||||
RecyclerView.ViewHolder target) {
|
||||
|
|
|
@ -0,0 +1,276 @@
|
|||
package eu.kanade.tachiyomi.ui.category
|
||||
|
||||
import android.content.Context
|
||||
import android.content.Intent
|
||||
import android.os.Bundle
|
||||
import android.support.v7.view.ActionMode
|
||||
import android.support.v7.widget.LinearLayoutManager
|
||||
import android.support.v7.widget.RecyclerView
|
||||
import android.support.v7.widget.helper.ItemTouchHelper
|
||||
import android.view.Menu
|
||||
import android.view.MenuItem
|
||||
import com.afollestad.materialdialogs.MaterialDialog
|
||||
import eu.kanade.tachiyomi.R
|
||||
import eu.kanade.tachiyomi.data.database.models.Category
|
||||
import eu.kanade.tachiyomi.ui.base.activity.BaseRxActivity
|
||||
import eu.kanade.tachiyomi.ui.base.adapter.FlexibleViewHolder
|
||||
import eu.kanade.tachiyomi.ui.base.adapter.OnStartDragListener
|
||||
import eu.kanade.tachiyomi.ui.library.LibraryCategoryAdapter
|
||||
import kotlinx.android.synthetic.main.activity_edit_categories.*
|
||||
import kotlinx.android.synthetic.main.toolbar.*
|
||||
import nucleus.factory.RequiresPresenter
|
||||
|
||||
|
||||
/**
|
||||
* Activity that shows categories.
|
||||
* Uses R.layout.activity_edit_categories.
|
||||
* UI related actions should be called from here.
|
||||
*/
|
||||
@RequiresPresenter(CategoryPresenter::class)
|
||||
class CategoryActivity : BaseRxActivity<CategoryPresenter>(), ActionMode.Callback, FlexibleViewHolder.OnListItemClickListener, OnStartDragListener {
|
||||
|
||||
/**
|
||||
* Object used to show actionMode toolbar.
|
||||
*/
|
||||
var actionMode: ActionMode? = null
|
||||
|
||||
/**
|
||||
* Adapter containing category items.
|
||||
*/
|
||||
private lateinit var adapter: CategoryAdapter
|
||||
|
||||
/**
|
||||
* TouchHelper used for reorder animation and movement.
|
||||
*/
|
||||
private lateinit var touchHelper: ItemTouchHelper
|
||||
|
||||
companion object {
|
||||
/**
|
||||
* Create new CategoryActivity intent.
|
||||
*
|
||||
* @param context context information.
|
||||
*/
|
||||
@JvmStatic
|
||||
fun newIntent(context: Context): Intent? {
|
||||
return Intent(context, CategoryActivity::class.java)
|
||||
}
|
||||
}
|
||||
|
||||
override fun onCreate(savedInstanceState: Bundle?) {
|
||||
super.onCreate(savedInstanceState)
|
||||
|
||||
// Inflate activity_edit_categories.xml.
|
||||
setContentView(R.layout.activity_edit_categories)
|
||||
|
||||
// Setup the toolbar.
|
||||
setupToolbar(toolbar)
|
||||
|
||||
// Get new adapter.
|
||||
adapter = CategoryAdapter(this)
|
||||
|
||||
// Create view and inject category items into view
|
||||
recycler.layoutManager = LinearLayoutManager(this)
|
||||
recycler.setHasFixedSize(true)
|
||||
recycler.adapter = adapter
|
||||
|
||||
// Touch helper to drag and reorder categories
|
||||
touchHelper = ItemTouchHelper(CategoryItemTouchHelper(adapter))
|
||||
touchHelper.attachToRecyclerView(recycler)
|
||||
|
||||
// Create OnClickListener for creating new category
|
||||
fab.setOnClickListener({ v ->
|
||||
MaterialDialog.Builder(this)
|
||||
.title(R.string.action_add_category)
|
||||
.negativeText(R.string.button_cancel)
|
||||
.input(R.string.name, 0, false)
|
||||
{ dialog, input -> presenter.createCategory(input.toString()) }
|
||||
.show()
|
||||
})
|
||||
}
|
||||
|
||||
/**
|
||||
* Finishes action mode.
|
||||
* Call this when action mode action is finished.
|
||||
*/
|
||||
fun destroyActionModeIfNeeded() {
|
||||
actionMode?.finish()
|
||||
}
|
||||
|
||||
/**
|
||||
* Fill adapter with category items
|
||||
*
|
||||
* @param categories list containing categories
|
||||
*/
|
||||
fun setCategories(categories: List<Category>) {
|
||||
destroyActionModeIfNeeded()
|
||||
adapter.setItems(categories)
|
||||
}
|
||||
|
||||
/**
|
||||
* Delete selected categories
|
||||
*
|
||||
* @param categories list containing categories
|
||||
*/
|
||||
private fun deleteCategories(categories: List<Category?>?) {
|
||||
presenter.deleteCategories(categories)
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the selected categories
|
||||
*
|
||||
* @return list of selected categories
|
||||
*/
|
||||
private fun getSelectedCategories(): List<Category?>? {
|
||||
// Create a list of the selected categories
|
||||
return adapter.selectedItems.map { adapter.getItem(it) }
|
||||
}
|
||||
|
||||
/**
|
||||
* Show MaterialDialog which let user change category name.
|
||||
*
|
||||
* @param category category that will be edited.
|
||||
*/
|
||||
private fun editCategory(category: Category?) {
|
||||
MaterialDialog.Builder(this)
|
||||
.title(R.string.action_rename_category)
|
||||
.negativeText(R.string.button_cancel)
|
||||
.onNegative { materialDialog, dialogAction -> destroyActionModeIfNeeded() }
|
||||
.input(getString(R.string.name), category?.name, false)
|
||||
{ dialog, input -> presenter.renameCategory(category as Category, input.toString()) }
|
||||
.show()
|
||||
}
|
||||
|
||||
/**
|
||||
* Toggle actionMode selection
|
||||
*
|
||||
* @param position position of selected item
|
||||
*/
|
||||
private fun toggleSelection(position: Int) {
|
||||
adapter.toggleSelection(position, false)
|
||||
|
||||
// Get selected item count
|
||||
val count = adapter.selectedItemCount
|
||||
|
||||
// If no item is selected finish action mode
|
||||
if (count == 0) {
|
||||
actionMode?.finish()
|
||||
} else {
|
||||
// This block will only run if actionMode is not null
|
||||
actionMode?.let {
|
||||
|
||||
// Set title equal to selected item
|
||||
it.title = getString(R.string.label_selected, count)
|
||||
it.invalidate()
|
||||
|
||||
// Show edit button only when one item is selected
|
||||
val editItem = it.menu?.findItem(R.id.action_edit)
|
||||
editItem?.isVisible = count == 1
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Called each time the action mode is shown.
|
||||
* Always called after onCreateActionMode
|
||||
*
|
||||
* @return false
|
||||
*/
|
||||
override fun onPrepareActionMode(p0: ActionMode?, p1: Menu?): Boolean {
|
||||
return false
|
||||
}
|
||||
|
||||
/**
|
||||
* Called when action mode item clicked.
|
||||
*
|
||||
* @param actionMode action mode toolbar.
|
||||
* @param menuItem selected menu item.
|
||||
*
|
||||
* @return action mode item clicked exist status
|
||||
*/
|
||||
override fun onActionItemClicked(actionMode: ActionMode, menuItem: MenuItem): Boolean {
|
||||
when (menuItem.itemId) {
|
||||
R.id.action_delete -> {
|
||||
// Delete select categories.
|
||||
deleteCategories(getSelectedCategories())
|
||||
return true
|
||||
}
|
||||
R.id.action_edit -> {
|
||||
// Edit selected category
|
||||
editCategory(getSelectedCategories()?.get(0))
|
||||
return true
|
||||
}
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
/**
|
||||
* Inflate menu when action mode selected.
|
||||
*
|
||||
* @param mode ActionMode object
|
||||
* @param menu Menu object
|
||||
*
|
||||
* @return true
|
||||
*/
|
||||
override fun onCreateActionMode(mode: ActionMode, menu: Menu): Boolean {
|
||||
// Inflate menu.
|
||||
mode.menuInflater.inflate(R.menu.category_selection, menu)
|
||||
// Enable adapter multi selection.
|
||||
adapter.mode = LibraryCategoryAdapter.MODE_MULTI
|
||||
return true
|
||||
}
|
||||
|
||||
/**
|
||||
* Called when action mode destroyed.
|
||||
*
|
||||
* @param mode ActionMode object.
|
||||
*/
|
||||
override fun onDestroyActionMode(mode: ActionMode?) {
|
||||
// Reset adapter to single selection
|
||||
adapter.mode = LibraryCategoryAdapter.MODE_SINGLE
|
||||
// Clear selected items
|
||||
adapter.clearSelection()
|
||||
actionMode = null
|
||||
}
|
||||
|
||||
/**
|
||||
* Called when item in list is clicked.
|
||||
*
|
||||
* @param position position of clicked item.
|
||||
*/
|
||||
override fun onListItemClick(position: Int): Boolean {
|
||||
// Check if action mode is initialized and selected item exist.
|
||||
if (actionMode != null && position != -1) {
|
||||
// Toggle selection of clicked item.
|
||||
toggleSelection(position)
|
||||
return true
|
||||
} else {
|
||||
return false
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Called when item long clicked
|
||||
*
|
||||
* @param position position of clicked item.
|
||||
*/
|
||||
override fun onListItemLongClick(position: Int) {
|
||||
// Check if action mode is initialized.
|
||||
if (actionMode == null)
|
||||
// Initialize action mode
|
||||
actionMode = startSupportActionMode(this)
|
||||
|
||||
// Set item as selected
|
||||
toggleSelection(position)
|
||||
}
|
||||
|
||||
/**
|
||||
* Called when item is dragged
|
||||
*
|
||||
* @param viewHolder view that contains dragged item
|
||||
*/
|
||||
override fun onStartDrag(viewHolder: RecyclerView.ViewHolder?) {
|
||||
// Notify touchHelper
|
||||
touchHelper.startDrag(viewHolder)
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,110 @@
|
|||
package eu.kanade.tachiyomi.ui.category
|
||||
|
||||
import android.view.ViewGroup
|
||||
import com.amulyakhare.textdrawable.util.ColorGenerator
|
||||
import eu.davidea.flexibleadapter.FlexibleAdapter
|
||||
import eu.kanade.tachiyomi.R
|
||||
import eu.kanade.tachiyomi.data.database.models.Category
|
||||
import eu.kanade.tachiyomi.ui.base.adapter.ItemTouchHelperAdapter
|
||||
import eu.kanade.tachiyomi.util.inflate
|
||||
import java.util.*
|
||||
|
||||
/**
|
||||
* Adapter of CategoryHolder.
|
||||
* Connection between Activity and Holder
|
||||
* Holder updates should be called from here.
|
||||
*
|
||||
* @param activity activity that created adapter
|
||||
* @constructor Creates a CategoryAdapter object
|
||||
*/
|
||||
class CategoryAdapter(private val activity: CategoryActivity) : FlexibleAdapter<CategoryHolder, Category>(), ItemTouchHelperAdapter {
|
||||
|
||||
/**
|
||||
* Generator used to generate circle letter icons
|
||||
*/
|
||||
private val generator: ColorGenerator
|
||||
|
||||
init {
|
||||
// Let generator use Material Design colors.
|
||||
// Material design is love, material design is live!
|
||||
generator = ColorGenerator.MATERIAL
|
||||
|
||||
// Set unique id's
|
||||
setHasStableIds(true)
|
||||
}
|
||||
|
||||
/**
|
||||
* Called when ViewHolder is created
|
||||
*
|
||||
* @param parent parent View
|
||||
* @param viewType int containing viewType
|
||||
*/
|
||||
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): CategoryHolder {
|
||||
// Inflate layout with item_edit_categories.xml
|
||||
val view = parent.inflate(R.layout.item_edit_categories)
|
||||
return CategoryHolder(view, this, activity, activity)
|
||||
}
|
||||
|
||||
/**
|
||||
* Called when ViewHolder is bind
|
||||
*
|
||||
* @param holder bind holder
|
||||
* @param position position of holder
|
||||
*/
|
||||
override fun onBindViewHolder(holder: CategoryHolder, position: Int) {
|
||||
// Update holder values.
|
||||
val category = getItem(position)
|
||||
holder.onSetValues(category, generator)
|
||||
|
||||
//When user scrolls this bind the correct selection status
|
||||
holder.itemView.isActivated = isSelected(position)
|
||||
}
|
||||
|
||||
/**
|
||||
* Update items with list of categories
|
||||
*
|
||||
* @param items list of categories
|
||||
*/
|
||||
fun setItems(items: List<Category>) {
|
||||
mItems = ArrayList(items)
|
||||
notifyDataSetChanged()
|
||||
}
|
||||
|
||||
/**
|
||||
* Get category by position
|
||||
*
|
||||
* @param position position of item
|
||||
*/
|
||||
override fun getItemId(position: Int): Long {
|
||||
return mItems[position].id!!.toLong()
|
||||
}
|
||||
|
||||
/**
|
||||
* Called when item is moved
|
||||
*
|
||||
* @param fromPosition previous position of item.
|
||||
* @param toPosition new position of item.
|
||||
*/
|
||||
override fun onItemMove(fromPosition: Int, toPosition: Int) {
|
||||
// Move items and notify touch helper
|
||||
Collections.swap(mItems, fromPosition, toPosition)
|
||||
notifyItemMoved(fromPosition, toPosition)
|
||||
|
||||
// Update database
|
||||
activity.presenter.reorderCategories(mItems)
|
||||
}
|
||||
|
||||
/**
|
||||
* Must be implemented, not used
|
||||
*/
|
||||
override fun onItemDismiss(position: Int) {
|
||||
// Empty method.
|
||||
}
|
||||
|
||||
/**
|
||||
* Must be implemented, not used
|
||||
*/
|
||||
override fun updateDataSet(p0: String?) {
|
||||
// Empty method.
|
||||
}
|
||||
}
|
|
@ -0,0 +1,74 @@
|
|||
package eu.kanade.tachiyomi.ui.category
|
||||
|
||||
import android.graphics.Color
|
||||
import android.graphics.Typeface
|
||||
import android.support.v4.view.MotionEventCompat
|
||||
import android.view.MotionEvent
|
||||
import android.view.View
|
||||
import com.amulyakhare.textdrawable.TextDrawable
|
||||
import com.amulyakhare.textdrawable.util.ColorGenerator
|
||||
import eu.kanade.tachiyomi.data.database.models.Category
|
||||
import eu.kanade.tachiyomi.ui.base.adapter.FlexibleViewHolder
|
||||
import eu.kanade.tachiyomi.ui.base.adapter.OnStartDragListener
|
||||
import kotlinx.android.synthetic.main.item_edit_categories.view.*
|
||||
|
||||
/**
|
||||
* Holder that contains category item.
|
||||
* Uses R.layout.item_edit_categories.
|
||||
* UI related actions should be called from here.
|
||||
*
|
||||
* @param view view of category item.
|
||||
* @param adapter adapter belonging to holder.
|
||||
* @param listener called when item clicked.
|
||||
* @param dragListener called when item dragged.
|
||||
*
|
||||
* @constructor Create CategoryHolder object
|
||||
*/
|
||||
class CategoryHolder(view: View, adapter: CategoryAdapter, listener: FlexibleViewHolder.OnListItemClickListener, dragListener: OnStartDragListener) : FlexibleViewHolder(view, adapter, listener) {
|
||||
|
||||
init {
|
||||
// Create round letter image onclick to simulate long click
|
||||
itemView.image.setOnClickListener({ v ->
|
||||
// Simulate long click on this view to enter selection mode
|
||||
onLongClick(view)
|
||||
})
|
||||
|
||||
// Set on touch listener for reorder image
|
||||
itemView.reorder.setOnTouchListener({ v, event ->
|
||||
if (MotionEventCompat.getActionMasked(event) == MotionEvent.ACTION_DOWN) {
|
||||
dragListener.onStartDrag(this)
|
||||
}
|
||||
false
|
||||
})
|
||||
}
|
||||
|
||||
/**
|
||||
* Update category item values.
|
||||
*
|
||||
* @param category category of item.
|
||||
* @param generator generator used to generate circle letter icons.
|
||||
*/
|
||||
fun onSetValues(category: Category, generator: ColorGenerator) {
|
||||
// Set capitalized title.
|
||||
itemView.title.text = category.name.capitalize()
|
||||
|
||||
// Update circle letter image.
|
||||
itemView.image.setImageDrawable(getRound(category.name.substring(0, 1).toUpperCase(), generator))
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns circle letter image
|
||||
*
|
||||
* @param text first letter of string
|
||||
* @param generator the generator used to generate circle letter image
|
||||
*/
|
||||
private fun getRound(text: String, generator: ColorGenerator): TextDrawable {
|
||||
return TextDrawable.builder()
|
||||
.beginConfig()
|
||||
.textColor(Color.WHITE)
|
||||
.useFont(Typeface.DEFAULT)
|
||||
.toUpperCase()
|
||||
.endConfig()
|
||||
.buildRound(text, generator.getColor(text))
|
||||
}
|
||||
}
|
|
@ -0,0 +1,26 @@
|
|||
package eu.kanade.tachiyomi.ui.category
|
||||
|
||||
import eu.kanade.tachiyomi.ui.base.adapter.ItemTouchHelperAdapter
|
||||
import eu.kanade.tachiyomi.ui.base.adapter.SimpleItemTouchHelperCallback
|
||||
|
||||
class CategoryItemTouchHelper(adapter: ItemTouchHelperAdapter) : SimpleItemTouchHelperCallback(adapter) {
|
||||
|
||||
/**
|
||||
* Disable items swipe remove
|
||||
*
|
||||
* @return false
|
||||
*/
|
||||
override fun isItemViewSwipeEnabled(): Boolean {
|
||||
return false
|
||||
}
|
||||
|
||||
/**
|
||||
* Disable long press item drag
|
||||
*
|
||||
* @return false
|
||||
*/
|
||||
override fun isLongPressDragEnabled(): Boolean {
|
||||
return false
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,106 @@
|
|||
package eu.kanade.tachiyomi.ui.category
|
||||
|
||||
import android.os.Bundle
|
||||
import eu.kanade.tachiyomi.data.database.DatabaseHelper
|
||||
import eu.kanade.tachiyomi.data.database.models.Category
|
||||
import eu.kanade.tachiyomi.ui.base.presenter.BasePresenter
|
||||
import rx.android.schedulers.AndroidSchedulers
|
||||
import javax.inject.Inject
|
||||
|
||||
/**
|
||||
* Presenter of CategoryActivity.
|
||||
* Contains information and data for activity.
|
||||
* Observable updates should be called from here.
|
||||
*/
|
||||
class CategoryPresenter : BasePresenter<CategoryActivity>() {
|
||||
|
||||
/**
|
||||
* Used to connect to database
|
||||
*/
|
||||
@Inject lateinit var db: DatabaseHelper
|
||||
|
||||
/**
|
||||
* List containing categories
|
||||
*/
|
||||
private var categories: List<Category>? = null
|
||||
|
||||
companion object {
|
||||
/**
|
||||
* The id of the restartable.
|
||||
*/
|
||||
final private val GET_CATEGORIES = 1
|
||||
}
|
||||
|
||||
override fun onCreate(savedState: Bundle?) {
|
||||
super.onCreate(savedState)
|
||||
|
||||
// Get categories as list
|
||||
restartableLatestCache(GET_CATEGORIES,
|
||||
{
|
||||
db.categories.asRxObservable()
|
||||
.doOnNext { categories -> this.categories = categories }
|
||||
.observeOn(AndroidSchedulers.mainThread())
|
||||
}, CategoryActivity::setCategories)
|
||||
|
||||
// Start get categories as list task
|
||||
start(GET_CATEGORIES)
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Create category and add it to database
|
||||
*
|
||||
* @param name name of category
|
||||
*/
|
||||
fun createCategory(name: String) {
|
||||
// Create category.
|
||||
val cat = Category.create(name)
|
||||
|
||||
// Set the new item in the last position.
|
||||
var max = 0
|
||||
if (categories != null) {
|
||||
for (cat2 in categories!!) {
|
||||
if (cat2.order > max) {
|
||||
max = cat2.order + 1
|
||||
}
|
||||
}
|
||||
}
|
||||
cat.order = max
|
||||
|
||||
// Insert into database.
|
||||
db.insertCategory(cat).asRxObservable().subscribe()
|
||||
}
|
||||
|
||||
/**
|
||||
* Delete category from database
|
||||
*
|
||||
* @param categories list of categories
|
||||
*/
|
||||
fun deleteCategories(categories: List<Category?>?) {
|
||||
db.deleteCategories(categories).asRxObservable().subscribe()
|
||||
}
|
||||
|
||||
/**
|
||||
* Reorder categories in database
|
||||
*
|
||||
* @param categories list of categories
|
||||
*/
|
||||
fun reorderCategories(categories: List<Category>) {
|
||||
for (i in categories.indices) {
|
||||
categories[i].order = i
|
||||
}
|
||||
|
||||
db.insertCategories(categories).asRxObservable().subscribe()
|
||||
}
|
||||
|
||||
/**
|
||||
* Rename a category
|
||||
*
|
||||
* @param category category that gets renamed
|
||||
* @param name new name of category
|
||||
*/
|
||||
fun renameCategory(category: Category, name: String) {
|
||||
category.name = name
|
||||
db.insertCategory(category).asRxObservable().subscribe()
|
||||
}
|
||||
}
|
|
@ -38,7 +38,7 @@ import eu.kanade.tachiyomi.data.io.IOHandler;
|
|||
import eu.kanade.tachiyomi.data.library.LibraryUpdateService;
|
||||
import eu.kanade.tachiyomi.event.LibraryMangasEvent;
|
||||
import eu.kanade.tachiyomi.ui.base.fragment.BaseRxFragment;
|
||||
import eu.kanade.tachiyomi.ui.library.category.CategoryActivity;
|
||||
import eu.kanade.tachiyomi.ui.category.CategoryActivity;
|
||||
import eu.kanade.tachiyomi.ui.main.MainActivity;
|
||||
import eu.kanade.tachiyomi.util.ToastUtil;
|
||||
import icepick.State;
|
||||
|
|
|
@ -1,180 +0,0 @@
|
|||
package eu.kanade.tachiyomi.ui.library.category;
|
||||
|
||||
import android.content.Context;
|
||||
import android.content.Intent;
|
||||
import android.os.Bundle;
|
||||
import android.support.design.widget.FloatingActionButton;
|
||||
import android.support.v4.content.res.ResourcesCompat;
|
||||
import android.support.v7.view.ActionMode;
|
||||
import android.support.v7.widget.LinearLayoutManager;
|
||||
import android.support.v7.widget.RecyclerView;
|
||||
import android.support.v7.widget.Toolbar;
|
||||
import android.support.v7.widget.helper.ItemTouchHelper;
|
||||
import android.view.Menu;
|
||||
import android.view.MenuItem;
|
||||
|
||||
import com.afollestad.materialdialogs.MaterialDialog;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
import butterknife.Bind;
|
||||
import butterknife.ButterKnife;
|
||||
import eu.kanade.tachiyomi.R;
|
||||
import eu.kanade.tachiyomi.data.database.models.Category;
|
||||
import eu.kanade.tachiyomi.ui.base.activity.BaseRxActivity;
|
||||
import eu.kanade.tachiyomi.ui.base.adapter.FlexibleViewHolder;
|
||||
import eu.kanade.tachiyomi.ui.base.adapter.OnStartDragListener;
|
||||
import eu.kanade.tachiyomi.ui.decoration.DividerItemDecoration;
|
||||
import eu.kanade.tachiyomi.ui.library.LibraryCategoryAdapter;
|
||||
import nucleus.factory.RequiresPresenter;
|
||||
import rx.Observable;
|
||||
|
||||
@RequiresPresenter(CategoryPresenter.class)
|
||||
public class CategoryActivity extends BaseRxActivity<CategoryPresenter> implements
|
||||
ActionMode.Callback, FlexibleViewHolder.OnListItemClickListener, OnStartDragListener {
|
||||
|
||||
@Bind(R.id.toolbar) Toolbar toolbar;
|
||||
@Bind(R.id.categories_list) RecyclerView recycler;
|
||||
@Bind(R.id.fab) FloatingActionButton fab;
|
||||
|
||||
private CategoryAdapter adapter;
|
||||
private ActionMode actionMode;
|
||||
private ItemTouchHelper touchHelper;
|
||||
|
||||
public static Intent newIntent(Context context) {
|
||||
return new Intent(context, CategoryActivity.class);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onCreate(Bundle savedState) {
|
||||
super.onCreate(savedState);
|
||||
setContentView(R.layout.activity_edit_categories);
|
||||
ButterKnife.bind(this);
|
||||
|
||||
setupToolbar(toolbar);
|
||||
|
||||
adapter = new CategoryAdapter(this);
|
||||
recycler.setLayoutManager(new LinearLayoutManager(this));
|
||||
recycler.setHasFixedSize(true);
|
||||
recycler.setAdapter(adapter);
|
||||
recycler.addItemDecoration(new DividerItemDecoration(
|
||||
ResourcesCompat.getDrawable(getResources(), R.drawable.line_divider, null)));
|
||||
|
||||
// Touch helper to drag and reorder categories
|
||||
touchHelper = new ItemTouchHelper(new CategoryItemTouchHelper(adapter));
|
||||
touchHelper.attachToRecyclerView(recycler);
|
||||
|
||||
fab.setOnClickListener(v -> {
|
||||
new MaterialDialog.Builder(this)
|
||||
.title(R.string.action_add_category)
|
||||
.input(R.string.name, 0, false, (dialog, input) -> {
|
||||
getPresenter().createCategory(input.toString());
|
||||
})
|
||||
.show();
|
||||
});
|
||||
}
|
||||
|
||||
public void setCategories(List<Category> categories) {
|
||||
destroyActionModeIfNeeded();
|
||||
adapter.setItems(categories);
|
||||
}
|
||||
|
||||
private List<Category> getSelectedCategories() {
|
||||
// Create a blocking copy of the selected categories
|
||||
return Observable.from(adapter.getSelectedItems())
|
||||
.map(adapter::getItem).toList().toBlocking().single();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean onListItemClick(int position) {
|
||||
if (actionMode != null && position != -1) {
|
||||
toggleSelection(position);
|
||||
return true;
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onListItemLongClick(int position) {
|
||||
if (actionMode == null)
|
||||
actionMode = startSupportActionMode(this);
|
||||
|
||||
toggleSelection(position);
|
||||
}
|
||||
|
||||
private void toggleSelection(int position) {
|
||||
adapter.toggleSelection(position, false);
|
||||
|
||||
int count = adapter.getSelectedItemCount();
|
||||
if (count == 0) {
|
||||
actionMode.finish();
|
||||
} else {
|
||||
setContextTitle(count);
|
||||
actionMode.invalidate();
|
||||
MenuItem editItem = actionMode.getMenu().findItem(R.id.action_edit);
|
||||
editItem.setVisible(count == 1);
|
||||
}
|
||||
}
|
||||
|
||||
private void setContextTitle(int count) {
|
||||
actionMode.setTitle(getString(R.string.label_selected, count));
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean onCreateActionMode(ActionMode mode, Menu menu) {
|
||||
mode.getMenuInflater().inflate(R.menu.category_selection, menu);
|
||||
adapter.setMode(LibraryCategoryAdapter.MODE_MULTI);
|
||||
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.action_delete:
|
||||
deleteCategories(getSelectedCategories());
|
||||
return true;
|
||||
case R.id.action_edit:
|
||||
editCategory(getSelectedCategories().get(0));
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onDestroyActionMode(ActionMode mode) {
|
||||
adapter.setMode(LibraryCategoryAdapter.MODE_SINGLE);
|
||||
adapter.clearSelection();
|
||||
actionMode = null;
|
||||
}
|
||||
|
||||
public void destroyActionModeIfNeeded() {
|
||||
if (actionMode != null) {
|
||||
actionMode.finish();
|
||||
}
|
||||
}
|
||||
|
||||
private void deleteCategories(List<Category> categories) {
|
||||
getPresenter().deleteCategories(categories);
|
||||
}
|
||||
|
||||
private void editCategory(Category category) {
|
||||
new MaterialDialog.Builder(this)
|
||||
.title(R.string.action_rename_category)
|
||||
.input(getString(R.string.name), category.name, false, (dialog, input) -> {
|
||||
getPresenter().renameCategory(category, input.toString());
|
||||
})
|
||||
.show();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onStartDrag(RecyclerView.ViewHolder viewHolder) {
|
||||
touchHelper.startDrag(viewHolder);
|
||||
}
|
||||
|
||||
}
|
|
@ -1,80 +0,0 @@
|
|||
package eu.kanade.tachiyomi.ui.library.category;
|
||||
|
||||
import android.view.LayoutInflater;
|
||||
import android.view.View;
|
||||
import android.view.ViewGroup;
|
||||
|
||||
import com.amulyakhare.textdrawable.util.ColorGenerator;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
|
||||
import eu.davidea.flexibleadapter.FlexibleAdapter;
|
||||
import eu.kanade.tachiyomi.R;
|
||||
import eu.kanade.tachiyomi.data.database.models.Category;
|
||||
import eu.kanade.tachiyomi.ui.base.adapter.ItemTouchHelperAdapter;
|
||||
|
||||
public class CategoryAdapter extends FlexibleAdapter<CategoryHolder, Category> implements
|
||||
ItemTouchHelperAdapter {
|
||||
|
||||
private final CategoryActivity activity;
|
||||
private final ColorGenerator generator;
|
||||
|
||||
public CategoryAdapter(CategoryActivity activity) {
|
||||
this.activity = activity;
|
||||
generator = ColorGenerator.DEFAULT;
|
||||
setHasStableIds(true);
|
||||
}
|
||||
|
||||
public void setItems(List<Category> items) {
|
||||
mItems = new ArrayList<>(items);
|
||||
notifyDataSetChanged();
|
||||
}
|
||||
|
||||
@Override
|
||||
public long getItemId(int position) {
|
||||
return mItems.get(position).id;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void updateDataSet(String param) {
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public CategoryHolder onCreateViewHolder(ViewGroup parent, int viewType) {
|
||||
LayoutInflater inflater = activity.getLayoutInflater();
|
||||
View v = inflater.inflate(R.layout.item_edit_categories, parent, false);
|
||||
return new CategoryHolder(v, this, activity, activity);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onBindViewHolder(CategoryHolder holder, int position) {
|
||||
final Category category = getItem(position);
|
||||
holder.onSetValues(category, generator);
|
||||
|
||||
//When user scrolls this bind the correct selection status
|
||||
holder.itemView.setActivated(isSelected(position));
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onItemMove(int fromPosition, int toPosition) {
|
||||
if (fromPosition < toPosition) {
|
||||
for (int i = fromPosition; i < toPosition; i++) {
|
||||
Collections.swap(mItems, i, i + 1);
|
||||
}
|
||||
} else {
|
||||
for (int i = fromPosition; i > toPosition; i--) {
|
||||
Collections.swap(mItems, i, i - 1);
|
||||
}
|
||||
}
|
||||
|
||||
activity.getPresenter().reorderCategories(mItems);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onItemDismiss(int position) {
|
||||
|
||||
}
|
||||
}
|
|
@ -1,58 +0,0 @@
|
|||
package eu.kanade.tachiyomi.ui.library.category;
|
||||
|
||||
import android.support.v4.view.MotionEventCompat;
|
||||
import android.view.MotionEvent;
|
||||
import android.view.View;
|
||||
import android.widget.ImageView;
|
||||
import android.widget.TextView;
|
||||
|
||||
import com.amulyakhare.textdrawable.TextDrawable;
|
||||
import com.amulyakhare.textdrawable.util.ColorGenerator;
|
||||
|
||||
import butterknife.Bind;
|
||||
import butterknife.ButterKnife;
|
||||
import butterknife.OnClick;
|
||||
import eu.kanade.tachiyomi.R;
|
||||
import eu.kanade.tachiyomi.data.database.models.Category;
|
||||
import eu.kanade.tachiyomi.ui.base.adapter.FlexibleViewHolder;
|
||||
import eu.kanade.tachiyomi.ui.base.adapter.OnStartDragListener;
|
||||
|
||||
public class CategoryHolder extends FlexibleViewHolder {
|
||||
|
||||
private View view;
|
||||
|
||||
@Bind(R.id.image) ImageView image;
|
||||
@Bind(R.id.title) TextView title;
|
||||
@Bind(R.id.reorder) ImageView reorder;
|
||||
|
||||
public CategoryHolder(View view, CategoryAdapter adapter,
|
||||
OnListItemClickListener listener, OnStartDragListener dragListener) {
|
||||
super(view, adapter, listener);
|
||||
ButterKnife.bind(this, view);
|
||||
this.view = view;
|
||||
|
||||
reorder.setOnTouchListener((v, event) -> {
|
||||
if (MotionEventCompat.getActionMasked(event) == MotionEvent.ACTION_DOWN) {
|
||||
dragListener.onStartDrag(this);
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
});
|
||||
}
|
||||
|
||||
public void onSetValues(Category category, ColorGenerator generator) {
|
||||
title.setText(category.name);
|
||||
image.setImageDrawable(getRound(category.name.substring(0, 1), generator));
|
||||
}
|
||||
|
||||
private TextDrawable getRound(String text, ColorGenerator generator) {
|
||||
return TextDrawable.builder().buildRound(text, generator.getColor(text));
|
||||
}
|
||||
|
||||
@OnClick(R.id.image)
|
||||
void onImageClick() {
|
||||
// Simulate long click on this view to enter selection mode
|
||||
onLongClick(view);
|
||||
}
|
||||
|
||||
}
|
|
@ -1,16 +0,0 @@
|
|||
package eu.kanade.tachiyomi.ui.library.category;
|
||||
|
||||
import eu.kanade.tachiyomi.ui.base.adapter.ItemTouchHelperAdapter;
|
||||
import eu.kanade.tachiyomi.ui.base.adapter.SimpleItemTouchHelperCallback;
|
||||
|
||||
public class CategoryItemTouchHelper extends SimpleItemTouchHelperCallback {
|
||||
|
||||
public CategoryItemTouchHelper(ItemTouchHelperAdapter adapter) {
|
||||
super(adapter);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isItemViewSwipeEnabled() {
|
||||
return false;
|
||||
}
|
||||
}
|
|
@ -1,68 +0,0 @@
|
|||
package eu.kanade.tachiyomi.ui.library.category;
|
||||
|
||||
import android.os.Bundle;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
import javax.inject.Inject;
|
||||
|
||||
import eu.kanade.tachiyomi.data.database.DatabaseHelper;
|
||||
import eu.kanade.tachiyomi.data.database.models.Category;
|
||||
import eu.kanade.tachiyomi.ui.base.presenter.BasePresenter;
|
||||
import rx.android.schedulers.AndroidSchedulers;
|
||||
|
||||
public class CategoryPresenter extends BasePresenter<CategoryActivity> {
|
||||
|
||||
@Inject DatabaseHelper db;
|
||||
|
||||
private List<Category> categories;
|
||||
|
||||
private static final int GET_CATEGORIES = 1;
|
||||
|
||||
@Override
|
||||
protected void onCreate(Bundle savedState) {
|
||||
super.onCreate(savedState);
|
||||
|
||||
restartableLatestCache(GET_CATEGORIES,
|
||||
() -> db.getCategories().asRxObservable()
|
||||
.doOnNext(categories -> this.categories = categories)
|
||||
.observeOn(AndroidSchedulers.mainThread()),
|
||||
CategoryActivity::setCategories);
|
||||
|
||||
start(GET_CATEGORIES);
|
||||
}
|
||||
|
||||
public void createCategory(String name) {
|
||||
Category cat = Category.create(name);
|
||||
|
||||
// Set the new item in the last position
|
||||
int max = 0;
|
||||
if (categories != null) {
|
||||
for (Category cat2 : categories) {
|
||||
if (cat2.order > max) {
|
||||
max = cat2.order + 1;
|
||||
}
|
||||
}
|
||||
}
|
||||
cat.order = max;
|
||||
|
||||
db.insertCategory(cat).asRxObservable().subscribe();
|
||||
}
|
||||
|
||||
public void deleteCategories(List<Category> categories) {
|
||||
db.deleteCategories(categories).asRxObservable().subscribe();
|
||||
}
|
||||
|
||||
public void reorderCategories(List<Category> categories) {
|
||||
for (int i = 0; i < categories.size(); i++) {
|
||||
categories.get(i).order = i;
|
||||
}
|
||||
|
||||
db.insertCategories(categories).asRxObservable().subscribe();
|
||||
}
|
||||
|
||||
public void renameCategory(Category category, String name) {
|
||||
category.name = name;
|
||||
db.insertCategory(category).asRxObservable().subscribe();
|
||||
}
|
||||
}
|
|
@ -0,0 +1,15 @@
|
|||
package eu.kanade.tachiyomi.util
|
||||
|
||||
import android.support.annotation.LayoutRes
|
||||
import android.view.LayoutInflater
|
||||
import android.view.View
|
||||
import android.view.ViewGroup
|
||||
|
||||
/**
|
||||
* Extension method to inflate a view directly from its parent.
|
||||
* @param layout the layout to inflate.
|
||||
* @param attachToRoot whether to attach the view to the root or not. Defaults to false.
|
||||
*/
|
||||
fun ViewGroup.inflate(@LayoutRes layout: Int, attachToRoot: Boolean = false): View {
|
||||
return LayoutInflater.from(context).inflate(layout, this, attachToRoot)
|
||||
}
|
BIN
app/src/main/res/drawable-hdpi/ic_action_reorder.png
Normal file
After Width: | Height: | Size: 327 B |
Before Width: | Height: | Size: 116 B |
Before Width: | Height: | Size: 148 B |
BIN
app/src/main/res/drawable-mdpi/ic_action_reorder.png
Normal file
After Width: | Height: | Size: 203 B |
Before Width: | Height: | Size: 89 B |
BIN
app/src/main/res/drawable-xhdpi/ic_action_reorder.png
Normal file
After Width: | Height: | Size: 368 B |
Before Width: | Height: | Size: 114 B |
BIN
app/src/main/res/drawable-xxhdpi/ic_action_reorder.png
Normal file
After Width: | Height: | Size: 671 B |
Before Width: | Height: | Size: 137 B |
BIN
app/src/main/res/drawable-xxxhdpi/ic_action_reorder.png
Normal file
After Width: | Height: | Size: 836 B |
Before Width: | Height: | Size: 174 B |
|
@ -4,6 +4,7 @@
|
|||
xmlns:app="http://schemas.android.com/apk/res-auto"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent"
|
||||
xmlns:tools="http://schemas.android.com/tools"
|
||||
android:gravity="center">
|
||||
|
||||
<include layout="@layout/toolbar"/>
|
||||
|
@ -12,9 +13,10 @@
|
|||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent"
|
||||
android:layout_marginTop="?attr/actionBarSize"
|
||||
android:id="@+id/categories_list"
|
||||
android:id="@+id/recycler"
|
||||
android:choiceMode="multipleChoice"
|
||||
android:listSelector="@color/list_choice_pressed_bg_light" />
|
||||
android:listSelector="@color/list_choice_pressed_bg_light"
|
||||
tools:listitem="@layout/item_edit_categories"/>
|
||||
|
||||
<android.support.design.widget.FloatingActionButton
|
||||
android:id="@+id/fab"
|
||||
|
@ -25,7 +27,7 @@
|
|||
android:scaleType="fitCenter"
|
||||
android:src="@drawable/ic_add_white_24dp"
|
||||
app:backgroundTint="@color/colorPrimary"
|
||||
app:layout_anchor="@id/categories_list"
|
||||
app:layout_anchor="@id/recycler"
|
||||
app:layout_anchorGravity="bottom|right|end"
|
||||
app:layout_behavior="eu.kanade.tachiyomi.ui.base.fab.FABAnimationUpDown"/>
|
||||
|
||||
|
|
|
@ -3,8 +3,8 @@
|
|||
xmlns:tools="http://schemas.android.com/tools"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="?android:attr/listPreferredItemHeightLarge"
|
||||
android:paddingTop="@dimen/margin_top"
|
||||
android:paddingBottom="@dimen/margin_bottom"
|
||||
android:paddingTop="8dp"
|
||||
android:paddingBottom="8dp"
|
||||
android:background="@drawable/selector_chapter_light">
|
||||
|
||||
<ImageView
|
||||
|
@ -14,7 +14,6 @@
|
|||
android:layout_alignParentLeft="true"
|
||||
android:layout_alignParentStart="true"
|
||||
android:layout_centerInParent="true"
|
||||
android:elevation="4dp"
|
||||
android:clickable="true"
|
||||
android:layout_marginLeft="@dimen/margin_left"
|
||||
android:layout_marginStart="@dimen/margin_left"
|
||||
|
@ -30,8 +29,10 @@
|
|||
android:layout_marginRight="@dimen/margin_right"
|
||||
android:layout_marginEnd="@dimen/margin_right"
|
||||
android:scaleType="center"
|
||||
android:layout_centerInParent="true"
|
||||
android:layout_alignParentRight="true"
|
||||
android:src="@drawable/ic_reorder_grey_600_24dp"/>
|
||||
android:layout_alignParentEnd="true"
|
||||
android:src="@drawable/ic_action_reorder"/>
|
||||
|
||||
<TextView
|
||||
android:id="@+id/title"
|
||||
|
|