Rename project

This commit is contained in:
inorichi
2016-01-15 15:18:19 +01:00
parent 1508bf42fb
commit 70f4c7fcc3
167 changed files with 647 additions and 654 deletions
@@ -0,0 +1,45 @@
package eu.kanade.tachiyomi.ui.library;
import android.support.v4.app.Fragment;
import android.support.v4.app.FragmentManager;
import java.util.List;
import eu.kanade.tachiyomi.data.database.models.Category;
import eu.kanade.tachiyomi.ui.base.adapter.SmartFragmentStatePagerAdapter;
public class LibraryAdapter extends SmartFragmentStatePagerAdapter {
protected List<Category> categories;
public LibraryAdapter(FragmentManager fm) {
super(fm);
}
@Override
public Fragment getItem(int position) {
return LibraryCategoryFragment.newInstance(position);
}
@Override
public int getCount() {
return categories == null ? 0 : categories.size();
}
@Override
public CharSequence getPageTitle(int position) {
return categories.get(position).name;
}
public void setCategories(List<Category> categories) {
this.categories = categories;
notifyDataSetChanged();
}
public void setSelectionMode(int mode) {
for (Fragment fragment : getRegisteredFragments()) {
((LibraryCategoryFragment) fragment).setMode(mode);
}
}
}
@@ -0,0 +1,109 @@
package eu.kanade.tachiyomi.ui.library;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.Filter;
import android.widget.Filterable;
import java.util.ArrayList;
import java.util.List;
import eu.davidea.flexibleadapter.FlexibleAdapter;
import eu.kanade.tachiyomi.R;
import eu.kanade.tachiyomi.data.database.models.Manga;
import rx.Observable;
public class LibraryCategoryAdapter extends FlexibleAdapter<LibraryHolder, Manga>
implements Filterable {
List<Manga> mangas;
Filter filter;
private LibraryCategoryFragment fragment;
public LibraryCategoryAdapter(LibraryCategoryFragment fragment) {
this.fragment = fragment;
mItems = new ArrayList<>();
filter = new LibraryFilter();
setHasStableIds(true);
}
public void setItems(List<Manga> list) {
mItems = list;
notifyDataSetChanged();
// TODO needed for filtering?
mangas = list;
}
public void clear() {
mItems.clear();
}
@Override
public long getItemId(int position) {
return mItems.get(position).id;
}
@Override
public void updateDataSet(String param) {
}
@Override
public LibraryHolder onCreateViewHolder(ViewGroup parent, int viewType) {
View v = LayoutInflater.from(fragment.getActivity()).inflate(R.layout.item_catalogue, parent, false);
return new LibraryHolder(v, this, fragment);
}
@Override
public void onBindViewHolder(LibraryHolder holder, int position) {
final LibraryPresenter presenter = ((LibraryFragment) fragment.getParentFragment()).getPresenter();
final Manga manga = getItem(position);
holder.onSetValues(manga, presenter);
//When user scrolls this bind the correct selection status
holder.itemView.setActivated(isSelected(position));
}
public int getCoverHeight() {
return fragment.recycler.getItemWidth() / 9 * 12;
}
@Override
public Filter getFilter() {
return filter;
}
private class LibraryFilter extends Filter {
@Override
protected FilterResults performFiltering(CharSequence charSequence) {
FilterResults results = new FilterResults();
String query = charSequence.toString().toLowerCase();
if (query.length() == 0) {
results.values = mangas;
results.count = mangas.size();
} else {
List<Manga> filteredMangas = Observable.from(mangas)
.filter(x ->
(x.title != null && x.title.toLowerCase().contains(query)) ||
(x.author != null && x.author.toLowerCase().contains(query)) ||
(x.artist != null && x.artist.toLowerCase().contains(query)))
.toList()
.toBlocking()
.single();
results.values = filteredMangas;
results.count = filteredMangas.size();
}
return results;
}
@Override
public void publishResults(CharSequence constraint, FilterResults results) {
setItems((List<Manga>) results.values);
}
}
}
@@ -0,0 +1,181 @@
package eu.kanade.tachiyomi.ui.library;
import android.content.Intent;
import android.content.res.Configuration;
import android.os.Bundle;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import com.f2prateek.rx.preferences.Preference;
import java.util.ArrayList;
import java.util.List;
import butterknife.Bind;
import butterknife.ButterKnife;
import eu.davidea.flexibleadapter.FlexibleAdapter;
import eu.kanade.tachiyomi.R;
import eu.kanade.tachiyomi.data.database.models.Category;
import eu.kanade.tachiyomi.data.database.models.Manga;
import eu.kanade.tachiyomi.event.LibraryMangasEvent;
import eu.kanade.tachiyomi.ui.base.adapter.FlexibleViewHolder;
import eu.kanade.tachiyomi.ui.base.fragment.BaseFragment;
import eu.kanade.tachiyomi.ui.manga.MangaActivity;
import eu.kanade.tachiyomi.util.EventBusHook;
import eu.kanade.tachiyomi.widget.AutofitRecyclerView;
import icepick.State;
import rx.Subscription;
public class LibraryCategoryFragment extends BaseFragment
implements FlexibleViewHolder.OnListItemClickListener {
@Bind(R.id.library_mangas) AutofitRecyclerView recycler;
@State int position;
private LibraryCategoryAdapter adapter;
private Subscription numColumnsSubscription;
public static LibraryCategoryFragment newInstance(int position) {
LibraryCategoryFragment fragment = new LibraryCategoryFragment();
fragment.position = position;
return fragment;
}
@Override
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedState) {
// Inflate the layout for this fragment
View view = inflater.inflate(R.layout.fragment_library_category, container, false);
ButterKnife.bind(this, view);
adapter = new LibraryCategoryAdapter(this);
recycler.setHasFixedSize(true);
recycler.setAdapter(adapter);
if (getLibraryFragment().getActionMode() != null) {
setMode(FlexibleAdapter.MODE_MULTI);
}
Preference<Integer> columnsPref = getResources().getConfiguration()
.orientation == Configuration.ORIENTATION_PORTRAIT ?
getLibraryPresenter().preferences.portraitColumns() :
getLibraryPresenter().preferences.landscapeColumns();
numColumnsSubscription = columnsPref.asObservable()
.doOnNext(recycler::setSpanCount)
.skip(1)
// Set again the adapter to recalculate the covers height
.subscribe(count -> recycler.setAdapter(adapter));
if (savedState != null) {
adapter.onRestoreInstanceState(savedState);
if (adapter.getMode() == FlexibleAdapter.MODE_SINGLE) {
adapter.clearSelection();
}
}
return view;
}
@Override
public void onDestroyView() {
numColumnsSubscription.unsubscribe();
super.onDestroyView();
}
@Override
public void onResume() {
super.onResume();
registerForStickyEvents();
}
@Override
public void onPause() {
unregisterForEvents();
super.onPause();
}
@Override
public void onSaveInstanceState(Bundle outState) {
adapter.onSaveInstanceState(outState);
super.onSaveInstanceState(outState);
}
@EventBusHook
public void onEventMainThread(LibraryMangasEvent event) {
List<Category> categories = getLibraryFragment().getAdapter().categories;
// When a category is deleted, the index can be greater than the number of categories
if (position >= categories.size())
return;
Category category = categories.get(position);
List<Manga> mangas = event.getMangasForCategory(category);
if (mangas == null) {
mangas = new ArrayList<>();
}
setMangas(mangas);
}
protected void openManga(Manga manga) {
getLibraryPresenter().onOpenManga(manga);
Intent intent = MangaActivity.newIntent(getActivity(), manga);
startActivity(intent);
}
public void setMangas(List<Manga> mangas) {
if (mangas != null) {
adapter.setItems(mangas);
} else {
adapter.clear();
}
}
@Override
public boolean onListItemClick(int position) {
if (getLibraryFragment().getActionMode() != null && position != -1) {
toggleSelection(position);
return true;
} else {
openManga(adapter.getItem(position));
return false;
}
}
@Override
public void onListItemLongClick(int position) {
getLibraryFragment().createActionModeIfNeeded();
toggleSelection(position);
}
private void toggleSelection(int position) {
LibraryFragment f = getLibraryFragment();
adapter.toggleSelection(position, false);
f.getPresenter().setSelection(adapter.getItem(position), adapter.isSelected(position));
int count = f.getPresenter().selectedMangas.size();
if (count == 0) {
f.destroyActionModeIfNeeded();
} else {
f.setContextTitle(count);
f.invalidateActionMode();
}
}
public void setMode(int mode) {
adapter.setMode(mode);
if (mode == FlexibleAdapter.MODE_SINGLE) {
adapter.clearSelection();
}
}
private LibraryFragment getLibraryFragment() {
return (LibraryFragment) getParentFragment();
}
private LibraryPresenter getLibraryPresenter() {
return getLibraryFragment().getPresenter();
}
}
@@ -0,0 +1,233 @@
package eu.kanade.tachiyomi.ui.library;
import android.content.Intent;
import android.os.Bundle;
import android.support.annotation.Nullable;
import android.support.design.widget.AppBarLayout;
import android.support.design.widget.TabLayout;
import android.support.v4.view.ViewPager;
import android.support.v7.view.ActionMode;
import android.view.LayoutInflater;
import android.view.Menu;
import android.view.MenuInflater;
import android.view.MenuItem;
import android.view.View;
import android.view.ViewGroup;
import com.afollestad.materialdialogs.MaterialDialog;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import butterknife.Bind;
import butterknife.ButterKnife;
import de.greenrobot.event.EventBus;
import eu.davidea.flexibleadapter.FlexibleAdapter;
import eu.kanade.tachiyomi.R;
import eu.kanade.tachiyomi.data.database.models.Category;
import eu.kanade.tachiyomi.data.database.models.Manga;
import eu.kanade.tachiyomi.data.sync.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.main.MainActivity;
import icepick.State;
import nucleus.factory.RequiresPresenter;
@RequiresPresenter(LibraryPresenter.class)
public class LibraryFragment extends BaseRxFragment<LibraryPresenter>
implements ActionMode.Callback {
@Bind(R.id.view_pager) ViewPager viewPager;
private TabLayout tabs;
private AppBarLayout appBar;
protected LibraryAdapter adapter;
private ActionMode actionMode;
@State int activeCategory;
public static LibraryFragment newInstance() {
return new LibraryFragment();
}
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setHasOptionsMenu(true);
}
@Override
public View onCreateView(LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState) {
// Inflate the layout for this fragment
View view = inflater.inflate(R.layout.fragment_library, container, false);
setToolbarTitle(getString(R.string.label_library));
ButterKnife.bind(this, view);
appBar = ((MainActivity) getActivity()).getAppBar();
tabs = (TabLayout) inflater.inflate(R.layout.library_tab_layout, appBar, false);
appBar.addView(tabs);
adapter = new LibraryAdapter(getChildFragmentManager());
viewPager.setAdapter(adapter);
tabs.setupWithViewPager(viewPager);
return view;
}
@Override
public void onDestroyView() {
appBar.removeView(tabs);
super.onDestroyView();
}
@Override
public void onPause() {
EventBus.getDefault().removeStickyEvent(LibraryMangasEvent.class);
super.onPause();
}
@Override
public void onSaveInstanceState(Bundle bundle) {
activeCategory = viewPager.getCurrentItem();
super.onSaveInstanceState(bundle);
}
@Override
public void onCreateOptionsMenu(Menu menu, MenuInflater inflater) {
inflater.inflate(R.menu.library, menu);
}
@Override
public boolean onOptionsItemSelected(MenuItem item) {
switch (item.getItemId()) {
case R.id.action_refresh:
LibraryUpdateService.start(getActivity());
return true;
case R.id.action_edit_categories:
onEditCategories();
return true;
}
return super.onOptionsItemSelected(item);
}
private void onEditCategories() {
Intent intent = CategoryActivity.newIntent(getActivity());
startActivity(intent);
}
public void onNextLibraryUpdate(List<Category> categories, Map<Integer, List<Manga>> mangas) {
boolean hasMangasInDefaultCategory = mangas.get(0) != null;
int activeCat = adapter.categories != null ? viewPager.getCurrentItem() : activeCategory;
if (hasMangasInDefaultCategory) {
setCategoriesWithDefault(categories);
} else {
setCategories(categories);
}
// Restore active category
viewPager.setCurrentItem(activeCat, false);
if (tabs.getTabCount() > 0) {
TabLayout.Tab tab = tabs.getTabAt(viewPager.getCurrentItem());
if (tab != null) tab.select();
}
// Send the mangas to child fragments after the adapter is updated
EventBus.getDefault().postSticky(new LibraryMangasEvent(mangas));
}
private void setCategoriesWithDefault(List<Category> categories) {
List<Category> categoriesWithDefault = new ArrayList<>();
categoriesWithDefault.add(Category.createDefault());
categoriesWithDefault.addAll(categories);
setCategories(categoriesWithDefault);
}
private void setCategories(List<Category> categories) {
adapter.setCategories(categories);
tabs.setTabsFromPagerAdapter(adapter);
tabs.setVisibility(categories.size() <= 1 ? View.GONE : View.VISIBLE);
}
public 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.library_selection, menu);
adapter.setSelectionMode(FlexibleAdapter.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_move_to_category:
moveMangasToCategories(getPresenter().selectedMangas);
return true;
case R.id.action_delete:
getPresenter().deleteMangas();
destroyActionModeIfNeeded();
return true;
}
return false;
}
@Override
public void onDestroyActionMode(ActionMode mode) {
adapter.setSelectionMode(FlexibleAdapter.MODE_SINGLE);
getPresenter().selectedMangas.clear();
actionMode = null;
}
public void destroyActionModeIfNeeded() {
if (actionMode != null) {
actionMode.finish();
}
}
private void moveMangasToCategories(List<Manga> mangas) {
new MaterialDialog.Builder(getActivity())
.title(R.string.action_move_category)
.items(getPresenter().getCategoriesNames())
.itemsCallbackMultiChoice(null, (dialog, which, text) -> {
getPresenter().moveMangasToCategories(which, mangas);
destroyActionModeIfNeeded();
return true;
})
.positiveText(R.string.button_ok)
.negativeText(R.string.button_cancel)
.show();
}
@Nullable
public ActionMode getActionMode() {
return actionMode;
}
public LibraryAdapter getAdapter() {
return adapter;
}
public void createActionModeIfNeeded() {
if (actionMode == null) {
actionMode = getBaseActivity().startSupportActionMode(this);
}
}
public void invalidateActionMode() {
actionMode.invalidate();
}
}
@@ -0,0 +1,51 @@
package eu.kanade.tachiyomi.ui.library;
import android.view.View;
import android.widget.ImageView;
import android.widget.TextView;
import butterknife.Bind;
import butterknife.ButterKnife;
import eu.kanade.tachiyomi.R;
import eu.kanade.tachiyomi.data.cache.CoverCache;
import eu.kanade.tachiyomi.data.database.models.Manga;
import eu.kanade.tachiyomi.data.source.base.Source;
import eu.kanade.tachiyomi.ui.base.adapter.FlexibleViewHolder;
import static android.view.ViewGroup.LayoutParams.MATCH_PARENT;
import static android.widget.RelativeLayout.LayoutParams;
public class LibraryHolder extends FlexibleViewHolder {
@Bind(R.id.thumbnail) ImageView thumbnail;
@Bind(R.id.title) TextView title;
@Bind(R.id.unreadText) TextView unreadText;
public LibraryHolder(View view, LibraryCategoryAdapter adapter, OnListItemClickListener listener) {
super(view, adapter, listener);
ButterKnife.bind(this, view);
thumbnail.setLayoutParams(new LayoutParams(MATCH_PARENT, adapter.getCoverHeight()));
}
public void onSetValues(Manga manga, LibraryPresenter presenter) {
title.setText(manga.title);
if (manga.unread > 0) {
unreadText.setVisibility(View.VISIBLE);
unreadText.setText(Integer.toString(manga.unread));
} else {
unreadText.setVisibility(View.GONE);
}
loadCover(manga, presenter.sourceManager.get(manga.source), presenter.coverCache);
}
private void loadCover(Manga manga, Source source, CoverCache coverCache) {
if (manga.thumbnail_url != null) {
coverCache.saveAndLoadFromCache(thumbnail, manga.thumbnail_url, source.getGlideHeaders());
} else {
thumbnail.setImageResource(android.R.color.transparent);
}
}
}
@@ -0,0 +1,138 @@
package eu.kanade.tachiyomi.ui.library;
import android.os.Bundle;
import android.util.Pair;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import javax.inject.Inject;
import de.greenrobot.event.EventBus;
import eu.kanade.tachiyomi.data.cache.CoverCache;
import eu.kanade.tachiyomi.data.database.DatabaseHelper;
import eu.kanade.tachiyomi.data.database.models.Category;
import eu.kanade.tachiyomi.data.database.models.Manga;
import eu.kanade.tachiyomi.data.database.models.MangaCategory;
import eu.kanade.tachiyomi.data.preference.PreferencesHelper;
import eu.kanade.tachiyomi.data.source.SourceManager;
import eu.kanade.tachiyomi.event.LibraryMangasEvent;
import eu.kanade.tachiyomi.ui.base.presenter.BasePresenter;
import rx.Observable;
import rx.android.schedulers.AndroidSchedulers;
public class LibraryPresenter extends BasePresenter<LibraryFragment> {
@Inject DatabaseHelper db;
@Inject PreferencesHelper preferences;
@Inject CoverCache coverCache;
@Inject SourceManager sourceManager;
protected List<Category> categories;
protected List<Manga> selectedMangas;
private static final int GET_LIBRARY = 1;
@Override
protected void onCreate(Bundle savedState) {
super.onCreate(savedState);
selectedMangas = new ArrayList<>();
restartableLatestCache(GET_LIBRARY,
this::getLibraryObservable,
(view, pair) -> view.onNextLibraryUpdate(pair.first, pair.second));
if (savedState == null) {
start(GET_LIBRARY);
}
}
@Override
protected void onDestroy() {
EventBus.getDefault().removeStickyEvent(LibraryMangasEvent.class);
super.onDestroy();
}
@Override
protected void onTakeView(LibraryFragment libraryFragment) {
super.onTakeView(libraryFragment);
if (!isSubscribed(GET_LIBRARY)) {
start(GET_LIBRARY);
}
}
private Observable<Pair<List<Category>, Map<Integer, List<Manga>>>> getLibraryObservable() {
return Observable.combineLatest(getCategoriesObservable(), getLibraryMangasObservable(),
Pair::create)
.observeOn(AndroidSchedulers.mainThread());
}
private Observable<List<Category>> getCategoriesObservable() {
return db.getCategories().createObservable()
.doOnNext(categories -> this.categories = categories);
}
private Observable<Map<Integer, List<Manga>>> getLibraryMangasObservable() {
return db.getLibraryMangas().createObservable()
.flatMap(mangas -> Observable.from(mangas)
.groupBy(manga -> manga.category)
.flatMap(group -> group.toList()
.map(list -> Pair.create(group.getKey(), list)))
.toMap(pair -> pair.first, pair -> pair.second));
}
public void onOpenManga(Manga manga) {
// Avoid further db updates for the library when it's not needed
stop(GET_LIBRARY);
}
public void setSelection(Manga manga, boolean selected) {
if (selected) {
selectedMangas.add(manga);
} else {
selectedMangas.remove(manga);
}
}
public String[] getCategoriesNames() {
int count = categories.size();
String[] names = new String[count];
for (int i = 0; i < count; i++) {
names[i] = categories.get(i).name;
}
return names;
}
public void deleteMangas() {
for (Manga manga : selectedMangas) {
manga.favorite = false;
}
db.insertMangas(selectedMangas).executeAsBlocking();
}
public void moveMangasToCategories(Integer[] positions, List<Manga> mangas) {
List<Category> categoriesToAdd = new ArrayList<>();
for (Integer index : positions) {
categoriesToAdd.add(categories.get(index));
}
moveMangasToCategories(categoriesToAdd, mangas);
}
public void moveMangasToCategories(List<Category> categories, List<Manga> mangas) {
List<MangaCategory> mc = new ArrayList<>();
for (Manga manga : mangas) {
for (Category cat : categories) {
mc.add(MangaCategory.create(manga, cat));
}
}
db.setMangaCategories(mc, mangas);
}
}
@@ -0,0 +1,180 @@
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);
}
}
@@ -0,0 +1,80 @@
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) {
}
}
@@ -0,0 +1,58 @@
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);
}
}
@@ -0,0 +1,16 @@
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;
}
}
@@ -0,0 +1,68 @@
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().createObservable()
.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).createObservable().subscribe();
}
public void deleteCategories(List<Category> categories) {
db.deleteCategories(categories).createObservable().subscribe();
}
public void reorderCategories(List<Category> categories) {
for (int i = 0; i < categories.size(); i++) {
categories.get(i).order = i;
}
db.insertCategories(categories).createObservable().subscribe();
}
public void renameCategory(Category category, String name) {
category.name = name;
db.insertCategory(category).createObservable().subscribe();
}
}