package me.ccrama.redditslide.Views; import android.app.Activity; import android.content.*; import android.content.res.TypedArray; import android.graphics.Bitmap; import android.graphics.Color; import android.net.Uri; import android.os.AsyncTask; import android.support.annotation.NonNull; import android.support.annotation.Nullable; import android.support.design.widget.Snackbar; import android.support.v4.app.Fragment; import android.support.v4.app.FragmentManager; import android.support.v4.content.ContextCompat; import android.text.Editable; import android.util.Base64; import android.view.LayoutInflater; import android.view.View; import android.view.inputmethod.InputMethodManager; import android.widget.EditText; import android.widget.LinearLayout; import android.widget.TextView; import com.afollestad.materialdialogs.AlertDialogWrapper; import com.afollestad.materialdialogs.DialogAction; import com.afollestad.materialdialogs.MaterialDialog; import gun0912.tedbottompicker.TedBottomPicker; import me.ccrama.redditslide.Activities.Draw; import me.ccrama.redditslide.*; import me.ccrama.redditslide.util.LogUtil; import me.ccrama.redditslide.util.SubmissionParser; import okhttp3.*; import okio.*; import org.apache.commons.text.StringEscapeUtils; import org.commonmark.Extension; import org.commonmark.ext.gfm.strikethrough.StrikethroughExtension; import org.commonmark.ext.gfm.tables.TablesExtension; import org.commonmark.html.HtmlRenderer; import org.commonmark.node.Node; import org.commonmark.parser.Parser; import org.json.JSONObject; import java.io.*; import java.util.ArrayList; import java.util.Arrays; import java.util.Collections; import java.util.List; /** * Created by carlo_000 on 10/18/2015. */ public class DoEditorActions { public static void doActions(final EditText editText, final View baseView, final FragmentManager fm, final Activity a, final String oldComment, @Nullable final String authors[]) { baseView.findViewById(R.id.bold).setOnClickListener(new View.OnClickListener() { @Override public void onClick(View v) { if (editText.hasSelection()) { wrapString("**", editText); //If the user has text selected, wrap that text in the symbols } else { //If the user doesn't have text selected, put the symbols around the cursor's position int pos = editText.getSelectionStart(); editText.getText().insert(pos, "**"); editText.getText().insert(pos + 1, "**"); editText.setSelection(pos + 2); //put the cursor between the symbols } } }); if (baseView.findViewById(R.id.author) != null) { if (authors != null && authors.length > 0) { baseView.findViewById(R.id.author).setOnClickListener(new View.OnClickListener() { @Override public void onClick(View v) { if (authors.length == 1) { String author = "/u/" + authors[0]; insertBefore(author, editText); } else { new AlertDialogWrapper.Builder(a).setTitle(R.string.authors_above) .setItems(authors, new DialogInterface.OnClickListener() { @Override public void onClick(DialogInterface dialog, int which) { String author = "/u/" + authors[which]; insertBefore(author, editText); } }) .setNeutralButton(R.string.btn_cancel, null) .show(); } } }); } else { baseView.findViewById(R.id.author).setVisibility(View.GONE); } } baseView.findViewById(R.id.italics).setOnClickListener(new View.OnClickListener() { @Override public void onClick(View v) { if (editText.hasSelection()) { wrapString("*", editText); //If the user has text selected, wrap that text in the symbols } else { //If the user doesn't have text selected, put the symbols around the cursor's position int pos = editText.getSelectionStart(); editText.getText().insert(pos, "*"); editText.getText().insert(pos + 1, "*"); editText.setSelection(pos + 1); //put the cursor between the symbols } } }); baseView.findViewById(R.id.strike).setOnClickListener(new View.OnClickListener() { @Override public void onClick(View v) { if (editText.hasSelection()) { wrapString("~~", editText); //If the user has text selected, wrap that text in the symbols } else { //If the user doesn't have text selected, put the symbols around the cursor's position int pos = editText.getSelectionStart(); editText.getText().insert(pos, "~~"); editText.getText().insert(pos + 2, "~~"); editText.setSelection(pos + 2); //put the cursor between the symbols } } }); baseView.findViewById(R.id.spoiler).setOnClickListener(new View.OnClickListener() { @Override public void onClick(View v) { if (editText.hasSelection()) { wrapString(">!", "!<", editText); //If the user has text selected, wrap that text in the symbols } else { //If the user doesn't have text selected, put the symbols around the cursor's position int pos = editText.getSelectionStart(); editText.getText().insert(pos, ">!"); editText.getText().insert(pos + 2, "!<"); editText.setSelection(pos + 2); //put the cursor between the symbols } } }); baseView.findViewById(R.id.savedraft).setOnClickListener(new View.OnClickListener() { @Override public void onClick(View v) { Drafts.addDraft(editText.getText().toString()); Snackbar s = Snackbar.make(baseView.findViewById(R.id.savedraft), "Draft saved", Snackbar.LENGTH_SHORT); View view = s.getView(); TextView tv = view.findViewById(android.support.design.R.id.snackbar_text); tv.setTextColor(Color.WHITE); s.setAction(R.string.btn_discard, new View.OnClickListener() { @Override public void onClick(View view) { Drafts.deleteDraft(Drafts.getDrafts().size() - 1); } }); s.show(); } }); baseView.findViewById(R.id.draft).setOnClickListener(new View.OnClickListener() { @Override public void onClick(View v) { final ArrayList<String> drafts = Drafts.getDrafts(); Collections.reverse(drafts); final String[] draftText = new String[drafts.size()]; for (int i = 0; i < drafts.size(); i++) { draftText[i] = drafts.get(i); } if (drafts.isEmpty()) { new AlertDialogWrapper.Builder(a).setTitle(R.string.dialog_no_drafts) .setMessage(R.string.dialog_no_drafts_msg) .setPositiveButton(R.string.btn_ok, null) .show(); } else { new AlertDialogWrapper.Builder(a).setTitle(R.string.choose_draft) .setItems(draftText, new DialogInterface.OnClickListener() { @Override public void onClick(DialogInterface dialog, int which) { editText.setText( editText.getText().toString() + draftText[which]); } }) .setNeutralButton(R.string.btn_cancel, null) .setPositiveButton(R.string.manage_drafts, new DialogInterface.OnClickListener() { @Override public void onClick(DialogInterface dialog, int which) { final boolean[] selected = new boolean[drafts.size()]; new AlertDialogWrapper.Builder(a).setTitle( R.string.choose_draft) .setNeutralButton(R.string.btn_cancel, null) .alwaysCallMultiChoiceCallback() .setNegativeButton(R.string.btn_delete, new DialogInterface.OnClickListener() { @Override public void onClick( DialogInterface dialog, int which) { new AlertDialogWrapper.Builder( a).setTitle( R.string.really_delete_drafts) .setCancelable(false) .setPositiveButton( R.string.btn_yes, new DialogInterface.OnClickListener() { @Override public void onClick( DialogInterface dialog, int which) { ArrayList<String> draf = new ArrayList<>(); for (int i = 0; i < draftText.length; i++) { if (!selected[i]) { draf.add( draftText[i]); } } Drafts.save( draf); } }) .setNegativeButton( R.string.btn_no, null) .show(); } }) .setMultiChoiceItems(draftText, selected, new DialogInterface.OnMultiChoiceClickListener() { @Override public void onClick( DialogInterface dialog, int which, boolean isChecked) { selected[which] = isChecked; } }) .show(); } }) .show(); } } }); baseView.findViewById(R.id.imagerep).setOnClickListener(new View.OnClickListener() { @Override public void onClick(View v) { e = editText.getText(); sStart = editText.getSelectionStart(); sEnd = editText.getSelectionEnd(); TedBottomPicker tedBottomPicker = new TedBottomPicker.Builder(editText.getContext()) .setOnImageSelectedListener(new TedBottomPicker.OnImageSelectedListener() { @Override public void onImageSelected(List<Uri> uri) { handleImageIntent(uri, editText, a); } }) .setLayoutResource(R.layout.image_sheet_dialog) .setTitle("Choose a photo") .create(); tedBottomPicker.show(fm); InputMethodManager imm = (InputMethodManager) editText.getContext() .getSystemService(Context.INPUT_METHOD_SERVICE); imm.hideSoftInputFromWindow(editText.getWindowToken(), 0); } }); baseView.findViewById(R.id.draw).setOnClickListener(new View.OnClickListener() { @Override public void onClick(View v) { if (SettingValues.isPro) { doDraw(a, editText, fm); } else { AlertDialogWrapper.Builder b = new AlertDialogWrapper.Builder(a).setTitle( R.string.general_cropdraw_ispro) .setMessage(R.string.pro_upgrade_msg) .setPositiveButton(R.string.btn_yes_exclaim, new DialogInterface.OnClickListener() { public void onClick(DialogInterface dialog, int whichButton) { try { a.startActivity(new Intent(Intent.ACTION_VIEW, Uri.parse("market://details?id=" + a.getString( R.string.ui_unlock_package)))); } catch (ActivityNotFoundException e) { a.startActivity(new Intent(Intent.ACTION_VIEW, Uri.parse( "http://play.google.com/store/apps/details?id=" + a.getString( R.string.ui_unlock_package)))); } } }) .setNegativeButton(R.string.btn_no_danks, new DialogInterface.OnClickListener() { public void onClick(DialogInterface dialog, int whichButton) { dialog.dismiss(); } }); if (SettingValues.previews > 0) { b.setNeutralButton( a.getString(R.string.pro_previews, SettingValues.previews), new DialogInterface.OnClickListener() { @Override public void onClick(DialogInterface dialog, int which) { SettingValues.prefs.edit() .putInt(SettingValues.PREVIEWS_LEFT, SettingValues.previews - 1) .apply(); SettingValues.previews = SettingValues.prefs.getInt( SettingValues.PREVIEWS_LEFT, 10); doDraw(a, editText, fm); } }); } b.show(); } } }); /*todo baseView.findViewById(R.id.superscript).setOnClickListener(new View.OnClickListener() { @Override public void onClick(View v) { insertBefore("^", editText); } });*/ baseView.findViewById(R.id.size).setOnClickListener(new View.OnClickListener() { @Override public void onClick(View v) { insertBefore("#", editText); } }); baseView.findViewById(R.id.quote).setOnClickListener(new View.OnClickListener() { @Override public void onClick(View v) { if (oldComment != null) { final TextView showText = new TextView(a); showText.setText(StringEscapeUtils.unescapeHtml4(oldComment)); // text we get is escaped, we don't want that showText.setTextIsSelectable(true); int sixteen = Reddit.dpToPxVertical(24); showText.setPadding(sixteen, 0, sixteen, 0); MaterialDialog.Builder builder = new MaterialDialog.Builder(a); builder.customView(showText, false) .title(R.string.editor_actions_quote_comment) .cancelable(true) .positiveText(a.getString(R.string.btn_select)) .onPositive(new MaterialDialog.SingleButtonCallback() { @Override public void onClick(@NonNull MaterialDialog dialog, @NonNull DialogAction which) { String selected = showText.getText() .toString() .substring(showText.getSelectionStart(), showText.getSelectionEnd()); if (selected.equals("")) { selected = StringEscapeUtils.unescapeHtml4(oldComment); } insertBefore("> " + selected.replaceAll("\n", "\n> ") + "\n\n", editText); } }) .negativeText(a.getString(R.string.btn_cancel)) .show(); InputMethodManager imm = (InputMethodManager) editText.getContext() .getSystemService(Context.INPUT_METHOD_SERVICE); imm.hideSoftInputFromWindow(editText.getWindowToken(), 0); } else { insertBefore("> ", editText); } } }); baseView.findViewById(R.id.bulletlist).setOnClickListener(new View.OnClickListener() { @Override public void onClick(View v) { int start = editText.getSelectionStart(); int end = editText.getSelectionEnd(); String selected = editText.getText().toString().substring(Math.min(start, end), Math.max(start, end)); if (!selected.equals("")) { selected = selected.replaceFirst("^[^\n]", "* $0").replaceAll("\n", "\n* "); editText.getText().replace(Math.min(start, end), Math.max(start, end), selected); } else { insertBefore("* ", editText); } } }); baseView.findViewById(R.id.numlist).setOnClickListener(new View.OnClickListener() { @Override public void onClick(View v) { int start = editText.getSelectionStart(); int end = editText.getSelectionEnd(); String selected = editText.getText().toString().substring(Math.min(start, end), Math.max(start, end)); if (!selected.equals("")) { selected = selected.replaceFirst("^[^\n]", "1. $0").replaceAll("\n", "\n1. "); editText.getText().replace(Math.min(start, end), Math.max(start, end), selected); } else { insertBefore("1. ", editText); } } }); baseView.findViewById(R.id.preview).setOnClickListener(new View.OnClickListener() { @Override public void onClick(View v) { List<Extension> extensions = Arrays.asList(TablesExtension.create(), StrikethroughExtension.create()); Parser parser = Parser.builder().extensions(extensions).build(); HtmlRenderer renderer = HtmlRenderer.builder().extensions(extensions).build(); Node document = parser.parse(editText.getText().toString()); String html = renderer.render(document); LayoutInflater inflater = a.getLayoutInflater(); final View dialoglayout = inflater.inflate(R.layout.parent_comment_dialog, null); final AlertDialogWrapper.Builder builder = new AlertDialogWrapper.Builder(a); setViews(html, "NO sub", (SpoilerRobotoTextView) dialoglayout.findViewById(R.id.firstTextView), (CommentOverflow) dialoglayout.findViewById(R.id.commentOverflow)); builder.setView(dialoglayout); builder.show(); } }); baseView.findViewById(R.id.link).setOnClickListener(new View.OnClickListener() { @Override public void onClick(View v) { final LayoutInflater inflater = LayoutInflater.from(a); final LinearLayout layout = (LinearLayout) inflater.inflate(R.layout.insert_link, null); int[] attrs = {R.attr.fontColor}; TypedArray ta = baseView.getContext() .obtainStyledAttributes( new ColorPreferences(baseView.getContext()).getFontStyle() .getBaseId(), attrs); ta.recycle(); String selectedText = ""; //if the user highlighted text before inputting a URL, use that text for the descriptionBox if (editText.hasSelection()) { final int startSelection = editText.getSelectionStart(); final int endSelection = editText.getSelectionEnd(); selectedText = editText.getText().toString().substring(startSelection, endSelection); } final boolean selectedTextNotEmpty = !selectedText.isEmpty(); final MaterialDialog dialog = new MaterialDialog.Builder(editText.getContext()).title( R.string.editor_title_link) .customView(layout, false) .positiveColorAttr(R.attr.tintColor) .positiveText(R.string.editor_action_link) .onPositive(new MaterialDialog.SingleButtonCallback() { @Override public void onClick(@NonNull MaterialDialog dialog, @NonNull DialogAction which) { final EditText urlBox = (EditText) dialog.findViewById(R.id.url_box); final EditText textBox = (EditText) dialog.findViewById(R.id.text_box); dialog.dismiss(); final String s = "[".concat(textBox.getText().toString()) .concat("](") .concat(urlBox.getText().toString()) .concat(")"); int start = Math.max(editText.getSelectionStart(), 0); int end = Math.max(editText.getSelectionEnd(), 0); editText.getText().insert(Math.max(start, end), s); //delete the selected text to avoid duplication if (selectedTextNotEmpty) { editText.getText().delete(start, end); } } }) .build(); //Tint the hint text if the base theme is Sepia if (SettingValues.currentTheme == 5) { ((EditText) dialog.findViewById(R.id.url_box)).setHintTextColor( ContextCompat.getColor(dialog.getContext(), R.color.md_grey_600)); ((EditText) dialog.findViewById(R.id.text_box)).setHintTextColor( ContextCompat.getColor(dialog.getContext(), R.color.md_grey_600)); } //use the selected text as the text for the link if (!selectedText.isEmpty()) { ((EditText) dialog.findViewById(R.id.text_box)).setText(selectedText); } dialog.show(); } }); try { ((ImageInsertEditText) editText).setImageSelectedCallback( new ImageInsertEditText.ImageSelectedCallback() { @Override public void onImageSelected(final Uri content, String mimeType) { e = editText.getText(); sStart = editText.getSelectionStart(); sEnd = editText.getSelectionEnd(); handleImageIntent(new ArrayList<Uri>() {{ add(content); }}, editText, a); } }); } catch (Exception e) { //if thrown, there is likely an issue implementing this on the user's version of Android. There shouldn't be an issue, but just in case } } public static Editable e; public static int sStart, sEnd; public static void doDraw(final Activity a, final EditText editText, final FragmentManager fm) { final Intent intent = new Intent(a, Draw.class); InputMethodManager imm = (InputMethodManager) editText.getContext() .getSystemService(Context.INPUT_METHOD_SERVICE); imm.hideSoftInputFromWindow(editText.getWindowToken(), 0); e = editText.getText(); TedBottomPicker tedBottomPicker = new TedBottomPicker.Builder(editText.getContext()).setOnImageSelectedListener( new TedBottomPicker.OnImageSelectedListener() { @Override public void onImageSelected(List<Uri> uri) { Draw.uri = uri.get(0); Fragment auxiliary = new AuxiliaryFragment(); sStart = editText.getSelectionStart(); sEnd = editText.getSelectionEnd(); fm.beginTransaction().add(auxiliary, "IMAGE_UPLOAD").commit(); fm.executePendingTransactions(); auxiliary.startActivityForResult(intent, 3333); } }) .setLayoutResource(R.layout.image_sheet_dialog) .setTitle("Choose a photo") .create(); tedBottomPicker.show(fm); } public static class AuxiliaryFragment extends Fragment { @Override public void onActivityResult(int requestCode, int resultCode, final Intent data) { super.onActivityResult(requestCode, resultCode, data); if(data != null && data.getData() != null) { handleImageIntent(new ArrayList<Uri>() {{ add(data.getData()); }}, e, getContext()); getActivity().getSupportFragmentManager().beginTransaction().remove(this).commit(); } } } public static String getImageLink(Bitmap b) { ByteArrayOutputStream baos = new ByteArrayOutputStream(); b.compress(Bitmap.CompressFormat.JPEG, 100, baos); // Not sure whether this should be jpeg or png, try both and see which works best return Base64.encodeToString(baos.toByteArray(), Base64.DEFAULT); } public static void insertBefore(String wrapText, EditText editText) { int start = Math.max(editText.getSelectionStart(), 0); int end = Math.max(editText.getSelectionEnd(), 0); editText.getText().insert(Math.min(start, end), wrapText); } /* not using this method anywhere ¯\_(ツ)_/¯ */ // public static void wrapNewline(String wrapText, EditText editText) { // int start = Math.max(editText.getSelectionStart(), 0); // int end = Math.max(editText.getSelectionEnd(), 0); // String s = editText.getText().toString().substring(Math.min(start, end), Math.max(start, end)); // s = s.replace("\n", "\n" + wrapText); // editText.getText().replace(Math.min(start, end), Math.max(start, end), s); // } /** * Wrap selected text in one or multiple characters, handling newlines and spaces properly for markdown * @param wrapText Character(s) to wrap the selected text in * @param editText EditText */ public static void wrapString(String wrapText, EditText editText) { wrapString(wrapText, wrapText, editText); } /** * Wrap selected text in one or multiple characters, handling newlines, spaces, >s properly for markdown, * with different start and end text. * @param startWrap Character(s) to start wrapping with * @param endWrap Character(s) to close wrapping with * @param editText EditText */ public static void wrapString(String startWrap, String endWrap, EditText editText) { int start = Math.max(editText.getSelectionStart(), 0); int end = Math.max(editText.getSelectionEnd(), 0); String selected = editText.getText().toString().substring(Math.min(start, end), Math.max(start, end)); // insert the wrapping character inside any selected spaces and >s because they stop markdown formatting // we use replaceFirst because anchors (\A, \Z) aren't consumed selected = selected.replaceFirst("\\A[\\n> ]*", "$0" + startWrap) .replaceFirst("[\\n> ]*\\Z", endWrap + "$0"); // 2+ newlines stop formatting, so we do the formatting on each instance of text surrounded by 2+ newlines /* in case anyone needs to understand this in the future: * ([^\n> ]) captures any character that isn't a newline, >, or space * (\n[> ]*){2,} captures any number of two or more newlines with any combination of spaces or >s since markdown ignores those by themselves * (?=[^\n> ]) performs a lookahead and ensures there's a character that isn't a newline, >, or space */ selected = selected.replaceAll("([^\\n> ])(\\n[> ]*){2,}(?=[^\\n> ])", "$1" + endWrap + "$2" + startWrap); editText.getText().replace(start, end, selected); } private static void setViews(String rawHTML, String subredditName, SpoilerRobotoTextView firstTextView, CommentOverflow commentOverflow) { if (rawHTML.isEmpty()) { return; } List<String> blocks = SubmissionParser.getBlocks(rawHTML); int startIndex = 0; // the <div class="md"> case is when the body contains a table or code block first if (!blocks.get(0).equals("<div class=\"md\">")) { firstTextView.setVisibility(View.VISIBLE); firstTextView.setTextHtml(blocks.get(0), subredditName); firstTextView.setLinkTextColor( new ColorPreferences(firstTextView.getContext()).getColor(subredditName)); startIndex = 1; } else { firstTextView.setText(""); firstTextView.setVisibility(View.GONE); } if (blocks.size() > 1) { if (startIndex == 0) { commentOverflow.setViews(blocks, subredditName); } else { commentOverflow.setViews(blocks.subList(startIndex, blocks.size()), subredditName); } } else { commentOverflow.removeAllViews(); } } private static class UploadImgur extends AsyncTask<Uri, Integer, JSONObject> { final Context c; private final MaterialDialog dialog; public Bitmap b; public UploadImgur(Context c) { this.c = c; dialog = new MaterialDialog.Builder(c).title( c.getString(R.string.editor_uploading_image)) .progress(false, 100) .cancelable(false) .show(); } //Following methods sourced from https://github.com/Kennyc1012/Opengur, Code by Kenny Campagna public static File createFile(Uri uri, @NonNull Context context) { InputStream in; ContentResolver resolver = context.getContentResolver(); String type = resolver.getType(uri); String extension; if ("image/png".equals(type)) { extension = ".gif"; } else if ("image/png".equals(type)) { extension = ".png"; } else { extension = ".jpg"; } try { in = resolver.openInputStream(uri); } catch (FileNotFoundException e) { return null; } // Create files from a uri in our cache directory so they eventually get deleted String timeStamp = String.valueOf(System.currentTimeMillis()); File cacheDir = ((Reddit) context.getApplicationContext()).getImageLoader() .getDiskCache() .getDirectory(); File tempFile = new File(cacheDir, timeStamp + extension); if (writeInputStreamToFile(in, tempFile)) { return tempFile; } else { // If writeInputStreamToFile fails, delete the excess file tempFile.delete(); } return null; } public static boolean writeInputStreamToFile(@NonNull InputStream in, @NonNull File file) { BufferedOutputStream buffer = null; boolean didFinish = false; try { buffer = new BufferedOutputStream(new FileOutputStream(file)); byte byt[] = new byte[1024]; int i; for (long l = 0L; (i = in.read(byt)) != -1; l += i) { buffer.write(byt, 0, i); } buffer.flush(); didFinish = true; } catch (IOException e) { didFinish = false; } finally { closeStream(in); closeStream(buffer); } return didFinish; } public static void closeStream(@Nullable Closeable closeable) { if (closeable != null) { try { closeable.close(); } catch (IOException ex) { } } } //End methods sourced from Opengur @Override protected JSONObject doInBackground(Uri... sub) { File bitmap = createFile(sub[0], c); final OkHttpClient client = Reddit.client; try { RequestBody formBody = new MultipartBody.Builder().setType(MultipartBody.FORM) .addFormDataPart("image", bitmap.getName(), RequestBody.create(MediaType.parse("image/*"), bitmap)) .build(); ProgressRequestBody body = new ProgressRequestBody(formBody, new ProgressRequestBody.Listener() { @Override public void onProgress(int progress) { publishProgress(progress); } }); Request request = new Request.Builder().header("Authorization", "Client-ID bef87913eb202e9") .url("https://api.imgur.com/3/image") .post(body) .build(); Response response = client.newCall(request).execute(); if (!response.isSuccessful()) throw new IOException("Unexpected code " + response); return new JSONObject(response.body().string()); } catch (Exception e) { e.printStackTrace(); } return null; } @Override protected void onPreExecute() { super.onPreExecute(); } @Override protected void onPostExecute(final JSONObject result) { dialog.dismiss(); try { int[] attrs = {R.attr.fontColor}; TypedArray ta = c.obtainStyledAttributes(new ColorPreferences(c).getFontStyle().getBaseId(), attrs); final String url = result.getJSONObject("data").getString("link"); LinearLayout layout = new LinearLayout(c); layout.setOrientation(LinearLayout.VERTICAL); final TextView titleBox = new TextView(c); titleBox.setText(url); layout.addView(titleBox); titleBox.setEnabled(false); titleBox.setTextColor(ta.getColor(0, Color.WHITE)); final EditText descriptionBox = new EditText(c); descriptionBox.setHint(R.string.editor_title); descriptionBox.setEnabled(true); descriptionBox.setTextColor(ta.getColor(0, Color.WHITE)); final InputMethodManager imm = (InputMethodManager) c.getSystemService(Context.INPUT_METHOD_SERVICE); imm.toggleSoftInput(InputMethodManager.SHOW_FORCED, InputMethodManager.HIDE_IMPLICIT_ONLY); if (DoEditorActions.e != null) { descriptionBox.setText(DoEditorActions.e.toString().substring(sStart, sEnd)); } ta.recycle(); int sixteen = Reddit.dpToPxVertical(16); layout.setPadding(sixteen, sixteen, sixteen, sixteen); layout.addView(descriptionBox); new MaterialDialog.Builder(c).title(R.string.editor_title_link) .customView(layout, false) .positiveText(R.string.editor_action_link) .onPositive(new MaterialDialog.SingleButtonCallback() { @Override public void onClick(@NonNull MaterialDialog dialog, @NonNull DialogAction which) { dialog.dismiss(); String s = "[" + descriptionBox.getText().toString() + "](" + url + ")"; if (descriptionBox.getText().toString().trim().isEmpty()) { s = url + " "; } int start = Math.max(sStart, 0); int end = Math.max(sEnd, 0); if (DoEditorActions.e != null) { DoEditorActions.e.insert(Math.max(start, end), s); DoEditorActions.e.delete(start, end); DoEditorActions.e = null; } sStart = 0; sEnd = 0; } }) .canceledOnTouchOutside(false) .show(); } catch (Exception e) { new AlertDialogWrapper.Builder(c).setTitle(R.string.err_title) .setMessage(R.string.editor_err_msg) .setPositiveButton(R.string.btn_ok, new DialogInterface.OnClickListener() { @Override public void onClick(DialogInterface dialog, int which) { } }) .show(); e.printStackTrace(); } } @Override protected void onProgressUpdate(Integer... values) { dialog.setProgress(values[0]); LogUtil.v("Progress:" + values[0]); } } private static class UploadImgurAlbum extends AsyncTask<Uri, Integer, String> { final Context c; private final MaterialDialog dialog; public Bitmap b; public UploadImgurAlbum(Context c) { this.c = c; dialog = new MaterialDialog.Builder(c).title( c.getString(R.string.editor_uploading_image)) .progress(false, 100) .cancelable(false) .show(); } //Following methods sourced from https://github.com/Kennyc1012/Opengur, Code by Kenny Campagna public static File createFile(Uri uri, @NonNull Context context) { InputStream in; ContentResolver resolver = context.getContentResolver(); String type = resolver.getType(uri); String extension; if ("image/png".equals(type)) { extension = ".gif"; } else if ("image/png".equals(type)) { extension = ".png"; } else { extension = ".jpg"; } try { in = resolver.openInputStream(uri); } catch (FileNotFoundException e) { return null; } // Create files from a uri in our cache directory so they eventually get deleted String timeStamp = String.valueOf(System.currentTimeMillis()); File cacheDir = ((Reddit) context.getApplicationContext()).getImageLoader() .getDiskCache() .getDirectory(); File tempFile = new File(cacheDir, timeStamp + extension); if (writeInputStreamToFile(in, tempFile)) { return tempFile; } else { // If writeInputStreamToFile fails, delete the excess file tempFile.delete(); } return null; } public static boolean writeInputStreamToFile(@NonNull InputStream in, @NonNull File file) { BufferedOutputStream buffer = null; boolean didFinish = false; try { buffer = new BufferedOutputStream(new FileOutputStream(file)); byte byt[] = new byte[1024]; int i; for (long l = 0L; (i = in.read(byt)) != -1; l += i) { buffer.write(byt, 0, i); } buffer.flush(); didFinish = true; } catch (IOException e) { didFinish = false; } finally { closeStream(in); closeStream(buffer); } return didFinish; } public static void closeStream(@Nullable Closeable closeable) { if (closeable != null) { try { closeable.close(); } catch (IOException ex) { } } } //End methods sourced from Opengur String finalUrl; @Override protected String doInBackground(Uri... sub) { totalCount = sub.length; final OkHttpClient client = Reddit.client; String albumurl; { Request request = new Request.Builder().header("Authorization", "Client-ID bef87913eb202e9") .url("https://api.imgur.com/3/album") .post(new RequestBody() { @Override public MediaType contentType() { return null; } @Override public void writeTo(BufferedSink sink) { } }) .build(); Response response = null; try { response = client.newCall(request).execute(); if (!response.isSuccessful()) { throw new IOException("Unexpected code " + response); } JSONObject album = new JSONObject(response.body().string()); albumurl = album.getJSONObject("data").getString("deletehash"); finalUrl = "http://imgur.com/a/" + album.getJSONObject("data").getString("id"); } catch (Exception e) { e.printStackTrace(); return null; } } try { MultipartBody.Builder formBodyBuilder = new MultipartBody.Builder().setType(MultipartBody.FORM); for (Uri uri : sub) { File bitmap = createFile(uri, c); formBodyBuilder.addFormDataPart("image", bitmap.getName(), RequestBody.create(MediaType.parse("image/*"), bitmap)); formBodyBuilder.addFormDataPart("album", albumurl); MultipartBody formBody = formBodyBuilder.build(); ProgressRequestBody body = new ProgressRequestBody(formBody, new ProgressRequestBody.Listener() { @Override public void onProgress(int progress) { publishProgress(progress); } }); Request request = new Request.Builder().header("Authorization", "Client-ID bef87913eb202e9") .url("https://api.imgur.com/3/image") .post(body) .build(); Response response = client.newCall(request).execute(); if (!response.isSuccessful()) { throw new IOException("Unexpected code " + response); } } } catch (Exception e) { e.printStackTrace(); } return null; } @Override protected void onPreExecute() { super.onPreExecute(); } @Override protected void onPostExecute(final String result) { dialog.dismiss(); try { int[] attrs = {R.attr.fontColor}; TypedArray ta = c.obtainStyledAttributes(new ColorPreferences(c).getFontStyle().getBaseId(), attrs); LinearLayout layout = new LinearLayout(c); layout.setOrientation(LinearLayout.VERTICAL); final TextView titleBox = new TextView(c); titleBox.setText(finalUrl); layout.addView(titleBox); titleBox.setEnabled(false); titleBox.setTextColor(ta.getColor(0, Color.WHITE)); final EditText descriptionBox = new EditText(c); descriptionBox.setHint(R.string.editor_title); descriptionBox.setEnabled(true); descriptionBox.setTextColor(ta.getColor(0, Color.WHITE)); if (DoEditorActions.e != null) { descriptionBox.setText(DoEditorActions.e.toString().substring(sStart, sEnd)); } ta.recycle(); int sixteen = Reddit.dpToPxVertical(16); layout.setPadding(sixteen, sixteen, sixteen, sixteen); layout.addView(descriptionBox); new MaterialDialog.Builder(c).title(R.string.editor_title_link) .customView(layout, false) .onPositive(new MaterialDialog.SingleButtonCallback() { @Override public void onClick(MaterialDialog dialog, DialogAction which) { dialog.dismiss(); String s = "[" + descriptionBox.getText().toString() + "](" + finalUrl + ")"; int start = Math.max(sStart, 0); int end = Math.max(sEnd, 0); DoEditorActions.e.insert(Math.max(start, end), s); DoEditorActions.e.delete(start, end); DoEditorActions.e = null; sStart = 0; sEnd = 0; } }) .positiveText(R.string.editor_action_link) .canceledOnTouchOutside(false) .show(); } catch (Exception e) { new AlertDialogWrapper.Builder(c).setTitle(R.string.err_title) .setMessage(R.string.editor_err_msg) .setPositiveButton(R.string.btn_ok, new DialogInterface.OnClickListener() { @Override public void onClick(DialogInterface dialog, int which) { } }) .show(); e.printStackTrace(); } } int uploadCount; int totalCount; @Override protected void onProgressUpdate(Integer... values) { int progress = values[0]; if (progress < dialog.getCurrentProgress() || uploadCount == 0) { uploadCount += 1; } dialog.setContent("Image " + uploadCount + "/" + totalCount); dialog.setProgress(progress); } } public static void handleImageIntent(List<Uri> uris, EditText ed, Context c) { handleImageIntent(uris, ed.getText(), c); } public static void handleImageIntent(List<Uri> uris, Editable ed, Context c) { if (uris.size() == 1) { // Get the Image from data (single image) try { new UploadImgur(c).execute(uris.get(0)); } catch (Exception e) { e.printStackTrace(); } } else { //Multiple images try { new UploadImgurAlbum(c).execute(uris.toArray(new Uri[uris.size()])); } catch (Exception e) { e.printStackTrace(); } } } public static class ProgressRequestBody extends RequestBody { protected RequestBody mDelegate; protected Listener mListener; protected CountingSink mCountingSink; public ProgressRequestBody(RequestBody delegate, Listener listener) { mDelegate = delegate; mListener = listener; } @Override public MediaType contentType() { return mDelegate.contentType(); } @Override public long contentLength() { try { return mDelegate.contentLength(); } catch (IOException e) { e.printStackTrace(); } return -1; } @Override public void writeTo(BufferedSink sink) throws IOException { mCountingSink = new CountingSink(sink); BufferedSink bufferedSink = Okio.buffer(mCountingSink); mDelegate.writeTo(bufferedSink); bufferedSink.flush(); } protected final class CountingSink extends ForwardingSink { private long bytesWritten = 0; public CountingSink(Sink delegate) { super(delegate); } @Override public void write(Buffer source, long byteCount) throws IOException { super.write(source, byteCount); bytesWritten += byteCount; mListener.onProgress((int) (100F * bytesWritten / contentLength())); } } public interface Listener { void onProgress(int progress); } } }