From afea1d063ab0da0929d5a648d03fafe89f62ed83 Mon Sep 17 00:00:00 2001 From: Floens Date: Thu, 16 Apr 2015 22:57:20 +0200 Subject: [PATCH] Post replies done --- .../floens/chan/controller/Controller.java | 5 + .../chan/controller/FadeInTransition.java | 4 +- .../chan/controller/FadeOutTransition.java | 4 +- .../java/org/floens/chan/core/model/Post.java | 14 -- .../floens/chan/core/model/PostLinkable.java | 45 +++- .../chan/ui/activity/StartActivity.java | 5 + .../chan/ui/controller/BrowseController.java | 4 +- .../ui/controller/PostRepliesController.java | 192 ++++++++++++++++++ .../chan/ui/controller/ThreadController.java | 10 + .../ui/controller/ViewThreadController.java | 2 +- .../chan/ui/fragment/PostRepliesFragment.java | 6 +- .../chan/ui/helper/PostPopupHelper.java | 63 +++--- .../floens/chan/ui/layout/ThreadLayout.java | 16 +- .../org/floens/chan/ui/view/LoadView.java | 6 + .../org/floens/chan/ui/view/PostView.java | 106 ++++++++-- .../org/floens/chan/utils/AndroidUtils.java | 4 + .../res/drawable-hdpi/dialog_full_dark.9.png | Bin 0 -> 1482 bytes .../res/drawable-hdpi/dialog_full_light.9.png | Bin 0 -> 1515 bytes .../res/drawable-mdpi/dialog_full_dark.9.png | Bin 0 -> 935 bytes .../res/drawable-mdpi/dialog_full_light.9.png | Bin 0 -> 944 bytes .../res/drawable-xhdpi/dialog_full_dark.9.png | Bin 0 -> 2201 bytes .../drawable-xhdpi/dialog_full_light.9.png | Bin 0 -> 2231 bytes .../drawable-xxhdpi/dialog_full_dark.9.png | Bin 0 -> 4143 bytes .../drawable-xxhdpi/dialog_full_light.9.png | Bin 0 -> 4272 bytes .../app/src/main/res/layout/post_replies.xml | 114 ++++++----- .../res/layout/post_replies_bottombuttons.xml | 113 ++++++----- .../res/layout/post_replies_container.xml | 22 ++ 27 files changed, 553 insertions(+), 182 deletions(-) create mode 100644 Clover/app/src/main/java/org/floens/chan/ui/controller/PostRepliesController.java create mode 100644 Clover/app/src/main/res/drawable-hdpi/dialog_full_dark.9.png create mode 100644 Clover/app/src/main/res/drawable-hdpi/dialog_full_light.9.png create mode 100644 Clover/app/src/main/res/drawable-mdpi/dialog_full_dark.9.png create mode 100644 Clover/app/src/main/res/drawable-mdpi/dialog_full_light.9.png create mode 100644 Clover/app/src/main/res/drawable-xhdpi/dialog_full_dark.9.png create mode 100644 Clover/app/src/main/res/drawable-xhdpi/dialog_full_light.9.png create mode 100644 Clover/app/src/main/res/drawable-xxhdpi/dialog_full_dark.9.png create mode 100644 Clover/app/src/main/res/drawable-xxhdpi/dialog_full_light.9.png create mode 100644 Clover/app/src/main/res/layout/post_replies_container.xml diff --git a/Clover/app/src/main/java/org/floens/chan/controller/Controller.java b/Clover/app/src/main/java/org/floens/chan/controller/Controller.java index e20e0d45..3b9d3f12 100644 --- a/Clover/app/src/main/java/org/floens/chan/controller/Controller.java +++ b/Clover/app/src/main/java/org/floens/chan/controller/Controller.java @@ -40,11 +40,14 @@ public abstract class Controller { public Controller presentingController; public Controller presentedController; + public boolean alive = false; + public Controller(Context context) { this.context = context; } public void onCreate() { + alive = true; // Logger.test(getClass().getSimpleName() + " onCreate"); } @@ -57,6 +60,7 @@ public abstract class Controller { } public void onDestroy() { + alive = false; // Logger.test(getClass().getSimpleName() + " onDestroy"); } @@ -111,6 +115,7 @@ public abstract class Controller { ControllerLogic.transition(this, null, true, false, contentView); } ((BoardActivity) context).removeController(this); + presentingController.presentedController = null; } public View inflateRes(int resId) { diff --git a/Clover/app/src/main/java/org/floens/chan/controller/FadeInTransition.java b/Clover/app/src/main/java/org/floens/chan/controller/FadeInTransition.java index af74af72..2475e711 100644 --- a/Clover/app/src/main/java/org/floens/chan/controller/FadeInTransition.java +++ b/Clover/app/src/main/java/org/floens/chan/controller/FadeInTransition.java @@ -5,14 +5,14 @@ import android.animation.AnimatorListenerAdapter; import android.animation.AnimatorSet; import android.animation.ObjectAnimator; import android.view.View; -import android.view.animation.DecelerateInterpolator; +import android.view.animation.AccelerateDecelerateInterpolator; public class FadeInTransition extends ControllerTransition { @Override public void perform() { Animator toAlpha = ObjectAnimator.ofFloat(to.view, View.ALPHA, 0f, 1f); toAlpha.setDuration(200); - toAlpha.setInterpolator(new DecelerateInterpolator(2f)); + toAlpha.setInterpolator(new AccelerateDecelerateInterpolator()); toAlpha.addListener(new AnimatorListenerAdapter() { @Override diff --git a/Clover/app/src/main/java/org/floens/chan/controller/FadeOutTransition.java b/Clover/app/src/main/java/org/floens/chan/controller/FadeOutTransition.java index e823ae8c..c0ab0bed 100644 --- a/Clover/app/src/main/java/org/floens/chan/controller/FadeOutTransition.java +++ b/Clover/app/src/main/java/org/floens/chan/controller/FadeOutTransition.java @@ -5,14 +5,14 @@ import android.animation.AnimatorListenerAdapter; import android.animation.AnimatorSet; import android.animation.ObjectAnimator; import android.view.View; -import android.view.animation.DecelerateInterpolator; +import android.view.animation.AccelerateDecelerateInterpolator; public class FadeOutTransition extends ControllerTransition { @Override public void perform() { Animator toAlpha = ObjectAnimator.ofFloat(from.view, View.ALPHA, 1f, 0f); toAlpha.setDuration(200); - toAlpha.setInterpolator(new DecelerateInterpolator(2f)); + toAlpha.setInterpolator(new AccelerateDecelerateInterpolator()); toAlpha.addListener(new AnimatorListenerAdapter() { @Override diff --git a/Clover/app/src/main/java/org/floens/chan/core/model/Post.java b/Clover/app/src/main/java/org/floens/chan/core/model/Post.java index 75c5599f..386cda60 100644 --- a/Clover/app/src/main/java/org/floens/chan/core/model/Post.java +++ b/Clover/app/src/main/java/org/floens/chan/core/model/Post.java @@ -23,7 +23,6 @@ import android.text.TextUtils; import org.floens.chan.ChanApplication; import org.floens.chan.chan.ChanUrls; import org.floens.chan.core.loader.ChanParser; -import org.floens.chan.ui.view.PostView; import org.jsoup.parser.Parser; import java.util.ArrayList; @@ -105,22 +104,9 @@ public class Post { public SpannableString capcodeSpan; public CharSequence nameTripcodeIdCapcodeSpan; - /** - * The PostView the Post is currently bound to. - */ - private PostView linkableListener; - public Post() { } - public void setLinkableListener(PostView listener) { - linkableListener = listener; - } - - public PostView getLinkableListener() { - return linkableListener; - } - /** * Finish up the data * diff --git a/Clover/app/src/main/java/org/floens/chan/core/model/PostLinkable.java b/Clover/app/src/main/java/org/floens/chan/core/model/PostLinkable.java index c2de1ca3..70b54366 100644 --- a/Clover/app/src/main/java/org/floens/chan/core/model/PostLinkable.java +++ b/Clover/app/src/main/java/org/floens/chan/core/model/PostLinkable.java @@ -17,17 +17,21 @@ */ package org.floens.chan.core.model; +import android.support.annotation.NonNull; import android.text.TextPaint; import android.text.style.ClickableSpan; import android.view.View; import org.floens.chan.utils.ThemeHelper; +import java.util.ArrayList; +import java.util.List; + /** * Anything that links to something in a post uses this entity. */ public class PostLinkable extends ClickableSpan { - public static enum Type { + public enum Type { QUOTE, LINK, SPOILER, THREAD } @@ -36,7 +40,8 @@ public class PostLinkable extends ClickableSpan { public final Object value; public final Type type; - private boolean clicked = false; + private List callbacks = new ArrayList<>(); + private boolean spoilerVisible = false; public PostLinkable(Post post, String key, Object value, Type type) { this.post = post; @@ -45,19 +50,33 @@ public class PostLinkable extends ClickableSpan { this.type = type; } + public void addCallback(Callback callback) { + callbacks.add(callback); + } + + public void removeCallback(Callback callback) { + callbacks.remove(callback); + } + + public boolean hasCallback(Callback callback) { + return callbacks.contains(callback); + } + @Override public void onClick(View widget) { - if (post.getLinkableListener() != null) { - post.getLinkableListener().onLinkableClick(this); + Callback top = topCallback(); + if (top != null) { + top.onLinkableClick(this); } - clicked = true; + spoilerVisible = !spoilerVisible; } @Override - public void updateDrawState(TextPaint ds) { + public void updateDrawState(@NonNull TextPaint ds) { if (type == Type.QUOTE || type == Type.LINK || type == Type.THREAD) { if (type == Type.QUOTE) { - if (value instanceof Integer && post.getLinkableListener() != null && (Integer) value == post.getLinkableListener().getHighlightQuotesWithNo()) { + Callback top = topCallback(); + if (value instanceof Integer && top != null && (Integer) value == top.getHighlightQuotesWithNo(this)) { ds.setColor(ThemeHelper.getInstance().getHighlightQuoteColor()); } else { ds.setColor(ThemeHelper.getInstance().getQuoteColor()); @@ -70,7 +89,7 @@ public class PostLinkable extends ClickableSpan { ds.setUnderlineText(true); } else if (type == Type.SPOILER) { - if (!clicked) { + if (!spoilerVisible) { ds.setColor(ThemeHelper.getInstance().getSpoilerColor()); ds.bgColor = ThemeHelper.getInstance().getSpoilerColor(); ds.setUnderlineText(false); @@ -78,6 +97,10 @@ public class PostLinkable extends ClickableSpan { } } + private Callback topCallback() { + return callbacks.size() > 0 ? callbacks.get(callbacks.size() - 1) : null; + } + public static class ThreadLink { public String board; public int threadId; @@ -89,4 +112,10 @@ public class PostLinkable extends ClickableSpan { this.postId = postId; } } + + public interface Callback { + void onLinkableClick(PostLinkable postLinkable); + + int getHighlightQuotesWithNo(PostLinkable postLinkable); + } } diff --git a/Clover/app/src/main/java/org/floens/chan/ui/activity/StartActivity.java b/Clover/app/src/main/java/org/floens/chan/ui/activity/StartActivity.java index 8245de29..29a486a3 100644 --- a/Clover/app/src/main/java/org/floens/chan/ui/activity/StartActivity.java +++ b/Clover/app/src/main/java/org/floens/chan/ui/activity/StartActivity.java @@ -77,6 +77,11 @@ public class StartActivity extends Activity { super.onDestroy(); stackTop().onDestroy(); + stack.clear(); + System.gc(); + System.gc(); + System.gc(); + System.runFinalization(); } @Override diff --git a/Clover/app/src/main/java/org/floens/chan/ui/controller/BrowseController.java b/Clover/app/src/main/java/org/floens/chan/ui/controller/BrowseController.java index 404db81c..8e786d32 100644 --- a/Clover/app/src/main/java/org/floens/chan/ui/controller/BrowseController.java +++ b/Clover/app/src/main/java/org/floens/chan/ui/controller/BrowseController.java @@ -137,7 +137,7 @@ public class BrowseController extends ThreadController implements ToolbarMenuIte } @Override - public void openThread(Loadable threadLoadable) { + public void showThread(Loadable threadLoadable) { ViewThreadController viewThreadController = new ViewThreadController(context); viewThreadController.setLoadable(threadLoadable); navigationController.pushController(viewThreadController); @@ -149,7 +149,7 @@ public class BrowseController extends ThreadController implements ToolbarMenuIte @Override public void onPinClicked(Pin pin) { - openThread(pin.loadable); + showThread(pin.loadable); } @Override diff --git a/Clover/app/src/main/java/org/floens/chan/ui/controller/PostRepliesController.java b/Clover/app/src/main/java/org/floens/chan/ui/controller/PostRepliesController.java new file mode 100644 index 00000000..102e1151 --- /dev/null +++ b/Clover/app/src/main/java/org/floens/chan/ui/controller/PostRepliesController.java @@ -0,0 +1,192 @@ +package org.floens.chan.ui.controller; + +import android.animation.ValueAnimator; +import android.app.Activity; +import android.content.Context; +import android.graphics.Color; +import android.os.Build; +import android.view.View; +import android.view.ViewGroup; +import android.view.Window; +import android.view.animation.LinearInterpolator; +import android.widget.AbsListView; +import android.widget.ArrayAdapter; +import android.widget.ListView; +import android.widget.TextView; + +import org.floens.chan.R; +import org.floens.chan.controller.Controller; +import org.floens.chan.core.model.Post; +import org.floens.chan.core.presenter.ThreadPresenter; +import org.floens.chan.core.settings.ChanSettings; +import org.floens.chan.ui.helper.PostPopupHelper; +import org.floens.chan.ui.view.LoadView; +import org.floens.chan.ui.view.PostView; +import org.floens.chan.utils.ThemeHelper; + +public class PostRepliesController extends Controller { + private static final int TRANSITION_DURATION = 200; + + private PostPopupHelper postPopupHelper; + private ThreadPresenter presenter; + + private int statusBarColorPrevious; + private boolean first = true; + + private LoadView loadView; + + public PostRepliesController(Context context, PostPopupHelper postPopupHelper, ThreadPresenter presenter) { + super(context); + this.postPopupHelper = postPopupHelper; + this.presenter = presenter; + } + + @Override + public void onCreate() { + super.onCreate(); + + view = inflateRes(R.layout.post_replies_container); + + // Clicking outside the popup view + view.setOnClickListener(new View.OnClickListener() { + @Override + public void onClick(View v) { + postPopupHelper.popAll(); + } + }); + + loadView = (LoadView) view.findViewById(R.id.loadview); + + if (Build.VERSION.SDK_INT >= 21) { + statusBarColorPrevious = getWindow().getStatusBarColor(); + if (statusBarColorPrevious != 0) { + animateStatusBar(true, statusBarColorPrevious); + } + } + } + + @Override + public void stopPresenting() { + super.stopPresenting(); + if (Build.VERSION.SDK_INT >= 21) { + if (statusBarColorPrevious != 0) { + animateStatusBar(false, statusBarColorPrevious); + } + } + } + + public void setPostRepliesData(PostPopupHelper.RepliesData data) { + displayData(data); + } + + private void displayData(final PostPopupHelper.RepliesData data) { + View dataView; + if (ChanSettings.repliesButtonsBottom.get()) { + dataView = inflateRes(R.layout.post_replies_bottombuttons); + } else { + dataView = inflateRes(R.layout.post_replies); + } + + ListView listView = (ListView) dataView.findViewById(R.id.post_list); + + View repliesBack = dataView.findViewById(R.id.replies_back); + repliesBack.setOnClickListener(new View.OnClickListener() { + @Override + public void onClick(View v) { + postPopupHelper.pop(); + } + }); + + View repliesClose = dataView.findViewById(R.id.replies_close); + repliesClose.setOnClickListener(new View.OnClickListener() { + @Override + public void onClick(View v) { + postPopupHelper.popAll(); + } + }); + + if (!ThemeHelper.getInstance().getTheme().isLightTheme) { + ((TextView) dataView.findViewById(R.id.replies_back_icon)).setCompoundDrawablesWithIntrinsicBounds(R.drawable.ic_action_back_dark, 0, 0, 0); + ((TextView) dataView.findViewById(R.id.replies_close_icon)).setCompoundDrawablesWithIntrinsicBounds(R.drawable.ic_action_done_dark, 0, 0, 0); + dataView.findViewById(R.id.container).setBackgroundResource(R.drawable.dialog_full_dark); + } + + ArrayAdapter adapter = new ArrayAdapter(context, 0) { + @Override + public View getView(int position, View convertView, ViewGroup parent) { + PostView postView; + if (convertView instanceof PostView) { + postView = (PostView) convertView; + } else { + postView = new PostView(context); + } + + final Post p = getItem(position); + + postView.setPost(p, presenter); + postView.setHighlightQuotesWithNo(data.forPost.no); + postView.setOnClickListeners(new View.OnClickListener() { + @Override + public void onClick(View v) { + postPopupHelper.postClicked(p); + } + }); + + return postView; + } + }; + + adapter.addAll(data.posts); + listView.setAdapter(adapter); + + listView.setSelectionFromTop(data.listViewIndex, data.listViewTop); + listView.setOnScrollListener(new AbsListView.OnScrollListener() { + @Override + public void onScrollStateChanged(AbsListView view, int scrollState) { + } + + @Override + public void onScroll(AbsListView view, int firstVisibleItem, int visibleItemCount, int totalItemCount) { + data.listViewIndex = view.getFirstVisiblePosition(); + View v = view.getChildAt(0); + data.listViewTop = (v == null) ? 0 : v.getTop(); + } + }); + + loadView.setFadeDuration(first ? 0 : 200); + first = false; + loadView.setView(dataView); + } + + @Override + public boolean onBack() { + postPopupHelper.pop(); + return true; + } + + private void animateStatusBar(boolean in, final int originalColor) { + ValueAnimator statusBar = ValueAnimator.ofFloat(in ? 0f : 0.5f, in ? 0.5f : 0f); + statusBar.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() { + @Override + public void onAnimationUpdate(ValueAnimator animation) { + if (Build.VERSION.SDK_INT >= 21) { // Make lint happy + float progress = (float) animation.getAnimatedValue(); + if (progress == 0f) { + getWindow().setStatusBarColor(originalColor); + } else { + int r = (int) ((1f - progress) * Color.red(originalColor)); + int g = (int) ((1f - progress) * Color.green(originalColor)); + int b = (int) ((1f - progress) * Color.blue(originalColor)); + getWindow().setStatusBarColor(Color.argb(255, r, g, b)); + } + } + } + }); + statusBar.setDuration(TRANSITION_DURATION).setInterpolator(new LinearInterpolator()); + statusBar.start(); + } + + private Window getWindow() { + return ((Activity) context).getWindow(); + } +} diff --git a/Clover/app/src/main/java/org/floens/chan/ui/controller/ThreadController.java b/Clover/app/src/main/java/org/floens/chan/ui/controller/ThreadController.java index b1d1218f..35be39ca 100644 --- a/Clover/app/src/main/java/org/floens/chan/ui/controller/ThreadController.java +++ b/Clover/app/src/main/java/org/floens/chan/ui/controller/ThreadController.java @@ -21,6 +21,16 @@ public abstract class ThreadController extends Controller implements ThreadLayou view = threadLayout; } + @Override + public void onDestroy() { + super.onDestroy(); + threadLayout.getPresenter().unbindLoadable(); + } + + public void presentRepliesController(Controller controller) { + presentController(controller); + } + @Override public void showImages(List images, int index, Loadable loadable, final ThumbnailView thumbnail) { // Just ignore the showImages request when the image is not loaded diff --git a/Clover/app/src/main/java/org/floens/chan/ui/controller/ViewThreadController.java b/Clover/app/src/main/java/org/floens/chan/ui/controller/ViewThreadController.java index 94addd4e..cd0cc8dc 100644 --- a/Clover/app/src/main/java/org/floens/chan/ui/controller/ViewThreadController.java +++ b/Clover/app/src/main/java/org/floens/chan/ui/controller/ViewThreadController.java @@ -105,7 +105,7 @@ public class ViewThreadController extends ThreadController implements ThreadLayo } @Override - public void openThread(final Loadable threadLoadable) { + public void showThread(final Loadable threadLoadable) { // TODO implement, scroll to post and fix title new AlertDialog.Builder(context) .setNegativeButton(R.string.cancel, null) diff --git a/Clover/app/src/main/java/org/floens/chan/ui/fragment/PostRepliesFragment.java b/Clover/app/src/main/java/org/floens/chan/ui/fragment/PostRepliesFragment.java index 71579f11..c54595d9 100644 --- a/Clover/app/src/main/java/org/floens/chan/ui/fragment/PostRepliesFragment.java +++ b/Clover/app/src/main/java/org/floens/chan/ui/fragment/PostRepliesFragment.java @@ -51,10 +51,10 @@ public class PostRepliesFragment extends DialogFragment { public static PostRepliesFragment newInstance(PostPopupHelper.RepliesData repliesData, PostPopupHelper postPopupHelper, ThreadPresenter presenter) { PostRepliesFragment fragment = new PostRepliesFragment(); + fragment.repliesData = repliesData; fragment.postPopupHelper = postPopupHelper; fragment.presenter = presenter; - return fragment; } @@ -75,7 +75,7 @@ public class PostRepliesFragment extends DialogFragment { super.onDismiss(dialog); if (postPopupHelper != null) { - postPopupHelper.onPostRepliesPop(); +// postPopupHelper.onPostRepliesPop(); } } @@ -101,7 +101,7 @@ public class PostRepliesFragment extends DialogFragment { @Override public void onClick(View v) { if (postPopupHelper != null) { - postPopupHelper.closeAllPostFragments(); +// postPopupHelper.closeAllPostFragments(); } } }); diff --git a/Clover/app/src/main/java/org/floens/chan/ui/helper/PostPopupHelper.java b/Clover/app/src/main/java/org/floens/chan/ui/helper/PostPopupHelper.java index e3fa4cf3..09af0a6e 100644 --- a/Clover/app/src/main/java/org/floens/chan/ui/helper/PostPopupHelper.java +++ b/Clover/app/src/main/java/org/floens/chan/ui/helper/PostPopupHelper.java @@ -17,13 +17,12 @@ */ package org.floens.chan.ui.helper; -import android.app.Activity; -import android.app.FragmentTransaction; import android.content.Context; +import org.floens.chan.controller.Controller; import org.floens.chan.core.model.Post; import org.floens.chan.core.presenter.ThreadPresenter; -import org.floens.chan.ui.fragment.PostRepliesFragment; +import org.floens.chan.ui.controller.PostRepliesController; import java.util.ArrayList; import java.util.List; @@ -31,13 +30,15 @@ import java.util.List; public class PostPopupHelper { private Context context; private ThreadPresenter presenter; + private final PostPopupHelperCallback callback; private final List dataQueue = new ArrayList<>(); - private PostRepliesFragment currentPopupFragment; + private PostRepliesController presentingController; - public PostPopupHelper(Context context, ThreadPresenter presenter) { + public PostPopupHelper(Context context, ThreadPresenter presenter, PostPopupHelperCallback callback) { this.context = context; this.presenter = presenter; + this.callback = callback; } public void showPosts(Post forPost, List posts) { @@ -45,47 +46,47 @@ public class PostPopupHelper { dataQueue.add(data); - if (currentPopupFragment != null) { - currentPopupFragment.dismissNoCallback(); + if (dataQueue.size() == 1) { + present(); } - - presentFragment(data); + presentingController.setPostRepliesData(data); } - public void onPostRepliesPop() { - if (dataQueue.size() == 0) - return; - - dataQueue.remove(dataQueue.size() - 1); + public void pop() { + if (dataQueue.size() > 0) { + dataQueue.remove(dataQueue.size() - 1); + } if (dataQueue.size() > 0) { - presentFragment(dataQueue.get(dataQueue.size() - 1)); + presentingController.setPostRepliesData(dataQueue.get(dataQueue.size() - 1)); } else { - currentPopupFragment = null; + dismiss(); } } - public void closeAllPostFragments() { + public void popAll() { dataQueue.clear(); - if (currentPopupFragment != null) { - currentPopupFragment.dismissNoCallback(); - currentPopupFragment = null; - } + dismiss(); } public void postClicked(Post p) { - closeAllPostFragments(); + popAll(); presenter.highlightPost(p.no); presenter.scrollToPost(p.no); } - private void presentFragment(RepliesData data) { - PostRepliesFragment fragment = PostRepliesFragment.newInstance(data, this, presenter); - // TODO fade animations on all platforms - FragmentTransaction ft = ((Activity) context).getFragmentManager().beginTransaction(); - ft.add(fragment, "postPopup"); - ft.commitAllowingStateLoss(); - currentPopupFragment = fragment; + private void dismiss() { + if (presentingController != null) { + presentingController.stopPresenting(); + presentingController = null; + } + } + + private void present() { + if (presentingController == null) { + presentingController = new PostRepliesController(context, this, presenter); + callback.presentRepliesController(presentingController); + } } public static class RepliesData { @@ -99,4 +100,8 @@ public class PostPopupHelper { this.posts = posts; } } + + public interface PostPopupHelperCallback { + void presentRepliesController(Controller controller); + } } diff --git a/Clover/app/src/main/java/org/floens/chan/ui/layout/ThreadLayout.java b/Clover/app/src/main/java/org/floens/chan/ui/layout/ThreadLayout.java index bf0b0d68..4cbe6d8d 100644 --- a/Clover/app/src/main/java/org/floens/chan/ui/layout/ThreadLayout.java +++ b/Clover/app/src/main/java/org/floens/chan/ui/layout/ThreadLayout.java @@ -28,6 +28,7 @@ import android.widget.Toast; import com.android.volley.VolleyError; import org.floens.chan.R; +import org.floens.chan.controller.Controller; import org.floens.chan.core.model.ChanThread; import org.floens.chan.core.model.Loadable; import org.floens.chan.core.model.Post; @@ -45,7 +46,7 @@ import java.util.List; /** * Wrapper around ThreadListLayout, so that it cleanly manages between loadbar and listview. */ -public class ThreadLayout extends LoadView implements ThreadPresenter.ThreadPresenterCallback { +public class ThreadLayout extends LoadView implements ThreadPresenter.ThreadPresenterCallback, PostPopupHelper.PostPopupHelperCallback { private ThreadLayoutCallback callback; private ThreadPresenter presenter; @@ -74,7 +75,7 @@ public class ThreadLayout extends LoadView implements ThreadPresenter.ThreadPres threadListLayout = new ThreadListLayout(getContext()); threadListLayout.setCallbacks(presenter, presenter); - postPopupHelper = new PostPopupHelper(getContext(), presenter); + postPopupHelper = new PostPopupHelper(getContext(), presenter, this); switchVisible(false); } @@ -157,7 +158,7 @@ public class ThreadLayout extends LoadView implements ThreadPresenter.ThreadPres @Override public void showThread(Loadable threadLoadable) { - callback.openThread(threadLoadable); + callback.showThread(threadLoadable); } public void showPostsPopup(Post forPost, List posts) { @@ -185,11 +186,18 @@ public class ThreadLayout extends LoadView implements ThreadPresenter.ThreadPres } } + @Override + public void presentRepliesController(Controller controller) { + callback.presentRepliesController(controller); + } + public interface ThreadLayoutCallback { - void openThread(Loadable threadLoadable); + void showThread(Loadable threadLoadable); void showImages(List images, int index, Loadable loadable, ThumbnailView thumbnail); void onShowPosts(); + + void presentRepliesController(Controller controller); } } diff --git a/Clover/app/src/main/java/org/floens/chan/ui/view/LoadView.java b/Clover/app/src/main/java/org/floens/chan/ui/view/LoadView.java index bddaa9c5..5f623d86 100644 --- a/Clover/app/src/main/java/org/floens/chan/ui/view/LoadView.java +++ b/Clover/app/src/main/java/org/floens/chan/ui/view/LoadView.java @@ -25,6 +25,7 @@ import android.content.Context; import android.util.AttributeSet; import android.view.Gravity; import android.view.View; +import android.view.animation.LinearInterpolator; import android.widget.FrameLayout; import android.widget.ProgressBar; @@ -141,6 +142,10 @@ public class LoadView extends FrameLayout { final AnimatorSet set = new AnimatorSet(); set.setDuration(fadeDuration); + if (fadeDuration > 0) { + set.setStartDelay(50); + } + set.setInterpolator(new LinearInterpolator()); set.play(ObjectAnimator.ofFloat(view, View.ALPHA, 0f)); animatorsOut.put(view, set); set.addListener(new AnimatorListenerAdapter() { @@ -156,6 +161,7 @@ public class LoadView extends FrameLayout { private void animateViewIn(View view) { final AnimatorSet set = new AnimatorSet(); set.setDuration(fadeDuration); + set.setInterpolator(new LinearInterpolator()); set.play(ObjectAnimator.ofFloat(view, View.ALPHA, 1f)); animatorsIn.put(view, set); set.addListener(new AnimatorListenerAdapter() { diff --git a/Clover/app/src/main/java/org/floens/chan/ui/view/PostView.java b/Clover/app/src/main/java/org/floens/chan/ui/view/PostView.java index 9cee09a3..340a6b48 100644 --- a/Clover/app/src/main/java/org/floens/chan/ui/view/PostView.java +++ b/Clover/app/src/main/java/org/floens/chan/ui/view/PostView.java @@ -23,10 +23,12 @@ import android.content.res.TypedArray; import android.text.Layout; import android.text.Spannable; import android.text.SpannableString; +import android.text.SpannedString; import android.text.TextUtils; import android.text.format.DateUtils; import android.text.method.LinkMovementMethod; import android.text.style.AbsoluteSizeSpan; +import android.text.style.BackgroundColorSpan; import android.text.style.ClickableSpan; import android.text.style.ForegroundColorSpan; import android.util.AttributeSet; @@ -54,7 +56,7 @@ import org.floens.chan.utils.Time; import static org.floens.chan.utils.AndroidUtils.setPressedDrawable; -public class PostView extends LinearLayout implements View.OnClickListener { +public class PostView extends LinearLayout implements View.OnClickListener, PostLinkable.Callback { private final static LinearLayout.LayoutParams matchParams = new LinearLayout.LayoutParams( LayoutParams.MATCH_PARENT, LayoutParams.MATCH_PARENT); private final static LinearLayout.LayoutParams wrapParams = new LinearLayout.LayoutParams( @@ -71,6 +73,8 @@ public class PostView extends LinearLayout implements View.OnClickListener { private Loadable loadable; private int highlightQuotesNo = -1; + private boolean ignoreNextOnClick = false; + private boolean isBuild = false; private LinearLayout full; private LinearLayout contentContainer; @@ -112,18 +116,23 @@ public class PostView extends LinearLayout implements View.OnClickListener { @Override protected void onDetachedFromWindow() { super.onDetachedFromWindow(); - - if (post != null) { - post.setLinkableListener(null); + if (this.post != null) { + setPostLinkableListener(null); } } public void setPost(final Post post, final PostViewCallback callback) { + if (this.post != null) { + // Remove callbacks from the old post while it is still set + setPostLinkableListener(null); + } + this.post = post; this.callback = callback; this.loadable = callback.getLoadable(); highlightQuotesNo = -1; + setPostLinkableListener(this); boolean boardCatalogMode = loadable.isBoardMode() || loadable.isCatalogMode(); @@ -174,11 +183,9 @@ public class PostView extends LinearLayout implements View.OnClickListener { commentView.setText(post.comment); if (loadable.isThreadMode()) { - post.setLinkableListener(this); commentView.setMovementMethod(new PostViewMovementMethod()); commentView.setOnClickListener(this); } else { - post.setLinkableListener(null); commentView.setOnClickListener(null); commentView.setClickable(false); commentView.setMovementMethod(null); @@ -267,8 +274,20 @@ public class PostView extends LinearLayout implements View.OnClickListener { highlightQuotesNo = no; } - public int getHighlightQuotesWithNo() { - return highlightQuotesNo; + private void setPostLinkableListener(PostLinkable.Callback callback) { + if (post.comment instanceof SpannedString) { + SpannedString commentSpannable = (SpannedString) post.comment; + PostLinkable[] linkables = commentSpannable.getSpans(0, commentSpannable.length(), PostLinkable.class); + for (PostLinkable linkable : linkables) { + if (callback == null) { + if (linkable.hasCallback(this)) { + linkable.removeCallback(this); + } + } else { + linkable.addCallback(callback); + } + } + } } private void buildView(final Context context, TypedArray ta) { @@ -466,18 +485,35 @@ public class PostView extends LinearLayout implements View.OnClickListener { wrapper.setOnClickListener(this); } - public void setOnClickListeners(View.OnClickListener listener) { - commentView.setOnClickListener(listener); - full.setOnClickListener(listener); + public void setOnClickListeners(final View.OnClickListener listener) { + commentView.setOnClickListener(new OnClickListener() { + @Override + public void onClick(View v) { + if (ignoreNextOnClick) { + ignoreNextOnClick = false; + } else { + listener.onClick(v); + } + } + }); } public void onLinkableClick(PostLinkable linkable) { callback.onPostLinkableClicked(linkable); } + @Override + public int getHighlightQuotesWithNo(PostLinkable postLinkable) { + return highlightQuotesNo; + } + @Override public void onClick(View v) { - callback.onPostClicked(post); + if (ignoreNextOnClick) { + ignoreNextOnClick = false; + } else { + callback.onPostClicked(post); + } } private boolean isList() { @@ -512,7 +548,51 @@ public class PostView extends LinearLayout implements View.OnClickListener { boolean isPostLastSeen(Post post); } + private static BackgroundColorSpan BACKGROUND_SPAN = new BackgroundColorSpan(0x6633B5E5); + private class PostViewMovementMethod extends LinkMovementMethod { + @Override + public boolean onTouchEvent(TextView widget, Spannable buffer, MotionEvent event) { + int action = event.getActionMasked(); + + if (action == MotionEvent.ACTION_UP || action == MotionEvent.ACTION_CANCEL || action == MotionEvent.ACTION_DOWN) { + int x = (int) event.getX(); + int y = (int) event.getY(); + + x -= widget.getTotalPaddingLeft(); + y -= widget.getTotalPaddingTop(); + + x += widget.getScrollX(); + y += widget.getScrollY(); + + Layout layout = widget.getLayout(); + int line = layout.getLineForVertical(y); + int off = layout.getOffsetForHorizontal(line, x); + + ClickableSpan[] link = buffer.getSpans(off, off, ClickableSpan.class); + + if (link.length != 0) { + if (action == MotionEvent.ACTION_UP) { + ignoreNextOnClick = true; + link[0].onClick(widget); + buffer.removeSpan(BACKGROUND_SPAN); + } else if (action == MotionEvent.ACTION_DOWN) { + buffer.setSpan(BACKGROUND_SPAN, buffer.getSpanStart(link[0]), buffer.getSpanEnd(link[0]), 0); + } else if (action == MotionEvent.ACTION_CANCEL) { + buffer.removeSpan(BACKGROUND_SPAN); + } + + return true; + } else { + buffer.removeSpan(BACKGROUND_SPAN); + } + } + + return true; + } + } + + /*private class PostViewMovementMethod extends LinkMovementMethod { @Override public boolean onTouchEvent(TextView widget, Spannable buffer, MotionEvent event) { int action = event.getAction(); @@ -551,5 +631,5 @@ public class PostView extends LinearLayout implements View.OnClickListener { return true; } } - } + }*/ } diff --git a/Clover/app/src/main/java/org/floens/chan/utils/AndroidUtils.java b/Clover/app/src/main/java/org/floens/chan/utils/AndroidUtils.java index c0e17bb7..aec48d97 100644 --- a/Clover/app/src/main/java/org/floens/chan/utils/AndroidUtils.java +++ b/Clover/app/src/main/java/org/floens/chan/utils/AndroidUtils.java @@ -136,6 +136,10 @@ public class AndroidUtils { new Handler(Looper.getMainLooper()).post(runnable); } + public static void runOnUiThread(Runnable runnable, long delay) { + new Handler(Looper.getMainLooper()).postDelayed(runnable, delay); + } + public static void requestKeyboardFocus(Dialog dialog, final View view) { view.requestFocus(); dialog.setOnShowListener(new DialogInterface.OnShowListener() { diff --git a/Clover/app/src/main/res/drawable-hdpi/dialog_full_dark.9.png b/Clover/app/src/main/res/drawable-hdpi/dialog_full_dark.9.png new file mode 100644 index 0000000000000000000000000000000000000000..911f3fee413bdde6b548d92c4e211e342d0d0132 GIT binary patch literal 1482 zcmV;*1vUDKP)t2eGVTvAWr^8X-Z~2Q)-UY>GgCs9s2x zocA8iIh=t%r?N2`UM+$5rht(F5BS;8Ct*wj6UoSeiDYEKL^86lcxAn_rg_HbIe8kE zB2r8mcfvl#u*Br>;#F(2s9Z6Tw1YXc<z-l zjf;>(DTQ!QWYR>ELdY^xUcuhWdd7H=Vt4>`vt6_I{8baGBU4}3CI#v%p$G-#z42?1 z(L;(+2a03cvEu>giljv&F3P+qa$CwIqmwZ_G74!DRZZdBQYbYl#l}cdBob$Fd3pKa z`uh6oYPGuY-v8D*+rS09*oD26I(-)gL;I%wuG~+n*LU35F;dPW<(TIjKlZ(I?!U4u z|GB%n`{Vifd8_T#ymq>^Sofnvp#_zztE)ff^ZDnerzccZWmkVEz-%_da=AoRRe#^! z-hKg4vce;k7M;|KwlW%Vk(L0K+wJz|@$nI5S(-!>p{{G}_j^=Tb%UmzacFZsgb7Zf z7C2ZH8ymHv5rB)LC_c?*c6DWHG(q7LfC~USv}bC378jkh#blyrv<9&D-diM^C{4Sr z(H)%6aGcE%FG7XZk+eZJg}YVuvp2@Z3hkNPym{4R0-#U-wJ{QPYb%&YZ-EsK;*>bF zJ4qz{c)K)$ZH;N8U67C7 z8Wf92q5F{RYX0=m+B|eeEP(!q#NX5Yc5P((@OIXQI%)-o9VOC2i%8QarOlJ3|5_#M z*4`E;wRxXNCXPWwU9jw1H* zL_~5#qMf-!WNEqCY`*XJ`#fiEjE1VJu-$I|g`Qk1I;;sj?8pV8ZAvOoJUl%7RTRZ? zy#Cc<)l-AW8vX58x*bNwt0e*aFx9 zcy6}0&F8yjKdMKPc>vx6J|!Zxq7nP?1&&>#XZtMKcP7#}ki!(-mU|pTq4Kl&Oh%<> zM3N$DQ+^VJew-o}Nkk_`xsyV92aVy7?MZFAM^`2(lI-Ht*_#%TLWArA?MHZluH9Nr z9l%5y8o8j%dl=r!P&Y?%6-XR(bCGybQY{q9hvm3LS{789xZM~QzQD+%5|@bzNkvhq zcNw5BA~_rvAgPPmq1)|IYC!(403;ICHHJfGrsdiF`LKmYCz)qEIU?~Nm3=DEj#hM1 zeB7jEu%LP-(y+)h$8y-B38f`#53X*3`@1_yI{KKA@IS$i_zFL%SX1HGllqA=J=XG)XD4zKJw0wEKxPS^5O>;*EFSd%(G% kX&Pq7v^hxz0Px=Z57&IqwkwoWPyhe`07*qoM6N<$f==(jmH+?% literal 0 HcmV?d00001 diff --git a/Clover/app/src/main/res/drawable-hdpi/dialog_full_light.9.png b/Clover/app/src/main/res/drawable-hdpi/dialog_full_light.9.png new file mode 100644 index 0000000000000000000000000000000000000000..2129567f2fa4e8a8b9322cdaca6bbd036346fabe GIT binary patch literal 1515 zcmVMRV(R;R@hBXl>Bu%u%K1R2M<&#g*1uMyzp%) zlp2*nVGTJz1^Swo6opVEQfbhhS)XD?=oTqA7q0fEScy;qnA$Z6TQwtfSE$fIW*u!zFLqJyZwhF^bt7u@TC*j-(B; zscY+OaBX}u#wgF^M){*A699esFK2ALYa7EvdJl|IjEfQ!5=lQcU6XI1qpcrN_hupu zpE2@@6{>56>bjs5Nv=xsu8qrN3>z*HUw8&QXD*sVB-zE8NW(`)Bk4nh&>Y^ovfbYC z8a;(aBY7TGMH8vX`8H$NXyYR7GNBEjt}S%Z))+S03HjiqL7|A`yAH|D<_{mO&3#wI z0;rEj{5kD!=SGGPZ)a_&qgH^}Q6eq0h%|hfw0WcHzgEe*wfDtVZC)pmiDM8!7i_#^ zw1SCbw1SCbw1SCbw1SCbw1SCbw1SCbw1SCbw1SCbv_hRocHfaf1YKwvk+eKH#<1C{ z&80|MC7GPbWEwtA+Pw6dFCs}3`E9#0Uyd<6v_uiKtp*ZJB$-2yCuFkj?R}uNxyjdz4>vTNbC}QPPw-2$*;UpJ&Dx|y zWa;Ya>gunDhX^^VxOpHQ8smTrO^I zZvH+yJNt5ffByjBDK87Gb0xh1SfZT8Z6la=hXB7dn{pzNNFVd^J=Riv%wM16`;A#W ze!H(-vfC}ZcS%uXVtd6}ju)(jdB$3vdA|LOLZl_DM2lk$c)DPq^g}3D^iiktPyrL|O94}RnB56~85`?~-A{I$R2S&M*pkW>_vYL@}(B2tupAy9NtFQ|5VG|S`v z3P2)JU86f>W?G)zpATDjbdq_t8%HGmrLs>2%F&7rijSML3>H++MCulq=2#9}G@-O) z?cUYx(5#I|CHXp8ttDjAW@{!=x5)FSzW*Rup)6J7Maepm0R3!#hH}bW`%|0#BlBj5 zgp%8}=i0RCzt)Ax?k*)eRkzS_*W=s&1jlsR%w R421vy002ovPDHLkV1gH##(@9; literal 0 HcmV?d00001 diff --git a/Clover/app/src/main/res/drawable-mdpi/dialog_full_dark.9.png b/Clover/app/src/main/res/drawable-mdpi/dialog_full_dark.9.png new file mode 100644 index 0000000000000000000000000000000000000000..dc37316094b66bc2c334cd40564a1486d0f6a259 GIT binary patch literal 935 zcmV;Y16cftP)OL*95T%V0v1dSIU!jV&RAW?!#97w5glIY&o) z*NO%Joeb-}nY9;q@Y(-vgb^?ydcv?{z0QM(k!0&2&?ZD|c&AcZ9V6hQ`;-WcvRH`N z(2mf3xhSvenjirmUDs4X_$cq9x~(1|#?OF3dDZuhw3i$+o~Gf8@~ZFUvl?0mF{l`N zP^Mv&Qi2$10ahxxvb-SJZe5mnNOw&lf*5{ueSQ6PJRaW^MRA#ILCb<9RsNP`+w$*O zr>#tXmSx%R`F#HU?(Xg<^_R9liQq^JA#eN;I0Z(t+3d%|!^5Sm0C(mp%d#8b7xi&L zCmnOd@<`F}J5NGO3jMsBPHFijhcHZ2ZpFX`>r zYD-(C^#l{5CzudD!G!1uCPYs#A$o!d(GyIFp0G`bwr~(*H@4a_B}5$l(TF%Oc1GJW zDM~Cve4Z+&NydJN9h2wP(6S{<{a7#0^L+Zg?L1tHqId*yDtjh-7`?@k6sLo(zpWO&XUIEX*9GC%9;7^$z%ls3tDC@0&wFe&``shftTz%(Q32B@D9wU0j8ApQW>b}z$ArZ>rW>BfDymI-HzE$Pl)UbilG9h6 zk0Uu!SNq3M9c|orizwdSs0rc0JMTR>w{b(tEWICFcTK%_{{Ys1Zo{)>p7j6#002ov JPDHLkV1fWpuL1x7 literal 0 HcmV?d00001 diff --git a/Clover/app/src/main/res/drawable-mdpi/dialog_full_light.9.png b/Clover/app/src/main/res/drawable-mdpi/dialog_full_light.9.png new file mode 100644 index 0000000000000000000000000000000000000000..0c5770a36f60d9298c4cb784a7f2090d263a3092 GIT binary patch literal 944 zcmV;h15f;kP)(~$!EHa<8vUjsetoeCn|4p3t z9_Jh#@m)I_0CX~}_h!~!;K66F{SZdLgy;!_j{Q0hB1V$dL7+*9*ziu3ranf%NB1ca zYHhI)v7sFye7UHu`0T#TD&Kg6;C6%|kjgi3no&;pOG!=aZ9@Z_c^H7k%*fSYn^cJ@cNg~7o zn4h1Ye?L1r`!XJnYw0vi|E);1cWG;@$8AsTzs|F@wa0yCv)T0G;^I3YgG0m+`&PU{ zf1{kr?GcXl-T`CaJ@BC@W8jeT3|98pjbP*lLdwh?FaxH*pQ79p^;_V+==T8RzDTFh z=uEYR`qCl6;J#?TS8XOlKtj))U^5ZpZ*eaWo^i&R;0tx%X^fBzWpOhoRn}gx@*sRmiT@iB zr&vl|ho$87RoCN8j?~ruF;qnxH{K$Ow>K(6c<|1956-PzHk+l_qjgWhd-oqEJxc1M ScWJo*0000|%e(eR4vP{&2WL5@qhf=U8ll7h zcApYc& z)y9C+5(1zq2w@rEA z!rOOhe`gplqp20u+=ZfzO zO2+RVc~kvanx;Y1 zGzcO5c6xgH(~~Dpe#b`SEjC7IF-)1n91=Ay4prKcQ6n$)5hE;O`@{3+&p$gmJ3|P8 zkG_#NPE}P{EEZoc7K={+e1oCbrL`kXzz(BEHka3V{zX~@pkA-n528b90&*>c5YTlU z`o4dFZRR3_Gg_y;c`tb#K@T+vwl0vDPKg=-w*VZqZTn&0_cQ^yR=TbOfDZv2W0MX6 zTe0gjNDr|B6C;8qWsC+u!$?5pBI>Te@B*8(ULEX`s}Wq$V;cz?0Jk%J5OQ7I#?}lS zF4qf^I_-R~kvDv*jUg=|b0Q|Nk-^h&@x4Z=TPZ~JN9uJ8n~@_z=HwPOI+Z#>bnT@` zt|7)A)%2o)9yO8|A#UID0`0qjHXAWXLIkF1Z@yP=EQs6WzsV*Q-)7dX%Qm= znHw7oa>j5eO^sldi+=zi*T7gLxN>TgONs386d`kA>w(EBQb~UAU5t>H5rNzUQlc-e z6Fej2KIDZM8iCBsR9#-$w#$eQO+aoUMOTnhOdUQY5EC1T+F6pb-cGKqC+W zfJPt$0F6Kh02+Z105k$20B8h40MH180H6^F0YD=V0)R#!1VFhF_?_?MZKX)Nj0l8a zHzTAbe@{c0%yD4V>>GeA#))*%xQJfcb}$6 z8G_M(+!Q=r7Hc$Rgp}B2y;rYp@*xwlUu51i9T_~`CXG=N5o{;ki;y|FvPM(iR96xS zBpP&W+rEl(-V$fn-ZTxG=H35-?5cXVzv}5G|9&9^ z^nH)jYV~-zT>gTs7F%p1Lx+5NG*};-Ix3ZK4br2tv$H=!2n~Rb>$?7^uIp==U~u36 ztX=zi)1sN$|2~fACKzo*6TInk^jv92ad{D!Z^!l| zzV}{i-;eE`*q+4wj^pQVsebJIR^D-t!$L$VA{Jqv$K`cwUt@FDe-*dAjO{YEXR&=1 z_glqQ`e|`FS|9)+7ZH**0fxnL9e;qd6}A&UpU3?w^-DJDbNn`KB3ONia61fzY||iV z$D#s|i3%wrNUb1OjrD;+wKAWr%xWlR2(j-r?sJaqOq>#NIPDa%=!g)o75Q4VEw+(i zg^jk>u|3CSgJ^RA2{U*`@ODf^1ZxoKPa*-ZomxtYY|jXc3WtrC8i^5l42>WWqQy}7 zsiD)9o(JS2f}}K1%Q8kPLa$XTeTi$ExGc3n75A}oTmdw~p<~l<5wm0$k{U_7!mwso ztG0umD%tyy222?>l8&??bgCq&kxqYXO&}3LPyb1XAVQT^7q{97IwI)WVXRh2l8Xp> zS<)N{S(R$V6e4)332F>SiN@x?C5MX_5p3ZsqewbcvJpbp2VN9N8log3*aHNHkwMP- z)*v;uQ9{=Q-puGU!eQf$P-+p25n9#egr6HZav6&Vwuq7U^l+%8H#!A6$F?T0#j#f; z(+G!6N^f5AtWzdEysC|oySltnM395i2damnv>9Zd+Po)`Lq`G{foM`jNJR!4Av&|~ zq-X&60<;q~Yw^}*xxp~?vFkP!s-1ob5v*`a4B;8X))<~4f?CL4Sxz0KEbHM_sAbvD zrb3e^fhGq>?rw%u5q+gdAfzs+X;Eur_|q$lE44i}I+VMBMpGOnxeH$UYH-RBxmJJH zjRrz#jUb5lz2PW(`?eI+Gy=&n#R%T$Wk~GjttXi>JGnQZvzPOa37=^Z$zcm;y__;e zYP;>nTi>Zhkcc4YI>C+7YV%nl2joCpsI>L;C6DA4-;}lt|VRn b2%-8PH~%shVHNw_00000NkvXXu0mjfC1>AC literal 0 HcmV?d00001 diff --git a/Clover/app/src/main/res/drawable-xhdpi/dialog_full_light.9.png b/Clover/app/src/main/res/drawable-xhdpi/dialog_full_light.9.png new file mode 100644 index 0000000000000000000000000000000000000000..d9bd3375fd4cad8696585d302aa1612061fd65c4 GIT binary patch literal 2231 zcmV;o2uSydP)%{LKI6CYzUMqAqYSy`#uRm zRskA;5YPyO0H6^F0YD=V0)R#!1OPBxQa?+-5kW`?u6-k*s}b4+UqqG{?T@S%If4$( za!g0r2FW!-jsfgGIi{cyh$bf7*!xAjEww#${3UHhXcKUeShmM^jTU5m@vi!MS+}ue zO{*bNM)1Vy30)hYjBJnfNkb5g6{$?fX$r0x=(J? zmoaYoTvz>n6GAwxs_Ngv!^5ARJ$rU|aBy%6|LUJAzAwoc`;VnU!}`4;fa2ib;QI#; z9{jkyz5QJX0n_Oeq<>aj(2FA9s60JA{p0oP*S|b^^yp8_L|$QLgbK}+NdzfTL%56{ zsWb?2Edo%MW%*?YfhHg~N(ceN;qWWWGZz`0(K?-)_oDX^^iY#v>jHV{ zh%msc78p(@le>e#0EFB$MN!-Zu!Y&xADD|>r$Ks%6>!Xu`xti27y|$UMg!6pweALJ zj#%1J%2rZ$mR-?f9tj2j?j-skh_)KRiw1hsNM3~WiPoZh>LIIE zNJRv1=+s7q4cvme$mF9(G&n{xMbg@ZX|p3j$_%ni97sr?$OYPa7S5(bE;-K{c@Ndu zs6a@cY+!bUA7VCAq>Ul^O^)x|t9jeAN8acWBLe9gt4FUcNNGDN;@HSh?A`()eG?VS=Jrd=s-xHc;9#F^qrS3!>6&dh!KJGjg1B=W4Pp|Mlj3SA3(?r&=v`< zof@T5B0D@qNMG1`pmU0plizzcBcx?SAge%3^x1WSXN1&2UWlO)NZ)kT<&|xVjQG$5 zWEIJ}gB+vl@F^j;jin5q60#Q12!wz}AOrx7KnMUDfe-*R0wDls1VRAN2!sHj5eNZ5 zBM<_BMj!+LjX($h8i5c1`9|PxzLVQZmUbBt2*F}Th)w=XNS|Cwm!1()op_Tv(+s3< z+?g^n_Xp4(5$pucc3sa@NJw9Z4pUm~^xLN~VuoNeAgh9>%WRFtj1UvMtm}DeC!J53 zkmVxtrs>Gw={9PNoQPmMx$@$Snxto1HQLre?3e0tB7sDM$-8&&KAfMQFZ@>!vItdG z;pF7x6txam=97Z(?J^XARr%a<>I!(59M=8<87bb8cSZ<{(QmTxuEqZcn;{5=|tUOjp8vo@=bshZXPL*g_0?tnoX_m~d0o$;%l^wamqq$rWdG4h>J7tNvNN?X+%B>h?q3p4Iz}>Q?%xa5Y*W03j6-qBQ}U#d2AHfV2hX z6F-mZ{R;I*4%Bh{HEkkTeTi@Vn&c!LGBvs6N74HK3kd9P|OhO zzN>nlG3GOIO2px`Q^cYpLcm<)7pko=j|?Nsv~^LpV_Y?eHU|(fgJ%S9$5=$L29f?G z5&-k5rKHIAjKHX{-gv2z7-5R05hOxXXbL|zbQ;rhgH%M2lm=>9?jnoOOVvtW;@UyI zEVV&V?_=k<0%(MF$EM*TX2~ujHIjCOX3cP++6nwr$v%(Npv$O{bfgWTQzc1_boyg! z0*MHE`cFax5vsJhxYb6`5kc1uZM8y_R7B9rlIBRrDpV_`5W!1LP@*|X)HeSuSzo+} zU<+p%MbfE~jS#v%@S;G}5IGUSo*>YS406@C1}QO*61pbvW=5wG)*EkxQj1uOP^mU0 z{M5*iOIt*+MU1?sheIX3(J9av^P0dG$6k?4Bdj+uy?Mp6PMP%Zsy0gM?(#wrK~7Ge zs2+;aW{`bK^PWTw9SLXzqKO$H78z`Wn3#P#MFYSWU}2WEcuTW9Kr{8R>oyfCoqh=s ztZ;J-;Tgo%7@i@5T1ehmj-8||>)}f;HYX%R_b3unC?Ge&H??Z;bRs78>8Am}>5jugQx195W^ z0;EOji8~buy#AsTAq1c(vJY@Oxz53aT$5{w7XU&i{sV+H)1k;Fq)h+-002ovPDHLk FV1h&Y8two9 literal 0 HcmV?d00001 diff --git a/Clover/app/src/main/res/drawable-xxhdpi/dialog_full_dark.9.png b/Clover/app/src/main/res/drawable-xxhdpi/dialog_full_dark.9.png new file mode 100644 index 0000000000000000000000000000000000000000..b029809dfbd57151bcc3b75145a96d9dc24356af GIT binary patch literal 4143 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 F`!6v-GwA>T literal 0 HcmV?d00001 diff --git a/Clover/app/src/main/res/layout/post_replies.xml b/Clover/app/src/main/res/layout/post_replies.xml index e59f3fcd..51cea0cb 100644 --- a/Clover/app/src/main/res/layout/post_replies.xml +++ b/Clover/app/src/main/res/layout/post_replies.xml @@ -15,65 +15,75 @@ GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program. If not, see . --> - + android:layout_height="match_parent"> + android:layout_gravity="center" + android:minWidth="320dp" + android:background="@drawable/dialog_full_light" + android:orientation="vertical"> - + - - + - + + - - - + + + + + - + + + - + diff --git a/Clover/app/src/main/res/layout/post_replies_bottombuttons.xml b/Clover/app/src/main/res/layout/post_replies_bottombuttons.xml index b5efee0f..9ce0573d 100644 --- a/Clover/app/src/main/res/layout/post_replies_bottombuttons.xml +++ b/Clover/app/src/main/res/layout/post_replies_bottombuttons.xml @@ -15,66 +15,75 @@ GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program. If not, see . --> - - - + android:background="#88000000"> + android:layout_gravity="center" + android:minWidth="320dp" + android:background="@drawable/dialog_full_light" + android:orientation="vertical"> + + + + + + - + + - - + - + + + - - - + diff --git a/Clover/app/src/main/res/layout/post_replies_container.xml b/Clover/app/src/main/res/layout/post_replies_container.xml new file mode 100644 index 00000000..5788e019 --- /dev/null +++ b/Clover/app/src/main/res/layout/post_replies_container.xml @@ -0,0 +1,22 @@ + +