Improve GalleryDetailScene
This commit is contained in:
1 parent
7b13da66af
commit
056a6eb394
59 files changed
+3451
-212
No files matched your search
@@ -17,29 +17,38 @@
|
||||
package com.hippo.ehviewer;
|
||||
|
||||
import android.app.ActivityManager;
|
||||
import android.app.Application;
|
||||
import android.content.Context;
|
||||
import android.support.annotation.NonNull;
|
||||
import android.util.SparseArray;
|
||||
|
||||
import com.hippo.beerbelly.LruMap;
|
||||
import com.hippo.conaco.Conaco;
|
||||
import com.hippo.drawable.ImageWrapper;
|
||||
import com.hippo.ehviewer.client.EhClient;
|
||||
import com.hippo.ehviewer.client.EhCookieStore;
|
||||
import com.hippo.ehviewer.client.data.GalleryDetail;
|
||||
import com.hippo.network.StatusCodeException;
|
||||
import com.hippo.okhttp.CookieDB;
|
||||
import com.hippo.scene.SceneApplication;
|
||||
import com.hippo.utils.ReadableTime;
|
||||
import com.hippo.yorozuya.IntIdGenerator;
|
||||
|
||||
import java.io.File;
|
||||
import java.util.Comparator;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
|
||||
import okhttp3.OkHttpClient;
|
||||
|
||||
public class EhApplication extends Application {
|
||||
public class EhApplication extends SceneApplication {
|
||||
|
||||
private IntIdGenerator mIdGenerator = new IntIdGenerator();
|
||||
private SparseArray<Object> mGlobalStuffMap = new SparseArray<>();
|
||||
private EhCookieStore mEhCookieStore;
|
||||
private EhClient mEhClient;
|
||||
private OkHttpClient mOkHttpClient;
|
||||
private ImageWrapperHelper mImageWrapperHelper;
|
||||
private Conaco<ImageWrapper> mConaco;
|
||||
private LruMap<Integer, GalleryDetail> mGalleryDetailCache;
|
||||
|
||||
@Override
|
||||
public void onCreate() {
|
||||
@@ -49,6 +58,42 @@ public class EhApplication extends Application {
|
||||
CookieDB.initialize(this);
|
||||
StatusCodeException.initialize(this);
|
||||
Settings.initialize(this);
|
||||
ReadableTime.initialize(this);
|
||||
}
|
||||
|
||||
public int putGlobalStuff(@NonNull Object o) {
|
||||
int id = mIdGenerator.nextId();
|
||||
mGlobalStuffMap.put(id, o);
|
||||
return id;
|
||||
}
|
||||
|
||||
public boolean containGlobalStuff(int id) {
|
||||
return mGlobalStuffMap.indexOfKey(id) >= 0;
|
||||
}
|
||||
|
||||
public Object getGlobalStuff(int id) {
|
||||
return mGlobalStuffMap.get(id);
|
||||
}
|
||||
|
||||
public Object removeGlobalStuff(int id) {
|
||||
int index = mGlobalStuffMap.indexOfKey(id);
|
||||
if (index >= 0) {
|
||||
Object o = mGlobalStuffMap.valueAt(index);
|
||||
mGlobalStuffMap.removeAt(index);
|
||||
return o;
|
||||
} else {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
public boolean removeGlobalStuff(Object o) {
|
||||
int index = mGlobalStuffMap.indexOfValue(o);
|
||||
if (index >= 0) {
|
||||
mGlobalStuffMap.removeAt(index);
|
||||
return true;
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
public static EhCookieStore getEhCookieStore(@NonNull Context context) {
|
||||
@@ -115,4 +160,18 @@ public class EhApplication extends Application {
|
||||
}
|
||||
return application.mConaco;
|
||||
}
|
||||
|
||||
@NonNull
|
||||
public static LruMap<Integer, GalleryDetail> getGalleryDetailCache(@NonNull Context context) {
|
||||
EhApplication application = ((EhApplication) context.getApplicationContext());
|
||||
if (application.mGalleryDetailCache == null) {
|
||||
application.mGalleryDetailCache = new LruMap<>(new Comparator<Integer>() {
|
||||
@Override
|
||||
public int compare(Integer lhs, Integer rhs) {
|
||||
return lhs - rhs;
|
||||
}
|
||||
}, 3 * 60 * 1000); // 3 min timeout
|
||||
}
|
||||
return application.mGalleryDetailCache;
|
||||
}
|
||||
}
|
||||
@@ -17,29 +17,48 @@
|
||||
package com.hippo.ehviewer.ui.scene;
|
||||
|
||||
import android.annotation.TargetApi;
|
||||
import android.content.Context;
|
||||
import android.content.res.Resources;
|
||||
import android.os.Build;
|
||||
import android.os.Bundle;
|
||||
import android.support.annotation.Nullable;
|
||||
import android.support.v7.widget.LinearLayoutManager;
|
||||
import android.support.v7.widget.RecyclerView;
|
||||
import android.support.v7.widget.PopupMenu;
|
||||
import android.text.TextUtils;
|
||||
import android.view.LayoutInflater;
|
||||
import android.view.MenuItem;
|
||||
import android.view.View;
|
||||
import android.view.ViewGroup;
|
||||
import android.widget.LinearLayout;
|
||||
import android.widget.RatingBar;
|
||||
import android.widget.ScrollView;
|
||||
import android.widget.TextView;
|
||||
|
||||
import com.hippo.easyrecyclerview.SimpleHolder;
|
||||
import com.hippo.ehviewer.EhApplication;
|
||||
import com.hippo.ehviewer.EhCacheKeyFactory;
|
||||
import com.hippo.ehviewer.R;
|
||||
import com.hippo.ehviewer.client.EhClient;
|
||||
import com.hippo.ehviewer.client.EhRequest;
|
||||
import com.hippo.ehviewer.client.EhUrl;
|
||||
import com.hippo.ehviewer.client.EhUtils;
|
||||
import com.hippo.ehviewer.client.data.GalleryComment;
|
||||
import com.hippo.ehviewer.client.data.GalleryDetail;
|
||||
import com.hippo.ehviewer.client.data.GalleryInfo;
|
||||
import com.hippo.ehviewer.client.data.GalleryTagGroup;
|
||||
import com.hippo.ehviewer.client.parser.GalleryDetailParser;
|
||||
import com.hippo.rippleold.RippleSalon;
|
||||
import com.hippo.scene.SceneFragment;
|
||||
import com.hippo.scene.StageActivity;
|
||||
import com.hippo.text.Html;
|
||||
import com.hippo.utils.ApiHelper;
|
||||
import com.hippo.view.ViewTransition;
|
||||
import com.hippo.utils.ReadableTime;
|
||||
import com.hippo.vector.AnimatedVectorDrawable;
|
||||
import com.hippo.vector.VectorDrawable;
|
||||
import com.hippo.widget.AutoWrapLayout;
|
||||
import com.hippo.widget.LoadImageView;
|
||||
import com.hippo.widget.SimpleImageView;
|
||||
|
||||
// TODO Update drawer checked item
|
||||
public class GalleryDetailScene extends BaseScene {
|
||||
public class GalleryDetailScene extends BaseScene implements View.OnClickListener {
|
||||
|
||||
public final static String KEY_ACTION = "action";
|
||||
public static final String ACTION_GALLERY_INFO = "action_gallery_info";
|
||||
@@ -49,22 +68,13 @@ public class GalleryDetailScene extends BaseScene {
|
||||
public static final String KEY_GID = "gid";
|
||||
public static final String KEY_TOKEN = "token";
|
||||
|
||||
public static final int TYPE_HEADER = 0;
|
||||
public static final int TYPE_BUTTON = 1;
|
||||
public static final int TYPE_INFO = 2;
|
||||
public static final int TYPE_ACTION = 3;
|
||||
public static final int TYPE_TAG = 4;
|
||||
public static final int TYPE_COMMENT = 5;
|
||||
public static final int TYPE_PREVIEW = 6;
|
||||
public static final int TYPE_PROGRESS = 7;
|
||||
private static final String KEY_GALLERY_DETAIL = "gallery_detail";
|
||||
private static final String KEY_REQUEST_ID = "request_id";
|
||||
|
||||
private EhClient mClient;
|
||||
|
||||
private RecyclerView mRecyclerView;
|
||||
private View mMainProgressView;
|
||||
private ScrollView mMainView;
|
||||
private View mProgressView;
|
||||
private ViewGroup mFailedView;
|
||||
private TextView mFailedText;
|
||||
private ViewTransition mViewTransition;
|
||||
|
||||
// Header
|
||||
private View mHeader;
|
||||
@@ -72,15 +82,51 @@ public class GalleryDetailScene extends BaseScene {
|
||||
private TextView mTitle;
|
||||
private TextView mUploader;
|
||||
private TextView mCategory;
|
||||
private SimpleImageView mOtherActions;
|
||||
private ViewGroup mActionGroup;
|
||||
private View mDownload;
|
||||
private View mRead;
|
||||
// Below header
|
||||
private View mBelowHeader;
|
||||
// Info
|
||||
private View mInfo;
|
||||
private TextView mLanguage;
|
||||
private TextView mPages;
|
||||
private TextView mSize;
|
||||
private TextView mPosted;
|
||||
private TextView mFavoredTimes;
|
||||
// Actions
|
||||
private View mActions;
|
||||
private TextView mRatingText;
|
||||
private RatingBar mRating;
|
||||
private View mHeartGroup;
|
||||
private TextView mHeart;
|
||||
private TextView mHeartOutline;
|
||||
private TextView mTorrent;
|
||||
private TextView mShare;
|
||||
private TextView mRate;
|
||||
// Tags
|
||||
private LinearLayout mTags;
|
||||
private TextView mNoTags;
|
||||
// Comments
|
||||
private LinearLayout mComments;
|
||||
private TextView mCommentsText;
|
||||
|
||||
|
||||
// Progress
|
||||
private View mProgress;
|
||||
|
||||
private AnimatedVectorDrawable mHeartDrawable;
|
||||
private AnimatedVectorDrawable mHeartOutlineDrawable;
|
||||
private PopupMenu mPopupMenu;
|
||||
|
||||
private String mAction;
|
||||
private GalleryInfo mGalleryInfo;
|
||||
private int mGid;
|
||||
private String mKey;
|
||||
private String mToken;
|
||||
|
||||
public GalleryDetailScene() {
|
||||
|
||||
}
|
||||
private GalleryDetail mGalleryDetail;
|
||||
private int mRequestId;
|
||||
|
||||
private void handleArgs(Bundle args) {
|
||||
if (args == null) {
|
||||
@@ -93,7 +139,39 @@ public class GalleryDetailScene extends BaseScene {
|
||||
mGalleryInfo = args.getParcelable(KEY_GALLERY_INFO);
|
||||
} else if (ACTION_GID_TOKEN.equals(action)) {
|
||||
mGid = args.getInt(KEY_GID);
|
||||
mKey = args.getString(KEY_TOKEN);
|
||||
mToken = args.getString(KEY_TOKEN);
|
||||
}
|
||||
}
|
||||
|
||||
@Nullable
|
||||
private String getGalleryDetailUrl() {
|
||||
int gid;
|
||||
String token;
|
||||
if (mGalleryDetail != null) {
|
||||
gid = mGalleryDetail.gid;
|
||||
token = mGalleryDetail.token;
|
||||
} else if (mGalleryInfo != null) {
|
||||
gid = mGalleryInfo.gid;
|
||||
token = mGalleryInfo.token;
|
||||
} else if (ACTION_GID_TOKEN.equals(mAction)) {
|
||||
gid = mGid;
|
||||
token = mToken;
|
||||
} else {
|
||||
return null;
|
||||
}
|
||||
return EhUrl.getGalleryDetailUrl(gid, token);
|
||||
}
|
||||
|
||||
// -1 for error
|
||||
private int getGid() {
|
||||
if (mGalleryDetail != null) {
|
||||
return mGalleryDetail.gid;
|
||||
} else if (mGalleryInfo != null) {
|
||||
return mGalleryInfo.gid;
|
||||
} else if (ACTION_GID_TOKEN.equals(mAction)) {
|
||||
return mGid;
|
||||
} else {
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -101,39 +179,24 @@ public class GalleryDetailScene extends BaseScene {
|
||||
public void onCreate(@Nullable Bundle savedInstanceState) {
|
||||
super.onCreate(savedInstanceState);
|
||||
|
||||
mClient = EhApplication.getEhClient(getContext());
|
||||
if (savedInstanceState == null) {
|
||||
onInit();
|
||||
} else {
|
||||
onRestore(savedInstanceState);
|
||||
}
|
||||
}
|
||||
|
||||
private void onInit() {
|
||||
handleArgs(getArguments());
|
||||
}
|
||||
|
||||
@TargetApi(Build.VERSION_CODES.LOLLIPOP)
|
||||
private void bindView() {
|
||||
int gid = -1;
|
||||
boolean notFound = true;
|
||||
if (ACTION_GALLERY_INFO.equals(mAction) && mGalleryInfo != null) {
|
||||
GalleryInfo gi = mGalleryInfo;
|
||||
gid = gi.gid;
|
||||
mThumb.load(EhCacheKeyFactory.getThumbKey(gi.gid), gi.thumb, true);
|
||||
mTitle.setText(gi.title);
|
||||
mUploader.setText(gi.uploader);
|
||||
mCategory.setText(EhUtils.getCategory(gi.category));
|
||||
mCategory.setTextColor(EhUtils.getCategoryColor(gi.category));
|
||||
notFound = false;
|
||||
} else if (ACTION_GID_TOKEN.equals(mAction)) {
|
||||
gid = mGid;
|
||||
// TODO
|
||||
}
|
||||
|
||||
if (ApiHelper.SUPPORT_TRANSITION && gid != -1) {
|
||||
mThumb.setTransitionName(TransitionNameFactory.getThumbTransitionName(gid));
|
||||
mTitle.setTransitionName(TransitionNameFactory.getTitleTransitionName(gid));
|
||||
mUploader.setTransitionName(TransitionNameFactory.getUploaderTransitionName(gid));
|
||||
mCategory.setTransitionName(TransitionNameFactory.getCategoryTransitionName(gid));
|
||||
}
|
||||
|
||||
if (notFound) {
|
||||
// TODO Show not found error
|
||||
}
|
||||
private void onRestore(Bundle savedInstanceState) {
|
||||
mAction = savedInstanceState.getString(KEY_ACTION);
|
||||
mGalleryInfo = savedInstanceState.getParcelable(KEY_GALLERY_INFO);
|
||||
mGid = savedInstanceState.getInt(KEY_GID);
|
||||
mToken = savedInstanceState.getString(KEY_TOKEN);
|
||||
mGalleryDetail = savedInstanceState.getParcelable(KEY_GALLERY_DETAIL);
|
||||
mRequestId = savedInstanceState.getInt(KEY_REQUEST_ID);
|
||||
}
|
||||
|
||||
@Nullable
|
||||
@@ -143,67 +206,500 @@ public class GalleryDetailScene extends BaseScene {
|
||||
View view = inflater.inflate(R.layout.scene_gallery_detail, container, false);
|
||||
|
||||
ViewGroup main = (ViewGroup) view.findViewById(R.id.main);
|
||||
mRecyclerView = (RecyclerView) main.findViewById(R.id.recycler_view);
|
||||
mMainProgressView = main.findViewById(R.id.progress_view);
|
||||
mMainView = (ScrollView) main.findViewById(R.id.scroll_view);
|
||||
mProgressView = main.findViewById(R.id.progress_view);
|
||||
mFailedView = (ViewGroup) main.findViewById(R.id.tip);
|
||||
mFailedText = (TextView) mFailedView.getChildAt(1);
|
||||
mViewTransition = new ViewTransition(mRecyclerView, mMainProgressView, mFailedView);
|
||||
|
||||
mRecyclerView.setAdapter(new GalleryDetailAdapter());
|
||||
mRecyclerView.setLayoutManager(new LinearLayoutManager(getContext()));
|
||||
|
||||
mHeader = inflater.inflate(R.layout.gallery_detail_header, mRecyclerView, false);
|
||||
View mainView = mMainView;
|
||||
mHeader = mainView.findViewById(R.id.header);
|
||||
mThumb = (LoadImageView) mHeader.findViewById(R.id.thumb);
|
||||
mTitle = (TextView) mHeader.findViewById(R.id.title);
|
||||
mUploader = (TextView) mHeader.findViewById(R.id.uploader);
|
||||
mCategory = (TextView) mHeader.findViewById(R.id.category);
|
||||
mOtherActions = (SimpleImageView) mHeader.findViewById(R.id.other_actions);
|
||||
mActionGroup = (ViewGroup) mHeader.findViewById(R.id.action_card);
|
||||
mDownload = mActionGroup.findViewById(R.id.download);
|
||||
mRead = mActionGroup.findViewById(R.id.read);
|
||||
mOtherActions.setDrawable(VectorDrawable.create(getContext(), R.drawable.ic_dots_vertical));
|
||||
RippleSalon.addRipple(mOtherActions, false);
|
||||
RippleSalon.addRipple(mDownload, false);
|
||||
RippleSalon.addRipple(mRead, false);
|
||||
mOtherActions.setOnClickListener(this);
|
||||
mDownload.setOnClickListener(this);
|
||||
mRead.setOnClickListener(this);
|
||||
|
||||
bindView();
|
||||
mBelowHeader = mainView.findViewById(R.id.below_header);
|
||||
View belowHeader = mBelowHeader;
|
||||
|
||||
mInfo = belowHeader.findViewById(R.id.info);
|
||||
mLanguage = (TextView) mInfo.findViewById(R.id.language);
|
||||
mPages = (TextView) mInfo.findViewById(R.id.pages);
|
||||
mSize = (TextView) mInfo.findViewById(R.id.size);
|
||||
mPosted = (TextView) mInfo.findViewById(R.id.posted);
|
||||
mFavoredTimes = (TextView) mInfo.findViewById(R.id.favoredTimes);
|
||||
RippleSalon.addRipple(mInfo, false);
|
||||
mInfo.setOnClickListener(this);
|
||||
|
||||
mActions = belowHeader.findViewById(R.id.actions);
|
||||
mRatingText = (TextView) mActions.findViewById(R.id.rating_text);
|
||||
mRating = (RatingBar) mActions.findViewById(R.id.rating);
|
||||
mHeartGroup = mActions.findViewById(R.id.heart_group);
|
||||
mHeart = (TextView) mHeartGroup.findViewById(R.id.heart);
|
||||
mHeartOutline = (TextView) mHeartGroup.findViewById(R.id.heart_outline);
|
||||
mTorrent = (TextView) mActions.findViewById(R.id.torrent);
|
||||
mShare = (TextView) mActions.findViewById(R.id.share);
|
||||
mRate = (TextView) mActions.findViewById(R.id.rate);
|
||||
RippleSalon.addRipple(mHeartGroup, false);
|
||||
RippleSalon.addRipple(mTorrent, false);
|
||||
RippleSalon.addRipple(mShare, false);
|
||||
RippleSalon.addRipple(mRate, false);
|
||||
mHeartGroup.setOnClickListener(this);
|
||||
mTorrent.setOnClickListener(this);
|
||||
mShare.setOnClickListener(this);
|
||||
mRate.setOnClickListener(this);
|
||||
|
||||
mTags = (LinearLayout) belowHeader.findViewById(R.id.tags);
|
||||
mNoTags = (TextView) mTags.findViewById(R.id.no_tags);
|
||||
|
||||
mComments = (LinearLayout) belowHeader.findViewById(R.id.comments);
|
||||
mCommentsText = (TextView) mComments.findViewById(R.id.comments_text);
|
||||
|
||||
mProgress = mainView.findViewById(R.id.progress);
|
||||
|
||||
fetchGalleryDetail();
|
||||
adjustViewVisibility();
|
||||
if (mGalleryDetail == null) {
|
||||
bindViewFirst();
|
||||
} else {
|
||||
bindViewSecond();
|
||||
}
|
||||
setTransitionName();
|
||||
|
||||
return view;
|
||||
}
|
||||
|
||||
private class GalleryDetailAdapter extends RecyclerView.Adapter<SimpleHolder> {
|
||||
@Override
|
||||
public void onDestroyView() {
|
||||
super.onDestroyView();
|
||||
|
||||
@Override
|
||||
public SimpleHolder onCreateViewHolder(ViewGroup parent, int viewType) {
|
||||
switch (viewType) {
|
||||
case TYPE_HEADER:
|
||||
return new SimpleHolder(mHeader);
|
||||
/*
|
||||
case TYPE_BUTTON:
|
||||
return new SimpleHolder(mButton);
|
||||
case TYPE_INFO:
|
||||
return new SimpleHolder(mInfo);
|
||||
case TYPE_ACTION:
|
||||
return new SimpleHolder(mAction);
|
||||
case TYPE_TAG:
|
||||
return new SimpleHolder(mTag);
|
||||
case TYPE_COMMENT:
|
||||
return new SimpleHolder(mComment);
|
||||
case TYPE_PREVIEW:
|
||||
return new SimpleHolder(mPreview);
|
||||
case TYPE_PROGRESS:
|
||||
return new SimpleHolder(mProgress);
|
||||
*/
|
||||
default:
|
||||
throw new IllegalStateException("Unknown type " + viewType);
|
||||
mMainView = null;
|
||||
mProgressView = null;
|
||||
mFailedView = null;
|
||||
mFailedText = null;
|
||||
|
||||
mHeader = null;
|
||||
mThumb = null;
|
||||
mTitle = null;
|
||||
mUploader = null;
|
||||
mCategory = null;
|
||||
mOtherActions = null;
|
||||
mActionGroup = null;
|
||||
mDownload = null;
|
||||
mRead = null;
|
||||
mBelowHeader = null;
|
||||
|
||||
mInfo = null;
|
||||
mLanguage = null;
|
||||
mPages = null;
|
||||
mSize = null;
|
||||
mPosted = null;
|
||||
mFavoredTimes = null;
|
||||
|
||||
mActions = null;
|
||||
mRatingText = null;
|
||||
mRating = null;
|
||||
mHeartGroup = null;
|
||||
mHeart = null;
|
||||
mHeartOutline = null;
|
||||
mTorrent = null;
|
||||
mShare = null;
|
||||
mRate = null;
|
||||
|
||||
mProgress = null;
|
||||
|
||||
mHeartDrawable = null;
|
||||
mHeartOutlineDrawable = null;
|
||||
mPopupMenu = null;
|
||||
}
|
||||
|
||||
private void fetchGalleryDetail() {
|
||||
if (mGalleryDetail != null) {
|
||||
return;
|
||||
}
|
||||
|
||||
int gid = getGid();
|
||||
if (gid == -1) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Get from cache
|
||||
mGalleryDetail = EhApplication.getGalleryDetailCache(getContext()).get(gid);
|
||||
if (mGalleryDetail != null) {
|
||||
return;
|
||||
}
|
||||
|
||||
EhApplication application = (EhApplication) getContext().getApplicationContext();
|
||||
if (application.containGlobalStuff(mRequestId)) {
|
||||
// request exist
|
||||
return;
|
||||
}
|
||||
|
||||
// Do request
|
||||
String url = getGalleryDetailUrl();
|
||||
if (url == null) {
|
||||
return;
|
||||
}
|
||||
|
||||
EhClient.Callback callback = new GetGalleryDetailListener(getContext(),
|
||||
((StageActivity) getActivity()).getStageId(), getTag());
|
||||
mRequestId = ((EhApplication) getContext().getApplicationContext()).putGlobalStuff(callback);
|
||||
EhRequest request = new EhRequest()
|
||||
.setMethod(EhClient.METHOD_GET_GALLERY_DETAIL)
|
||||
.setArgs(url)
|
||||
.setCallback(callback);
|
||||
EhApplication.getEhClient(getContext()).execute(request);
|
||||
}
|
||||
|
||||
private void adjustViewVisibility() {
|
||||
if (mGalleryDetail != null) {
|
||||
mMainView.setVisibility(View.VISIBLE);
|
||||
mProgressView.setVisibility(View.GONE);
|
||||
mFailedView.setVisibility(View.GONE);
|
||||
|
||||
mBelowHeader.setVisibility(View.VISIBLE);
|
||||
mProgress.setVisibility(View.GONE);
|
||||
} else if (mGalleryInfo != null) {
|
||||
mMainView.setVisibility(View.VISIBLE);
|
||||
mProgressView.setVisibility(View.GONE);
|
||||
mFailedView.setVisibility(View.GONE);
|
||||
|
||||
mBelowHeader.setVisibility(View.GONE);
|
||||
mProgress.setVisibility(View.VISIBLE);
|
||||
} else {
|
||||
mMainView.setVisibility(View.GONE);
|
||||
if (TextUtils.isEmpty(mFailedText.getText())) {
|
||||
mProgressView.setVisibility(View.VISIBLE);
|
||||
mFailedView.setVisibility(View.GONE);
|
||||
} else {
|
||||
mProgressView.setVisibility(View.GONE);
|
||||
mFailedView.setVisibility(View.VISIBLE);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void bindViewFirst() {
|
||||
if (mGalleryDetail != null) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (ACTION_GALLERY_INFO.equals(mAction) && mGalleryInfo != null) {
|
||||
GalleryInfo gi = mGalleryInfo;
|
||||
mThumb.load(EhCacheKeyFactory.getThumbKey(gi.gid), gi.thumb, true);
|
||||
mTitle.setText(gi.title);
|
||||
mUploader.setText(gi.uploader);
|
||||
mCategory.setText(EhUtils.getCategory(gi.category));
|
||||
mCategory.setTextColor(EhUtils.getCategoryColor(gi.category));
|
||||
|
||||
// Hide actions
|
||||
mActionGroup.setVisibility(View.GONE);
|
||||
}
|
||||
}
|
||||
|
||||
private void ensureHeartDrawable() {
|
||||
if (mHeartDrawable == null) {
|
||||
mHeartDrawable = AnimatedVectorDrawable.create(getContext(), R.drawable.ic_heart_animated);
|
||||
if (mHeartDrawable == null) {
|
||||
throw new IllegalStateException("Can't parse heart drawable");
|
||||
}
|
||||
mHeartDrawable.setBounds(0, 0, mHeartDrawable.getIntrinsicWidth(),
|
||||
mHeartDrawable.getIntrinsicHeight());
|
||||
mHeart.setCompoundDrawables(null, mHeartDrawable, null, null);
|
||||
}
|
||||
|
||||
if (mHeartOutlineDrawable == null) {
|
||||
mHeartOutlineDrawable = AnimatedVectorDrawable.create(getContext(), R.drawable.ic_heart_outline_animated);
|
||||
if (mHeartOutlineDrawable == null) {
|
||||
throw new IllegalStateException("Can't parse heart drawable");
|
||||
}
|
||||
mHeartOutlineDrawable.setBounds(0, 0, mHeartOutlineDrawable.getIntrinsicWidth(),
|
||||
mHeartOutlineDrawable.getIntrinsicHeight());
|
||||
mHeartOutline.setCompoundDrawables(null, mHeartOutlineDrawable, null, null);
|
||||
}
|
||||
}
|
||||
|
||||
private void bindViewSecond() {
|
||||
GalleryDetail gd = mGalleryDetail;
|
||||
|
||||
if (gd == null) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (mActionGroup.getVisibility() == View.GONE) {
|
||||
mActionGroup.setVisibility(View.VISIBLE);
|
||||
}
|
||||
|
||||
Resources resources = getContext().getResources();
|
||||
|
||||
if (TextUtils.isEmpty(mCategory.getText())) {
|
||||
mThumb.load(EhCacheKeyFactory.getThumbKey(gd.gid), gd.thumb, true);
|
||||
mTitle.setText(gd.title);
|
||||
mUploader.setText(gd.uploader);
|
||||
mCategory.setText(EhUtils.getCategory(gd.category));
|
||||
mCategory.setTextColor(EhUtils.getCategoryColor(gd.category));
|
||||
}
|
||||
|
||||
mLanguage.setText(gd.language);
|
||||
mPages.setText(resources.getQuantityString(
|
||||
R.plurals.page_count, gd.pages, gd.pages));
|
||||
mSize.setText(gd.size);
|
||||
mPosted.setText(gd.posted);
|
||||
mFavoredTimes.setText(resources.getString(R.string.favored_times, gd.favoredTimes));
|
||||
|
||||
mRatingText.setText(getAllRatingText(gd.rating, gd.ratedTimes));
|
||||
mRating.setRating(gd.rating);
|
||||
|
||||
ensureHeartDrawable();
|
||||
if (gd.isFavored) {
|
||||
mHeart.setVisibility(View.VISIBLE);
|
||||
mHeartOutline.setVisibility(View.GONE);
|
||||
} else {
|
||||
mHeart.setVisibility(View.GONE);
|
||||
mHeartOutline.setVisibility(View.VISIBLE);
|
||||
}
|
||||
mTorrent.setText(resources.getString(R.string.torrent_count, gd.torrentCount));
|
||||
|
||||
bindTags(gd.tags);
|
||||
bindComments(gd.comments);
|
||||
}
|
||||
|
||||
private void bindTags(GalleryTagGroup[] tagGroups) {
|
||||
mTags.removeViews(1, mTags.getChildCount() - 1);
|
||||
if (tagGroups == null || tagGroups.length == 0) {
|
||||
mNoTags.setVisibility(View.VISIBLE);
|
||||
return;
|
||||
} else {
|
||||
mNoTags.setVisibility(View.GONE);
|
||||
}
|
||||
|
||||
LayoutInflater inflater = getActivity().getLayoutInflater();
|
||||
int colorTag = getResources().getColor(R.color.colorPrimary);
|
||||
int colorName = getResources().getColor(R.color.purple_a400);
|
||||
for (int i = 0, s = tagGroups.length; i < s; i++) {
|
||||
GalleryTagGroup tg = tagGroups[i];
|
||||
|
||||
LinearLayout ll = (LinearLayout) inflater.inflate(R.layout.gallery_tag_group, mTags, false);
|
||||
ll.setOrientation(LinearLayout.HORIZONTAL);
|
||||
mTags.addView(ll);
|
||||
|
||||
TextView tgName = (TextView) inflater.inflate(R.layout.item_gallery_tag, ll, false);
|
||||
ll.addView(tgName);
|
||||
tgName.setText(tg.groupName);
|
||||
tgName.setBackgroundColor(colorName);
|
||||
|
||||
AutoWrapLayout awl = new AutoWrapLayout(getContext());
|
||||
ll.addView(awl, ViewGroup.LayoutParams.WRAP_CONTENT, ViewGroup.LayoutParams.WRAP_CONTENT);
|
||||
for (int j = 0, z = tg.size(); j < z; j++) {
|
||||
TextView tag = (TextView) inflater.inflate(R.layout.item_gallery_tag, awl, false);
|
||||
awl.addView(tag);
|
||||
tag.setText(tg.getTagAt(j));
|
||||
tag.setBackgroundColor(colorTag);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void bindComments(GalleryComment[] comments) {
|
||||
mComments.removeViews(0, mComments.getChildCount() - 1);
|
||||
|
||||
final int maxShowCount = 2;
|
||||
if (comments == null || comments.length == 0) {
|
||||
mCommentsText.setText(R.string.no_comments);
|
||||
return;
|
||||
} else if (comments.length <= maxShowCount) {
|
||||
mCommentsText.setText(R.string.no_more_comments);
|
||||
} else {
|
||||
mCommentsText.setText(R.string.more_comment);
|
||||
}
|
||||
|
||||
LayoutInflater inflater = getActivity().getLayoutInflater();
|
||||
int length = Math.min(maxShowCount, comments.length);
|
||||
for (int i = 0; i < length; i++) {
|
||||
GalleryComment comment = comments[i];
|
||||
View v = inflater.inflate(R.layout.item_gallery_comment, mComments, false);
|
||||
mComments.addView(v, i);
|
||||
((TextView) v.findViewById(R.id.user)).setText(comment.user);
|
||||
((TextView) v.findViewById(R.id.time)).setText(ReadableTime.getTimeAgo(comment.time));
|
||||
((TextView) v.findViewById(R.id.comment)).setText(Html.fromHtml(comment.comment));
|
||||
}
|
||||
}
|
||||
|
||||
private static String getRatingText(float rating, Resources resources) {
|
||||
String undefine = "(´_ゝ`)";
|
||||
if (Float.isNaN(rating)) {
|
||||
return undefine;
|
||||
}
|
||||
|
||||
int resId = 0;
|
||||
switch (Math.round(rating * 2)) {
|
||||
case 0:
|
||||
resId = R.string.rating0; break;
|
||||
case 1:
|
||||
resId = R.string.rating1; break;
|
||||
case 2:
|
||||
resId = R.string.rating2; break;
|
||||
case 3:
|
||||
resId = R.string.rating3; break;
|
||||
case 4:
|
||||
resId = R.string.rating4; break;
|
||||
case 5:
|
||||
resId = R.string.rating5; break;
|
||||
case 6:
|
||||
resId = R.string.rating6; break;
|
||||
case 7:
|
||||
resId = R.string.rating7; break;
|
||||
case 8:
|
||||
resId = R.string.rating8; break;
|
||||
case 9:
|
||||
resId = R.string.rating9; break;
|
||||
case 10:
|
||||
resId = R.string.rating10; break;
|
||||
}
|
||||
|
||||
if (resId == 0) {
|
||||
return undefine;
|
||||
} else {
|
||||
return resources.getString(resId);
|
||||
}
|
||||
}
|
||||
|
||||
private String getAllRatingText(float rating, int ratedTimes) {
|
||||
Resources resources = getResources();
|
||||
return resources.getString(R.string.rating_text, getRatingText(rating, resources), ratedTimes);
|
||||
}
|
||||
|
||||
@TargetApi(Build.VERSION_CODES.LOLLIPOP)
|
||||
private void setTransitionName() {
|
||||
int gid = getGid();
|
||||
|
||||
if (gid != -1 && ApiHelper.SUPPORT_TRANSITION) {
|
||||
mThumb.setTransitionName(TransitionNameFactory.getThumbTransitionName(gid));
|
||||
mTitle.setTransitionName(TransitionNameFactory.getTitleTransitionName(gid));
|
||||
mUploader.setTransitionName(TransitionNameFactory.getUploaderTransitionName(gid));
|
||||
mCategory.setTransitionName(TransitionNameFactory.getCategoryTransitionName(gid));
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onSaveInstanceState(Bundle outState) {
|
||||
super.onSaveInstanceState(outState);
|
||||
|
||||
if (mAction != null) {
|
||||
outState.putString(KEY_ACTION, mAction);
|
||||
}
|
||||
if (mGalleryInfo != null) {
|
||||
outState.putParcelable(KEY_GALLERY_INFO, mGalleryInfo);
|
||||
}
|
||||
outState.putInt(KEY_GID, mGid);
|
||||
if (mToken != null) {
|
||||
outState.putString(KEY_TOKEN, mAction);
|
||||
}
|
||||
if (mGalleryDetail != null) {
|
||||
outState.putParcelable(KEY_GALLERY_DETAIL, mGalleryDetail);
|
||||
}
|
||||
outState.putInt(KEY_REQUEST_ID, mRequestId);
|
||||
}
|
||||
|
||||
private void ensurePopMenu() {
|
||||
if (mPopupMenu != null) {
|
||||
return;
|
||||
}
|
||||
|
||||
PopupMenu popup = new PopupMenu(getContext(), mOtherActions);
|
||||
mPopupMenu = popup;
|
||||
popup.getMenuInflater().inflate(R.menu.gallery_detail_other_action, popup.getMenu());
|
||||
popup.setOnMenuItemClickListener(new PopupMenu.OnMenuItemClickListener() {
|
||||
@Override
|
||||
public boolean onMenuItemClick(MenuItem item) {
|
||||
switch (item.getItemId()) {
|
||||
case R.id.action_open_in_other_app:
|
||||
// TODO
|
||||
break;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onClick(View v) {
|
||||
if (mOtherActions == v) {
|
||||
ensurePopMenu();
|
||||
mPopupMenu.show();
|
||||
}
|
||||
}
|
||||
|
||||
private void onGetGalleryDetailSuccess(GalleryDetailParser.Result result) {
|
||||
mGalleryDetail = result.galleryDetail;
|
||||
adjustViewVisibility();
|
||||
bindViewSecond();
|
||||
}
|
||||
|
||||
private void onGetGalleryDetailFailure(Exception e) {
|
||||
|
||||
}
|
||||
|
||||
private static class GetGalleryDetailListener implements EhClient.Callback<GalleryDetailParser.Result> {
|
||||
|
||||
private EhApplication mApplication;
|
||||
private int mStageId;
|
||||
private String mSceneTag;
|
||||
|
||||
public GetGalleryDetailListener(Context context, int stageId, String sceneTag) {
|
||||
mApplication = (EhApplication) context.getApplicationContext();
|
||||
mStageId = stageId;
|
||||
mSceneTag = sceneTag;
|
||||
}
|
||||
|
||||
private GalleryDetailScene getScene() {
|
||||
StageActivity stage = mApplication.findStageActivityById(mStageId);
|
||||
if (stage == null) {
|
||||
return null;
|
||||
}
|
||||
SceneFragment scene = stage.findSceneByTag(mSceneTag);
|
||||
if (scene instanceof GalleryDetailScene) {
|
||||
return (GalleryDetailScene) scene;
|
||||
} else {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onBindViewHolder(SimpleHolder holder, int position) {
|
||||
public void onSuccess(GalleryDetailParser.Result result) {
|
||||
mApplication.removeGlobalStuff(this);
|
||||
|
||||
// Put gallery detail to cache
|
||||
GalleryDetail gd = result.galleryDetail;
|
||||
EhApplication.getGalleryDetailCache(mApplication).put(gd.gid, gd);
|
||||
|
||||
// Notify success
|
||||
GalleryDetailScene scene = getScene();
|
||||
if (scene != null) {
|
||||
scene.onGetGalleryDetailSuccess(result);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getItemCount() {
|
||||
return 1;
|
||||
public void onFailure(Exception e) {
|
||||
mApplication.removeGlobalStuff(this);
|
||||
GalleryDetailScene scene = getScene();
|
||||
if (scene != null) {
|
||||
scene.onGetGalleryDetailFailure(e);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getItemViewType(int position) {
|
||||
return TYPE_HEADER; // TODO
|
||||
public void onCancel() {
|
||||
mApplication.removeGlobalStuff(this);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -65,6 +65,9 @@ import com.hippo.ehviewer.widget.SearchBar;
|
||||
import com.hippo.ehviewer.widget.SearchLayout;
|
||||
import com.hippo.ehviewer.widget.SimpleRatingView;
|
||||
import com.hippo.rippleold.RippleSalon;
|
||||
import com.hippo.scene.SceneApplication;
|
||||
import com.hippo.scene.SceneFragment;
|
||||
import com.hippo.scene.StageActivity;
|
||||
import com.hippo.scene.TransitionHelper;
|
||||
import com.hippo.utils.ApiHelper;
|
||||
import com.hippo.vector.VectorDrawable;
|
||||
@@ -129,8 +132,6 @@ public final class GalleryListScene extends BaseScene
|
||||
// Double click back exit
|
||||
private long mPressBackTime = 0;
|
||||
|
||||
private EhRequest mRequest;
|
||||
|
||||
private Animator.AnimatorListener mFabAnimatorListener = null;
|
||||
|
||||
private boolean mHasFirstRefresh = false;
|
||||
@@ -178,6 +179,8 @@ public final class GalleryListScene extends BaseScene
|
||||
|
||||
if (savedInstanceState == null) {
|
||||
onInit();
|
||||
} else {
|
||||
onRestore(savedInstanceState);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -186,6 +189,13 @@ public final class GalleryListScene extends BaseScene
|
||||
handleArgs(getArguments());
|
||||
}
|
||||
|
||||
@SuppressWarnings("WrongConstant")
|
||||
private void onRestore(Bundle savedInstanceState) {
|
||||
mHasFirstRefresh = savedInstanceState.getBoolean(KEY_HAS_FIRST_REFRESH);
|
||||
mUrlBuilder = savedInstanceState.getParcelable(KEY_LIST_URL_BUILDER);
|
||||
mState = savedInstanceState.getInt(KEY_STATE);
|
||||
}
|
||||
|
||||
private void setSearchBarHint(Context context, SearchBar searchBar) {
|
||||
Resources resources = context.getResources();
|
||||
Drawable searchImage = VectorDrawable.create(context, R.drawable.ic_magnify);
|
||||
@@ -235,22 +245,10 @@ public final class GalleryListScene extends BaseScene
|
||||
setNavCheckedItem(checkedItemId);
|
||||
}
|
||||
|
||||
private void restoreState(@Nullable Bundle savedInstanceState) {
|
||||
if (savedInstanceState == null) {
|
||||
return;
|
||||
}
|
||||
|
||||
mHasFirstRefresh = savedInstanceState.getBoolean(KEY_HAS_FIRST_REFRESH);
|
||||
mUrlBuilder = savedInstanceState.getParcelable(KEY_LIST_URL_BUILDER);
|
||||
}
|
||||
|
||||
@Nullable
|
||||
@Override
|
||||
public View onCreateView(LayoutInflater inflater, @Nullable ViewGroup container,
|
||||
@Nullable Bundle savedInstanceState) {
|
||||
// Restore url builder
|
||||
restoreState(savedInstanceState);
|
||||
|
||||
View view = inflater.inflate(R.layout.scene_gallery_list, container, false);
|
||||
|
||||
Resources resources = getContext().getResources();
|
||||
@@ -304,15 +302,15 @@ public final class GalleryListScene extends BaseScene
|
||||
// Update list url builder
|
||||
onUpdateUrlBuilder();
|
||||
|
||||
// Restore state
|
||||
int newState = mState;
|
||||
mState = STATE_NORMAL;
|
||||
setState(newState, false);
|
||||
|
||||
// Only refresh for the first time
|
||||
if (!mHasFirstRefresh) {
|
||||
mHasFirstRefresh = true;
|
||||
mHelper.firstRefresh();
|
||||
} else {
|
||||
// Restore state
|
||||
int newState = mState;
|
||||
mState = STATE_NORMAL;
|
||||
setState(newState, false);
|
||||
}
|
||||
|
||||
return view;
|
||||
@@ -325,11 +323,6 @@ public final class GalleryListScene extends BaseScene
|
||||
// End animation
|
||||
mSearchBarMoveHelper.cancelAnimation();
|
||||
|
||||
// Cancel request
|
||||
if (mRequest != null) {
|
||||
mRequest.cancel();
|
||||
}
|
||||
|
||||
mFitPaddingLayout = null;
|
||||
mRecyclerView = null;
|
||||
mSearchLayout = null;
|
||||
@@ -342,7 +335,6 @@ public final class GalleryListScene extends BaseScene
|
||||
mRightDrawable = null;
|
||||
mSearchBarMoveHelper = null;
|
||||
mFabAnimatorListener = null;
|
||||
mRequest = null;
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -353,13 +345,6 @@ public final class GalleryListScene extends BaseScene
|
||||
outState.putInt(KEY_STATE, mState);
|
||||
}
|
||||
|
||||
@Override
|
||||
@SuppressWarnings("WrongConstant")
|
||||
protected void onRestoreInstanceState(Bundle savedInstanceState) {
|
||||
super.onRestoreInstanceState(savedInstanceState);
|
||||
setState(savedInstanceState.getInt(KEY_STATE), false);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onClick(View v) {
|
||||
if (mFab == v) {
|
||||
@@ -904,9 +889,9 @@ public final class GalleryListScene extends BaseScene
|
||||
|
||||
String url = mUrlBuilder.build();
|
||||
EhRequest request = new EhRequest();
|
||||
mRequest = request;
|
||||
request.setMethod(EhClient.METHOD_GET_GALLERY_LIST);
|
||||
request.setCallback(new GetGalleryListListener(taskId));
|
||||
request.setCallback(new GetGalleryListListener(getContext(),
|
||||
((StageActivity) getActivity()).getStageId(), getTag(), taskId));
|
||||
request.setArgs(url);
|
||||
mClient.execute(request);
|
||||
}
|
||||
@@ -932,38 +917,67 @@ public final class GalleryListScene extends BaseScene
|
||||
}
|
||||
}
|
||||
|
||||
private class GetGalleryListListener implements EhClient.Callback<GalleryListParser.Result> {
|
||||
private void onGetGalleryListSuccess(GalleryListParser.Result result, int taskId) {
|
||||
if (mHelper != null && mSearchBarMoveHelper != null &&
|
||||
mHelper.isCurrentTask(taskId)) {
|
||||
mHelper.setPages(taskId, result.pages);
|
||||
mHelper.onGetPageData(taskId, result.galleryInfos);
|
||||
mSearchBarMoveHelper.returnSearchBarPosition();
|
||||
}
|
||||
}
|
||||
|
||||
private void onGetGalleryListFailure(Exception e, int taskId) {
|
||||
if (mHelper != null && mSearchBarMoveHelper != null &&
|
||||
mHelper.isCurrentTask(taskId)) {
|
||||
mHelper.onGetExpection(taskId, e);
|
||||
mSearchBarMoveHelper.returnSearchBarPosition();
|
||||
}
|
||||
}
|
||||
|
||||
private static class GetGalleryListListener implements EhClient.Callback<GalleryListParser.Result> {
|
||||
|
||||
private SceneApplication mApplication;
|
||||
private int mStageId;
|
||||
private String mSceneTag;
|
||||
private int mTaskId;
|
||||
|
||||
public GetGalleryListListener(int taskId) {
|
||||
public GetGalleryListListener(Context context, int stageId, String sceneTag, int taskId) {
|
||||
mApplication = (SceneApplication) context.getApplicationContext();
|
||||
mStageId = stageId;
|
||||
mSceneTag = sceneTag;
|
||||
mTaskId = taskId;
|
||||
}
|
||||
|
||||
private GalleryListScene getScene() {
|
||||
StageActivity stage = mApplication.findStageActivityById(mStageId);
|
||||
if (stage == null) {
|
||||
return null;
|
||||
}
|
||||
SceneFragment scene = stage.findSceneByTag(mSceneTag);
|
||||
if (scene instanceof GalleryListScene) {
|
||||
return (GalleryListScene) scene;
|
||||
} else {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onSuccess(GalleryListParser.Result result) {
|
||||
if (mHelper != null && mHelper.isCurrentTask(mTaskId)) {
|
||||
mRequest = null;
|
||||
mHelper.setPages(mTaskId, result.pages);
|
||||
mHelper.onGetPageData(mTaskId, result.galleryInfos);
|
||||
mSearchBarMoveHelper.returnSearchBarPosition();
|
||||
GalleryListScene scene = getScene();
|
||||
if (scene != null) {
|
||||
scene.onGetGalleryListSuccess(result, mTaskId);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onFailure(Exception e) {
|
||||
if (mHelper != null && mHelper.isCurrentTask(mTaskId)) {
|
||||
mRequest = null;
|
||||
mHelper.onGetExpection(mTaskId, e);
|
||||
mSearchBarMoveHelper.returnSearchBarPosition();
|
||||
GalleryListScene scene = getScene();
|
||||
if (scene != null) {
|
||||
scene.onGetGalleryListFailure(e, mTaskId);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onCancel() {
|
||||
if (mHelper != null && mHelper.isCurrentTask(mTaskId)) {
|
||||
mRequest = null;
|
||||
}
|
||||
}
|
||||
public void onCancel() {}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,84 @@
|
||||
/*
|
||||
* Copyright 2016 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.content.res.TypedArray;
|
||||
import android.graphics.Canvas;
|
||||
import android.graphics.Color;
|
||||
import android.graphics.Paint;
|
||||
import android.graphics.Rect;
|
||||
import android.util.AttributeSet;
|
||||
import android.view.View;
|
||||
|
||||
import com.hippo.ehviewer.R;
|
||||
|
||||
public class DividerView extends View {
|
||||
|
||||
private Paint mPaint;
|
||||
private Rect mRect;
|
||||
private int mDividerWidth;
|
||||
private int mDividerHeight;
|
||||
|
||||
public DividerView(Context context, AttributeSet attrs) {
|
||||
super(context, attrs);
|
||||
init(context, attrs);
|
||||
}
|
||||
|
||||
public DividerView(Context context, AttributeSet attrs, int defStyleAttr) {
|
||||
super(context, attrs, defStyleAttr);
|
||||
init(context, attrs);
|
||||
}
|
||||
|
||||
private void init(Context context, AttributeSet attrs) {
|
||||
TypedArray a = context.obtainStyledAttributes(attrs, R.styleable.DividerView);
|
||||
int color = a.getColor(R.styleable.DividerView_dividerColor, Color.BLACK);
|
||||
mDividerWidth = a.getDimensionPixelOffset(R.styleable.DividerView_dividerWidth, 0);
|
||||
mDividerHeight = a.getDimensionPixelOffset(R.styleable.DividerView_dividerHeight, 0);
|
||||
a.recycle();
|
||||
|
||||
mPaint = new Paint(Paint.ANTI_ALIAS_FLAG | Paint.DITHER_FLAG);
|
||||
mPaint.setColor(color);
|
||||
mRect = new Rect();
|
||||
}
|
||||
|
||||
@Override
|
||||
protected int getSuggestedMinimumWidth() {
|
||||
return Math.max((getPaddingLeft() + getPaddingRight() + mDividerWidth), super.getSuggestedMinimumWidth());
|
||||
}
|
||||
|
||||
@Override
|
||||
protected int getSuggestedMinimumHeight() {
|
||||
return Math.max((getPaddingTop() + getPaddingBottom() + mDividerHeight), super.getSuggestedMinimumHeight());
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
|
||||
super.onMeasure(widthMeasureSpec, heightMeasureSpec);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onDraw(Canvas canvas) {
|
||||
super.onDraw(canvas);
|
||||
Rect rect = mRect;
|
||||
rect.set(getPaddingLeft(), getPaddingTop(),
|
||||
getWidth() - getPaddingRight(), getHeight() - getPaddingBottom());
|
||||
if (!rect.isEmpty()) {
|
||||
canvas.drawRect(rect, mPaint);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -32,6 +32,15 @@ public class SceneApplication extends Application {
|
||||
stage.onRegister(id);
|
||||
}
|
||||
|
||||
void registerStageActivity(StageActivity stage, int id) {
|
||||
if (mStageMap.indexOfKey(id) >= 0) {
|
||||
throw new IllegalStateException("The id exists: " + id);
|
||||
}
|
||||
|
||||
mStageMap.put(id, stage);
|
||||
stage.onRegister(id);
|
||||
}
|
||||
|
||||
void unregisterStageActivity(int id) {
|
||||
int index = mStageMap.indexOfKey(id);
|
||||
if (index >= 0) {
|
||||
|
||||
@@ -33,6 +33,7 @@ public abstract class StageActivity extends AppCompatActivity {
|
||||
|
||||
private static final String TAG = StageActivity.class.getSimpleName();
|
||||
|
||||
private static final String KEY_STAGE_ACTIVITY_STAGE_ID = "stage_activity_stage_id";
|
||||
private static final String KEY_STAGE_ACTIVITY_SCENE_TAG_LIST = "stage_activity_scene_tag_list";
|
||||
private static final String KEY_STAGE_ACTIVITY_NEXT_ID = "stage_activity_next_id";
|
||||
|
||||
@@ -47,7 +48,18 @@ public abstract class StageActivity extends AppCompatActivity {
|
||||
@Override
|
||||
protected void onCreate(@Nullable Bundle savedInstanceState) {
|
||||
super.onCreate(savedInstanceState);
|
||||
((SceneApplication) getApplicationContext()).registerStageActivity(this);
|
||||
|
||||
if (savedInstanceState != null) {
|
||||
mStageId = savedInstanceState.getInt(KEY_STAGE_ACTIVITY_STAGE_ID, IntIdGenerator.INVALID_ID);
|
||||
mSceneTagList = savedInstanceState.getStringArrayList(KEY_STAGE_ACTIVITY_SCENE_TAG_LIST);
|
||||
mIdGenerator.setNextId(savedInstanceState.getInt(KEY_STAGE_ACTIVITY_NEXT_ID));
|
||||
}
|
||||
|
||||
if (mStageId == IntIdGenerator.INVALID_ID) {
|
||||
((SceneApplication) getApplicationContext()).registerStageActivity(this);
|
||||
} else {
|
||||
((SceneApplication) getApplicationContext()).registerStageActivity(this, mStageId);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -61,7 +73,6 @@ public abstract class StageActivity extends AppCompatActivity {
|
||||
}
|
||||
|
||||
protected void onUnregister() {
|
||||
mStageId = IntIdGenerator.INVALID_ID;
|
||||
}
|
||||
|
||||
public int getStageId() {
|
||||
@@ -250,14 +261,8 @@ public abstract class StageActivity extends AppCompatActivity {
|
||||
@Override
|
||||
protected void onSaveInstanceState(Bundle outState) {
|
||||
super.onSaveInstanceState(outState);
|
||||
outState.putInt(KEY_STAGE_ACTIVITY_STAGE_ID, mStageId);
|
||||
outState.putStringArrayList(KEY_STAGE_ACTIVITY_SCENE_TAG_LIST, mSceneTagList);
|
||||
outState.putInt(KEY_STAGE_ACTIVITY_NEXT_ID, mIdGenerator.nextId());
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onRestoreInstanceState(Bundle savedInstanceState) {
|
||||
super.onRestoreInstanceState(savedInstanceState);
|
||||
mSceneTagList = savedInstanceState.getStringArrayList(KEY_STAGE_ACTIVITY_SCENE_TAG_LIST);
|
||||
mIdGenerator.setNextId(savedInstanceState.getInt(KEY_STAGE_ACTIVITY_NEXT_ID));
|
||||
}
|
||||
}
|
||||
@@ -125,7 +125,7 @@ public class StageLayout extends FrameLayout {
|
||||
protected boolean drawChild(Canvas canvas, View child, long drawingTime) {
|
||||
ArrayList<View> disappearingChildren = mSuperDisappearingChildren;
|
||||
|
||||
if (mDoTrick && disappearingChildren != null && disappearingChildren.size() > 0) {
|
||||
if (mDoTrick) {
|
||||
if (child == mDumpView) {
|
||||
boolean more = false;
|
||||
for (int i = disappearingChildren.size() - 1; i >= 0; i--) {
|
||||
|
||||
File diff suppressed because it is too large.
Load diff
@@ -0,0 +1,178 @@
|
||||
/*
|
||||
* 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.utils;
|
||||
|
||||
import android.content.Context;
|
||||
import android.content.res.Resources;
|
||||
|
||||
import com.hippo.ehviewer.R;
|
||||
|
||||
import java.text.SimpleDateFormat;
|
||||
import java.util.Calendar;
|
||||
import java.util.Date;
|
||||
import java.util.Locale;
|
||||
import java.util.TimeZone;
|
||||
|
||||
public final class ReadableTime {
|
||||
|
||||
private static Resources sResources;
|
||||
|
||||
public static final long SECOND_MILLIS = 1000;
|
||||
public static final long MINUTE_MILLIS = 60 * SECOND_MILLIS;
|
||||
public static final long HOUR_MILLIS = 60 * MINUTE_MILLIS;
|
||||
public static final long DAY_MILLIS = 24 * HOUR_MILLIS;
|
||||
public static final long WEEK_MILLIS = 7 * DAY_MILLIS;
|
||||
public static final long YEAR_MILLIS = 365 * DAY_MILLIS;
|
||||
|
||||
public static final int SIZE = 5;
|
||||
|
||||
public static final long[] MULTIPLES = {
|
||||
YEAR_MILLIS,
|
||||
DAY_MILLIS,
|
||||
HOUR_MILLIS,
|
||||
MINUTE_MILLIS,
|
||||
SECOND_MILLIS
|
||||
};
|
||||
|
||||
public static final int[] UNITS = {
|
||||
R.plurals.year,
|
||||
R.plurals.day,
|
||||
R.plurals.hour,
|
||||
R.plurals.minute,
|
||||
R.plurals.second
|
||||
};
|
||||
|
||||
private static final Calendar sCalendar = Calendar.getInstance(TimeZone.getTimeZone("GMT+08:00"));
|
||||
private static final Object sCalendarLock = new Object();
|
||||
|
||||
private static final SimpleDateFormat DATE_FORMAT_WITHOUT_YEAR =
|
||||
new SimpleDateFormat("MMM d", Locale.getDefault());
|
||||
|
||||
private static final SimpleDateFormat DATE_FORMAT_WIT_YEAR =
|
||||
new SimpleDateFormat("yyy MMM d", Locale.getDefault());
|
||||
|
||||
private static final SimpleDateFormat DATE_FORMAT =
|
||||
new SimpleDateFormat("yy-MM-dd HH:mm", Locale.getDefault());
|
||||
private static final Object sDateFormatLock1 = new Object();
|
||||
|
||||
private static final SimpleDateFormat FILENAMABLE_DATE_FORMAT =
|
||||
new SimpleDateFormat("yyyy-MM-dd-HH-mm-ss-SSS", Locale.getDefault());
|
||||
private static final Object sDateFormatLock2 = new Object();
|
||||
|
||||
static {
|
||||
// The website use GMT+08:00, so tell user the same
|
||||
DATE_FORMAT.setTimeZone(TimeZone.getTimeZone("GMT+08:00"));
|
||||
|
||||
DATE_FORMAT_WITHOUT_YEAR.setTimeZone(TimeZone.getTimeZone("GMT+08:00"));
|
||||
}
|
||||
|
||||
public static void initialize(Context context) {
|
||||
sResources = context.getApplicationContext().getResources();
|
||||
}
|
||||
|
||||
/*
|
||||
public static String getDisplayTime(long time) {
|
||||
if (Settings.getPrettyTime()) {
|
||||
return getTimeAgo(time);
|
||||
} else {
|
||||
return getPlainTime(time);
|
||||
}
|
||||
}
|
||||
*/
|
||||
|
||||
public static String getPlainTime(long time) {
|
||||
synchronized (sDateFormatLock1) {
|
||||
return DATE_FORMAT.format(new Date(time));
|
||||
}
|
||||
}
|
||||
|
||||
public static String getTimeAgo(long time) {
|
||||
Resources resources = sResources;
|
||||
|
||||
long now = System.currentTimeMillis();
|
||||
if (time > now + (2 * MINUTE_MILLIS) || time <= 0) {
|
||||
return resources.getString(R.string.from_the_future);
|
||||
}
|
||||
|
||||
final long diff = now - time;
|
||||
if (diff < MINUTE_MILLIS) {
|
||||
return resources.getString(R.string.just_now);
|
||||
} else if (diff < 2 * MINUTE_MILLIS) {
|
||||
return resources.getQuantityString(R.plurals.some_minutes_ago, 1, 1);
|
||||
} else if (diff < 50 * MINUTE_MILLIS) {
|
||||
int minutes = (int) (diff / MINUTE_MILLIS);
|
||||
return resources.getQuantityString(R.plurals.some_minutes_ago, minutes, minutes);
|
||||
} else if (diff < 90 * MINUTE_MILLIS) {
|
||||
return resources.getQuantityString(R.plurals.some_hours_ago, 1, 1);
|
||||
} else if (diff < 24 * HOUR_MILLIS) {
|
||||
int hours = (int) (diff / HOUR_MILLIS);
|
||||
return resources.getQuantityString(R.plurals.some_hours_ago, hours, hours);
|
||||
} else if (diff < 48 * HOUR_MILLIS) {
|
||||
return resources.getString(R.string.yesterday);
|
||||
} else if (diff < WEEK_MILLIS) {
|
||||
int days = (int) (diff / DAY_MILLIS);
|
||||
return resources.getString(R.string.some_days_ago, days);
|
||||
} else {
|
||||
synchronized (sCalendarLock) {
|
||||
Date nowDate = new Date(now);
|
||||
Date timeDate = new Date(time);
|
||||
sCalendar.setTime(nowDate);
|
||||
int nowYear = sCalendar.get(Calendar.YEAR);
|
||||
sCalendar.setTime(timeDate);
|
||||
int timeYear = sCalendar.get(Calendar.YEAR);
|
||||
|
||||
if (nowYear == timeYear) {
|
||||
return DATE_FORMAT_WITHOUT_YEAR.format(timeDate);
|
||||
} else {
|
||||
return DATE_FORMAT_WIT_YEAR.format(timeDate);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public static String getTimeInterval(long time) {
|
||||
StringBuilder sb = new StringBuilder();
|
||||
Resources resources = sResources;
|
||||
|
||||
long leftover = time;
|
||||
boolean start = false;
|
||||
|
||||
for (int i = 0; i < SIZE; i++) {
|
||||
long multiple = MULTIPLES[i];
|
||||
long quotient = leftover / multiple;
|
||||
long remainder = leftover % multiple;
|
||||
if (start || quotient != 0 || i == SIZE - 1) {
|
||||
if (start) {
|
||||
sb.append(" ");
|
||||
}
|
||||
sb.append(quotient)
|
||||
.append(" ")
|
||||
.append(resources.getQuantityString(UNITS[i], (int) quotient));
|
||||
start = true;
|
||||
}
|
||||
leftover = remainder;
|
||||
}
|
||||
|
||||
return sb.toString();
|
||||
}
|
||||
|
||||
public static String getFilenamableTime(long time) {
|
||||
synchronized (sDateFormatLock2) {
|
||||
return FILENAMABLE_DATE_FORMAT.format(new Date(time));
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,268 @@
|
||||
/*
|
||||
* 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.widget;
|
||||
|
||||
import android.annotation.SuppressLint;
|
||||
import android.content.Context;
|
||||
import android.content.res.TypedArray;
|
||||
import android.graphics.Rect;
|
||||
import android.util.AttributeSet;
|
||||
import android.view.View;
|
||||
import android.view.ViewGroup;
|
||||
|
||||
import com.hippo.ehviewer.R;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* A ViewGroup that can layout views in line and auto wrap
|
||||
*
|
||||
* @author Hippo
|
||||
*
|
||||
*/
|
||||
public class AutoWrapLayout extends ViewGroup {
|
||||
|
||||
private final List<Rect> rectList = new ArrayList<>();
|
||||
|
||||
private Alignment mAlignment;
|
||||
|
||||
private static final Alignment[] sBaseLineArray = { Alignment.TOP,
|
||||
Alignment.CENTER, Alignment.BOTTOM };
|
||||
|
||||
|
||||
public enum Alignment {
|
||||
TOP(0),
|
||||
CENTER(1),
|
||||
BOTTOM(2);
|
||||
|
||||
Alignment(int ni) {
|
||||
nativeInt = ni;
|
||||
}
|
||||
|
||||
final int nativeInt;
|
||||
}
|
||||
|
||||
public AutoWrapLayout(Context context) {
|
||||
super(context);
|
||||
}
|
||||
|
||||
public AutoWrapLayout(Context context, AttributeSet attrs) {
|
||||
this(context, attrs, 0);
|
||||
}
|
||||
|
||||
public AutoWrapLayout(Context context, AttributeSet attrs, int defStyle) {
|
||||
super(context, attrs, defStyle);
|
||||
|
||||
TypedArray a = context.obtainStyledAttributes(attrs,
|
||||
R.styleable.AutoWrapLayout, defStyle, 0);
|
||||
|
||||
int index = a.getInt(R.styleable.AutoWrapLayout_alignment, -1);
|
||||
if (index >= 0) {
|
||||
setAlignment(sBaseLineArray[index]);
|
||||
}
|
||||
|
||||
a.recycle();
|
||||
}
|
||||
|
||||
public void setAlignment(Alignment baseLine) {
|
||||
if (baseLine == null) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (mAlignment != baseLine) {
|
||||
mAlignment = baseLine;
|
||||
|
||||
requestLayout();
|
||||
invalidate();
|
||||
}
|
||||
}
|
||||
|
||||
public Alignment getAlignment() {
|
||||
return mAlignment;
|
||||
}
|
||||
|
||||
private void adjustBaseLine(int lineHeight, int startIndex, int endIndex) {
|
||||
if (mAlignment == Alignment.TOP)
|
||||
return;
|
||||
|
||||
for (int index = startIndex; index < endIndex; index++) {
|
||||
final View child = getChildAt(index);
|
||||
final MarginLayoutParams lp =
|
||||
(MarginLayoutParams)child.getLayoutParams();
|
||||
Rect rect = rectList.get(index);
|
||||
int offsetRaw = lineHeight - rect.height() - lp.topMargin - lp.bottomMargin;
|
||||
if (mAlignment == Alignment.CENTER)
|
||||
rect.offset(0, offsetRaw/2);
|
||||
else if (mAlignment == Alignment.BOTTOM)
|
||||
rect.offset(0, offsetRaw);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// TODO Take vertical mode
|
||||
/**
|
||||
* each row or line at least show one child
|
||||
*
|
||||
* horizontal only show child can show or partly show in parent
|
||||
*/
|
||||
@SuppressLint("DrawAllocation")
|
||||
@Override
|
||||
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
|
||||
int widthMode = MeasureSpec.getMode(widthMeasureSpec);
|
||||
int heightMode = MeasureSpec.getMode(heightMeasureSpec);
|
||||
|
||||
int maxWidth = MeasureSpec.getSize(widthMeasureSpec);
|
||||
int maxHeight = MeasureSpec.getSize(heightMeasureSpec);
|
||||
|
||||
if (widthMode == MeasureSpec.UNSPECIFIED)
|
||||
maxWidth = Integer.MAX_VALUE;
|
||||
if (heightMode == MeasureSpec.UNSPECIFIED)
|
||||
maxHeight = Integer.MAX_VALUE;
|
||||
|
||||
int paddingLeft = getPaddingLeft();
|
||||
int paddingTop = getPaddingTop();
|
||||
int paddingRight = getPaddingRight();
|
||||
int paddingBottom = getPaddingBottom();
|
||||
|
||||
int maxRightBound = maxWidth - paddingRight;
|
||||
int maxBottomBound = maxHeight - paddingBottom;
|
||||
|
||||
int left;
|
||||
int top;
|
||||
int right;
|
||||
int bottom;
|
||||
int rightBound = paddingLeft;
|
||||
int maxRightNoPadding = rightBound;
|
||||
int bottomBound;
|
||||
int lastMaxBottom = paddingTop;
|
||||
int maxBottom = lastMaxBottom;
|
||||
int childWidth;
|
||||
int childHeight;
|
||||
|
||||
int lineStartIndex = 0;
|
||||
int lineEndIndex; // endIndex + 1
|
||||
|
||||
rectList.clear();
|
||||
int childCount = getChildCount();
|
||||
for (int index = 0; index < childCount; index++) {
|
||||
final View child = getChildAt(index);
|
||||
child.measure(MeasureSpec.UNSPECIFIED, MeasureSpec.UNSPECIFIED);
|
||||
if (child.getVisibility() == View.GONE)
|
||||
continue;
|
||||
final MarginLayoutParams lp =
|
||||
(MarginLayoutParams)child.getLayoutParams();
|
||||
childWidth = child.getMeasuredWidth();
|
||||
childHeight = child.getMeasuredHeight();
|
||||
|
||||
left = rightBound + lp.leftMargin;
|
||||
right = left + childWidth;
|
||||
rightBound = right + lp.rightMargin;
|
||||
if (rightBound > maxRightBound) { // Go to next row
|
||||
lineEndIndex = index;
|
||||
// Adjust child position base on baseline
|
||||
adjustBaseLine(maxBottom - lastMaxBottom, lineStartIndex, lineEndIndex);
|
||||
|
||||
// If child can't show in parent begin this line
|
||||
if (maxBottom >= maxBottomBound)
|
||||
break;
|
||||
|
||||
// If it is first item in line, try to show it all
|
||||
if (lineEndIndex == lineStartIndex) {
|
||||
child.measure(MeasureSpec.makeMeasureSpec(
|
||||
maxWidth - paddingLeft - paddingRight
|
||||
- lp.leftMargin - lp.rightMargin, MeasureSpec.AT_MOST),
|
||||
MeasureSpec.UNSPECIFIED);
|
||||
childWidth = child.getMeasuredWidth();
|
||||
childHeight = child.getMeasuredHeight();
|
||||
}
|
||||
left = paddingLeft + lp.leftMargin;
|
||||
right = left + childWidth;
|
||||
rightBound = right + lp.rightMargin;
|
||||
|
||||
lastMaxBottom = maxBottom;
|
||||
top = lastMaxBottom + lp.topMargin;
|
||||
bottom = top + childHeight;
|
||||
bottomBound = bottom + lp.bottomMargin;
|
||||
|
||||
lineStartIndex = index;
|
||||
} else {
|
||||
top = lastMaxBottom + lp.topMargin;
|
||||
bottom = top + childHeight;
|
||||
bottomBound = bottom + lp.bottomMargin;
|
||||
}
|
||||
// Update max
|
||||
if (rightBound > maxRightNoPadding)
|
||||
maxRightNoPadding = rightBound;
|
||||
if (bottomBound > maxBottom)
|
||||
maxBottom = bottomBound;
|
||||
Rect rect = new Rect();
|
||||
rect.left = left;
|
||||
rect.top = top;
|
||||
rect.right = right;
|
||||
rect.bottom = bottom;
|
||||
rectList.add(rect);
|
||||
}
|
||||
|
||||
// Handle last line baseline
|
||||
adjustBaseLine(maxBottom - lastMaxBottom, lineStartIndex, rectList.size());
|
||||
|
||||
int measuredWidth;
|
||||
int measuredHeight;
|
||||
|
||||
if (widthMode == MeasureSpec.EXACTLY)
|
||||
measuredWidth = maxWidth;
|
||||
else
|
||||
measuredWidth = maxRightNoPadding + paddingRight;
|
||||
if (heightMode == MeasureSpec.EXACTLY)
|
||||
measuredHeight = maxHeight;
|
||||
else {
|
||||
measuredHeight = maxBottom + paddingBottom;
|
||||
if (heightMode == MeasureSpec.AT_MOST)
|
||||
measuredHeight = measuredHeight > maxHeight ? maxHeight : measuredHeight;
|
||||
}
|
||||
|
||||
setMeasuredDimension(measuredWidth, measuredHeight);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onLayout(boolean changed, int l, int t, int r, int b) {
|
||||
final int count = rectList.size();
|
||||
for(int i = 0; i < count; i++){
|
||||
final View child = this.getChildAt(i);
|
||||
if (child.getVisibility() == View.GONE)
|
||||
continue;
|
||||
Rect rect = rectList.get(i);
|
||||
child.layout(rect.left, rect.top, rect.right, rect.bottom);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public MarginLayoutParams generateLayoutParams(AttributeSet attrs) {
|
||||
return new MarginLayoutParams(getContext(), attrs);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected MarginLayoutParams generateDefaultLayoutParams() {
|
||||
return new MarginLayoutParams(LayoutParams.WRAP_CONTENT, LayoutParams.WRAP_CONTENT);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected MarginLayoutParams generateLayoutParams(ViewGroup.LayoutParams p) {
|
||||
return new MarginLayoutParams(p);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,88 @@
|
||||
/*
|
||||
* 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.widget;
|
||||
|
||||
import android.content.Context;
|
||||
import android.support.annotation.NonNull;
|
||||
import android.text.Layout;
|
||||
import android.text.Spanned;
|
||||
import android.text.style.ClickableSpan;
|
||||
import android.util.AttributeSet;
|
||||
import android.view.MotionEvent;
|
||||
import android.widget.TextView;
|
||||
|
||||
public class LinkifyTextView extends TextView {
|
||||
|
||||
private ClickableSpan mCurrentSpan;
|
||||
|
||||
public LinkifyTextView(Context context) {
|
||||
super(context);
|
||||
}
|
||||
|
||||
public LinkifyTextView(Context context, AttributeSet attrs) {
|
||||
super(context, attrs);
|
||||
}
|
||||
|
||||
public LinkifyTextView(Context context, AttributeSet attrs, int defStyleAttr) {
|
||||
super(context, attrs, defStyleAttr);
|
||||
}
|
||||
|
||||
public ClickableSpan getCurrentSpan() {
|
||||
return mCurrentSpan;
|
||||
}
|
||||
|
||||
public void clearCurrentSpan() {
|
||||
mCurrentSpan = null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean onTouchEvent(@NonNull MotionEvent event) {
|
||||
// Let the parent or grandparent of TextView to handles click aciton.
|
||||
// Otherwise click effect like ripple will not work, and if touch area
|
||||
// do not contain a url, the TextView will still get MotionEvent.
|
||||
// onTouchEven must be called with MotionEvent.ACTION_DOWN for each touch
|
||||
// action on it, so we analyze touched url here.
|
||||
if (event.getAction() == MotionEvent.ACTION_DOWN) {
|
||||
mCurrentSpan = null;
|
||||
|
||||
if (getText() instanceof Spanned) {
|
||||
// Get this code from android.text.method.LinkMovementMethod.
|
||||
// Work fine !
|
||||
int x = (int) event.getX();
|
||||
int y = (int) event.getY();
|
||||
|
||||
x -= getTotalPaddingLeft();
|
||||
y -= getTotalPaddingTop();
|
||||
|
||||
x += getScrollX();
|
||||
y += getScrollY();
|
||||
|
||||
Layout layout = getLayout();
|
||||
int line = layout.getLineForVertical(y);
|
||||
int off = layout.getOffsetForHorizontal(line, x);
|
||||
|
||||
ClickableSpan[] spans = ((Spanned)getText()).getSpans(off, off, ClickableSpan.class);
|
||||
|
||||
if (spans.length > 0) {
|
||||
mCurrentSpan = spans[0];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return super.onTouchEvent(event);
|
||||
}
|
||||
}
|
||||
Reference in new issue
Block a user