mirror of
				https://github.com/mihonapp/mihon.git
				synced 2025-10-31 06:17:57 +01:00 
			
		
		
		
	- Rewrote Category to Kotlin
- Moved category to ui - Reworked Animation (smoother) - Updated TextDrawable
This commit is contained in:
		| @@ -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) | ||||
| } | ||||
		Reference in New Issue
	
	Block a user