UI improvement Phase 1

- Simplified theme/style settings and corrected UI styles
- Move «Add To Library» button from toolbar to be simple  to find/press
it
- Toolbar in chapter list with sort/filtration
- library/catalog layout fixes
This commit is contained in:
Yuri Revich
2015-11-24 20:45:53 +03:00
parent 85dcfd2beb
commit 18130e931f
34 changed files with 585 additions and 400 deletions

View File

@ -156,14 +156,23 @@ public class DatabaseHelper {
.prepare();
}
public PreparedGetListOfObjects<Chapter> getChapters(long manga_id) {
public PreparedGetListOfObjects<Chapter> getChapters(long manga_id, boolean sortAToZ, boolean onlyUnread) {
Query.CompleteBuilder query = Query.builder()
.table(ChaptersTable.TABLE)
.orderBy(ChaptersTable.COLUMN_CHAPTER_NUMBER + (sortAToZ ? " ASC" : " DESC"));
if (onlyUnread) {
query = query.where(ChaptersTable.COLUMN_MANGA_ID + "=? AND " + ChaptersTable.COLUMN_READ + "=?")
.whereArgs(manga_id, 0);
} else {
query = query.where(ChaptersTable.COLUMN_MANGA_ID + "=?")
.whereArgs(manga_id);
}
return db.get()
.listOfObjects(Chapter.class)
.withQuery(Query.builder()
.table(ChaptersTable.TABLE)
.where(ChaptersTable.COLUMN_MANGA_ID + "=?")
.whereArgs(manga_id)
.build())
.withQuery(query.build())
.prepare();
}
@ -236,7 +245,7 @@ public class DatabaseHelper {
.filter(c -> !chapters.contains(c))
.toList()
.flatMap(deletedChapters -> deleteChapters(deletedChapters).createObservable())
.map( d -> d.results().size() ));
.map(d -> d.results().size()));
return Observable.zip(newChaptersObs, deletedChaptersObs,
(insertions, deletions) -> new PostResult(0, insertions, deletions)

View File

@ -7,6 +7,8 @@ import android.widget.TextView;
import com.bumptech.glide.Glide;
import com.bumptech.glide.load.engine.DiskCacheStrategy;
import java.util.Objects;
import eu.kanade.mangafeed.R;
import eu.kanade.mangafeed.data.database.models.Manga;
import uk.co.ribot.easyadapter.ItemViewHolder;
@ -17,11 +19,14 @@ import uk.co.ribot.easyadapter.annotations.ViewId;
@LayoutId(R.layout.item_catalogue)
public class CatalogueHolder extends ItemViewHolder<Manga> {
@ViewId(R.id.catalogue_title)
@ViewId(R.id.title)
TextView title;
@ViewId(R.id.catalogue_thumbnail)
ImageView image;
@ViewId(R.id.author)
TextView author;
@ViewId(R.id.thumbnail)
ImageView thumbnail;
public CatalogueHolder(View view) {
super(view);
@ -30,15 +35,16 @@ public class CatalogueHolder extends ItemViewHolder<Manga> {
@Override
public void onSetValues(Manga manga, PositionInfo positionInfo) {
title.setText(manga.title);
author.setText(manga.author);
if (manga.thumbnail_url != null) {
Glide.with(getContext())
.load(manga.thumbnail_url)
.diskCacheStrategy(DiskCacheStrategy.RESULT)
.centerCrop()
.into(image);
.into(thumbnail);
} else {
image.setImageResource(android.R.color.transparent);
thumbnail.setImageResource(android.R.color.transparent);
}
}
}

View File

@ -0,0 +1,77 @@
package eu.kanade.mangafeed.ui.decoration;
import android.content.Context;
import android.content.res.TypedArray;
import android.util.AttributeSet;
import android.support.v7.widget.RecyclerView;
import android.support.v7.widget.LinearLayoutManager;
import android.view.View;
import android.graphics.Rect;
import android.graphics.drawable.Drawable;
import android.graphics.Canvas;
public class DividerItemDecoration extends RecyclerView.ItemDecoration {
private Drawable mDivider;
public DividerItemDecoration(Context context, AttributeSet attrs) {
final TypedArray a = context.obtainStyledAttributes(attrs, new int [] { android.R.attr.listDivider });
mDivider = a.getDrawable(0);
a.recycle();
}
public DividerItemDecoration(Drawable divider) { mDivider = divider; }
@Override
public void getItemOffsets (Rect outRect, View view, RecyclerView parent, RecyclerView.State state) {
super.getItemOffsets(outRect, view, parent, state);
if (mDivider == null) return;
if (parent.getChildPosition(view) < 1) return;
if (getOrientation(parent) == LinearLayoutManager.VERTICAL) outRect.top = mDivider.getIntrinsicHeight();
else outRect.left = mDivider.getIntrinsicWidth();
}
@Override
public void onDrawOver(Canvas c, RecyclerView parent) {
if (mDivider == null) { super.onDrawOver(c, parent); return; }
if (getOrientation(parent) == LinearLayoutManager.VERTICAL) {
final int left = parent.getPaddingLeft();
final int right = parent.getWidth() - parent.getPaddingRight();
final int childCount = parent.getChildCount();
for (int i=1; i < childCount; i++) {
final View child = parent.getChildAt(i);
final RecyclerView.LayoutParams params = (RecyclerView.LayoutParams) child.getLayoutParams();
final int size = mDivider.getIntrinsicHeight();
final int top = child.getTop() - params.topMargin;
final int bottom = top + size;
mDivider.setBounds(left, top, right, bottom);
mDivider.draw(c);
}
} else { //horizontal
final int top = parent.getPaddingTop();
final int bottom = parent.getHeight() - parent.getPaddingBottom();
final int childCount = parent.getChildCount();
for (int i=1; i < childCount; i++) {
final View child = parent.getChildAt(i);
final RecyclerView.LayoutParams params = (RecyclerView.LayoutParams) child.getLayoutParams();
final int size = mDivider.getIntrinsicWidth();
final int left = child.getLeft() - params.leftMargin;
final int right = left + size;
mDivider.setBounds(left, top, right, bottom);
mDivider.draw(c);
}
}
}
private int getOrientation(RecyclerView parent) {
if (parent.getLayoutManager() instanceof LinearLayoutManager) {
LinearLayoutManager layoutManager = (LinearLayoutManager) parent.getLayoutManager();
return layoutManager.getOrientation();
} else throw new IllegalStateException("DividerItemDecoration can only be used with a LinearLayoutManager.");
}
}

View File

@ -7,6 +7,8 @@ import android.widget.TextView;
import com.bumptech.glide.Glide;
import com.bumptech.glide.load.engine.DiskCacheStrategy;
import java.util.Objects;
import eu.kanade.mangafeed.R;
import eu.kanade.mangafeed.data.database.models.Manga;
import uk.co.ribot.easyadapter.ItemViewHolder;
@ -15,30 +17,35 @@ import uk.co.ribot.easyadapter.annotations.LayoutId;
import uk.co.ribot.easyadapter.annotations.ViewId;
@LayoutId(R.layout.item_library)
@LayoutId(R.layout.item_catalogue)
public class LibraryHolder extends ItemViewHolder<Manga> {
@ViewId(R.id.thumbnailImage)
ImageView mThumbImage;
@ViewId(R.id.thumbnail)
ImageView thumbnail;
@ViewId(R.id.titleText)
TextView mTitleText;
@ViewId(R.id.title)
TextView title;
@ViewId(R.id.author)
TextView author;
@ViewId(R.id.unreadText)
TextView mUnreadText;
TextView unreadText;
public LibraryHolder(View view) {
super(view);
}
public void onSetValues(Manga manga, PositionInfo positionInfo) {
mTitleText.setText(manga.title);
title.setText(manga.title);
author.setText(manga.author);
if (manga.unread > 0) {
mUnreadText.setVisibility(View.VISIBLE);
mUnreadText.setText(Integer.toString(manga.unread));
unreadText.setVisibility(View.VISIBLE);
unreadText.setText(Integer.toString(manga.unread));
}
else {
mUnreadText.setVisibility(View.GONE);
unreadText.setVisibility(View.GONE);
}
String thumbnail;
@ -51,7 +58,7 @@ public class LibraryHolder extends ItemViewHolder<Manga> {
.load(thumbnail)
.diskCacheStrategy(DiskCacheStrategy.RESULT)
.centerCrop()
.into(mThumbImage);
.into(this.thumbnail);
}
}

View File

@ -2,16 +2,19 @@ package eu.kanade.mangafeed.ui.manga.chapter;
import android.content.Intent;
import android.os.Bundle;
import android.support.v4.content.ContextCompat;
import android.support.v4.widget.SwipeRefreshLayout;
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.view.LayoutInflater;
import android.view.Menu;
import android.view.MenuInflater;
import android.view.MenuItem;
import android.view.View;
import android.view.ViewGroup;
import android.widget.CheckBox;
import java.util.List;
@ -20,6 +23,7 @@ import butterknife.ButterKnife;
import eu.kanade.mangafeed.R;
import eu.kanade.mangafeed.data.database.models.Chapter;
import eu.kanade.mangafeed.data.download.DownloadService;
import eu.kanade.mangafeed.ui.decoration.DividerItemDecoration;
import eu.kanade.mangafeed.ui.manga.MangaActivity;
import eu.kanade.mangafeed.ui.reader.ReaderActivity;
import eu.kanade.mangafeed.ui.base.activity.BaseActivity;
@ -31,8 +35,15 @@ import rx.Observable;
public class ChaptersFragment extends BaseRxFragment<ChaptersPresenter> implements
ActionMode.Callback, ChaptersAdapter.OnItemClickListener {
@Bind(R.id.chapter_list) RecyclerView chapters;
@Bind(R.id.swipe_refresh) SwipeRefreshLayout swipeRefresh;
@Bind(R.id.chapter_list)
RecyclerView chapters;
@Bind(R.id.swipe_refresh)
SwipeRefreshLayout swipeRefresh;
Toolbar toolbarBottom;
private MenuItem sortUpBtn;
private MenuItem sortDownBtn;
private CheckBox readCb;
private ChaptersAdapter adapter;
@ -56,9 +67,26 @@ public class ChaptersFragment extends BaseRxFragment<ChaptersPresenter> implemen
ButterKnife.bind(this, view);
chapters.setLayoutManager(new LinearLayoutManager(getActivity()));
chapters.addItemDecoration(new DividerItemDecoration(ContextCompat.getDrawable(this.getContext(), R.drawable.line_divider)));
createAdapter();
setSwipeRefreshListener();
toolbarBottom = (Toolbar) view.findViewById(R.id.toolbar_bottom);
toolbarBottom.inflateMenu(R.menu.chapter_filter);
sortUpBtn = toolbarBottom.getMenu().findItem(R.id.action_sort_up);
sortDownBtn = toolbarBottom.getMenu().findItem(R.id.action_sort_down);
readCb = (CheckBox) toolbarBottom.findViewById(R.id.action_show_unread);
readCb.setOnCheckedChangeListener((arg, isCheked) -> getPresenter().setReadFilter(isCheked));
toolbarBottom.setOnMenuItemClickListener(arg0 -> {
switch (arg0.getItemId()) {
case R.id.action_sort_up:
case R.id.action_sort_down:
getPresenter().revertSortOrder();
return true;
}
return false;
});
return view;
}
@ -66,6 +94,9 @@ public class ChaptersFragment extends BaseRxFragment<ChaptersPresenter> implemen
public void onCreateOptionsMenu(Menu menu, MenuInflater inflater) {
inflater.inflate(R.menu.chapters, menu);
super.onCreateOptionsMenu(menu, inflater);
getPresenter().initSortIcon();
getPresenter().initReadCb();
}
@Override
@ -101,7 +132,7 @@ public class ChaptersFragment extends BaseRxFragment<ChaptersPresenter> implemen
}
public boolean isOnlineManga() {
return ((MangaActivity)getActivity()).isOnlineManga();
return ((MangaActivity) getActivity()).isOnlineManga();
}
@Override
@ -174,7 +205,7 @@ public class ChaptersFragment extends BaseRxFragment<ChaptersPresenter> implemen
@Override
public void onListItemLongClick(int position) {
if (actionMode == null)
actionMode = ((BaseActivity)getActivity()).startSupportActionMode(this);
actionMode = ((BaseActivity) getActivity()).startSupportActionMode(this);
toggleSelection(position);
}
@ -196,4 +227,12 @@ public class ChaptersFragment extends BaseRxFragment<ChaptersPresenter> implemen
actionMode.setTitle(getString(R.string.selected_chapters_title, count));
}
public void setSortIcon(boolean aToZ) {
if (sortUpBtn != null) sortUpBtn.setVisible(aToZ);
if (sortDownBtn != null) sortDownBtn.setVisible(!aToZ);
}
public void setReadFilter(boolean onlyUnread) {
if (readCb != null) readCb.setChecked(onlyUnread);
}
}

View File

@ -1,13 +1,19 @@
package eu.kanade.mangafeed.ui.manga.chapter;
import android.content.Context;
import android.graphics.Color;
import android.support.v4.content.ContextCompat;
import android.support.v7.widget.RecyclerView;
import android.view.View;
import android.widget.ImageView;
import android.widget.TextView;
import java.text.DateFormat;
import java.text.DecimalFormat;
import java.text.DecimalFormatSymbols;
import java.text.NumberFormat;
import java.text.SimpleDateFormat;
import java.util.Date;
import butterknife.Bind;
import butterknife.ButterKnife;
import eu.kanade.mangafeed.R;
@ -21,6 +27,9 @@ public class ChaptersHolder extends RecyclerView.ViewHolder implements
@Bind(R.id.chapter_title) TextView title;
@Bind(R.id.chapter_download_image) ImageView download_icon;
@Bind(R.id.chapter_pages) TextView pages;
@Bind(R.id.chapter_date) TextView date;
SimpleDateFormat sdf = new SimpleDateFormat("dd/MM/yyyy");
public ChaptersHolder(View view) {
super(view);
@ -38,11 +47,10 @@ public class ChaptersHolder extends RecyclerView.ViewHolder implements
public void onSetValues(Context context, Chapter chapter) {
title.setText(chapter.name);
if (chapter.read) {
title.setTextColor(ContextCompat.getColor(context, R.color.chapter_read_text));
title.setTextColor(ContextCompat.getColor(context, R.color.hint_text));
} else {
title.setTextColor(Color.BLACK);
title.setTextColor(ContextCompat.getColor(context, R.color.primary_text));
}
if (chapter.last_page_read > 0 && !chapter.read) {
@ -59,6 +67,7 @@ public class ChaptersHolder extends RecyclerView.ViewHolder implements
else if (chapter.downloaded == Chapter.NOT_DOWNLOADED)
download_icon.setImageResource(R.drawable.ic_file_download_black_36dp);
date.setText(sdf.format(new Date(chapter.date_fetch)));
toggleActivation();
}

View File

@ -1,6 +1,7 @@
package eu.kanade.mangafeed.ui.manga.chapter;
import android.os.Bundle;
import android.support.v7.widget.RecyclerView;
import java.io.File;
import java.util.List;
@ -29,13 +30,19 @@ import rx.schedulers.Schedulers;
public class ChaptersPresenter extends BasePresenter<ChaptersFragment> {
@Inject DatabaseHelper db;
@Inject SourceManager sourceManager;
@Inject PreferencesHelper preferences;
@Inject DownloadManager downloadManager;
@Inject
DatabaseHelper db;
@Inject
SourceManager sourceManager;
@Inject
PreferencesHelper preferences;
@Inject
DownloadManager downloadManager;
private Manga manga;
private Source source;
private boolean sortOrderAToZ = true;
private boolean onlyUnread = true;
private static final int DB_CHAPTERS = 1;
private static final int ONLINE_CHAPTERS = 2;
@ -52,7 +59,7 @@ public class ChaptersPresenter extends BasePresenter<ChaptersFragment> {
this::getDbChaptersObs,
(view, chapters) -> {
view.onNextChapters(chapters);
EventBus.getDefault().postSticky( new ChapterCountEvent(chapters.size()) );
EventBus.getDefault().postSticky(new ChapterCountEvent(chapters.size()));
}
);
@ -102,7 +109,7 @@ public class ChaptersPresenter extends BasePresenter<ChaptersFragment> {
}
private Observable<List<Chapter>> getDbChaptersObs() {
return db.getChapters(manga.id).createObservable()
return db.getChapters(manga.id, sortOrderAToZ, onlyUnread).createObservable()
.subscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread());
}
@ -146,7 +153,7 @@ public class ChaptersPresenter extends BasePresenter<ChaptersFragment> {
public void deleteChapters(Observable<Chapter> selectedChapters) {
deleteSubscription = selectedChapters
.doOnCompleted( () -> remove(deleteSubscription) )
.doOnCompleted(() -> remove(deleteSubscription))
.subscribe(chapter -> {
downloadManager.deleteChapter(source, manga, chapter);
chapter.downloaded = Chapter.NOT_DOWNLOADED;
@ -164,4 +171,27 @@ public class ChaptersPresenter extends BasePresenter<ChaptersFragment> {
}
}
public void initSortIcon() {
if (getView() != null) {
getView().setSortIcon(sortOrderAToZ);//TODO do we need save order for manga?
}
}
public void initReadCb(){
if (getView() != null) {
getView().setReadFilter(onlyUnread);//TODO do we need save filter for manga?
}
}
public void revertSortOrder() {
sortOrderAToZ = !sortOrderAToZ;
initSortIcon();
start(DB_CHAPTERS);
}
public void setReadFilter(boolean onlyUnread) {
this.onlyUnread = onlyUnread;
initReadCb();
start(DB_CHAPTERS);
}
}

View File

@ -1,12 +1,15 @@
package eu.kanade.mangafeed.ui.manga.info;
import android.os.Bundle;
import android.support.v7.widget.Toolbar;
import android.view.LayoutInflater;
import android.view.Menu;
import android.view.MenuInflater;
import android.view.MenuItem;
import android.view.MotionEvent;
import android.view.View;
import android.view.ViewGroup;
import android.widget.Button;
import android.widget.ImageView;
import android.widget.TextView;
@ -23,16 +26,24 @@ import nucleus.factory.RequiresPresenter;
@RequiresPresenter(MangaInfoPresenter.class)
public class MangaInfoFragment extends BaseRxFragment<MangaInfoPresenter> {
@Bind(R.id.manga_artist) TextView mArtist;
@Bind(R.id.manga_author) TextView mAuthor;
@Bind(R.id.manga_chapters) TextView mChapters;
@Bind(R.id.manga_genres) TextView mGenres;
@Bind(R.id.manga_status) TextView mStatus;
@Bind(R.id.manga_summary) TextView mDescription;
@Bind(R.id.manga_cover) ImageView mCover;
@Bind(R.id.manga_artist)
TextView mArtist;
@Bind(R.id.manga_author)
TextView mAuthor;
@Bind(R.id.manga_chapters)
TextView mChapters;
@Bind(R.id.manga_genres)
TextView mGenres;
@Bind(R.id.manga_status)
TextView mStatus;
@Bind(R.id.manga_summary)
TextView mDescription;
@Bind(R.id.manga_cover)
ImageView mCover;
@Bind(R.id.action_favorite)
Button favoriteBtn;
private MenuItem favoriteBtn;
private MenuItem removeFavoriteBtn;
public static MangaInfoFragment newInstance() {
return new MangaInfoFragment();
@ -50,27 +61,27 @@ public class MangaInfoFragment extends BaseRxFragment<MangaInfoPresenter> {
// Inflate the layout for this fragment
View view = inflater.inflate(R.layout.fragment_manga_info, container, false);
ButterKnife.bind(this, view);
favoriteBtn.setOnTouchListener((v, event) -> {
if (event.getAction() == MotionEvent.ACTION_DOWN) {
getPresenter().toggleFavorite();
return true;
}
return false;
});
getPresenter().initFavoriteText();
return view;
}
@Override
public void onCreateOptionsMenu(Menu menu, MenuInflater inflater) {
inflater.inflate(R.menu.manga_info, menu);
favoriteBtn = menu.findItem(R.id.action_favorite);
removeFavoriteBtn = menu.findItem(R.id.action_remove_favorite);
getPresenter().initFavoriteIcon();
super.onCreateOptionsMenu(menu, inflater);
}
@Override
public boolean onOptionsItemSelected(MenuItem item) {
switch (item.getItemId()) {
case R.id.action_favorite:
case R.id.action_remove_favorite:
getPresenter().toggleFavorite();
break;
}
return super.onOptionsItemSelected(item);
}
@ -81,7 +92,7 @@ public class MangaInfoFragment extends BaseRxFragment<MangaInfoPresenter> {
mStatus.setText("Ongoing"); //TODO
mDescription.setText(manga.description);
setFavoriteIcon(manga.favorite);
setFavoriteText(manga.favorite);
Glide.with(getActivity())
.load(manga.thumbnail_url)
@ -94,9 +105,8 @@ public class MangaInfoFragment extends BaseRxFragment<MangaInfoPresenter> {
mChapters.setText(String.valueOf(count));
}
public void setFavoriteIcon(boolean isFavorite) {
if (favoriteBtn != null) favoriteBtn.setVisible(!isFavorite);
if (removeFavoriteBtn != null) removeFavoriteBtn.setVisible(isFavorite);
public void setFavoriteText(boolean isFavorite) {
favoriteBtn.setText(!isFavorite ? R.string.add_to_library : R.string.remove_from_library);
}
}

View File

@ -13,7 +13,8 @@ import rx.Observable;
public class MangaInfoPresenter extends BasePresenter<MangaInfoFragment> {
@Inject DatabaseHelper db;
@Inject
DatabaseHelper db;
private Manga manga;
private int count = -1;
@ -60,9 +61,9 @@ public class MangaInfoPresenter extends BasePresenter<MangaInfoFragment> {
}
}
public void initFavoriteIcon() {
public void initFavoriteText() {
if (getView() != null)
getView().setFavoriteIcon(manga.favorite);
getView().setFavoriteText(manga.favorite);
}
public void toggleFavorite() {