mirror of https://github.com/kurisufriend/Clover
You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
514 lines
17 KiB
514 lines
17 KiB
package org.floens.chan.manager;
|
|
|
|
import java.util.ArrayList;
|
|
import java.util.List;
|
|
|
|
import org.floens.chan.R;
|
|
import org.floens.chan.activity.ReplyActivity;
|
|
import org.floens.chan.database.DatabaseManager;
|
|
import org.floens.chan.fragment.PostRepliesFragment;
|
|
import org.floens.chan.fragment.ReplyFragment;
|
|
import org.floens.chan.loader.Loader;
|
|
import org.floens.chan.loader.LoaderPool;
|
|
import org.floens.chan.manager.ReplyManager.DeleteListener;
|
|
import org.floens.chan.manager.ReplyManager.DeleteResponse;
|
|
import org.floens.chan.model.Loadable;
|
|
import org.floens.chan.model.Post;
|
|
import org.floens.chan.model.PostLinkable;
|
|
import org.floens.chan.model.SavedReply;
|
|
import org.floens.chan.utils.ChanPreferences;
|
|
import org.floens.chan.utils.Logger;
|
|
|
|
import android.app.Activity;
|
|
import android.app.AlertDialog;
|
|
import android.app.FragmentTransaction;
|
|
import android.app.ProgressDialog;
|
|
import android.content.ClipData;
|
|
import android.content.ClipboardManager;
|
|
import android.content.Context;
|
|
import android.content.DialogInterface;
|
|
import android.content.DialogInterface.OnClickListener;
|
|
import android.content.Intent;
|
|
import android.net.Uri;
|
|
import android.text.TextUtils;
|
|
import android.view.Gravity;
|
|
import android.widget.CheckBox;
|
|
import android.widget.LinearLayout.LayoutParams;
|
|
import android.widget.TextView;
|
|
import android.widget.Toast;
|
|
|
|
import com.android.volley.NetworkError;
|
|
import com.android.volley.NoConnectionError;
|
|
import com.android.volley.ServerError;
|
|
import com.android.volley.VolleyError;
|
|
|
|
/**
|
|
* All PostView's need to have this referenced.
|
|
* This manages some things like pages, starting and stopping of loading,
|
|
* handling linkables, replies popups etc.
|
|
* onDestroy, onStart and onStop must be called from the activity/fragment
|
|
*/
|
|
public class ThreadManager implements Loader.LoaderListener {
|
|
private static final String TAG = "ThreadManager";
|
|
|
|
private final Activity activity;
|
|
private final ThreadManager.ThreadManagerListener threadManagerListener;
|
|
private final List<List<Post>> popupQueue = new ArrayList<List<Post>>();
|
|
private PostRepliesFragment currentPopupFragment;
|
|
|
|
private Loader loader;
|
|
|
|
public ThreadManager(Activity context, final ThreadManagerListener listener) {
|
|
this.activity = context;
|
|
threadManagerListener = listener;
|
|
}
|
|
|
|
public void onDestroy() {
|
|
unbindLoader();
|
|
}
|
|
|
|
public void onStart() {
|
|
if (loader != null) {
|
|
loader.onStart();
|
|
}
|
|
}
|
|
|
|
public void onStop() {
|
|
if (loader != null) {
|
|
loader.onStop();
|
|
}
|
|
}
|
|
|
|
public void bindLoader(Loadable loadable) {
|
|
if (loader != null) {
|
|
unbindLoader();
|
|
}
|
|
|
|
loader = LoaderPool.getInstance().obtain(loadable, this);
|
|
}
|
|
|
|
public void unbindLoader() {
|
|
if (loader != null) {
|
|
LoaderPool.getInstance().release(loader, this);
|
|
loader = null;
|
|
} else {
|
|
Logger.e(TAG, "Loader already unbinded");
|
|
}
|
|
}
|
|
|
|
public void requestData() {
|
|
if (loader != null) {
|
|
loader.requestData();
|
|
} else {
|
|
Logger.e(TAG, "Loader null in requestData");
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Called by postadapter and threadwatchcounterview.onclick
|
|
*/
|
|
public void requestNextData() {
|
|
if (loader != null) {
|
|
loader.requestNextData();
|
|
} else {
|
|
Logger.e(TAG, "Loader null in requestData");
|
|
}
|
|
}
|
|
|
|
@Override
|
|
public void onError(VolleyError error) {
|
|
threadManagerListener.onThreadLoadError(error);
|
|
}
|
|
|
|
@Override
|
|
public void onData(List<Post> result, boolean append) {
|
|
threadManagerListener.onThreadLoaded(result, append);
|
|
}
|
|
|
|
public boolean hasLoader() {
|
|
return loader != null;
|
|
}
|
|
|
|
public Post findPostById(int id) {
|
|
if (loader == null) return null;
|
|
return loader.findPostById(id);
|
|
}
|
|
|
|
public Loadable getLoadable() {
|
|
if (loader == null) return null;
|
|
return loader.getLoadable();
|
|
}
|
|
|
|
public Loader getLoader() {
|
|
return loader;
|
|
}
|
|
|
|
public void onThumbnailClicked(Post post) {
|
|
threadManagerListener.onThumbnailClicked(post);
|
|
}
|
|
|
|
public void onPostClicked(Post post) {
|
|
if (loader != null && loader.getLoadable().isBoardMode()) {
|
|
threadManagerListener.onOPClicked(post);
|
|
}
|
|
}
|
|
|
|
public void onPostLongClicked(final Post post) {
|
|
AlertDialog.Builder builder = new AlertDialog.Builder(activity);
|
|
|
|
String[] items = null;
|
|
|
|
String[] temp = activity.getResources().getStringArray(R.array.post_options);
|
|
// Only add the delete option when the post is a saved reply
|
|
if (DatabaseManager.getInstance().isSavedReply(post.board, post.no)) {
|
|
items = new String[temp.length + 1];
|
|
System.arraycopy(temp, 0, items, 0, temp.length);
|
|
items[items.length - 1] = activity.getString(R.string.delete);
|
|
} else {
|
|
items = temp;
|
|
}
|
|
|
|
builder.setItems(items, new DialogInterface.OnClickListener() {
|
|
@Override
|
|
public void onClick(DialogInterface dialog, int which) {
|
|
switch(which) {
|
|
case 0: // Reply
|
|
openReply(true); // todo if tablet
|
|
// Pass through
|
|
case 1: // Quote
|
|
ReplyManager.getInstance().quote(post.no);
|
|
break;
|
|
case 2: // Info
|
|
showPostInfo(post);
|
|
break;
|
|
case 3: // Show clickables
|
|
showPostLinkables(post);
|
|
break;
|
|
case 4: // Copy text
|
|
copyToClipboard(post.comment.toString());
|
|
break;
|
|
case 5: // Delete
|
|
deletePost(post);
|
|
break;
|
|
}
|
|
}
|
|
});
|
|
|
|
builder.create().show();
|
|
}
|
|
|
|
public void openReply(boolean startInActivity) {
|
|
if (loader == null) return;
|
|
|
|
if (startInActivity) {
|
|
ReplyActivity.setLoadable(loader.getLoadable());
|
|
Intent i = new Intent(activity, ReplyActivity.class);
|
|
activity.startActivity(i);
|
|
} else {
|
|
ReplyFragment reply = ReplyFragment.newInstance(loader.getLoadable());
|
|
reply.show(activity.getFragmentManager(), "replyDialog");
|
|
}
|
|
}
|
|
|
|
public void onPostLinkableClicked(PostLinkable linkable) {
|
|
handleLinkableSelected(linkable);
|
|
}
|
|
|
|
/**
|
|
* Returns an TextView containing the appropriate error message
|
|
* @param error
|
|
* @return
|
|
*/
|
|
public TextView getLoadErrorTextView(VolleyError error) {
|
|
String errorMessage = "";
|
|
|
|
if ((error instanceof NoConnectionError) || (error instanceof NetworkError)) {
|
|
errorMessage = activity.getString(R.string.thread_load_failed_network);
|
|
} else if (error instanceof ServerError) {
|
|
errorMessage = activity.getString(R.string.thread_load_failed_server);
|
|
} else {
|
|
errorMessage = activity.getString(R.string.thread_load_failed_parsing);
|
|
}
|
|
|
|
TextView view = new TextView(activity);
|
|
view.setLayoutParams(new LayoutParams(LayoutParams.MATCH_PARENT, LayoutParams.MATCH_PARENT));
|
|
view.setText(errorMessage);
|
|
view.setTextSize(24f);
|
|
view.setGravity(Gravity.CENTER);
|
|
|
|
return view;
|
|
}
|
|
|
|
private void copyToClipboard(String comment) {
|
|
ClipboardManager clipboard = (ClipboardManager) activity.getSystemService(Context.CLIPBOARD_SERVICE);
|
|
ClipData clip = ClipData.newPlainText("Post text", comment);
|
|
clipboard.setPrimaryClip(clip);
|
|
}
|
|
|
|
private void showPostInfo(Post post) {
|
|
String text = "";
|
|
|
|
if (post.hasImage) {
|
|
text += "File: " + post.filename + " \nSize: " + post.imageWidth + "x" + post.imageHeight + "\n\n";
|
|
}
|
|
|
|
text += "Time: " + post.date ;
|
|
|
|
if (!TextUtils.isEmpty(post.id)) {
|
|
text += "\nId: " + post.id;
|
|
}
|
|
|
|
if (!TextUtils.isEmpty(post.email)) {
|
|
text += "\nEmail: " + post.email;
|
|
}
|
|
|
|
if (!TextUtils.isEmpty(post.tripcode)) {
|
|
text += "\nTripcode: " + post.tripcode;
|
|
}
|
|
|
|
if (!TextUtils.isEmpty(post.countryName)) {
|
|
text += "\nCountry: " + post.countryName;
|
|
}
|
|
|
|
if (!TextUtils.isEmpty(post.capcode)) {
|
|
text += "\nCapcode: " + post.capcode;
|
|
}
|
|
|
|
AlertDialog dialog = new AlertDialog.Builder(activity)
|
|
.setTitle(R.string.post_info)
|
|
.setMessage(text)
|
|
.setNeutralButton(android.R.string.ok, new DialogInterface.OnClickListener() {
|
|
@Override
|
|
public void onClick(DialogInterface dialog, int which) {
|
|
}
|
|
})
|
|
.create();
|
|
|
|
dialog.show();
|
|
}
|
|
|
|
/**
|
|
* When the user clicks a post:
|
|
* a. when there's one linkable, open the linkable.
|
|
* b. when there's more than one linkable, show the user multiple options to select from.
|
|
* @param post The post that was clicked.
|
|
*/
|
|
public void showPostLinkables(Post post) {
|
|
AlertDialog.Builder builder = new AlertDialog.Builder(activity);
|
|
final ArrayList<PostLinkable> linkables = post.linkables;
|
|
|
|
if (linkables.size() > 0) {
|
|
if (linkables.size() == 1) {
|
|
handleLinkableSelected(linkables.get(0));
|
|
} else {
|
|
String[] keys = new String[linkables.size()];
|
|
for (int i = 0; i < linkables.size(); i++) {
|
|
keys[i] = linkables.get(i).key;
|
|
}
|
|
|
|
builder.setItems(keys, new DialogInterface.OnClickListener() {
|
|
@Override
|
|
public void onClick(DialogInterface dialog, int which) {
|
|
handleLinkableSelected(linkables.get(which));
|
|
}
|
|
});
|
|
|
|
AlertDialog dialog = builder.create();
|
|
dialog.show();
|
|
}
|
|
}
|
|
}
|
|
|
|
public void showPostReplies(Post post) {
|
|
List<Post> p = new ArrayList<Post>();
|
|
for (int no : post.repliesFrom) {
|
|
Post r = findPostById(no);
|
|
if (r != null) {
|
|
p.add(r);
|
|
}
|
|
}
|
|
|
|
if (p.size() > 0) {
|
|
showPostsReplies(p);
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Handle when a linkable has been clicked.
|
|
* @param linkable the selected linkable.
|
|
*/
|
|
private void handleLinkableSelected(final PostLinkable linkable) {
|
|
if (linkable.type == PostLinkable.Type.QUOTE) {
|
|
showPostReply(linkable);
|
|
} else if (linkable.type == PostLinkable.Type.LINK) {
|
|
if (ChanPreferences.getOpenLinkConfirmation()) {
|
|
AlertDialog dialog = new AlertDialog.Builder(activity)
|
|
.setNegativeButton(android.R.string.cancel, new DialogInterface.OnClickListener() {
|
|
@Override
|
|
public void onClick(DialogInterface dialog, int which) {
|
|
}
|
|
})
|
|
.setPositiveButton(android.R.string.ok, new DialogInterface.OnClickListener() {
|
|
@Override
|
|
public void onClick(DialogInterface dialog, int which) {
|
|
openLink(linkable);
|
|
}
|
|
})
|
|
.setTitle(R.string.open_link_confirmation)
|
|
.setMessage(linkable.value)
|
|
.create();
|
|
|
|
dialog.show();
|
|
} else {
|
|
openLink(linkable);
|
|
}
|
|
}
|
|
}
|
|
|
|
/**
|
|
* When a linkable to a post has been clicked,
|
|
* show a dialog with the referenced post in it.
|
|
* @param linkable the clicked linkable.
|
|
*/
|
|
private void showPostReply(PostLinkable linkable) {
|
|
String value = linkable.value;
|
|
|
|
Post post = null;
|
|
|
|
try {
|
|
// Get post id
|
|
String[] splitted = value.split("#p");
|
|
if (splitted.length == 2) {
|
|
int id = Integer.parseInt(splitted[1]);
|
|
|
|
post = findPostById(id);
|
|
|
|
if (post != null) {
|
|
List<Post> l = new ArrayList<Post>();
|
|
l.add(post);
|
|
showPostsReplies(l);
|
|
}
|
|
}
|
|
} catch(NumberFormatException e) {
|
|
e.printStackTrace();
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Open an url.
|
|
* @param linkable Linkable with an url.
|
|
*/
|
|
private void openLink(PostLinkable linkable) {
|
|
activity.startActivity(new Intent(Intent.ACTION_VIEW, Uri.parse(linkable.value)));
|
|
}
|
|
|
|
private void showPostsReplies(List<Post> list) {
|
|
// Post popups are now queued up, more than 32 popups on top of each other makes the system crash!
|
|
popupQueue.add(list);
|
|
|
|
if (currentPopupFragment != null) {
|
|
currentPopupFragment.dismissNoCallback();
|
|
}
|
|
|
|
PostRepliesFragment popup = PostRepliesFragment.newInstance(list, this);
|
|
|
|
FragmentTransaction ft = activity.getFragmentManager().beginTransaction();
|
|
ft.add(popup, "postPopup");
|
|
ft.commit();
|
|
|
|
currentPopupFragment = popup;
|
|
}
|
|
|
|
public void onPostRepliesPop() {
|
|
if (popupQueue.size() == 0) return;
|
|
|
|
popupQueue.remove(popupQueue.size() - 1);
|
|
|
|
if (popupQueue.size() > 0) {
|
|
PostRepliesFragment popup = PostRepliesFragment.newInstance(popupQueue.get(popupQueue.size() - 1), this);
|
|
|
|
FragmentTransaction ft = activity.getFragmentManager().beginTransaction();
|
|
ft.add(popup, "postPopup");
|
|
ft.commit();
|
|
|
|
currentPopupFragment = popup;
|
|
} else {
|
|
currentPopupFragment = null;
|
|
}
|
|
}
|
|
|
|
public void closeAllPostFragments() {
|
|
popupQueue.clear();
|
|
currentPopupFragment = null;
|
|
}
|
|
|
|
private void deletePost(final Post post) {
|
|
final CheckBox view = new CheckBox(activity);
|
|
view.setText(R.string.delete_image_only);
|
|
int padding = activity.getResources().getDimensionPixelSize(R.dimen.general_padding);
|
|
view.setPadding(padding, padding, padding, padding);
|
|
|
|
new AlertDialog.Builder(activity)
|
|
.setTitle(R.string.delete_confirm)
|
|
.setView(view)
|
|
.setPositiveButton(R.string.delete, new OnClickListener() {
|
|
@Override
|
|
public void onClick(DialogInterface dialog, int which) {
|
|
doDeletePost(post, view.isChecked());
|
|
}
|
|
})
|
|
.setNegativeButton(R.string.cancel, new OnClickListener() {
|
|
@Override
|
|
public void onClick(DialogInterface dialog, int which) {
|
|
}
|
|
})
|
|
.show();
|
|
}
|
|
|
|
private void doDeletePost(Post post, boolean onlyImageDelete) {
|
|
SavedReply reply = DatabaseManager.getInstance().getSavedReply(post.board, post.no);
|
|
if (reply == null) {
|
|
/*reply = new SavedReply();
|
|
reply.board = "g";
|
|
reply.no = 1234;
|
|
reply.password = "boom";*/
|
|
return;
|
|
}
|
|
|
|
final ProgressDialog dialog = ProgressDialog.show(activity, null, activity.getString(R.string.delete_wait));
|
|
|
|
ReplyManager.getInstance().sendDelete(reply, onlyImageDelete, new DeleteListener() {
|
|
@Override
|
|
public void onResponse(DeleteResponse response) {
|
|
dialog.dismiss();
|
|
|
|
if (response.isNetworkError || response.isUserError) {
|
|
int resId = 0;
|
|
|
|
if (response.isTooSoonError) {
|
|
resId = R.string.delete_too_soon;
|
|
} else if (response.isInvalidPassword) {
|
|
resId = R.string.delete_password_incorrect;
|
|
} else if (response.isTooOldError) {
|
|
resId = R.string.delete_too_old;
|
|
} else {
|
|
resId = R.string.delete_fail;
|
|
}
|
|
|
|
Toast.makeText(activity, resId, Toast.LENGTH_LONG).show();
|
|
} else if (response.isSuccessful) {
|
|
Toast.makeText(activity, R.string.delete_success, Toast.LENGTH_SHORT).show();
|
|
} else {
|
|
Toast.makeText(activity, R.string.delete_fail, Toast.LENGTH_LONG).show();
|
|
}
|
|
}
|
|
});
|
|
}
|
|
|
|
public interface ThreadManagerListener {
|
|
public void onThreadLoaded(List<Post> result, boolean append);
|
|
public void onThreadLoadError(VolleyError error);
|
|
public void onOPClicked(Post post);
|
|
public void onThumbnailClicked(Post post);
|
|
}
|
|
}
|
|
|