diff --git a/app/src/main/java/com/hippo/ehviewer/Constants.java b/app/src/main/java/com/hippo/ehviewer/Constants.java index 41a9ebc38..679fe56fa 100644 --- a/app/src/main/java/com/hippo/ehviewer/Constants.java +++ b/app/src/main/java/com/hippo/ehviewer/Constants.java @@ -31,7 +31,8 @@ public final class Constants { public static final int MESSENGER_ID_UPDATE_QUICK_SEARCH; public static final int MESSENGER_ID_UPDATE_QUICK_SEARCH_NAME; public static final int MESSENGER_ID_UPDATE_DOWNLOAD; - public static final int MESSENGER_ID_UPDATE_DOWNLOAD_TAG; + public static final int MESSENGER_ID_MODIFY_DOWNLOAD_LABEL_FROM_MANAGER; + public static final int MESSENGER_ID_MODIFY_DOWNLOAD_LABEL_FROM_SCENE; static { Messenger messenger = Messenger.getInstance(); @@ -46,6 +47,7 @@ public final class Constants { MESSENGER_ID_UPDATE_QUICK_SEARCH = messenger.newId(); MESSENGER_ID_UPDATE_QUICK_SEARCH_NAME = messenger.newId(); MESSENGER_ID_UPDATE_DOWNLOAD = messenger.newId(); - MESSENGER_ID_UPDATE_DOWNLOAD_TAG = messenger.newId(); + MESSENGER_ID_MODIFY_DOWNLOAD_LABEL_FROM_MANAGER = messenger.newId(); + MESSENGER_ID_MODIFY_DOWNLOAD_LABEL_FROM_SCENE = messenger.newId(); } } diff --git a/app/src/main/java/com/hippo/ehviewer/client/data/DownloadInfo.java b/app/src/main/java/com/hippo/ehviewer/client/data/DownloadInfo.java index 5e34666a9..c9e031815 100644 --- a/app/src/main/java/com/hippo/ehviewer/client/data/DownloadInfo.java +++ b/app/src/main/java/com/hippo/ehviewer/client/data/DownloadInfo.java @@ -26,7 +26,7 @@ public class DownloadInfo { public static final int STATE_DOWNLOAD = 0x2; public static final int STATE_FINISH = 0x3; - public String tag; + public String label; public GalleryBase galleryBase; public int state = STATE_NONE; // byte/second, -1 for can't get speed now @@ -38,14 +38,17 @@ public class DownloadInfo { // Integer.MAX_VALUE for unknown public int legacy = Integer.MAX_VALUE; + public long time; + public static DownloadInfo fromDownloadInfoObj(DownloadInfoObj obj) { GalleryBase galleryBase = DBUtils.getGalleryBase((int) (long) obj.getGid()); if (galleryBase != null) { DownloadInfo downloadInfo = new DownloadInfo(); downloadInfo.galleryBase = galleryBase; - downloadInfo.tag = obj.getTag(); + downloadInfo.label = obj.getLabel(); downloadInfo.state = obj.getState(); downloadInfo.legacy = obj.getLegacy(); + downloadInfo.time = obj.getTime(); return downloadInfo; } else { return null; diff --git a/app/src/main/java/com/hippo/ehviewer/client/data/DownloadLabel.java b/app/src/main/java/com/hippo/ehviewer/client/data/DownloadLabel.java new file mode 100644 index 000000000..7ba16c4a5 --- /dev/null +++ b/app/src/main/java/com/hippo/ehviewer/client/data/DownloadLabel.java @@ -0,0 +1,33 @@ +/* + * Copyright 2015 Hippo Seven + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.hippo.ehviewer.client.data; + +public class DownloadLabel implements Cloneable { + + public long id; + public String label; + public long time; + + @Override + public DownloadLabel clone() { + try { + return (DownloadLabel) super.clone(); + } catch (CloneNotSupportedException e) { + throw new IllegalStateException(e); + } + } +} diff --git a/app/src/main/java/com/hippo/ehviewer/service/DownloadManager.java b/app/src/main/java/com/hippo/ehviewer/service/DownloadManager.java index 351fb9c3e..ad26558fa 100644 --- a/app/src/main/java/com/hippo/ehviewer/service/DownloadManager.java +++ b/app/src/main/java/com/hippo/ehviewer/service/DownloadManager.java @@ -19,21 +19,24 @@ package com.hippo.ehviewer.service; import android.os.SystemClock; import android.support.annotation.Nullable; import android.text.TextUtils; -import android.util.Log; import com.hippo.ehviewer.Constants; import com.hippo.ehviewer.client.data.DownloadInfo; +import com.hippo.ehviewer.client.data.DownloadLabel; import com.hippo.ehviewer.client.data.GalleryBase; import com.hippo.ehviewer.gallery.GallerySpider; import com.hippo.ehviewer.gallery.ImageHandler; import com.hippo.ehviewer.gallery.SpiderQueen; import com.hippo.ehviewer.util.DBUtils; import com.hippo.ehviewer.util.EhUtils; +import com.hippo.yorozuya.AssertUtils; import com.hippo.yorozuya.Messenger; import com.hippo.yorozuya.SimpleHandler; import java.io.IOException; import java.util.ArrayList; +import java.util.Collections; +import java.util.Comparator; import java.util.HashMap; import java.util.Iterator; import java.util.LinkedList; @@ -64,6 +67,17 @@ public class DownloadManager { return (gid & ~OPS_MASK) | (ops & OPS_MASK); } + public static Comparator TIME_ASC_COMPARATOR = new Comparator() { + @Override + public int compare(DownloadInfo lhs, DownloadInfo rhs) { + return lhs.time > rhs.time ? 1 : lhs.time < rhs.time ? -1 : 0; + + + //return (int) (lhs.time - rhs.time); + } + }; + + private List mDownloadInfos; private Map> mDownloadInfoMap; private List mDefaultDownloadInfos; @@ -84,19 +98,75 @@ public class DownloadManager { sInstance = new DownloadManager(); } + private Messenger.Receiver mReceiver = new Messenger.Receiver() { + @Override + public void onReceive(int id, Object obj) { + AssertUtils.assertInstanceof("Messenger obj must be DownloadLabelOps", obj, DownloadLabelModify.class); + DownloadLabelModify modify = (DownloadLabelModify) obj; + + List list; + switch (modify.ops) { + case DownloadLabelModify.OPS_ADD: + // Add new label download info list + mDownloadInfoMap.put(modify.value, new ArrayList()); + // Save label to DB + DBUtils.addDownloadLabel(modify.value); + break; + case DownloadLabelModify.OPS_REMOVE: + // Add target label download info to default list + list = mDownloadInfoMap.remove(modify.label.label); + if (list != null) { + // Remove label in download info + for (DownloadInfo info : list) { + info.label = null; + // Update in DB + DBUtils.updateDownloadInfo(info); + } + mDefaultDownloadInfos.addAll(list); + Collections.sort(mDefaultDownloadInfos, TIME_ASC_COMPARATOR); + } + // Remove label from DB + DBUtils.removeDownloadLabel(modify.label.id); + break; + case DownloadLabelModify.OPS_MOVE: + // Only need to move in DB + DBUtils.moveDownloadLabel(modify.label.id, modify.label2.id); + break; + case DownloadLabelModify.OPS_CHANGE: + list = mDownloadInfoMap.remove(modify.label.label); + if (list != null) { + for (DownloadInfo info : list) { + info.label = modify.value; + // Update in DB + DBUtils.updateDownloadInfo(info); + } + // Use new key + mDownloadInfoMap.put(modify.value, list); + } + // Update in DB + DownloadLabel label = modify.label.clone(); + label.label = modify.value; + DBUtils.updateDownloadLabel(label); + break; + default: + throw new IllegalStateException("Unknown download label ops"); + } + } + }; + public static DownloadManager getInstance() { return sInstance; } public DownloadManager() { - List tags = DBUtils.getAllDownloadTag(); + List labels = DBUtils.getAllDownloadLabel(); List downloadInfos = DBUtils.getAllDownloadInfo(); mDownloadInfos = downloadInfos; mDownloadInfoMap = new HashMap<>(); - // Create list for each tag - for (String tag : tags) { - mDownloadInfoMap.put(tag, new ArrayList()); + // Create list for each label + for (String label : labels) { + mDownloadInfoMap.put(label, new ArrayList()); } // Create default for non tag mDefaultDownloadInfos = new ArrayList<>(); @@ -109,10 +179,10 @@ public class DownloadManager { } List list; - if (TextUtils.isEmpty(info.tag)) { + if (TextUtils.isEmpty(info.label)) { list = mDefaultDownloadInfos; } else { - list = mDownloadInfoMap.get(info.tag); + list = mDownloadInfoMap.get(info.label); } if (list == null) { @@ -126,28 +196,37 @@ public class DownloadManager { mSpeedReminder = new SpeedReminder(); mDownloadSpiderListener = new DownloadSpiderListener(); mUpdateInfo = new DownloadUpdateInfo(); + + Messenger.getInstance().register(Constants.MESSENGER_ID_MODIFY_DOWNLOAD_LABEL_FROM_SCENE, mReceiver); } public void setDownloadUpdateListener(@Nullable DownloadUpdateListener listener) { mDownloadUpdateListener = listener; } - public List getDownloadList(String tag) { - return getDownloadListInternal(tag, false); + public List getAllDownloadList() { + return mDownloadInfos; } - private List getDownloadListInternal(String tag, boolean create) { - if (TextUtils.isEmpty(tag)) { + public List getDownloadList(String label) { + return getDownloadListInternal(label, false); + } + + private List getDownloadListInternal(String label, boolean create) { + if (TextUtils.isEmpty(label)) { return mDefaultDownloadInfos; } else { - List list = mDownloadInfoMap.get(tag); + List list = mDownloadInfoMap.get(label); if (list == null && create) { list = new ArrayList<>(); - mDownloadInfoMap.put(tag, list); + mDownloadInfoMap.put(label, list); + // Save label to DB + DBUtils.addDownloadLabel(label); // notify - Messenger.getInstance().notify(Constants.MESSENGER_ID_UPDATE_DOWNLOAD_TAG, null); - // Save tag to DB - DBUtils.addDownloadTag(tag); + DownloadManager.DownloadLabelModify modify = new DownloadManager.DownloadLabelModify(); + modify.ops = DownloadManager.DownloadLabelModify.OPS_ADD; + modify.value = label; + Messenger.getInstance().notify(Constants.MESSENGER_ID_MODIFY_DOWNLOAD_LABEL_FROM_MANAGER, modify); } return list; } @@ -169,8 +248,6 @@ public class DownloadManager { return; } - Log.v(TAG, "ensureDownload"); - // get download from wait list if (!mWaitList.isEmpty()) { DownloadInfo info = mWaitList.remove(0); @@ -241,19 +318,19 @@ public class DownloadManager { } /** - * @param tag null for default + * @param label null for default */ - public void startDownload(GalleryBase galleryBase, @Nullable String tag) { + void startDownload(GalleryBase galleryBase, @Nullable String label) { try { if (mCurrentTask != null && mCurrentTask.galleryBase.gid == galleryBase.gid) { - Log.v(TAG, "It is current task"); + // It is current task return; } // Check in download list for (DownloadInfo info: mDownloadInfos) { if (info.galleryBase.gid == galleryBase.gid) { - Log.v(TAG, "It is in download list"); + // It is in download list if (info.state == DownloadInfo.STATE_FINISH || info.state == DownloadInfo.STATE_NONE) { info.state = DownloadInfo.STATE_WAIT; // Add to wait list @@ -270,15 +347,14 @@ public class DownloadManager { } } - Log.v(TAG, "It is not in download list"); - // It is new download info DownloadInfo downloadInfo = new DownloadInfo(); downloadInfo.galleryBase = galleryBase; - downloadInfo.tag = tag; + downloadInfo.label = label; downloadInfo.state = DownloadInfo.STATE_WAIT; + downloadInfo.time = System.currentTimeMillis(); - List list = getDownloadListInternal(tag, true); + List list = getDownloadListInternal(label, true); list.add(downloadInfo); mDownloadInfos.add(downloadInfo); @@ -294,7 +370,7 @@ public class DownloadManager { } } - public void startAllDownload() { + void startAllDownload() { for (DownloadInfo info : mDownloadInfos) { if (info.state == DownloadInfo.STATE_NONE || (info.state == DownloadInfo.STATE_FINISH && info.legacy != 0)) { @@ -468,6 +544,9 @@ public class DownloadManager { // Release releaseCurrent(); + + // Continue + ensureDownload(); } @Override @@ -593,7 +672,7 @@ public class DownloadManager { void onCancel(DownloadUpdateInfo info); } - public class DownloadUpdateInfo { + public static class DownloadUpdateInfo { public int gid; public String title; public int pages; @@ -601,4 +680,20 @@ public class DownloadManager { public long speed; public int legacy; } + + public static class DownloadLabelModify { + + public static final int OPS_ADD = 0; + public static final int OPS_REMOVE = 1; + public static final int OPS_MOVE = 2; + public static final int OPS_CHANGE = 3; + + public int ops; + // for remove and change + public DownloadLabel label; + // for add and change + public String value; + // for move + public DownloadLabel label2; + } } diff --git a/app/src/main/java/com/hippo/ehviewer/service/DownloadService.java b/app/src/main/java/com/hippo/ehviewer/service/DownloadService.java index 88b8cf9b7..9087bed1d 100644 --- a/app/src/main/java/com/hippo/ehviewer/service/DownloadService.java +++ b/app/src/main/java/com/hippo/ehviewer/service/DownloadService.java @@ -31,6 +31,7 @@ import com.hippo.ehviewer.ui.ContentActivity; import com.hippo.ehviewer.ui.scene.DownloadScene; import com.hippo.yorozuya.FileUtils; +// TODO Avoid frequent notification public class DownloadService extends Service implements DownloadManager.DownloadUpdateListener { private static final String TAG = DownloadService.class.getSimpleName(); diff --git a/app/src/main/java/com/hippo/ehviewer/ui/scene/DownloadLabelScene.java b/app/src/main/java/com/hippo/ehviewer/ui/scene/DownloadLabelScene.java new file mode 100644 index 000000000..5e0439f77 --- /dev/null +++ b/app/src/main/java/com/hippo/ehviewer/ui/scene/DownloadLabelScene.java @@ -0,0 +1,199 @@ +/* + * Copyright 2015 Hippo Seven + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.hippo.ehviewer.ui.scene; + +import android.text.TextUtils; +import android.view.KeyEvent; +import android.view.View; +import android.view.inputmethod.EditorInfo; +import android.widget.EditText; +import android.widget.TextView; +import android.widget.Toast; + +import com.hippo.ehviewer.Constants; +import com.hippo.ehviewer.R; +import com.hippo.ehviewer.client.data.DownloadLabel; +import com.hippo.ehviewer.service.DownloadManager; +import com.hippo.ehviewer.util.DBUtils; +import com.hippo.scene.AppbarScene; +import com.hippo.scene.SimpleDialog; +import com.hippo.widget.FloatLabelEditText; +import com.hippo.widget.recyclerview.EasyRecyclerView; +import com.hippo.yorozuya.Messenger; + +import java.util.List; + +public class DownloadLabelScene extends AbsDragSortScene implements AppbarScene.OnClickActionListener { + + private List mDownloadLabels; + + private boolean mChanged = false; + + private static final int POSITION_ADD = 0; + + @Override + protected void onCreate(boolean rebirth) { + super.onCreate(rebirth); + + addAction(R.drawable.ic_plus_dark_x24); + + setOnClickActionListener(this); + } + + @Override + protected void onDestroy(boolean die) { + super.onDestroy(die); + + // notify update + if (mChanged) { + //Messenger.getInstance().notify(Constants.MESSENGER_ID_UPDATE_DOWNLOAD_LABEL, null); + mChanged = false; + } + } + + @Override + protected String getTitle() { + return "Download label"; // TODO hardcode + } + + @Override + protected SortAdapter getAdapter() { + mDownloadLabels = DBUtils.getAllDownloadLabelWithId(); + return new DownloadTagAdapter(mDownloadLabels); + } + + + private class AddDownloadTagHelper implements SimpleDialog.OnCreateCustomViewListener, + SimpleDialog.OnClickListener { + + private EditText mEditText; + + @Override + public void onCreateCustomView(final SimpleDialog dialog, View view) { + FloatLabelEditText floatLabelEditText = (FloatLabelEditText) view.findViewById(R.id.float_label_edit_text); + floatLabelEditText.setHint("Download label"); // TODO hardcode + mEditText = (EditText) view.findViewById(R.id.edit_text); + mEditText.setOnEditorActionListener(new TextView.OnEditorActionListener() { + @Override + public boolean onEditorAction(TextView v, int actionId, KeyEvent event) { + if (actionId == EditorInfo.IME_ACTION_DONE || actionId == EditorInfo.IME_NULL) { + dialog.pressPositiveButton(); + return true; + } else { + return false; + } + } + }); + } + + private boolean containLabel(String label) { + for (DownloadLabel downloadLabel : mDownloadLabels) { + if (downloadLabel.label.equals(label)) { + return true; + } + } + return false; + } + + @Override + public boolean onClick(SimpleDialog dialog, int which) { + if (which == SimpleDialog.POSITIVE) { + String label = mEditText.getText().toString().trim(); + if (TextUtils.isEmpty(label)) { + Toast.makeText(getStageActivity(), "Empty label", Toast.LENGTH_SHORT).show(); // TODO hardcode + return false; + } else if (containLabel(label)) { + Toast.makeText(getStageActivity(), "The label already exists", Toast.LENGTH_SHORT).show(); // TODO hardcode + return false; + } else { + // Tell DownloadManager + DownloadManager.DownloadLabelModify modify = new DownloadManager.DownloadLabelModify(); + modify.ops = DownloadManager.DownloadLabelModify.OPS_ADD; + modify.value = label; + Messenger.getInstance().notifyAtOnce(Constants.MESSENGER_ID_MODIFY_DOWNLOAD_LABEL_FROM_SCENE, modify); + + // Update data + mDownloadLabels.clear(); + mDownloadLabels.addAll(DBUtils.getAllDownloadLabelWithId()); + notifyDataSetChanged(); + + mChanged = true; + return true; + } + } else { + return true; + } + } + } + + @Override + public void onClickAction(int index) { + switch (index) { + case POSITION_ADD: + AddDownloadTagHelper helper = new AddDownloadTagHelper(); + new SimpleDialog.Builder(getContext()).setTitle("Add download tag") + .setCustomView(R.layout.dialog_edit_text, helper) + .setPositiveButton(android.R.string.ok) + .setOnButtonClickListener(helper).show(this); + break; + } + } + + private class DownloadTagAdapter extends SortAdapter { + + public DownloadTagAdapter(List list) { + super(list); + } + + @Override + public String getString(DownloadLabel downloadLabel, int position) { + return downloadLabel.label; + } + + @Override + public long getId(DownloadLabel downloadLabel) { + return downloadLabel.id; + } + + @Override + protected void onMove(int fromPosition, int toPosition) { + DownloadManager.DownloadLabelModify modify = new DownloadManager.DownloadLabelModify(); + modify.ops = DownloadManager.DownloadLabelModify.OPS_MOVE; + modify.label = getData(fromPosition); + modify.label2 = getData(toPosition); + Messenger.getInstance().notify(Constants.MESSENGER_ID_MODIFY_DOWNLOAD_LABEL_FROM_SCENE, modify); + + mChanged = true; + } + + @Override + protected void onRemove(int position) { + DownloadManager.DownloadLabelModify modify = new DownloadManager.DownloadLabelModify(); + modify.ops = DownloadManager.DownloadLabelModify.OPS_REMOVE; + modify.label = getData(position); + Messenger.getInstance().notify(Constants.MESSENGER_ID_MODIFY_DOWNLOAD_LABEL_FROM_SCENE, modify); + + mChanged = true; + } + + @Override + public boolean onItemClick(EasyRecyclerView parent, View view, int position, long id) { + // TODO + return false; + } + } +} diff --git a/app/src/main/java/com/hippo/ehviewer/ui/scene/DownloadScene.java b/app/src/main/java/com/hippo/ehviewer/ui/scene/DownloadScene.java index e2b5d8161..f192d3500 100644 --- a/app/src/main/java/com/hippo/ehviewer/ui/scene/DownloadScene.java +++ b/app/src/main/java/com/hippo/ehviewer/ui/scene/DownloadScene.java @@ -16,14 +16,20 @@ package com.hippo.ehviewer.ui.scene; +import android.animation.Animator; +import android.animation.ObjectAnimator; +import android.animation.PropertyValuesHolder; import android.content.Intent; +import android.content.res.Resources; import android.support.v7.widget.LinearLayoutManager; import android.support.v7.widget.RecyclerView; +import android.util.SparseBooleanArray; import android.view.View; import android.view.ViewGroup; import android.widget.ProgressBar; import android.widget.TextView; +import com.hippo.animation.SimpleAnimatorListener; import com.hippo.conaco.Conaco; import com.hippo.ehviewer.Constants; import com.hippo.ehviewer.EhApplication; @@ -42,16 +48,33 @@ import com.hippo.ehviewer.widget.SimpleRatingView; import com.hippo.rippleold.RippleSalon; import com.hippo.scene.Announcer; import com.hippo.scene.Scene; +import com.hippo.widget.FabLayout; import com.hippo.widget.recyclerview.EasyRecyclerView; +import com.hippo.yorozuya.AssertUtils; import com.hippo.yorozuya.FileUtils; import com.hippo.yorozuya.Messenger; import java.util.List; public class DownloadScene extends Scene implements DrawerProvider, - EasyRecyclerView.OnItemClickListener, Messenger.Receiver { + EasyRecyclerView.OnItemClickListener, Messenger.Receiver, View.OnClickListener, + AppbarRecyclerView.Helper { + + private static String LABEL_ALL = null; + private static String LABEL_DEFAULT = ""; + + private static int INDEX_ALL = 0; + private static int INDEX_DEFAULT = 1; + + private static long ANIMATE_TIME = 300l; private EasyRecyclerView mRecyclerView; + private FabLayout mFabLayout; + private View mStart; + private View mStop; + private View mDelete; + private View mCheckAll; + private View mMainFab; private DownloadAdapter mAdapter; @@ -59,40 +82,42 @@ public class DownloadScene extends Scene implements DrawerProvider, private GidPositionMap mGidPositionMap; - private int mOriginalPaddingBottom; + private int mOriginalRecyclerViewPaddingBottom; + private int mOriginalFabLayoutPaddingBottom; private int mLastUpdateSize = -1; private AppbarRecyclerView mRightDrawerView; - private DownloadTagAdapter mDownloadTagAdapter; - private List mTags; + private DownloadLabelAdapter mDownloadLabelAdapter; + + private List mLabels; + private String mActivatedLabel; + private int mActivatedLabelPosition; @Override public int getLaunchMode() { return LAUNCH_MODE_SINGLE_TOP; } - protected List getTags() { - mTags = DBUtils.getAllDownloadTag(); - mTags.add(0, "Default"); - mTags.add(0, "All"); - - if (mDownloadTagAdapter != null) { - mDownloadTagAdapter.notifyDataSetChanged(); - } - - return mTags; + private void initLabels() { + Resources resources = getContext().getResources(); + mLabels = DBUtils.getAllDownloadLabel(); + mLabels.add(0, resources.getString(R.string.download_tag_default)); + mLabels.add(0, resources.getString(R.string.download_tag_all)); } @Override protected void onInit() { super.onInit(); + + mActivatedLabel = LABEL_DEFAULT; + mActivatedLabelPosition = INDEX_DEFAULT; mDownloadInfos = DownloadManager.getInstance().getDownloadList(null); mGidPositionMap = new GidPositionMap(); Messenger.getInstance().register(Constants.MESSENGER_ID_UPDATE_DOWNLOAD, this); - - mTags = getTags(); + Messenger.getInstance().register(Constants.MESSENGER_ID_MODIFY_DOWNLOAD_LABEL_FROM_SCENE, this); + Messenger.getInstance().register(Constants.MESSENGER_ID_MODIFY_DOWNLOAD_LABEL_FROM_MANAGER, this); } @Override @@ -100,6 +125,8 @@ public class DownloadScene extends Scene implements DrawerProvider, super.onDie(); Messenger.getInstance().unregister(Constants.MESSENGER_ID_UPDATE_DOWNLOAD, this); + Messenger.getInstance().unregister(Constants.MESSENGER_ID_MODIFY_DOWNLOAD_LABEL_FROM_SCENE, this); + Messenger.getInstance().unregister(Constants.MESSENGER_ID_MODIFY_DOWNLOAD_LABEL_FROM_MANAGER, this); } @Override @@ -109,7 +136,15 @@ public class DownloadScene extends Scene implements DrawerProvider, ((ContentActivity) getStageActivity()).setDrawerListActivatedPosition(ContentActivity.DRAWER_LIST_DOWNLOAD); + initLabels(); + mRecyclerView = (EasyRecyclerView) findViewById(R.id.recycler_view); + mFabLayout = (FabLayout) findViewById(R.id.fab_layout); + mMainFab = mFabLayout.getPrimaryFab(); + mStart = mFabLayout.getSecondaryFabAt(0); + mStop = mFabLayout.getSecondaryFabAt(1); + mDelete = mFabLayout.getSecondaryFabAt(2); + mCheckAll = mFabLayout.getSecondaryFabAt(3); mAdapter = new DownloadAdapter(); mAdapter.setHasStableIds(true); @@ -120,12 +155,26 @@ public class DownloadScene extends Scene implements DrawerProvider, mRecyclerView.setChoiceMode(EasyRecyclerView.CHOICE_MODE_MULTIPLE_CUSTOM); mRecyclerView.setCustomCheckedListener(new DownloadChoiceListener()); - mOriginalPaddingBottom = mRecyclerView.getPaddingBottom(); + mFabLayout.setAutoCancel(false); + mFabLayout.setExpanded(false, false); + mFabLayout.setVisibility(View.INVISIBLE); + + mMainFab.setOnClickListener(this); + mStart.setOnClickListener(this); + mStop.setOnClickListener(this); + mDelete.setOnClickListener(this); + mCheckAll.setOnClickListener(this); + + mOriginalRecyclerViewPaddingBottom = mRecyclerView.getPaddingBottom(); + mOriginalFabLayoutPaddingBottom = mFabLayout.getPaddingBottom(); mRightDrawerView = new AppbarRecyclerView(getStageActivity()); - mRightDrawerView.setTitle("TAG"); - mDownloadTagAdapter = new DownloadTagAdapter(); - mRightDrawerView.setAdapter(mDownloadTagAdapter); + mRightDrawerView.setTitle("TAG"); // TODO hardcode + mDownloadLabelAdapter = new DownloadLabelAdapter(); + mRightDrawerView.setAdapter(mDownloadLabelAdapter); + mRightDrawerView.setOnItemClickListener(new DownloadTagClickListener()); + mRightDrawerView.setPlusVisibility(View.GONE); + mRightDrawerView.setHelper(this); } @Override @@ -149,7 +198,9 @@ public class DownloadScene extends Scene implements DrawerProvider, super.onGetFitPaddingBottom(b); mRecyclerView.setPadding(mRecyclerView.getPaddingLeft(), mRecyclerView.getPaddingTop(), - mRecyclerView.getPaddingRight(), b + mOriginalPaddingBottom); + mRecyclerView.getPaddingRight(), b + mOriginalRecyclerViewPaddingBottom); + mFabLayout.setPadding(mFabLayout.getPaddingLeft(), mFabLayout.getPaddingTop(), + mFabLayout.getPaddingRight(), b + mOriginalFabLayoutPaddingBottom); } @Override @@ -261,11 +312,7 @@ public class DownloadScene extends Scene implements DrawerProvider, // Add to cache DownloadInfo info = mDownloadInfos.get(position); mGidPositionMap.add(info.galleryBase.gid, position); - - Intent intent = new Intent(getStageActivity(), DownloadService.class); - intent.setAction(DownloadService.ACTION_STOP); - intent.putExtra(DownloadService.KEY_GID, info.galleryBase.gid); - getStageActivity().startService(intent); + DownloadManager.getInstance().stopDownload(info.galleryBase.gid); } } }; @@ -338,31 +385,138 @@ public class DownloadScene extends Scene implements DrawerProvider, int ops = DownloadManager.getOps(mix); int gid = DownloadManager.getGid(mix); - if (ops == DownloadManager.OPS_ALL_CHANGE) { + if (ops == DownloadManager.OPS_ALL_CHANGE || + ops == DownloadManager.OPS_REMOVE) { + // OPS_REMOVE can't find position by gid, just notifyDataSetChanged + mGidPositionMap.clear(); mAdapter.notifyDataSetChanged(); return; + } else if (ops == DownloadManager.OPS_ADD) { + // Add break list, need clean gid position map + mGidPositionMap.clear(); } int position = getPositionForGid(gid); - if (position == -1 && mLastUpdateSize != mAdapter.getItemCount()) { - // Can't get position, notifyDataSetChanged for safe - mAdapter.notifyDataSetChanged(); + if (position == -1) { + if (mLastUpdateSize != mAdapter.getItemCount()) { + // Can't get position, notifyDataSetChanged for safe + mAdapter.notifyDataSetChanged(); + } return; } if (ops == DownloadManager.OPS_ADD) { mAdapter.notifyItemInserted(position); - } else if (ops == DownloadManager.OPS_REMOVE) { - mAdapter.notifyItemRemoved(position); } else if (ops == DownloadManager.OPS_UPDATE) { RecyclerView.ViewHolder holder = mRecyclerView.findViewHolderForAdapterPosition(position); if (holder != null) { bindHolder((DownloadHolder) holder, mDownloadInfos.get(position)); } } + } else if (id == Constants.MESSENGER_ID_MODIFY_DOWNLOAD_LABEL_FROM_SCENE || + id == Constants.MESSENGER_ID_MODIFY_DOWNLOAD_LABEL_FROM_MANAGER) { + AssertUtils.assertInstanceof("Messenger obj must be DownloadLabelOps", obj, DownloadManager.DownloadLabelModify.class); + DownloadManager.DownloadLabelModify modify = (DownloadManager.DownloadLabelModify) obj; + + switch (modify.ops) { + case DownloadManager.DownloadLabelModify.OPS_ADD: + mLabels.add(modify.value); + mDownloadLabelAdapter.notifyDataSetChanged(); + break; + case DownloadManager.DownloadLabelModify.OPS_REMOVE: + mLabels.remove(modify.label.label); + mDownloadLabelAdapter.notifyDataSetChanged(); + + // Update activated info or maybe download info list + if (modify.label.label.equals(mActivatedLabel)) { + // Select default + mActivatedLabel = LABEL_DEFAULT; + mActivatedLabelPosition = INDEX_DEFAULT; + + mDownloadInfos = DownloadManager.getInstance().getDownloadList(null); + mGidPositionMap.clear(); + mAdapter.notifyDataSetChanged(); + mRecyclerView.scrollToPosition(0); + } + // TODO + break; + case DownloadManager.DownloadLabelModify.OPS_MOVE: + int fromPosition = mLabels.indexOf(modify.label.label); + int toPosition = mLabels.indexOf(modify.label.label); + if (fromPosition >= 0 && toPosition >= 0) { + mLabels.add(toPosition, mLabels.remove(fromPosition)); + mDownloadLabelAdapter.notifyDataSetChanged(); + + // Update activated info + if (fromPosition == mActivatedLabelPosition) { + mActivatedLabelPosition = toPosition; + } + } + break; + case DownloadManager.DownloadLabelModify.OPS_CHANGE: + int position = mLabels.indexOf(modify.label.label); + if (position >= 0) { + mLabels.set(position, modify.value); + mDownloadLabelAdapter.notifyDataSetChanged(); + + // Update activated info + if (position == mActivatedLabelPosition) { + mActivatedLabel = modify.value; + } + } + break; + default: + throw new IllegalStateException("Unknown download label ops"); + } } } + @Override + public void onClick(View v) { + if (mMainFab == v) { + mRecyclerView.outOfCustomChoiceMode(); + } else if (mStart == v) { + SparseBooleanArray checkedState = mRecyclerView.getCheckedItemPositions(); + for (int i = 0, n = checkedState.size(); i < n; i++) { + if (checkedState.valueAt(i)) { + int position = checkedState.keyAt(i); + DownloadInfo info = mDownloadInfos.get(position); + Intent intent = new Intent(getStageActivity(), DownloadService.class); + intent.setAction(DownloadService.ACTION_START); + intent.putExtra(DownloadService.KEY_GALLERY_BASE, info.galleryBase); + getStageActivity().startService(intent); + } + } + } else if (mStop == v) { + DownloadManager manager = DownloadManager.getInstance(); + SparseBooleanArray checkedState = mRecyclerView.getCheckedItemPositions(); + for (int i = 0, n = checkedState.size(); i < n; i++) { + if (checkedState.valueAt(i)) { + int position = checkedState.keyAt(i); + manager.stopDownload(mDownloadInfos.get(position).galleryBase.gid); + } + } + } else if (mDelete == v) { + // TODO + } else if (mCheckAll == v) { + SparseBooleanArray checkedState = mRecyclerView.getCheckedItemPositions(); + for (int i = 0, n = mAdapter.getItemCount(); i < n; i++) { + if (!checkedState.get(i, false)) { + mRecyclerView.setItemChecked(i, true); + } + } + } + } + + @Override + public void onClickPlusListener() { + } + + @Override + public void onClickSettingsListener() { + startScene(DownloadLabelScene.class); + } + private class DownloadHolder extends RecyclerView.ViewHolder { public LoadImageView thumb; @@ -547,14 +701,42 @@ public class DownloadScene extends Scene implements DrawerProvider, } } + public void openFabLayout() { + mFabLayout.setVisibility(View.VISIBLE); + + mFabLayout.setExpanded(true); + PropertyValuesHolder scaleXPvh = PropertyValuesHolder.ofFloat("scaleX", 0f, 1f); + PropertyValuesHolder scaleYPvh = PropertyValuesHolder.ofFloat("scaleY", 0f, 1f); + ObjectAnimator oa = ObjectAnimator.ofPropertyValuesHolder(mMainFab, scaleXPvh, scaleYPvh); + oa.setDuration(ANIMATE_TIME); + oa.start(); + } + + public void closeFabLayout() { + mFabLayout.setExpanded(false); + PropertyValuesHolder scaleXPvh = PropertyValuesHolder.ofFloat("scaleX", 1f, 0f); + PropertyValuesHolder scaleYPvh = PropertyValuesHolder.ofFloat("scaleY", 1f, 0f); + ObjectAnimator oa = ObjectAnimator.ofPropertyValuesHolder(mMainFab, scaleXPvh, scaleYPvh); + oa.setDuration(ANIMATE_TIME); + oa.addListener(new SimpleAnimatorListener() { + @Override + public void onAnimationEnd(Animator animation) { + mFabLayout.setVisibility(View.INVISIBLE); + } + }); + oa.start(); + } + public class DownloadChoiceListener implements EasyRecyclerView.CustomChoiceListener { @Override public void onIntoCustomChoice(EasyRecyclerView view) { + openFabLayout(); } @Override public void onOutOfCustomChoice(EasyRecyclerView view) { + closeFabLayout(); } @Override @@ -565,6 +747,33 @@ public class DownloadScene extends Scene implements DrawerProvider, } } + public class DownloadTagClickListener implements EasyRecyclerView.OnItemClickListener { + + @Override + public boolean onItemClick(EasyRecyclerView parent, View view, int position, long id) { + if (mActivatedLabelPosition != position) { + mActivatedLabelPosition = position; + + if (position == 0) { + mActivatedLabel = LABEL_ALL; + mDownloadInfos = DownloadManager.getInstance().getAllDownloadList(); + } else if (position == 1) { + mActivatedLabel = LABEL_DEFAULT; + mDownloadInfos = DownloadManager.getInstance().getDownloadList(null); + } else { + mActivatedLabel = mLabels.get(position); + mDownloadInfos = DownloadManager.getInstance().getDownloadList(mActivatedLabel); + } + mGidPositionMap.clear(); + mAdapter.notifyDataSetChanged(); + + mRecyclerView.scrollToPosition(0); + ((ContentActivity) getStageActivity()).closeDrawers(); + } + return true; + } + } + private class SimpleHolder extends RecyclerView.ViewHolder { public SimpleHolder(View itemView) { @@ -572,22 +781,22 @@ public class DownloadScene extends Scene implements DrawerProvider, } } - public class DownloadTagAdapter extends RecyclerView.Adapter { + public class DownloadLabelAdapter extends RecyclerView.Adapter { @Override public SimpleHolder onCreateViewHolder(ViewGroup parent, int viewType) { return new SimpleHolder(getStageActivity().getLayoutInflater().inflate( - R.layout.item_quick_search, parent, false)); + R.layout.item_appbar_recycler_view, parent, false)); } @Override public void onBindViewHolder(SimpleHolder holder, int position) { - ((TextView) holder.itemView).setText(mTags.get(position)); + ((TextView) holder.itemView).setText(mLabels.get(position)); } @Override public int getItemCount() { - return mTags == null ? 0 : mTags.size(); + return mLabels == null ? 0 : mLabels.size(); } } } diff --git a/app/src/main/java/com/hippo/ehviewer/ui/scene/GalleryDetailScene.java b/app/src/main/java/com/hippo/ehviewer/ui/scene/GalleryDetailScene.java index aaa40d797..22ad6973a 100644 --- a/app/src/main/java/com/hippo/ehviewer/ui/scene/GalleryDetailScene.java +++ b/app/src/main/java/com/hippo/ehviewer/ui/scene/GalleryDetailScene.java @@ -24,6 +24,7 @@ import android.graphics.drawable.Drawable; import android.support.v7.widget.LinearLayoutManager; import android.support.v7.widget.RecyclerView; import android.text.Html; +import android.util.Log; import android.view.LayoutInflater; import android.view.View; import android.view.ViewGroup; @@ -49,6 +50,7 @@ import com.hippo.ehviewer.client.data.TagGroup; import com.hippo.ehviewer.service.DownloadService; import com.hippo.ehviewer.ui.ContentActivity; import com.hippo.ehviewer.ui.GalleryActivity; +import com.hippo.ehviewer.util.DBUtils; import com.hippo.ehviewer.util.EhUtils; import com.hippo.ehviewer.util.Settings; import com.hippo.ehviewer.widget.LoadImageView; @@ -547,6 +549,40 @@ public class GalleryDetailScene extends Scene implements View.OnClickListener, } } + private class DownloadLabelHelper implements SimpleDialog.OnClickListener, SimpleDialog.OnCloseListener { + + private String[] mLabels; + + public DownloadLabelHelper() { + mLabels = getLabels(); + } + + private String[] getLabels() { + List labels = DBUtils.getAllDownloadLabel(); + labels.add(0, getContext().getResources().getString(R.string.download_tag_default)); + return labels.toArray(new String[labels.size()]); + } + + @Override + public boolean onClick(SimpleDialog dialog, int which) { + Intent intent = new Intent(getStageActivity(), DownloadService.class); + intent.setAction(DownloadService.ACTION_START); + intent.putExtra(DownloadService.KEY_GALLERY_BASE, mGalleryDetail); + if (which > 0) { + intent.putExtra(DownloadService.KEY_TAG, mLabels[which]); + } + getStageActivity().startService(intent); + dialog.finish(); + return true; + } + + @Override + public void onClose(SimpleDialog dialog, boolean cancel) { + boolean checked = ((ListViewCheckBoxDialog) dialog).isCheckBoxChecked(); + Log.d("TAG", "checked = " + checked); + } + } + @Override public void onClick(View v) { if (mFailedView == v) { @@ -559,10 +595,13 @@ public class GalleryDetailScene extends Scene implements View.OnClickListener, requestGalleryDetail(mGid, mToken); } } else if (mDownload == v) { - Intent intent = new Intent(getStageActivity(), DownloadService.class); - intent.setAction(DownloadService.ACTION_START); - intent.putExtra(DownloadService.KEY_GALLERY_BASE, mGalleryDetail); - getStageActivity().startService(intent); + DownloadLabelHelper helper = new DownloadLabelHelper(); + new ListViewCheckBoxDialog.Builder(getContext()) + .setListItems(helper.mLabels, helper) + .setCheckBoxText("Remeber") // TODO hardcode + .setTitle("Choose label") // TODO hardcode + .setOnCloseListener(helper) + .show(this); } else if (mRead == v) { GalleryBase galleryBase = mGalleryDetail; if (galleryBase == null) { diff --git a/app/src/main/java/com/hippo/ehviewer/ui/scene/GalleryListScene.java b/app/src/main/java/com/hippo/ehviewer/ui/scene/GalleryListScene.java index 9df9c6d05..30aaf2525 100644 --- a/app/src/main/java/com/hippo/ehviewer/ui/scene/GalleryListScene.java +++ b/app/src/main/java/com/hippo/ehviewer/ui/scene/GalleryListScene.java @@ -72,6 +72,7 @@ import com.hippo.scene.Scene; import com.hippo.scene.SimpleDialog; import com.hippo.util.AnimationUtils; import com.hippo.widget.FabLayout; +import com.hippo.widget.FloatLabelEditText; import com.hippo.widget.FloatingActionButton; import com.hippo.widget.recyclerview.EasyRecyclerView; import com.hippo.yorozuya.AssertUtils; @@ -87,9 +88,8 @@ import java.util.List; // TODO disable click action when animating // TODO Dim when expand search list public final class GalleryListScene extends Scene implements ListSearchBar.Helper, - View.OnClickListener, FabLayout.OnCancelListener, - SearchLayout.SearhLayoutHelper, EasyRecyclerView.OnItemClickListener, - Messenger.Receiver, DrawerProvider, AppbarRecyclerView.Helper { + View.OnClickListener, SearchLayout.SearhLayoutHelper, EasyRecyclerView.OnItemClickListener, + Messenger.Receiver, DrawerProvider, AppbarRecyclerView.Helper, FabLayout.OnExpandListener { public static final String KEY_MODE = "mode"; @@ -149,6 +149,9 @@ public final class GalleryListScene extends Scene implements ListSearchBar.Helpe private QuickSearchAdapter mQuickSearchAdapter; private List mQuickSearches; + private boolean mLockLeft = false; + private boolean mLockRight = false; + private SimpleDialog.OnCreateCustomViewListener mGoToCreateCustomViewListener = new SimpleDialog.OnCreateCustomViewListener() { @Override @@ -310,7 +313,7 @@ public final class GalleryListScene extends Scene implements ListSearchBar.Helpe mContentRecyclerView.setOnItemClickListener(this); // Fab Layout - mFabLayout.setOnCancelListener(this); + mFabLayout.setOnExpandListener(this); // Secondary Fab mRefreshFab.setOnClickListener(this); @@ -390,12 +393,17 @@ public final class GalleryListScene extends Scene implements ListSearchBar.Helpe @Override public boolean showLeftDrawer() { - return true; + return !mLockLeft; } @Override public void bindRightDrawer(ContentActivity activity) { activity.setRightDrawerView(mRightDrawerView); + if (mLockRight) { + activity.lockRightDrawer(); + } else { + activity.unlockRightDrawer(); + } } private void setFabState(int fabState, boolean animation) { @@ -531,7 +539,6 @@ public final class GalleryListScene extends Scene implements ListSearchBar.Helpe public void onBackPressed() { if (mFabLayout.isExpanded()) { mFabLayout.setExpanded(false); - mAddDeleteDrawable.setShape(false, ANIMATE_TIME); } else { switch (mState) { case STATE_NORMAL: @@ -557,11 +564,6 @@ public final class GalleryListScene extends Scene implements ListSearchBar.Helpe } } - private void toggleFabLayout() { - mFabLayout.toggle(); - mAddDeleteDrawable.setShape(mFabLayout.isExpanded(), ANIMATE_TIME); - } - public void showGoToDialog() { int[] center = new int[2]; ViewUtils.getCenterInAncestor(mGoToFab, center, R.id.stage); @@ -576,15 +578,15 @@ public final class GalleryListScene extends Scene implements ListSearchBar.Helpe public void onClick(View v) { if (v == mCornerFab) { if (mFabState == FAB_STATE_NORMAL) { - toggleFabLayout(); + mFabLayout.toggle(); } else if (mFabState == FAB_STATE_SEARCH) { onApplySearch(mSearchBar.getText()); } } else if (v == mRefreshFab) { - toggleFabLayout(); + mFabLayout.toggle(); mGalleryListHelper.refreshWithSameSearch(); } else if (v == mGoToFab) { - toggleFabLayout(); + mFabLayout.toggle(); if (mGalleryListHelper.canGoTo()) { showGoToDialog(); } @@ -598,7 +600,7 @@ public final class GalleryListScene extends Scene implements ListSearchBar.Helpe mode == ListUrlBuilder.MODE_TAG) { AddToQuickSearchHelper helper = new AddToQuickSearchHelper(mListUrlBuilder.clone()); new SimpleDialog.Builder(getStageActivity()).setTitle("Add to quick search") // TODO hardcode - .setCustomView(R.layout.dialog_add_to_quick_search, helper) + .setCustomView(R.layout.dialog_edit_text, helper) .setPositiveButton(android.R.string.ok) .setOnButtonClickListener(helper).show(this); } else { @@ -612,8 +614,20 @@ public final class GalleryListScene extends Scene implements ListSearchBar.Helpe } @Override - public void onCancel(FabLayout fabLayout) { - mAddDeleteDrawable.setAdd(ANIMATE_TIME); + public void onExpand(boolean expanded) { + ContentActivity contentActivity = (ContentActivity) getStageActivity(); + if (expanded) { + contentActivity.lockLeftDrawer(); + contentActivity.lockRightDrawer(); + mLockLeft = true; + mLockRight = true; + } else { + contentActivity.unlockLeftDrawer(); + contentActivity.unlockRightDrawer(); + mLockLeft = false; + mLockRight = false; + } + mAddDeleteDrawable.setShape(mFabLayout.isExpanded(), ANIMATE_TIME); } @Override @@ -818,6 +832,8 @@ public final class GalleryListScene extends Scene implements ListSearchBar.Helpe @Override public void onCreateCustomView(final SimpleDialog dialog, View view) { + FloatLabelEditText floatLabelEditText = (FloatLabelEditText) view.findViewById(R.id.float_label_edit_text); + floatLabelEditText.setHint(getStageActivity().getString(R.string.displayname)); mEditText = (EditText) view.findViewById(R.id.edit_text); mEditText.setOnEditorActionListener(new TextView.OnEditorActionListener() { @Override @@ -860,7 +876,7 @@ public final class GalleryListScene extends Scene implements ListSearchBar.Helpe builder.setKeyword(mSearchBar.getText()); AddToQuickSearchHelper helper = new AddToQuickSearchHelper(builder); new SimpleDialog.Builder(getStageActivity()).setTitle("Add to quick search") // TODO hardcode - .setCustomView(R.layout.dialog_add_to_quick_search, helper) + .setCustomView(R.layout.dialog_edit_text, helper) .setPositiveButton(android.R.string.ok) .setOnButtonClickListener(helper).show(this); } @@ -1242,7 +1258,7 @@ public final class GalleryListScene extends Scene implements ListSearchBar.Helpe @Override public SimpleHolder onCreateViewHolder(ViewGroup parent, int viewType) { return new SimpleHolder(getStageActivity().getLayoutInflater().inflate( - R.layout.item_quick_search, parent, false)); + R.layout.item_appbar_recycler_view, parent, false)); } @Override diff --git a/app/src/main/java/com/hippo/ehviewer/ui/scene/ListViewCheckBoxDialog.java b/app/src/main/java/com/hippo/ehviewer/ui/scene/ListViewCheckBoxDialog.java new file mode 100644 index 000000000..5c1e3cb2a --- /dev/null +++ b/app/src/main/java/com/hippo/ehviewer/ui/scene/ListViewCheckBoxDialog.java @@ -0,0 +1,92 @@ +/* + * Copyright 2015 Hippo Seven + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.hippo.ehviewer.ui.scene; + +import android.content.Context; +import android.view.View; +import android.view.ViewGroup; +import android.widget.AdapterView; +import android.widget.ArrayAdapter; +import android.widget.CheckBox; +import android.widget.ListView; + +import com.hippo.ehviewer.R; +import com.hippo.scene.SimpleDialog; + +public class ListViewCheckBoxDialog extends SimpleDialog { + + private ListView mListView; + private CheckBox mCheckBox; + + @Override + protected void onCreate(boolean rebirth) { + super.onCreate(rebirth); + + ViewGroup customView = getCustomView(); + mListView = (ListView) customView.findViewById(R.id.list); + mCheckBox = (CheckBox) customView.findViewById(R.id.check_box); + + Builder builder = (Builder) getBuilder(); + mListView.setAdapter(new ArrayAdapter<>(getStageActivity(), + builder.mListItemLayoutResId, android.R.id.text1, builder.mListeItems)); + mListView.setOnItemClickListener(new ListItemClickListener()); + mCheckBox.setText(builder.mCheckBoxText); + } + + public boolean isCheckBoxChecked() { + return mCheckBox.isChecked(); + } + + public class ListItemClickListener implements ListView.OnItemClickListener { + + @Override + public void onItemClick(AdapterView parent, View view, int position, long id) { + ((Builder) getBuilder()).mOnListItemClickListener.onClick(ListViewCheckBoxDialog.this, position); + } + } + + public static class Builder extends SimpleDialog.Builder { + + private CharSequence[] mListeItems; + private int mListItemLayoutResId; + private OnClickListener mOnListItemClickListener; + + private CharSequence mCheckBoxText; + + public Builder(Context context) { + super(context); + setCustomView(R.layout.dialog_list_view_check_box, null); + } + + public Builder setListItems(CharSequence[] items, final OnClickListener listener) { + mListeItems = items; + mListItemLayoutResId = R.layout.select_dialog_item; + mOnListItemClickListener = listener; + return this; + } + + public Builder setCheckBoxText(CharSequence text) { + mCheckBoxText = text; + return this; + } + + @Override + public Class getDialogClass() { + return ListViewCheckBoxDialog.class; + } + } +} diff --git a/app/src/main/java/com/hippo/ehviewer/ui/scene/ModifyQuickSearchScene.java b/app/src/main/java/com/hippo/ehviewer/ui/scene/ModifyQuickSearchScene.java index a5866a72b..63b8029e4 100644 --- a/app/src/main/java/com/hippo/ehviewer/ui/scene/ModifyQuickSearchScene.java +++ b/app/src/main/java/com/hippo/ehviewer/ui/scene/ModifyQuickSearchScene.java @@ -180,7 +180,7 @@ public class ModifyQuickSearchScene extends AppbarScene implements SearchLayout. public void onAction1() { RenameHelper helper = new RenameHelper(); new SimpleDialog.Builder(getStageActivity()).setTitle(R.string.rename) - .setCustomView(R.layout.dialog_add_to_quick_search, helper) + .setCustomView(R.layout.dialog_edit_text, helper) .setPositiveButton(android.R.string.ok) .setOnButtonClickListener(helper).show(this); } diff --git a/app/src/main/java/com/hippo/ehviewer/ui/scene/QuickSearchScene.java b/app/src/main/java/com/hippo/ehviewer/ui/scene/QuickSearchScene.java index bef17a111..9b76e846e 100644 --- a/app/src/main/java/com/hippo/ehviewer/ui/scene/QuickSearchScene.java +++ b/app/src/main/java/com/hippo/ehviewer/ui/scene/QuickSearchScene.java @@ -53,6 +53,7 @@ public class QuickSearchScene extends AbsDragSortScene implements Messenger.Rece // notify update if (mChanged) { Messenger.getInstance().notify(Constants.MESSENGER_ID_UPDATE_QUICK_SEARCH, null); + mChanged = false; } } diff --git a/app/src/main/java/com/hippo/ehviewer/util/DBUtils.java b/app/src/main/java/com/hippo/ehviewer/util/DBUtils.java index d8c46f6ba..44ea206a7 100644 --- a/app/src/main/java/com/hippo/ehviewer/util/DBUtils.java +++ b/app/src/main/java/com/hippo/ehviewer/util/DBUtils.java @@ -21,6 +21,7 @@ import android.database.sqlite.SQLiteDatabase; import android.text.TextUtils; import com.hippo.ehviewer.client.data.DownloadInfo; +import com.hippo.ehviewer.client.data.DownloadLabel; import com.hippo.ehviewer.client.data.GalleryBase; import com.hippo.ehviewer.client.data.ListUrlBuilder; import com.hippo.ehviewer.client.data.QuickSearch; @@ -30,8 +31,8 @@ import com.hippo.ehviewer.dao.DirnameObj; import com.hippo.ehviewer.dao.DirnameObjDao; import com.hippo.ehviewer.dao.DownloadInfoObj; import com.hippo.ehviewer.dao.DownloadInfoObjDao; -import com.hippo.ehviewer.dao.DownloadTagObj; -import com.hippo.ehviewer.dao.DownloadTagObjDao; +import com.hippo.ehviewer.dao.DownloadLabelObj; +import com.hippo.ehviewer.dao.DownloadLabelObjDao; import com.hippo.ehviewer.dao.GalleryBaseObj; import com.hippo.ehviewer.dao.GalleryBaseObjDao; import com.hippo.ehviewer.dao.QuickSearchObj; @@ -198,8 +199,6 @@ public class DBUtils { return result; } - - public static List getAllDownloadInfo() { DownloadInfoObjDao dao = sDaoSession.getDownloadInfoObjDao(); List list = dao.queryBuilder().orderAsc(DownloadInfoObjDao.Properties.Time).list(); @@ -213,10 +212,10 @@ public class DBUtils { public static void addDownloadInfo(DownloadInfo downloadInfo) { DownloadInfoObj obj = new DownloadInfoObj(); obj.setGid((long) downloadInfo.galleryBase.gid); - obj.setTag(downloadInfo.tag); + obj.setLabel(downloadInfo.label); obj.setState(downloadInfo.state); obj.setLegacy(downloadInfo.legacy); - obj.setTime(System.currentTimeMillis()); + obj.setTime(downloadInfo.time); sDaoSession.getDownloadInfoObjDao().insert(obj); addGalleryBase(downloadInfo.galleryBase); } @@ -226,22 +225,81 @@ public class DBUtils { DownloadInfoObj obj = dao.load((long) downloadInfo.galleryBase.gid); obj.setState(downloadInfo.state); obj.setLegacy(downloadInfo.legacy); + obj.setLabel(downloadInfo.label); dao.update(obj); } - public static List getAllDownloadTag() { - DownloadTagObjDao dao = sDaoSession.getDownloadTagObjDao(); - List list = dao.queryBuilder().list(); + public static List getAllDownloadLabel() { + DownloadLabelObjDao dao = sDaoSession.getDownloadLabelObjDao(); + List list = dao.queryBuilder().orderAsc(DownloadLabelObjDao.Properties.Time).list(); List result = new ArrayList<>(list.size()); - for (DownloadTagObj obj : list) { - result.add(obj.getTag()); + for (DownloadLabelObj obj : list) { + result.add(obj.getLabel()); } return result; } - public static void addDownloadTag(String tag) { - DownloadTagObj obj = new DownloadTagObj(); - obj.setTag(tag); - sDaoSession.getDownloadTagObjDao().insert(obj); + public static List getAllDownloadLabelWithId() { + DownloadLabelObjDao dao = sDaoSession.getDownloadLabelObjDao(); + List list = dao.queryBuilder().orderAsc(DownloadLabelObjDao.Properties.Time).list(); + List result = new ArrayList<>(list.size()); + for (DownloadLabelObj obj : list) { + DownloadLabel tag = new DownloadLabel(); + tag.id = obj.getId(); + tag.label = obj.getLabel(); + tag.time = obj.getTime(); + result.add(tag); + } + return result; + } + + public static void addDownloadLabel(String label) { + if (TextUtils.isEmpty(label)) { + return; + } + + DownloadLabelObj obj = new DownloadLabelObj(); + obj.setLabel(label); + obj.setTime(System.currentTimeMillis()); + sDaoSession.getDownloadLabelObjDao().insert(obj); + } + + public static void moveDownloadLabel(long fromId, long toId) { + DownloadLabelObjDao dao = sDaoSession.getDownloadLabelObjDao(); + DownloadLabelObj from = dao.load(fromId); + DownloadLabelObj to = dao.load(toId); + long fromTime = from.getTime(); + long toTime = to.getTime(); + from.setTime(toTime); + to.setTime(fromTime); + dao.update(from); + dao.update(to); + } + + public static void updateDownloadLabel(DownloadLabel label) { + DownloadLabelObjDao dao = sDaoSession.getDownloadLabelObjDao(); + DownloadLabelObj obj = dao.load(label.id); + obj.setId(label.id); + obj.setLabel(label.label); + obj.setTime(label.time); + dao.update(obj); + } + + public static void removeDownloadLabel(long id) { + sDaoSession.getDownloadLabelObjDao().deleteByKey(id); + } + + public static void removeDownloadLabel(String label) { + DownloadLabelObjDao dao = sDaoSession.getDownloadLabelObjDao(); + List list = dao.queryBuilder() + .where(DownloadLabelObjDao.Properties.Label.eq(label)).list(); + for (DownloadLabelObj obj : list) { + dao.delete(obj); + } + } + + public static boolean containDownloadLabel(String label) { + return sDaoSession.getDownloadLabelObjDao().queryBuilder() + .where(DownloadLabelObjDao.Properties.Label.eq(label)).count() != 0; } } diff --git a/app/src/main/java/com/hippo/scene/AppbarScene.java b/app/src/main/java/com/hippo/scene/AppbarScene.java index 009e97bde..ccb6ab691 100644 --- a/app/src/main/java/com/hippo/scene/AppbarScene.java +++ b/app/src/main/java/com/hippo/scene/AppbarScene.java @@ -28,12 +28,40 @@ import com.hippo.ehviewer.R; import com.hippo.rippleold.RippleSalon; import com.hippo.widget.SimpleImageView; -public class AppbarScene extends Scene { +import java.util.ArrayList; +import java.util.List; +public abstract class AppbarScene extends Scene { + + private ViewGroup mAppbar; private SimpleImageView mIcon; private TextView mTitle; private FrameLayout mContent; + private OnClickActionListener mOnClickActionListener; + private List mActions; + + private View.OnClickListener mOnClickListener = new View.OnClickListener() { + @Override + public void onClick(View v) { + if (mIcon == v) { + onIconClick(); + } else { + int index = -1; + for (int i = 0, n = mActions.size(); i < n; i++) { + if (mActions.get(i) == v) { + index = i; + break; + } + } + + if (index >= 0 && mOnClickActionListener != null) { + mOnClickActionListener.onClickAction(index); + } + } + } + }; + @Override protected void setContentView(int resId) { getStageActivity().getLayoutInflater().inflate(resId, mContent); @@ -54,17 +82,29 @@ public class AppbarScene extends Scene { super.onCreate(rebirth); super.setContentView(R.layout.scene_appbar); + mAppbar = (ViewGroup) super.findViewById(R.id.appbar); mIcon = (SimpleImageView) super.findViewById(R.id.appbar_icon); mTitle = (TextView) super.findViewById(R.id.appbar_title); mContent = (FrameLayout) super.findViewById(R.id.appbar_content); RippleSalon.addRipple(mIcon, true); - mIcon.setOnClickListener(new View.OnClickListener() { - @Override - public void onClick(View v) { - onIconClick(); - } - }); + mIcon.setOnClickListener(mOnClickListener); + + mActions = new ArrayList<>(); + } + + public void setOnClickActionListener(OnClickActionListener listener) { + mOnClickActionListener = listener; + } + + public void addAction(@DrawableRes int drawableRes) { + int index = mAppbar.getChildCount(); + getStageActivity().getLayoutInflater().inflate(R.layout.item_appbar_action, mAppbar); + SimpleImageView action = (SimpleImageView) mAppbar.getChildAt(index); + action.setDrawable(drawableRes); + action.setOnClickListener(mOnClickListener); + RippleSalon.addRipple(action, true); + mActions.add(action); } public void onIconClick() { @@ -87,4 +127,9 @@ public class AppbarScene extends Scene { public void setTitle(String title) { mTitle.setText(title); } + + public interface OnClickActionListener { + + void onClickAction(int index); + } } diff --git a/app/src/main/java/com/hippo/scene/SimpleDialog.java b/app/src/main/java/com/hippo/scene/SimpleDialog.java index 9d1f6b95e..4d4f4e5e9 100644 --- a/app/src/main/java/com/hippo/scene/SimpleDialog.java +++ b/app/src/main/java/com/hippo/scene/SimpleDialog.java @@ -69,6 +69,10 @@ public class SimpleDialog extends SceneDialog implements View.OnClickListener, mBuilder = builder; } + protected Builder getBuilder() { + return mBuilder; + } + @Override protected void onCreate(boolean rebirth) { super.onCreate(rebirth); @@ -197,6 +201,10 @@ public class SimpleDialog extends SceneDialog implements View.OnClickListener, mLayout.setFitPaddingBottom(b); } + protected FrameLayout getCustomView() { + return mCustom; + } + public ViewGroup getLayout() { return mLayout; } @@ -427,11 +435,15 @@ public class SimpleDialog extends SceneDialog implements View.OnClickListener, return this; } + public Class getDialogClass() { + return SimpleDialog.class; + } + public @NonNull SimpleDialog show(Scene scene) { // Avoid memory leak mContext = null; - SimpleDialog dialog = (SimpleDialog) SceneDialog.createSceneDialog(scene, SimpleDialog.class); + SimpleDialog dialog = (SimpleDialog) SceneDialog.createSceneDialog(scene, getDialogClass()); dialog.setBuilder(this); dialog.show(new SimpleDialogCurtain(mStartX, mStartY)); diff --git a/app/src/main/java/com/hippo/widget/FabLayout.java b/app/src/main/java/com/hippo/widget/FabLayout.java index 0ac2cfb29..f51d491f3 100644 --- a/app/src/main/java/com/hippo/widget/FabLayout.java +++ b/app/src/main/java/com/hippo/widget/FabLayout.java @@ -48,7 +48,7 @@ public class FabLayout extends ViewGroup implements View.OnClickListener { private boolean mAutoCancel = true; private float mMainFabCenterY = -1f; - private OnCancelListener mOnCancelListener; + private OnExpandListener mOnExpandListener; public FabLayout(Context context) { super(context); @@ -196,8 +196,8 @@ public class FabLayout extends ViewGroup implements View.OnClickListener { } } - public void setOnCancelListener(OnCancelListener listener) { - mOnCancelListener = listener; + public void setOnExpandListener(OnExpandListener listener) { + mOnExpandListener = listener; } public void setAutoCancel(boolean autoCancel) { @@ -258,6 +258,10 @@ public class FabLayout extends ViewGroup implements View.OnClickListener { } } } + + if (mOnExpandListener != null) { + mOnExpandListener.onExpand(expanded); + } } } @@ -314,9 +318,6 @@ public class FabLayout extends ViewGroup implements View.OnClickListener { @Override public void onClick(View v) { setExpanded(false); - if (mOnCancelListener != null) { - mOnCancelListener.onCancel(this); - } } @Override @@ -343,7 +344,7 @@ public class FabLayout extends ViewGroup implements View.OnClickListener { } } - public interface OnCancelListener { - void onCancel(FabLayout fabLayout); + public interface OnExpandListener { + void onExpand(boolean expanded); } } diff --git a/app/src/main/java/com/hippo/widget/FloatLabelEditText.java b/app/src/main/java/com/hippo/widget/FloatLabelEditText.java index 29f2ebaa6..822708807 100644 --- a/app/src/main/java/com/hippo/widget/FloatLabelEditText.java +++ b/app/src/main/java/com/hippo/widget/FloatLabelEditText.java @@ -121,7 +121,7 @@ public class FloatLabelEditText extends FrameLayout { // If we do not have a valid hint, try and retrieve it from the EditText if (TextUtils.isEmpty(mHint)) { - setHint(mEditText.getHint()); + setHintInternal(mEditText.getHint()); } // Align with label and edittext @@ -133,6 +133,11 @@ public class FloatLabelEditText extends FrameLayout { } public void setHint(CharSequence hint) { + mEditText.setHint(hint); + setHintInternal(hint); + } + + private void setHintInternal(CharSequence hint) { mHint = hint; mLabel.setText(hint); } diff --git a/app/src/main/java/com/hippo/widget/recyclerview/EasyRecyclerView.java b/app/src/main/java/com/hippo/widget/recyclerview/EasyRecyclerView.java index 7dcf10def..e74ac74e5 100644 --- a/app/src/main/java/com/hippo/widget/recyclerview/EasyRecyclerView.java +++ b/app/src/main/java/com/hippo/widget/recyclerview/EasyRecyclerView.java @@ -106,6 +106,8 @@ public class EasyRecyclerView extends RecyclerView { private boolean mCustomChoice = false; + private boolean mOutOfCustomChoiceModing = false; + private SparseBooleanArray mTempCheckStates; private FixCheckedHelper mFixCheckedHelper; @@ -520,7 +522,8 @@ public class EasyRecyclerView extends RecyclerView { } public void outOfCustomChoiceMode() { - if (mChoiceMode == CHOICE_MODE_MULTIPLE_CUSTOM && mCustomChoice) { + if (mChoiceMode == CHOICE_MODE_MULTIPLE_CUSTOM && mCustomChoice && !mOutOfCustomChoiceModing) { + mOutOfCustomChoiceModing = true; // Copy mCheckStates mTempCheckStates.clear(); @@ -536,6 +539,8 @@ public class EasyRecyclerView extends RecyclerView { mCustomChoice = false; mCustomChoiceListener.onOutOfCustomChoice(this); + + mOutOfCustomChoiceModing = false; } } diff --git a/app/src/main/res/color/card_background.xml b/app/src/main/res/color/card_background.xml index 2bdc18fd6..8025f66f4 100644 --- a/app/src/main/res/color/card_background.xml +++ b/app/src/main/res/color/card_background.xml @@ -17,7 +17,7 @@ - + - \ No newline at end of file + diff --git a/app/src/main/res/drawable/ic_check_all_dark_x24.xml b/app/src/main/res/drawable/ic_check_all_dark_x24.xml new file mode 100644 index 000000000..3add08fb2 --- /dev/null +++ b/app/src/main/res/drawable/ic_check_all_dark_x24.xml @@ -0,0 +1,40 @@ + + + + + + + diff --git a/app/src/main/res/drawable/ic_close_dark_x24.xml b/app/src/main/res/drawable/ic_close_dark_x24.xml new file mode 100644 index 000000000..79f7ebd35 --- /dev/null +++ b/app/src/main/res/drawable/ic_close_dark_x24.xml @@ -0,0 +1,40 @@ + + + + + + + diff --git a/app/src/main/res/drawable/ic_pause_dark_x24.xml b/app/src/main/res/drawable/ic_pause_dark_x24.xml new file mode 100644 index 000000000..5916d0e10 --- /dev/null +++ b/app/src/main/res/drawable/ic_pause_dark_x24.xml @@ -0,0 +1,40 @@ + + + + + + + diff --git a/app/src/main/res/drawable/ic_play_dark_x24.xml b/app/src/main/res/drawable/ic_play_dark_x24.xml new file mode 100644 index 000000000..f089402b3 --- /dev/null +++ b/app/src/main/res/drawable/ic_play_dark_x24.xml @@ -0,0 +1,40 @@ + + + + + + + diff --git a/app/src/main/res/layout-v21/scene_appbar.xml b/app/src/main/res/layout-v21/scene_appbar.xml index 1c8fe4e28..a432487ff 100644 --- a/app/src/main/res/layout-v21/scene_appbar.xml +++ b/app/src/main/res/layout-v21/scene_appbar.xml @@ -20,6 +20,7 @@ android:orientation="vertical"> + + diff --git a/app/src/main/res/layout/dialog_list_view_check_box.xml b/app/src/main/res/layout/dialog_list_view_check_box.xml new file mode 100644 index 000000000..959e73ae4 --- /dev/null +++ b/app/src/main/res/layout/dialog_list_view_check_box.xml @@ -0,0 +1,41 @@ + + + + + + + + + diff --git a/app/src/main/res/layout/item_appbar_action.xml b/app/src/main/res/layout/item_appbar_action.xml new file mode 100644 index 000000000..1c96ac4e0 --- /dev/null +++ b/app/src/main/res/layout/item_appbar_action.xml @@ -0,0 +1,22 @@ + + + diff --git a/app/src/main/res/layout/item_quick_search.xml b/app/src/main/res/layout/item_appbar_recycler_view.xml similarity index 100% rename from app/src/main/res/layout/item_quick_search.xml rename to app/src/main/res/layout/item_appbar_recycler_view.xml diff --git a/app/src/main/res/layout/scene_appbar.xml b/app/src/main/res/layout/scene_appbar.xml index f6ffc881c..c778eca59 100644 --- a/app/src/main/res/layout/scene_appbar.xml +++ b/app/src/main/res/layout/scene_appbar.xml @@ -20,6 +20,7 @@ android:orientation="vertical"> + + + + + + + + + + + + + + + + diff --git a/app/src/main/res/values/colors.xml b/app/src/main/res/values/colors.xml index 450896e6c..458084e1b 100644 --- a/app/src/main/res/values/colors.xml +++ b/app/src/main/res/values/colors.xml @@ -65,8 +65,10 @@ #ff7cb342 #fff9a825 #ffff7043 + #ff7986cb #ff3f51b5 #ff303f9f + #ff9e9e9e #de000000 diff --git a/app/src/main/res/values/pathdata.xml b/app/src/main/res/values/pathdata.xml index 70636e3a4..867144f66 100644 --- a/app/src/main/res/values/pathdata.xml +++ b/app/src/main/res/values/pathdata.xml @@ -52,6 +52,7 @@ M14,19.14H18V5.14H14M6,19.14H10V5.14H6V19.14Z M19,7.1L16.9 5 12 9.9 7.1 5 5 7.1 9.9 12 5 16.9 7.1 19 12 14.1 16.9 19 19 16.9 14.1 12 z M19,13H13V19H11V13H5V11H11V5H13V11H19V13Z + M0.41,13.41L6,19L7.41,17.58L1.83,12M22.24,5.58L11.66,16.17L7.5,12L6.07,13.41L11.66,19L23.66,7M18,7L16.59,5.58L10.24,11.93L11.66,13.34L18,7Z M13,0c7.2,0,13,5.9,13,13.2c0,3.7-2,7.6-7.3,12.9L13,32l-5.8-5.8C2,20.8,0,16.9,0,13.3C0,5.9,5.8,0,13,0z diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml index 1fc5265bc..b739a5505 100644 --- a/app/src/main/res/values/strings.xml +++ b/app/src/main/res/values/strings.xml @@ -233,10 +233,13 @@ IDLE Waiting - Done Done Incomplete + + All + Default + Keyword Search Retry diff --git a/daogenerator/src/main/java/com/hippo/ehviewer/EhDaoGenerator.java b/daogenerator/src/main/java/com/hippo/ehviewer/EhDaoGenerator.java index 98647ca62..eedf2465d 100644 --- a/daogenerator/src/main/java/com/hippo/ehviewer/EhDaoGenerator.java +++ b/daogenerator/src/main/java/com/hippo/ehviewer/EhDaoGenerator.java @@ -37,7 +37,7 @@ public class EhDaoGenerator { addGalleryBase(schema); addQuickSearch(schema); addDownloadInfo(schema); - addDownloadTag(schema); + addDownloadLabel(schema); addDirname(schema); addReadInfo(schema); new DaoGenerator().generateAll(schema, OUT_DIR); @@ -75,17 +75,18 @@ public class EhDaoGenerator { Entity entity = schema.addEntity("DownloadInfoObj"); entity.setTableName("DOWNLOAD_INFO"); entity.addLongProperty("gid").primaryKey(); - entity.addStringProperty("tag"); + entity.addStringProperty("label"); entity.addIntProperty("state"); entity.addIntProperty("legacy"); entity.addLongProperty("time"); } - public static void addDownloadTag(Schema schema) { - Entity entity = schema.addEntity("DownloadTagObj"); - entity.setTableName("DOWNLOAD_TAG"); + public static void addDownloadLabel(Schema schema) { + Entity entity = schema.addEntity("DownloadLabelObj"); + entity.setTableName("DOWNLOAD_LABEL"); entity.addIdProperty(); - entity.addStringProperty("tag"); + entity.addStringProperty("label"); + entity.addLongProperty("time"); } public static void addDirname(Schema schema) { diff --git a/vectorold b/vectorold index b07793e9c..ad4a29839 160000 --- a/vectorold +++ b/vectorold @@ -1 +1 @@ -Subproject commit b07793e9c539eb9f5ce5b7e306e5b067f93ad74b +Subproject commit ad4a29839224f22ef160732e8fc67e3b4a8913cc diff --git a/yorozuya b/yorozuya index 805f0c5f7..4c12e63b5 160000 --- a/yorozuya +++ b/yorozuya @@ -1 +1 @@ -Subproject commit 805f0c5f7aa80d82ddde2360e9f9b6d27f797124 +Subproject commit 4c12e63b5011b3142562947826079540832d5feb