diff --git a/app/src/main/java/com/hippo/ehviewer/client/GalleryListParser.java b/app/src/main/java/com/hippo/ehviewer/client/GalleryListParser.java index f958bb43d..45ca426e3 100644 --- a/app/src/main/java/com/hippo/ehviewer/client/GalleryListParser.java +++ b/app/src/main/java/com/hippo/ehviewer/client/GalleryListParser.java @@ -88,6 +88,10 @@ public class GalleryListParser { Result result = new Result(); Matcher m; + if (!body.startsWith("<")) { + throw new EhException(body); + } + // pages m = PAGES_PATTERN.matcher(body); if (m.find()) { @@ -147,6 +151,10 @@ public class GalleryListParser { Result result = new Result(); Matcher m; + if (!body.startsWith("<")) { + throw new EhException(body); + } + List list = new ArrayList<>(25); result.galleryInfos = list; m = LOFI_PATTERN.matcher(body); diff --git a/app/src/main/java/com/hippo/ehviewer/client/data/ListUrlBuilder.java b/app/src/main/java/com/hippo/ehviewer/client/data/ListUrlBuilder.java index 29ef5c9c7..9867f3de9 100644 --- a/app/src/main/java/com/hippo/ehviewer/client/data/ListUrlBuilder.java +++ b/app/src/main/java/com/hippo/ehviewer/client/data/ListUrlBuilder.java @@ -25,7 +25,7 @@ import com.hippo.network.UrlBuilder; import java.io.UnsupportedEncodingException; import java.net.URLEncoder; -public class ListUrlBuilder { +public class ListUrlBuilder implements Cloneable { // Mode public static final int MODE_NORMAL = 0x0; @@ -62,6 +62,15 @@ public class ListUrlBuilder { mMinRating = -1; } + @Override + public ListUrlBuilder clone() { + try { + return (ListUrlBuilder) super.clone(); + } catch (CloneNotSupportedException e) { + throw new IllegalStateException(e); + } + } + public int getMode() { return mMode; } diff --git a/app/src/main/java/com/hippo/ehviewer/ui/ContentActivity.java b/app/src/main/java/com/hippo/ehviewer/ui/ContentActivity.java index 53da83cb7..46dfa6521 100644 --- a/app/src/main/java/com/hippo/ehviewer/ui/ContentActivity.java +++ b/app/src/main/java/com/hippo/ehviewer/ui/ContentActivity.java @@ -88,12 +88,12 @@ public final class ContentActivity extends StageActivity case DRAWER_LIST_HOMEPAGE: announcer = new Announcer(); announcer.putExtra(GalleryListScene.KEY_MODE, GalleryListScene.MODE_HOMEPAGE); - startScene(GalleryListScene.class); + startScene(GalleryListScene.class, announcer); break; case DRAWER_LIST_WHATS_HOT: announcer = new Announcer(); announcer.putExtra(GalleryListScene.KEY_MODE, GalleryListScene.MODE_POPULAR); - startScene(GalleryListScene.class); + startScene(GalleryListScene.class, announcer); break; case DRAWER_LIST_HISTORY: break; @@ -324,6 +324,22 @@ public final class ContentActivity extends StageActivity mDrawerLayout.closeDrawers(); } + public void lockLeftDrawer() { + mDrawerLayout.setDrawerLockMode(DrawerLayout.LOCK_MODE_LOCKED_CLOSED, Gravity.LEFT); + } + + public void lockRightDrawer() { + mDrawerLayout.setDrawerLockMode(DrawerLayout.LOCK_MODE_LOCKED_CLOSED, Gravity.RIGHT); + } + + public void unlockLeftDrawer() { + mDrawerLayout.setDrawerLockMode(DrawerLayout.LOCK_MODE_UNLOCKED, Gravity.LEFT); + } + + public void unlockRightDrawer() { + mDrawerLayout.setDrawerLockMode(DrawerLayout.LOCK_MODE_UNLOCKED, Gravity.RIGHT); + } + @Override public void onFitPaddingBottom(int bottom) { setFitPaddingBottom(bottom); 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 16b8f0861..e2b5d8161 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 @@ -34,7 +34,9 @@ import com.hippo.ehviewer.client.data.GalleryBase; import com.hippo.ehviewer.service.DownloadManager; import com.hippo.ehviewer.service.DownloadService; import com.hippo.ehviewer.ui.ContentActivity; +import com.hippo.ehviewer.util.DBUtils; import com.hippo.ehviewer.util.EhUtils; +import com.hippo.ehviewer.widget.AppbarRecyclerView; import com.hippo.ehviewer.widget.LoadImageView; import com.hippo.ehviewer.widget.SimpleRatingView; import com.hippo.rippleold.RippleSalon; @@ -61,11 +63,27 @@ public class DownloadScene extends Scene implements DrawerProvider, private int mLastUpdateSize = -1; + private AppbarRecyclerView mRightDrawerView; + private DownloadTagAdapter mDownloadTagAdapter; + private List mTags; + @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; + } + @Override protected void onInit() { super.onInit(); @@ -73,6 +91,8 @@ public class DownloadScene extends Scene implements DrawerProvider, mDownloadInfos = DownloadManager.getInstance().getDownloadList(null); mGidPositionMap = new GidPositionMap(); Messenger.getInstance().register(Constants.MESSENGER_ID_UPDATE_DOWNLOAD, this); + + mTags = getTags(); } @Override @@ -97,10 +117,16 @@ public class DownloadScene extends Scene implements DrawerProvider, mRecyclerView.setLayoutManager(new LinearLayoutManager(getStageActivity())); mRecyclerView.setOnItemClickListener(this); mRecyclerView.setHasFixedSize(true); + mRecyclerView.setChoiceMode(EasyRecyclerView.CHOICE_MODE_MULTIPLE_CUSTOM); + mRecyclerView.setCustomCheckedListener(new DownloadChoiceListener()); mOriginalPaddingBottom = mRecyclerView.getPaddingBottom(); - } + mRightDrawerView = new AppbarRecyclerView(getStageActivity()); + mRightDrawerView.setTitle("TAG"); + mDownloadTagAdapter = new DownloadTagAdapter(); + mRightDrawerView.setAdapter(mDownloadTagAdapter); + } @Override protected void onResume() { @@ -109,6 +135,15 @@ public class DownloadScene extends Scene implements DrawerProvider, ((ContentActivity) getStageActivity()).setDrawerListActivatedPosition(ContentActivity.DRAWER_LIST_DOWNLOAD); } + @Override + public void onBackPressed() { + if (mRecyclerView.inCustomChoice()) { + mRecyclerView.outOfCustomChoiceMode(); + } else { + super.onBackPressed(); + } + } + @Override protected void onGetFitPaddingBottom(int b) { super.onGetFitPaddingBottom(b); @@ -124,20 +159,49 @@ public class DownloadScene extends Scene implements DrawerProvider, @Override public void bindRightDrawer(ContentActivity activity) { - // TODO - activity.clearRightDrawerView(); + activity.setRightDrawerView(mRightDrawerView); } @Override public boolean onItemClick(EasyRecyclerView parent, View view, int position, long id) { - DownloadInfo info = mDownloadInfos.get(position); - Announcer announcer = new Announcer(); - announcer.setAction(GalleryDetailScene.ACTION_GALLERY_BASE); - announcer.putExtra(GalleryDetailScene.KEY_GALLERY_BASE, info.galleryBase); - startScene(GalleryDetailScene.class, announcer); + if (parent.inCustomChoice()) { + parent.toggleItemChecked(position); + } else { + DownloadInfo info = mDownloadInfos.get(position); + Announcer announcer = new Announcer(); + announcer.setAction(GalleryDetailScene.ACTION_GALLERY_BASE); + announcer.putExtra(GalleryDetailScene.KEY_GALLERY_BASE, info.galleryBase); + startScene(GalleryDetailScene.class, announcer); + } return true; } + private View.OnClickListener mThumbClickListener = new View.OnClickListener() { + + private int getAdapterPosition(View thumbView) { + EasyRecyclerView recyclerView = mRecyclerView; + for (int i = 0, n = recyclerView.getChildCount(); i < n; i++) { + View child = recyclerView.getChildAt(i); + DownloadHolder holder = (DownloadHolder) recyclerView.getChildViewHolder(child); + if (thumbView == holder.thumb) { + return holder.getAdapterPosition(); + } + } + return -1; + } + + @Override + public void onClick(View v) { + int position = getAdapterPosition(v); + if (position >= 0) { + if (!mRecyclerView.inCustomChoice()) { + mRecyclerView.intoCustomChoiceMode(); + } + mRecyclerView.toggleItemChecked(position); + } + } + }; + private View.OnClickListener mStartListener = new View.OnClickListener() { private int getAdapterPosition(View startView) { @@ -154,6 +218,10 @@ public class DownloadScene extends Scene implements DrawerProvider, @Override public void onClick(View v) { + if (mRecyclerView.inCustomChoice()) { + return; + } + int position = getAdapterPosition(v); if (position >= 0) { // Add to cache @@ -184,6 +252,10 @@ public class DownloadScene extends Scene implements DrawerProvider, @Override public void onClick(View v) { + if (mRecyclerView.inCustomChoice()) { + return; + } + int position = getAdapterPosition(v); if (position >= 0) { // Add to cache @@ -214,6 +286,10 @@ public class DownloadScene extends Scene implements DrawerProvider, @Override public void onClick(View v) { + if (mRecyclerView.inCustomChoice()) { + return; + } + int position = getAdapterPosition(v); if (position >= 0) { // Add to cache @@ -322,6 +398,7 @@ public class DownloadScene extends Scene implements DrawerProvider, RippleSalon.addRipple(stop, false); RippleSalon.addRipple(delete, false); + thumb.setOnClickListener(mThumbClickListener); start.setOnClickListener(mStartListener); stop.setOnClickListener(mStopListener); delete.setOnClickListener(mDeleteListener); @@ -469,4 +546,48 @@ public class DownloadScene extends Scene implements DrawerProvider, return -1; } } + + public class DownloadChoiceListener implements EasyRecyclerView.CustomChoiceListener { + + @Override + public void onIntoCustomChoice(EasyRecyclerView view) { + } + + @Override + public void onOutOfCustomChoice(EasyRecyclerView view) { + } + + @Override + public void onItemCheckedStateChanged(EasyRecyclerView view, int position, long id, boolean checked) { + if (view.getCheckedItemCount() == 0) { + view.outOfCustomChoiceMode(); + } + } + } + + private class SimpleHolder extends RecyclerView.ViewHolder { + + public SimpleHolder(View itemView) { + super(itemView); + } + } + + public class DownloadTagAdapter extends RecyclerView.Adapter { + + @Override + public SimpleHolder onCreateViewHolder(ViewGroup parent, int viewType) { + return new SimpleHolder(getStageActivity().getLayoutInflater().inflate( + R.layout.item_quick_search, parent, false)); + } + + @Override + public void onBindViewHolder(SimpleHolder holder, int position) { + ((TextView) holder.itemView).setText(mTags.get(position)); + } + + @Override + public int getItemCount() { + return mTags == null ? 0 : mTags.size(); + } + } } 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 2d30912c3..9df9c6d05 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 @@ -24,7 +24,6 @@ import android.content.Context; import android.content.res.Resources; import android.graphics.drawable.Drawable; import android.support.annotation.NonNull; -import android.support.v7.widget.LinearLayoutManager; import android.support.v7.widget.RecyclerView; import android.support.v7.widget.StaggeredGridLayoutManager; import android.text.TextUtils; @@ -57,6 +56,7 @@ import com.hippo.ehviewer.ui.ContentActivity; import com.hippo.ehviewer.util.DBUtils; import com.hippo.ehviewer.util.EhUtils; import com.hippo.ehviewer.util.Settings; +import com.hippo.ehviewer.widget.AppbarRecyclerView; import com.hippo.ehviewer.widget.ContentLayout; import com.hippo.ehviewer.widget.ListSearchBar; import com.hippo.ehviewer.widget.LoadImageView; @@ -65,7 +65,6 @@ import com.hippo.ehviewer.widget.SearchBar; import com.hippo.ehviewer.widget.SearchDatabase; import com.hippo.ehviewer.widget.SearchLayout; import com.hippo.ehviewer.widget.SimpleRatingView; -import com.hippo.rippleold.RippleSalon; import com.hippo.scene.Announcer; import com.hippo.scene.Curtain; import com.hippo.scene.ElementsSharedCurtain; @@ -75,9 +74,7 @@ import com.hippo.util.AnimationUtils; import com.hippo.widget.FabLayout; import com.hippo.widget.FloatingActionButton; import com.hippo.widget.recyclerview.EasyRecyclerView; -import com.hippo.widget.recyclerview.LinearDividerItemDecoration; import com.hippo.yorozuya.AssertUtils; -import com.hippo.yorozuya.LayoutUtils; import com.hippo.yorozuya.MathUtils; import com.hippo.yorozuya.Messenger; import com.hippo.yorozuya.SimpleHandler; @@ -92,7 +89,7 @@ import java.util.List; public final class GalleryListScene extends Scene implements ListSearchBar.Helper, View.OnClickListener, FabLayout.OnCancelListener, SearchLayout.SearhLayoutHelper, EasyRecyclerView.OnItemClickListener, - Messenger.Receiver, DrawerProvider { + Messenger.Receiver, DrawerProvider, AppbarRecyclerView.Helper { public static final String KEY_MODE = "mode"; @@ -148,10 +145,7 @@ public final class GalleryListScene extends Scene implements ListSearchBar.Helpe private boolean mJpnTitle; - - private ViewGroup mRightDrawerView; - private View mQuickSearchSettings; - private EasyRecyclerView mQuickSearchView; + private AppbarRecyclerView mRightDrawerView; private QuickSearchAdapter mQuickSearchAdapter; private List mQuickSearches; @@ -330,28 +324,13 @@ public final class GalleryListScene extends Scene implements ListSearchBar.Helpe mSearchDrawable = mResources.getDrawable(R.drawable.ic_search_dark); mCornerFab.setDrawable(mAddDeleteDrawable); - mRightDrawerView = (ViewGroup) getStageActivity().getLayoutInflater().inflate(R.layout.drawer_right_gallery_list, null); - mQuickSearchSettings = mRightDrawerView.findViewById(R.id.settings); - RippleSalon.addRipple(mQuickSearchSettings, true); - mQuickSearchSettings.setOnClickListener(this); - TextView rightDrawerTitle = (TextView) mRightDrawerView.findViewById(R.id.drawer_right_title); - rightDrawerTitle.setText(R.string.quick_search); - mQuickSearchView = (EasyRecyclerView) mRightDrawerView.findViewById(R.id.drawer_right_recycler_view); + mRightDrawerView = new AppbarRecyclerView(getStageActivity()); + mRightDrawerView.setTitle(mResources.getString(R.string.quick_search)); mQuickSearches = DBUtils.getAllQuickSearch(); mQuickSearchAdapter = new QuickSearchAdapter(); - mQuickSearchView.setAdapter(mQuickSearchAdapter); - mQuickSearchView.setLayoutManager(new LinearLayoutManager(getStageActivity())); - mQuickSearchView.setSelector(RippleSalon.generateRippleDrawable(false)); - mQuickSearchView.setDrawSelectorOnTop(true); - mQuickSearchView.setOnItemClickListener(new QuickSearchClickListener()); - mQuickSearchView.hasFixedSize(); - mQuickSearchView.setClipToPadding(false); - LinearDividerItemDecoration decoration = new LinearDividerItemDecoration( - LinearDividerItemDecoration.VERTICAL, - mResources.getColor(R.color.divider_light), - LayoutUtils.dp2pix(getStageActivity(), 1)); - decoration.setOverlap(true); - mQuickSearchView.addItemDecoration(decoration); + mRightDrawerView.setAdapter(mQuickSearchAdapter); + mRightDrawerView.setOnItemClickListener(new QuickSearchClickListener()); + mRightDrawerView.setHelper(this); // When scene start if (!rebirth) { @@ -405,7 +384,8 @@ public final class GalleryListScene extends Scene implements ListSearchBar.Helpe mSearchLayout.setFitPaddingBottom(b); // Corner Fab mFabLayout.setPadding(mFabLayout.getPaddingLeft(), mFabLayout.getPaddingTop(), mFabLayout.getPaddingRight(), mCornerFabOriginalBottom + b); - mQuickSearchView.setPadding(0, 0, 0, b); + // + mRightDrawerView.setFitPaddingBottom(b); } @Override @@ -608,11 +588,29 @@ public final class GalleryListScene extends Scene implements ListSearchBar.Helpe if (mGalleryListHelper.canGoTo()) { showGoToDialog(); } - } else if (v == mQuickSearchSettings) { - startScene(QuickSearchScene.class); } } + @Override + public void onClickPlusListener() { + int mode = mListUrlBuilder.getMode(); + if (mode == ListUrlBuilder.MODE_NORMAL || mode == ListUrlBuilder.MODE_UPLOADER || + 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) + .setPositiveButton(android.R.string.ok) + .setOnButtonClickListener(helper).show(this); + } else { + Toast.makeText(getStageActivity(), "Can't add", Toast.LENGTH_SHORT).show(); // TODO hardcode + } + } + + @Override + public void onClickSettingsListener() { + startScene(QuickSearchScene.class); + } + @Override public void onCancel(FabLayout fabLayout) { mAddDeleteDrawable.setAdd(ANIMATE_TIME); diff --git a/app/src/main/java/com/hippo/ehviewer/widget/AppbarRecyclerView.java b/app/src/main/java/com/hippo/ehviewer/widget/AppbarRecyclerView.java new file mode 100644 index 000000000..75f86d70d --- /dev/null +++ b/app/src/main/java/com/hippo/ehviewer/widget/AppbarRecyclerView.java @@ -0,0 +1,138 @@ +/* + * 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.widget; + +import android.content.Context; +import android.support.v7.widget.LinearLayoutManager; +import android.support.v7.widget.RecyclerView; +import android.util.AttributeSet; +import android.view.LayoutInflater; +import android.view.View; +import android.widget.LinearLayout; +import android.widget.TextView; + +import com.hippo.ehviewer.R; +import com.hippo.rippleold.RippleSalon; +import com.hippo.widget.recyclerview.EasyRecyclerView; +import com.hippo.widget.recyclerview.LinearDividerItemDecoration; +import com.hippo.yorozuya.LayoutUtils; + +public class AppbarRecyclerView extends LinearLayout implements View.OnClickListener { + + public TextView mTitle; + public View mPlus; + public View mSettings; + public EasyRecyclerView mRecyclerView; + + public int mOriginalPaddingBottom; + + public Helper mHelper; + + public AppbarRecyclerView(Context context) { + super(context); + init(context); + } + + public AppbarRecyclerView(Context context, AttributeSet attrs) { + super(context, attrs); + init(context); + } + + public AppbarRecyclerView(Context context, AttributeSet attrs, int defStyleAttr) { + super(context, attrs, defStyleAttr); + init(context); + } + + public void init(Context context) { + setOrientation(VERTICAL); + + LayoutInflater.from(context).inflate(R.layout.widget_appbar_recycler_view, this); + mTitle = (TextView) findViewById(R.id.title); + mPlus = findViewById(R.id.plus); + mSettings = findViewById(R.id.settings); + mRecyclerView = (EasyRecyclerView) findViewById(R.id.recycler_view); + + mRecyclerView.setHasFixedSize(true); + mRecyclerView.setClipToPadding(false); + mRecyclerView.setLayoutManager(new LinearLayoutManager(context)); + mRecyclerView.setSelector(RippleSalon.generateRippleDrawable(false)); + mRecyclerView.setDrawSelectorOnTop(true); + LinearDividerItemDecoration decoration = new LinearDividerItemDecoration( + LinearDividerItemDecoration.VERTICAL, + context.getResources().getColor(R.color.divider_light), + LayoutUtils.dp2pix(context, 1)); + decoration.setOverlap(true); + mRecyclerView.addItemDecoration(decoration); + + mPlus.setOnClickListener(this); + mSettings.setOnClickListener(this); + + RippleSalon.addRipple(mPlus, true); + RippleSalon.addRipple(mSettings, true); + + mOriginalPaddingBottom = mRecyclerView.getPaddingBottom(); + } + + public void setHelper(Helper helper) { + mHelper = helper; + } + + public void setTitle(String title) { + mTitle.setText(title); + } + + public void setAdapter(RecyclerView.Adapter adapter) { + mRecyclerView.setAdapter(adapter); + } + + public void setOnItemClickListener(EasyRecyclerView.OnItemClickListener listener) { + mRecyclerView.setOnItemClickListener(listener); + } + + public void setFitPaddingBottom(int fitPaddingBottom) { + mRecyclerView.setPadding(mRecyclerView.getPaddingLeft(), mRecyclerView.getPaddingTop(), + mRecyclerView.getPaddingRight(), mOriginalPaddingBottom + fitPaddingBottom); + } + + public void setPlusVisibility(int visibility) { + mPlus.setVisibility(visibility); + } + + public void setSettingsVisibility(int visibility) { + mSettings.setVisibility(visibility); + } + + @Override + public void onClick(View v) { + if (mPlus == v) { + if (mHelper != null) { + mHelper.onClickPlusListener(); + } + } else if (mSettings == v) { + if (mHelper != null) { + mHelper.onClickSettingsListener(); + } + } + } + + public interface Helper { + + void onClickPlusListener(); + + void onClickSettingsListener(); + } +} diff --git a/app/src/main/java/com/hippo/widget/CardButton.java b/app/src/main/java/com/hippo/widget/CardButton.java index f07c7cd81..4572cc298 100644 --- a/app/src/main/java/com/hippo/widget/CardButton.java +++ b/app/src/main/java/com/hippo/widget/CardButton.java @@ -1,6 +1,7 @@ package com.hippo.widget; import android.content.Context; +import android.content.res.ColorStateList; import android.graphics.drawable.Drawable; import android.util.AttributeSet; import android.widget.Button; @@ -67,12 +68,12 @@ public class CardButton extends Button implements CardViewDelegate { } @Override - public void setCardBackgroundColor(int color) { + public void setCardBackgroundColor(ColorStateList color) { mCardHelper.setCardBackgroundColor(color); } @Override - public int getCardBackgroundColor() { + public ColorStateList getCardBackgroundColor() { return mCardHelper.getCardBackgroundColor(); } @@ -87,12 +88,12 @@ public class CardButton extends Button implements CardViewDelegate { } @Override - public void setCardBoundColor(int color) { + public void setCardBoundColor(ColorStateList color) { mCardHelper.setCardBoundColor(color); } @Override - public int getCardBoundColor() { + public ColorStateList getCardBoundColor() { return mCardHelper.getCardBoundColor(); } diff --git a/app/src/main/java/com/hippo/widget/CardLinearLayout.java b/app/src/main/java/com/hippo/widget/CardLinearLayout.java index a3d17aa51..3059d829c 100644 --- a/app/src/main/java/com/hippo/widget/CardLinearLayout.java +++ b/app/src/main/java/com/hippo/widget/CardLinearLayout.java @@ -1,6 +1,7 @@ package com.hippo.widget; import android.content.Context; +import android.content.res.ColorStateList; import android.graphics.drawable.Drawable; import android.util.AttributeSet; import android.widget.LinearLayout; @@ -67,12 +68,12 @@ public class CardLinearLayout extends LinearLayout implements CardViewDelegate { } @Override - public void setCardBackgroundColor(int color) { + public void setCardBackgroundColor(ColorStateList color) { mCardHelper.setCardBackgroundColor(color); } @Override - public int getCardBackgroundColor() { + public ColorStateList getCardBackgroundColor() { return mCardHelper.getCardBackgroundColor(); } @@ -87,12 +88,12 @@ public class CardLinearLayout extends LinearLayout implements CardViewDelegate { } @Override - public void setCardBoundColor(int color) { + public void setCardBoundColor(ColorStateList color) { mCardHelper.setCardBoundColor(color); } @Override - public int getCardBoundColor() { + public ColorStateList getCardBoundColor() { return mCardHelper.getCardBoundColor(); } 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 ab543f127..7dcf10def 100644 --- a/app/src/main/java/com/hippo/widget/recyclerview/EasyRecyclerView.java +++ b/app/src/main/java/com/hippo/widget/recyclerview/EasyRecyclerView.java @@ -78,6 +78,11 @@ public class EasyRecyclerView extends RecyclerView { */ public static final int CHOICE_MODE_MULTIPLE_MODAL = 3; + /** + * The list allows multiple choices in custom action + */ + public static final int CHOICE_MODE_MULTIPLE_CUSTOM = 4; + /** * Controls if/how the user may choose/check items in the list */ @@ -94,6 +99,17 @@ public class EasyRecyclerView extends RecyclerView { */ MultiChoiceModeWrapper mMultiChoiceModeCallback; + /** + * Listener for custom multiple choices + */ + private CustomChoiceListener mCustomChoiceListener; + + private boolean mCustomChoice = false; + + private SparseBooleanArray mTempCheckStates; + + private FixCheckedHelper mFixCheckedHelper; + /** * Running count of how many items are currently checked */ @@ -491,6 +507,38 @@ public class EasyRecyclerView extends RecyclerView { return ids; } + public boolean inCustomChoice() { + return mCustomChoice; + } + + public void intoCustomChoiceMode() { + if (mChoiceMode == CHOICE_MODE_MULTIPLE_CUSTOM && !mCustomChoice) { + mCustomChoice = true; + + mCustomChoiceListener.onIntoCustomChoice(this); + } + } + + public void outOfCustomChoiceMode() { + if (mChoiceMode == CHOICE_MODE_MULTIPLE_CUSTOM && mCustomChoice) { + + // Copy mCheckStates + mTempCheckStates.clear(); + for (int i = 0, n = mCheckStates.size(); i < n; i++) { + mTempCheckStates.put(mCheckStates.keyAt(i), mCheckStates.valueAt(i)); + } + // Uncheck remain checked items + for (int i = 0, n = mTempCheckStates.size(); i < n; i++) { + if (mTempCheckStates.valueAt(i)) { + setItemChecked(mTempCheckStates.keyAt(i), false); + } + } + + mCustomChoice = false; + mCustomChoiceListener.onOutOfCustomChoice(this); + } + } + /** * Clear any choices previously set */ @@ -505,6 +553,12 @@ public class EasyRecyclerView extends RecyclerView { updateOnScreenCheckedViews(); } + public void toggleItemChecked(int position) { + if (mCheckStates != null) { + setItemChecked(position, !mCheckStates.get(position)); + } + } + /** * Sets the checked state of the specified position. The is only valid if * the choice mode has been set to {@link #CHOICE_MODE_SINGLE} or @@ -518,6 +572,11 @@ public class EasyRecyclerView extends RecyclerView { return; } + // Check is intoCheckMode + if (mChoiceMode == CHOICE_MODE_MULTIPLE_CUSTOM && !mCustomChoice) { + throw new IllegalStateException("Call intoCheckMode first"); + } + // Start selection mode if needed. We don't need to if we're unchecking something. if (value && mChoiceMode == CHOICE_MODE_MULTIPLE_MODAL && mChoiceActionMode == null) { if (mMultiChoiceModeCallback == null || @@ -529,7 +588,7 @@ public class EasyRecyclerView extends RecyclerView { mChoiceActionMode = startActionMode(mMultiChoiceModeCallback); } - if (mChoiceMode == CHOICE_MODE_MULTIPLE || mChoiceMode == CHOICE_MODE_MULTIPLE_MODAL) { + if (mChoiceMode == CHOICE_MODE_MULTIPLE || mChoiceMode == CHOICE_MODE_MULTIPLE_MODAL || mChoiceMode == CHOICE_MODE_MULTIPLE_CUSTOM) { boolean oldValue = mCheckStates.get(position); mCheckStates.put(position, value); if (mCheckedIdStates != null && mAdapter.hasStableIds()) { @@ -551,6 +610,10 @@ public class EasyRecyclerView extends RecyclerView { mMultiChoiceModeCallback.onItemCheckedStateChanged(mChoiceActionMode, position, id, value); } + if (mChoiceMode == CHOICE_MODE_MULTIPLE_CUSTOM) { + final long id = mAdapter.getItemId(position); + mCustomChoiceListener.onItemCheckedStateChanged(this, position, id, value); + } } else { boolean updateIds = mCheckedIdStates != null && mAdapter.hasStableIds(); // Clear all values if we're checking something, or unchecking the currently @@ -608,10 +671,19 @@ public class EasyRecyclerView extends RecyclerView { if (mCheckedIdStates == null && mAdapter != null && mAdapter.hasStableIds()) { mCheckedIdStates = new LongSparseArray<>(0); } + if (mFixCheckedHelper == null) { + mFixCheckedHelper = new FixCheckedHelper(); + addOnChildAttachStateChangeListener(mFixCheckedHelper); + } // Modal multi-choice mode only has choices when the mode is active. Clear them. if (mChoiceMode == CHOICE_MODE_MULTIPLE_MODAL) { clearChoices(); setLongClickable(true); + } else if (mChoiceMode == CHOICE_MODE_MULTIPLE_CUSTOM) { + if (mTempCheckStates == null) { + mTempCheckStates = new SparseBooleanArray(0); + } + clearChoices(); } } } @@ -632,6 +704,14 @@ public class EasyRecyclerView extends RecyclerView { mMultiChoiceModeCallback.setWrapped(listener); } + /** + * + * @param listener + */ + public void setCustomCheckedListener(CustomChoiceListener listener) { + mCustomChoiceListener = listener; + } + /** * Perform a quick, in-place update of the checked or activated state * on all visible item views. This should only be called when a valid @@ -850,7 +930,7 @@ public class EasyRecyclerView extends RecyclerView { boolean handled = false; boolean dispatchItemClick = true; - if (mChoiceMode != CHOICE_MODE_NONE) { + if (mChoiceMode != CHOICE_MODE_NONE && mChoiceMode != CHOICE_MODE_MULTIPLE_CUSTOM) { handled = true; boolean checkedStateChanged = false; @@ -952,13 +1032,6 @@ public class EasyRecyclerView extends RecyclerView { mOnItemLongClickListener = listener; } - /** - * If choice mode is set, call it in {@link Adapter#onBindViewHolder} - */ - public void checkItemCheckedState(View view, int position) { - setViewChecked(view, isItemChecked(position)); - } - /** * This saved state class is a Parcelable and should not extend * {@link android.view.View.BaseSavedState} nor {@link android.view.AbsSavedState} @@ -1274,6 +1347,22 @@ public class EasyRecyclerView extends RecyclerView { boolean beforeDrawSelector(int position); } + public class FixCheckedHelper implements OnChildAttachStateChangeListener { + + @Override + public void onChildViewAttachedToWindow(View view) { + int position = getChildAdapterPosition(view); + if (position >= 0) { + setViewChecked(view, mCheckStates.get(position)); + } + } + + @Override + public void onChildViewDetachedFromWindow(View view) { + + } + } + /** * A MultiChoiceModeListener receives events for {@link android.widget.AbsListView#CHOICE_MODE_MULTIPLE_MODAL}. * It acts as the {@link android.support.v7.view.ActionMode.Callback} for the selection mode and also receives @@ -1349,4 +1438,16 @@ public class EasyRecyclerView extends RecyclerView { } } } + + /** + * Custom checked + */ + public interface CustomChoiceListener { + + void onIntoCustomChoice(EasyRecyclerView view); + + void onOutOfCustomChoice(EasyRecyclerView view); + + void onItemCheckedStateChanged(EasyRecyclerView view, int position, long id, boolean checked); + } } diff --git a/app/src/main/res/color/card_background.xml b/app/src/main/res/color/card_background.xml new file mode 100644 index 000000000..2bdc18fd6 --- /dev/null +++ b/app/src/main/res/color/card_background.xml @@ -0,0 +1,23 @@ + + + + + + + + \ No newline at end of file diff --git a/app/src/main/res/drawable/ic_plus_dark_x24.xml b/app/src/main/res/drawable/ic_plus_dark_x24.xml new file mode 100644 index 000000000..3c21248f2 --- /dev/null +++ b/app/src/main/res/drawable/ic_plus_dark_x24.xml @@ -0,0 +1,40 @@ + + + + + + + diff --git a/app/src/main/res/layout-v21/widget_appbar_recycler_view.xml b/app/src/main/res/layout-v21/widget_appbar_recycler_view.xml new file mode 100644 index 000000000..296788e89 --- /dev/null +++ b/app/src/main/res/layout-v21/widget_appbar_recycler_view.xml @@ -0,0 +1,72 @@ + + + + + + + + + + + + + + + + + + + + + + + diff --git a/app/src/main/res/layout/item_download.xml b/app/src/main/res/layout/item_download.xml index 23ca52b6f..bbc506cd6 100644 --- a/app/src/main/res/layout/item_download.xml +++ b/app/src/main/res/layout/item_download.xml @@ -19,6 +19,7 @@ xmlns:auto="http://schemas.android.com/apk/res-auto" android:layout_width="wrap_content" android:layout_height="wrap_content" + auto:cardBackgroundColor="@color/card_background" auto:cardElevation="4dp" auto:forceNoElevation="true"> diff --git a/app/src/main/res/layout/widget_appbar_recycler_view.xml b/app/src/main/res/layout/widget_appbar_recycler_view.xml new file mode 100644 index 000000000..fa2dcd855 --- /dev/null +++ b/app/src/main/res/layout/widget_appbar_recycler_view.xml @@ -0,0 +1,77 @@ + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/app/src/main/res/values/pathdata.xml b/app/src/main/res/values/pathdata.xml index 5f90920d2..70636e3a4 100644 --- a/app/src/main/res/values/pathdata.xml +++ b/app/src/main/res/values/pathdata.xml @@ -51,6 +51,7 @@ M8,5.14V19.14L19,12.14L8,5.14Z 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 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/cardsalon b/cardsalon index 18444fc75..e4a8465f0 160000 --- a/cardsalon +++ b/cardsalon @@ -1 +1 @@ -Subproject commit 18444fc75ef70a335884f395d1ddf5080e216ed5 +Subproject commit e4a8465f0b0a9762711313d1e7be203afb891e0e