. */
-package com.keylesspalace.tusky.util;
+package com.keylesspalace.tusky.receiver;
import android.content.BroadcastReceiver;
import android.content.Context;
diff --git a/app/src/main/java/com/keylesspalace/tusky/util/TimelineReceiver.java b/app/src/main/java/com/keylesspalace/tusky/receiver/TimelineReceiver.java
similarity index 96%
rename from app/src/main/java/com/keylesspalace/tusky/util/TimelineReceiver.java
rename to app/src/main/java/com/keylesspalace/tusky/receiver/TimelineReceiver.java
index f6acc0d9..71a09413 100644
--- a/app/src/main/java/com/keylesspalace/tusky/util/TimelineReceiver.java
+++ b/app/src/main/java/com/keylesspalace/tusky/receiver/TimelineReceiver.java
@@ -1,4 +1,4 @@
-package com.keylesspalace.tusky.util;
+package com.keylesspalace.tusky.receiver;
import android.content.BroadcastReceiver;
import android.content.Context;
diff --git a/app/src/main/java/com/keylesspalace/tusky/util/MediaUtils.java b/app/src/main/java/com/keylesspalace/tusky/util/MediaUtils.java
new file mode 100644
index 00000000..76d18bde
--- /dev/null
+++ b/app/src/main/java/com/keylesspalace/tusky/util/MediaUtils.java
@@ -0,0 +1,117 @@
+package com.keylesspalace.tusky.util;
+
+import android.content.ContentResolver;
+import android.content.Context;
+import android.database.Cursor;
+import android.graphics.Bitmap;
+import android.graphics.drawable.Drawable;
+import android.net.Uri;
+import android.os.Environment;
+import android.provider.OpenableColumns;
+import android.support.annotation.Nullable;
+import android.support.v4.content.FileProvider;
+
+import com.squareup.picasso.Picasso;
+import com.squareup.picasso.Target;
+
+import java.io.ByteArrayOutputStream;
+import java.io.File;
+import java.io.FileOutputStream;
+import java.io.IOException;
+import java.io.InputStream;
+
+/**
+ * Class who will have all the code link with Media
+ *
+ * Motivation : try to keep the ComposeActivity "smaller" and make modular method
+ */
+public class MediaUtils {
+ public static final int MEDIA_SIZE_UNKNOWN = -1;
+
+ @Nullable
+ public static byte[] inputStreamGetBytes(InputStream stream) {
+ ByteArrayOutputStream buffer = new ByteArrayOutputStream();
+ int read;
+ byte[] data = new byte[16384];
+ try {
+ while ((read = stream.read(data, 0, data.length)) != -1) {
+ buffer.write(data, 0, read);
+ }
+ buffer.flush();
+ } catch (IOException e) {
+ return null;
+ }
+ return buffer.toByteArray();
+ }
+
+ public static long getMediaSize(ContentResolver contentResolver, Uri uri) {
+ long mediaSize;
+ Cursor cursor = contentResolver.query(uri, null, null, null, null);
+ if (cursor != null) {
+ int sizeIndex = cursor.getColumnIndex(OpenableColumns.SIZE);
+ cursor.moveToFirst();
+ mediaSize = cursor.getLong(sizeIndex);
+ cursor.close();
+ } else {
+ mediaSize = MEDIA_SIZE_UNKNOWN;
+ }
+ return mediaSize;
+ }
+
+ // Download an image with picasso
+ public static Target picassoImageTarget(final Context context, final MediaListener mediaListener) {
+ final String imageName = "temp";
+ return new Target() {
+ @Override
+ public void onBitmapLoaded(final Bitmap bitmap, Picasso.LoadedFrom from) {
+ new Thread(new Runnable() {
+ @Override
+ public void run() {
+ FileOutputStream fos = null;
+ Uri uriForFile;
+ try {
+ // we download only a "temp" file
+ File storageDir = context.getExternalFilesDir(Environment.DIRECTORY_PICTURES);
+ File tempFile = File.createTempFile(
+ imageName,
+ ".jpg",
+ storageDir
+ );
+
+ fos = new FileOutputStream(tempFile);
+ bitmap.compress(Bitmap.CompressFormat.PNG, 100, fos);
+ uriForFile = FileProvider.getUriForFile(context,
+ "com.keylesspalace.tusky.fileprovider",
+ tempFile);
+
+ // giving to the activity the URI callback
+ mediaListener.onCallback(uriForFile);
+ } catch (IOException e) {
+ e.printStackTrace();
+ } finally {
+ try {
+ if (fos != null) {
+ fos.close();
+ }
+ } catch (IOException e) {
+ e.printStackTrace();
+ }
+ }
+ }
+ }).start();
+ }
+
+ @Override
+ public void onBitmapFailed(Drawable errorDrawable) {
+ }
+
+ @Override
+ public void onPrepareLoad(Drawable placeHolderDrawable) {
+ }
+ };
+ }
+
+ public interface MediaListener {
+ void onCallback(Uri headerInfo);
+ }
+}
diff --git a/app/src/main/java/com/keylesspalace/tusky/util/NotificationMaker.java b/app/src/main/java/com/keylesspalace/tusky/util/NotificationMaker.java
index e0c31280..156a9542 100644
--- a/app/src/main/java/com/keylesspalace/tusky/util/NotificationMaker.java
+++ b/app/src/main/java/com/keylesspalace/tusky/util/NotificationMaker.java
@@ -33,6 +33,8 @@ import android.util.Log;
import com.keylesspalace.tusky.MainActivity;
import com.keylesspalace.tusky.R;
import com.keylesspalace.tusky.entity.Notification;
+import com.keylesspalace.tusky.receiver.NotificationClearBroadcastReceiver;
+import com.keylesspalace.tusky.view.RoundedTransformation;
import com.squareup.picasso.Picasso;
import com.squareup.picasso.Target;
diff --git a/app/src/main/java/com/keylesspalace/tusky/util/ParserUtils.java b/app/src/main/java/com/keylesspalace/tusky/util/ParserUtils.java
new file mode 100644
index 00000000..90a155b1
--- /dev/null
+++ b/app/src/main/java/com/keylesspalace/tusky/util/ParserUtils.java
@@ -0,0 +1,124 @@
+package com.keylesspalace.tusky.util;
+
+import android.content.ClipData;
+import android.content.ClipboardManager;
+import android.content.Context;
+import android.os.AsyncTask;
+import android.text.TextUtils;
+import android.util.Log;
+import android.webkit.URLUtil;
+
+import org.jsoup.Connection;
+import org.jsoup.Jsoup;
+import org.jsoup.helper.HttpConnection;
+import org.jsoup.nodes.Document;
+import org.jsoup.select.Elements;
+
+import java.util.List;
+
+import static com.keylesspalace.tusky.util.StringUtils.QUOTE;
+
+/**
+ * Inspect and Get the information from an URL
+ */
+public class ParserUtils {
+ private static final String TAG = "ParserUtils";
+ private ParserListener parserListener;
+
+ public ParserUtils(ParserListener parserListener) {
+ this.parserListener = parserListener;
+ }
+
+ // ComposeActivity : EditText inside the onTextChanged
+ public String getPastedURLText(Context context) {
+ ClipboardManager clipboard = (ClipboardManager) context.getSystemService(Context.CLIPBOARD_SERVICE);
+ String pasteData;
+ if (clipboard.hasPrimaryClip()) {
+ // get what is in the clipboard
+ ClipData.Item item = clipboard.getPrimaryClip().getItemAt(0);
+ pasteData = item.getText().toString();
+
+ // If we share with an app, it's not only an url
+ List strings = StringUtils.extractUrl(pasteData);
+ String url = strings.get(0); // we assume that the first url is the good one
+ if (strings.size() > 0) {
+ if (URLUtil.isValidUrl(url)) {
+ new ThreadHeaderInfo().execute(url);
+ }
+ }
+ }
+ return null;
+ }
+
+ public void putInClipboardManager(Context context, String string) {
+ ClipboardManager clipboard = (ClipboardManager) context.getSystemService(Context.CLIPBOARD_SERVICE);
+ ClipData clip = ClipData.newPlainText("", string);
+ clipboard.setPrimaryClip(clip);
+ }
+
+ // parse the HTML page
+ private HeaderInfo parsePageHeaderInfo(String urlStr) throws Exception {
+ Connection con = Jsoup.connect(urlStr);
+ HeaderInfo headerInfo = new HeaderInfo();
+ con.userAgent(HttpConnection.DEFAULT_UA);
+ Document doc = con.get();
+
+ // get info
+ String text;
+ Elements metaOgTitle = doc.select("meta[property=og:title]");
+ if (metaOgTitle != null) {
+ text = metaOgTitle.attr("content");
+ } else {
+ text = doc.title();
+ }
+
+ String imageUrl = null;
+ Elements metaOgImage = doc.select("meta[property=og:image]");
+ if (metaOgImage != null) {
+ imageUrl = metaOgImage.attr("content");
+ }
+
+ // set info
+ headerInfo.baseUrl = urlStr;
+ if (!TextUtils.isEmpty(text)) {
+ headerInfo.title = QUOTE + text.toUpperCase() + QUOTE;
+ }
+ if (!TextUtils.isEmpty(imageUrl)) {
+ headerInfo.image = (imageUrl);
+ }
+ return headerInfo;
+ }
+
+ public interface ParserListener {
+ void onReceiveHeaderInfo(HeaderInfo headerInfo);
+
+ void onErrorHeaderInfo();
+ }
+
+ public class HeaderInfo {
+ public String baseUrl;
+ public String title;
+ public String image;
+ }
+
+ private class ThreadHeaderInfo extends AsyncTask {
+ protected HeaderInfo doInBackground(String... urls) {
+ try {
+ String url = urls[0];
+ return parsePageHeaderInfo(url);
+ } catch (Exception e) {
+ Log.e(TAG, "ThreadHeaderInfo#parsePageHeaderInfo() failed." + e.getMessage());
+ return null;
+ }
+ }
+
+ protected void onPostExecute(HeaderInfo headerInfo) {
+ if (headerInfo != null) {
+ Log.i(TAG, "ThreadHeaderInfo#parsePageHeaderInfo() success." + headerInfo.title + " " + headerInfo.image);
+ parserListener.onReceiveHeaderInfo(headerInfo);
+ } else {
+ parserListener.onErrorHeaderInfo();
+ }
+ }
+ }
+}
diff --git a/app/src/main/java/com/keylesspalace/tusky/util/StringUtils.java b/app/src/main/java/com/keylesspalace/tusky/util/StringUtils.java
new file mode 100644
index 00000000..1d909751
--- /dev/null
+++ b/app/src/main/java/com/keylesspalace/tusky/util/StringUtils.java
@@ -0,0 +1,34 @@
+package com.keylesspalace.tusky.util;
+
+import android.util.Patterns;
+
+import java.util.ArrayList;
+import java.util.List;
+import java.util.Random;
+import java.util.regex.Matcher;
+
+public class StringUtils {
+
+ public final static String carriageReturn = System.getProperty("line.separator");
+ final static String QUOTE = "\"";
+
+ public static String randomAlphanumericString(int count) {
+ char[] chars = new char[count];
+ Random random = new Random();
+ final String POSSIBLE_CHARS = "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ";
+ for (int i = 0; i < count; i++) {
+ chars[i] = POSSIBLE_CHARS.charAt(random.nextInt(POSSIBLE_CHARS.length()));
+ }
+ return new String(chars);
+ }
+
+ static List extractUrl(String text) {
+ List links = new ArrayList<>();
+ Matcher m = Patterns.WEB_URL.matcher(text);
+ while (m.find()) {
+ String url = m.group();
+ links.add(url);
+ }
+ return links;
+ }
+}
diff --git a/app/src/main/java/com/keylesspalace/tusky/util/ConversationLineItemDecoration.java b/app/src/main/java/com/keylesspalace/tusky/view/ConversationLineItemDecoration.java
similarity index 98%
rename from app/src/main/java/com/keylesspalace/tusky/util/ConversationLineItemDecoration.java
rename to app/src/main/java/com/keylesspalace/tusky/view/ConversationLineItemDecoration.java
index dab773c3..fee358d0 100644
--- a/app/src/main/java/com/keylesspalace/tusky/util/ConversationLineItemDecoration.java
+++ b/app/src/main/java/com/keylesspalace/tusky/view/ConversationLineItemDecoration.java
@@ -13,7 +13,7 @@
* You should have received a copy of the GNU General Public License along with Tusky; if not,
* see . */
-package com.keylesspalace.tusky.util;
+package com.keylesspalace.tusky.view;
import android.content.Context;
import android.graphics.Canvas;
diff --git a/app/src/main/java/com/keylesspalace/tusky/util/EditTextTyped.java b/app/src/main/java/com/keylesspalace/tusky/view/EditTextTyped.java
similarity index 73%
rename from app/src/main/java/com/keylesspalace/tusky/util/EditTextTyped.java
rename to app/src/main/java/com/keylesspalace/tusky/view/EditTextTyped.java
index c32f666f..4693ee35 100644
--- a/app/src/main/java/com/keylesspalace/tusky/util/EditTextTyped.java
+++ b/app/src/main/java/com/keylesspalace/tusky/view/EditTextTyped.java
@@ -13,7 +13,7 @@
* You should have received a copy of the GNU General Public License along with Tusky; if not,
* see . */
-package com.keylesspalace.tusky.util;
+package com.keylesspalace.tusky.view;
import android.content.Context;
import android.support.v13.view.inputmethod.EditorInfoCompat;
@@ -23,9 +23,13 @@ import android.util.AttributeSet;
import android.view.inputmethod.EditorInfo;
import android.view.inputmethod.InputConnection;
+import com.keylesspalace.tusky.util.Assert;
+
public class EditTextTyped extends AppCompatEditText {
+
InputConnectionCompat.OnCommitContentListener onCommitContentListener;
String[] mimeTypes;
+ private OnPasteListener mOnPasteListener;
public EditTextTyped(Context context) {
super(context);
@@ -35,6 +39,10 @@ public class EditTextTyped extends AppCompatEditText {
super(context, attributeSet);
}
+ public void addOnPasteListener(OnPasteListener mOnPasteListener) {
+ this.mOnPasteListener = mOnPasteListener;
+ }
+
public void setMimeTypes(String[] types,
InputConnectionCompat.OnCommitContentListener listener) {
mimeTypes = types;
@@ -53,4 +61,26 @@ public class EditTextTyped extends AppCompatEditText {
return connection;
}
}
+
+ @Override
+ public boolean onTextContextMenuItem(int id) {
+ boolean consumed = super.onTextContextMenuItem(id);
+ switch (id) {
+ case android.R.id.paste:
+ onPaste();
+ }
+ return consumed;
+ }
+
+ /**
+ * Text was pasted into the EditText.
+ */
+ public void onPaste() {
+ if (mOnPasteListener != null)
+ mOnPasteListener.onPaste();
+ }
+
+ public interface OnPasteListener {
+ void onPaste();
+ }
}
diff --git a/app/src/main/java/com/keylesspalace/tusky/util/EndlessOnScrollListener.java b/app/src/main/java/com/keylesspalace/tusky/view/EndlessOnScrollListener.java
similarity index 98%
rename from app/src/main/java/com/keylesspalace/tusky/util/EndlessOnScrollListener.java
rename to app/src/main/java/com/keylesspalace/tusky/view/EndlessOnScrollListener.java
index 0b149de0..8b45d11f 100644
--- a/app/src/main/java/com/keylesspalace/tusky/util/EndlessOnScrollListener.java
+++ b/app/src/main/java/com/keylesspalace/tusky/view/EndlessOnScrollListener.java
@@ -13,7 +13,7 @@
* You should have received a copy of the GNU General Public License along with Tusky; if not,
* see . */
-package com.keylesspalace.tusky.util;
+package com.keylesspalace.tusky.view;
import android.support.v7.widget.LinearLayoutManager;
import android.support.v7.widget.RecyclerView;
diff --git a/app/src/main/java/com/keylesspalace/tusky/util/FlowLayout.java b/app/src/main/java/com/keylesspalace/tusky/view/FlowLayout.java
similarity index 98%
rename from app/src/main/java/com/keylesspalace/tusky/util/FlowLayout.java
rename to app/src/main/java/com/keylesspalace/tusky/view/FlowLayout.java
index 9436ae18..236aa2ec 100644
--- a/app/src/main/java/com/keylesspalace/tusky/util/FlowLayout.java
+++ b/app/src/main/java/com/keylesspalace/tusky/view/FlowLayout.java
@@ -13,7 +13,7 @@
* You should have received a copy of the GNU General Public License along with Tusky; if not,
* see . */
-package com.keylesspalace.tusky.util;
+package com.keylesspalace.tusky.view;
import android.content.Context;
import android.content.res.TypedArray;
@@ -22,6 +22,7 @@ import android.view.View;
import android.view.ViewGroup;
import com.keylesspalace.tusky.R;
+import com.keylesspalace.tusky.util.Assert;
public class FlowLayout extends ViewGroup {
private int paddingHorizontal; // internal padding between child views
diff --git a/app/src/main/java/com/keylesspalace/tusky/util/RoundedTransformation.java b/app/src/main/java/com/keylesspalace/tusky/view/RoundedTransformation.java
similarity index 98%
rename from app/src/main/java/com/keylesspalace/tusky/util/RoundedTransformation.java
rename to app/src/main/java/com/keylesspalace/tusky/view/RoundedTransformation.java
index ff153d68..9a023010 100644
--- a/app/src/main/java/com/keylesspalace/tusky/util/RoundedTransformation.java
+++ b/app/src/main/java/com/keylesspalace/tusky/view/RoundedTransformation.java
@@ -13,7 +13,7 @@
* You should have received a copy of the GNU General Public License along with Tusky; if not,
* see . */
-package com.keylesspalace.tusky.util;
+package com.keylesspalace.tusky.view;
import android.graphics.Bitmap;
import android.graphics.BitmapShader;
diff --git a/app/src/main/res/layout/activity_compose.xml b/app/src/main/res/layout/activity_compose.xml
index 0252cce7..1f5b14f1 100644
--- a/app/src/main/res/layout/activity_compose.xml
+++ b/app/src/main/res/layout/activity_compose.xml
@@ -54,7 +54,7 @@
android:paddingLeft="16dp"
android:paddingRight="16dp">
-
-
-
+