From d23a0b8ceb97c7bf393d7a905cf6278f6b59b1d1 Mon Sep 17 00:00:00 2001 From: AL-Session <160798022+AL-Session@users.noreply.github.com> Date: Thu, 18 Jul 2024 13:17:17 +1000 Subject: [PATCH] Converted three classes to kotlin (#1552) --- .../emoji/CompositeEmojiPageModel.java | 64 --- .../emoji/CompositeEmojiPageModel.kt | 39 ++ .../securesms/conversation/v2/Util.java | 381 ----------------- .../securesms/conversation/v2/Util.kt | 384 ++++++++++++++++++ .../org/thoughtcrime/securesms/mms/Slide.java | 209 ---------- .../org/thoughtcrime/securesms/mms/Slide.kt | 180 ++++++++ 6 files changed, 603 insertions(+), 654 deletions(-) delete mode 100644 app/src/main/java/org/thoughtcrime/securesms/components/emoji/CompositeEmojiPageModel.java create mode 100644 app/src/main/java/org/thoughtcrime/securesms/components/emoji/CompositeEmojiPageModel.kt delete mode 100644 app/src/main/java/org/thoughtcrime/securesms/conversation/v2/Util.java create mode 100644 app/src/main/java/org/thoughtcrime/securesms/conversation/v2/Util.kt delete mode 100644 app/src/main/java/org/thoughtcrime/securesms/mms/Slide.java create mode 100644 app/src/main/java/org/thoughtcrime/securesms/mms/Slide.kt diff --git a/app/src/main/java/org/thoughtcrime/securesms/components/emoji/CompositeEmojiPageModel.java b/app/src/main/java/org/thoughtcrime/securesms/components/emoji/CompositeEmojiPageModel.java deleted file mode 100644 index cc36ab31c6..0000000000 --- a/app/src/main/java/org/thoughtcrime/securesms/components/emoji/CompositeEmojiPageModel.java +++ /dev/null @@ -1,64 +0,0 @@ -package org.thoughtcrime.securesms.components.emoji; - -import android.net.Uri; - -import androidx.annotation.AttrRes; -import androidx.annotation.NonNull; -import androidx.annotation.Nullable; - -import org.thoughtcrime.securesms.conversation.v2.Util; - -import java.util.LinkedList; -import java.util.List; - -public class CompositeEmojiPageModel implements EmojiPageModel { - @AttrRes private final int iconAttr; - @NonNull private final List models; - - public CompositeEmojiPageModel(@AttrRes int iconAttr, @NonNull List models) { - this.iconAttr = iconAttr; - this.models = models; - } - - @Override - public String getKey() { - return Util.hasItems(models) ? models.get(0).getKey() : ""; - } - - public int getIconAttr() { - return iconAttr; - } - - @Override - public @NonNull List getEmoji() { - List emojis = new LinkedList<>(); - for (EmojiPageModel model : models) { - emojis.addAll(model.getEmoji()); - } - return emojis; - } - - @Override - public @NonNull List getDisplayEmoji() { - List emojis = new LinkedList<>(); - for (EmojiPageModel model : models) { - emojis.addAll(model.getDisplayEmoji()); - } - return emojis; - } - - @Override - public boolean hasSpriteMap() { - return false; - } - - @Override - public @Nullable Uri getSpriteUri() { - return null; - } - - @Override - public boolean isDynamic() { - return false; - } -} diff --git a/app/src/main/java/org/thoughtcrime/securesms/components/emoji/CompositeEmojiPageModel.kt b/app/src/main/java/org/thoughtcrime/securesms/components/emoji/CompositeEmojiPageModel.kt new file mode 100644 index 0000000000..37c30b5974 --- /dev/null +++ b/app/src/main/java/org/thoughtcrime/securesms/components/emoji/CompositeEmojiPageModel.kt @@ -0,0 +1,39 @@ +package org.thoughtcrime.securesms.components.emoji + +import android.net.Uri +import androidx.annotation.AttrRes +import java.util.LinkedList + +class CompositeEmojiPageModel( + @field:AttrRes @param:AttrRes private val iconAttr: Int, + private val models: List +) : EmojiPageModel { + + override fun getKey(): String { + return if (models.isEmpty()) "" else models[0].key + } + + override fun getIconAttr(): Int { return iconAttr } + + override fun getEmoji(): List { + val emojis: MutableList = LinkedList() + for (model in models) { + emojis.addAll(model.emoji) + } + return emojis + } + + override fun getDisplayEmoji(): List { + val emojis: MutableList = LinkedList() + for (model in models) { + emojis.addAll(model.displayEmoji) + } + return emojis + } + + override fun hasSpriteMap(): Boolean { return false } + + override fun getSpriteUri(): Uri? { return null } + + override fun isDynamic(): Boolean { return false } +} diff --git a/app/src/main/java/org/thoughtcrime/securesms/conversation/v2/Util.java b/app/src/main/java/org/thoughtcrime/securesms/conversation/v2/Util.java deleted file mode 100644 index e81f06d0ee..0000000000 --- a/app/src/main/java/org/thoughtcrime/securesms/conversation/v2/Util.java +++ /dev/null @@ -1,381 +0,0 @@ -/* - * Copyright (C) 2011 Whisper Systems - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * 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 . - */ -package org.thoughtcrime.securesms.conversation.v2; - -import android.annotation.TargetApi; -import android.app.ActivityManager; -import android.content.ClipData; -import android.content.ClipboardManager; -import android.content.Context; -import android.graphics.Typeface; -import android.net.Uri; -import android.os.Build.VERSION; -import android.os.Build.VERSION_CODES; -import android.text.Spannable; -import android.text.SpannableString; -import android.text.TextUtils; -import android.text.style.StyleSpan; - -import androidx.annotation.NonNull; -import androidx.annotation.Nullable; - -import com.annimon.stream.Stream; -import com.google.android.mms.pdu_alt.CharacterSets; -import com.google.android.mms.pdu_alt.EncodedStringValue; - -import org.session.libsignal.utilities.Log; -import org.thoughtcrime.securesms.components.ComposeText; - -import java.io.ByteArrayOutputStream; -import java.io.IOException; -import java.io.UnsupportedEncodingException; -import java.security.SecureRandom; -import java.util.ArrayList; -import java.util.Arrays; -import java.util.Collection; -import java.util.Collections; -import java.util.LinkedList; -import java.util.List; -import java.util.Map; -import java.util.concurrent.TimeUnit; - -import network.loki.messenger.R; - -public class Util { - private static final String TAG = Log.tag(Util.class); - - private static final long BUILD_LIFESPAN = TimeUnit.DAYS.toMillis(90); - - public static List asList(T... elements) { - List result = new LinkedList<>(); - Collections.addAll(result, elements); - return result; - } - - public static String join(String[] list, String delimiter) { - return join(Arrays.asList(list), delimiter); - } - - public static String join(Collection list, String delimiter) { - StringBuilder result = new StringBuilder(); - int i = 0; - - for (T item : list) { - result.append(item); - - if (++i < list.size()) - result.append(delimiter); - } - - return result.toString(); - } - - public static String join(long[] list, String delimeter) { - List boxed = new ArrayList<>(list.length); - - for (int i = 0; i < list.length; i++) { - boxed.add(list[i]); - } - - return join(boxed, delimeter); - } - - @SafeVarargs - public static @NonNull List join(@NonNull List... lists) { - int totalSize = Stream.of(lists).reduce(0, (sum, list) -> sum + list.size()); - List joined = new ArrayList<>(totalSize); - - for (List list : lists) { - joined.addAll(list); - } - - return joined; - } - - public static String join(List list, String delimeter) { - StringBuilder sb = new StringBuilder(); - - for (int j = 0; j < list.size(); j++) { - if (j != 0) sb.append(delimeter); - sb.append(list.get(j)); - } - - return sb.toString(); - } - - public static String rightPad(String value, int length) { - if (value.length() >= length) { - return value; - } - - StringBuilder out = new StringBuilder(value); - while (out.length() < length) { - out.append(" "); - } - - return out.toString(); - } - - public static boolean isEmpty(EncodedStringValue[] value) { - return value == null || value.length == 0; - } - - public static boolean isEmpty(ComposeText value) { - return value == null || value.getText() == null || TextUtils.isEmpty(value.getTextTrimmed()); - } - - public static boolean isEmpty(Collection collection) { - return collection == null || collection.isEmpty(); - } - - public static boolean isEmpty(@Nullable CharSequence charSequence) { - return charSequence == null || charSequence.length() == 0; - } - - public static boolean hasItems(@Nullable Collection collection) { - return collection != null && !collection.isEmpty(); - } - - public static V getOrDefault(@NonNull Map map, K key, V defaultValue) { - return map.containsKey(key) ? map.get(key) : defaultValue; - } - - public static String getFirstNonEmpty(String... values) { - for (String value : values) { - if (!Util.isEmpty(value)) { - return value; - } - } - return ""; - } - - public static @NonNull String emptyIfNull(@Nullable String value) { - return value != null ? value : ""; - } - - public static @NonNull CharSequence emptyIfNull(@Nullable CharSequence value) { - return value != null ? value : ""; - } - - public static CharSequence getBoldedString(String value) { - SpannableString spanned = new SpannableString(value); - spanned.setSpan(new StyleSpan(Typeface.BOLD), 0, - spanned.length(), - Spannable.SPAN_EXCLUSIVE_EXCLUSIVE); - - return spanned; - } - - public static @NonNull String toIsoString(byte[] bytes) { - try { - return new String(bytes, CharacterSets.MIMENAME_ISO_8859_1); - } catch (UnsupportedEncodingException e) { - throw new AssertionError("ISO_8859_1 must be supported!"); - } - } - - public static byte[] toIsoBytes(String isoString) { - try { - return isoString.getBytes(CharacterSets.MIMENAME_ISO_8859_1); - } catch (UnsupportedEncodingException e) { - throw new AssertionError("ISO_8859_1 must be supported!"); - } - } - - public static byte[] toUtf8Bytes(String utf8String) { - try { - return utf8String.getBytes(CharacterSets.MIMENAME_UTF_8); - } catch (UnsupportedEncodingException e) { - throw new AssertionError("UTF_8 must be supported!"); - } - } - - public static void wait(Object lock, long timeout) { - try { - lock.wait(timeout); - } catch (InterruptedException ie) { - throw new AssertionError(ie); - } - } - - public static List split(String source, String delimiter) { - List results = new LinkedList<>(); - - if (TextUtils.isEmpty(source)) { - return results; - } - - String[] elements = source.split(delimiter); - Collections.addAll(results, elements); - - return results; - } - - public static byte[][] split(byte[] input, int firstLength, int secondLength) { - byte[][] parts = new byte[2][]; - - parts[0] = new byte[firstLength]; - System.arraycopy(input, 0, parts[0], 0, firstLength); - - parts[1] = new byte[secondLength]; - System.arraycopy(input, firstLength, parts[1], 0, secondLength); - - return parts; - } - - public static byte[] combine(byte[]... elements) { - try { - ByteArrayOutputStream baos = new ByteArrayOutputStream(); - - for (byte[] element : elements) { - baos.write(element); - } - - return baos.toByteArray(); - } catch (IOException e) { - throw new AssertionError(e); - } - } - - public static byte[] trim(byte[] input, int length) { - byte[] result = new byte[length]; - System.arraycopy(input, 0, result, 0, result.length); - - return result; - } - - public static byte[] getSecretBytes(int size) { - return getSecretBytes(new SecureRandom(), size); - } - - public static byte[] getSecretBytes(@NonNull SecureRandom secureRandom, int size) { - byte[] secret = new byte[size]; - secureRandom.nextBytes(secret); - return secret; - } - - public static T getRandomElement(T[] elements) { - return elements[new SecureRandom().nextInt(elements.length)]; - } - - public static T getRandomElement(List elements) { - return elements.get(new SecureRandom().nextInt(elements.size())); - } - - public static boolean equals(@Nullable Object a, @Nullable Object b) { - return a == b || (a != null && a.equals(b)); - } - - public static int hashCode(@Nullable Object... objects) { - return Arrays.hashCode(objects); - } - - public static @Nullable Uri uri(@Nullable String uri) { - if (uri == null) return null; - else return Uri.parse(uri); - } - - @TargetApi(VERSION_CODES.KITKAT) - public static boolean isLowMemory(Context context) { - ActivityManager activityManager = (ActivityManager) context.getSystemService(Context.ACTIVITY_SERVICE); - - return (VERSION.SDK_INT >= VERSION_CODES.KITKAT && activityManager.isLowRamDevice()) || - activityManager.getLargeMemoryClass() <= 64; - } - - public static int clamp(int value, int min, int max) { - return Math.min(Math.max(value, min), max); - } - - public static long clamp(long value, long min, long max) { - return Math.min(Math.max(value, min), max); - } - - public static float clamp(float value, float min, float max) { - return Math.min(Math.max(value, min), max); - } - - /** - * Returns half of the difference between the given length, and the length when scaled by the - * given scale. - */ - public static float halfOffsetFromScale(int length, float scale) { - float scaledLength = length * scale; - return (length - scaledLength) / 2; - } - - public static @Nullable String readTextFromClipboard(@NonNull Context context) { - { - ClipboardManager clipboardManager = (ClipboardManager)context.getSystemService(Context.CLIPBOARD_SERVICE); - - if (clipboardManager.hasPrimaryClip() && clipboardManager.getPrimaryClip().getItemCount() > 0) { - return clipboardManager.getPrimaryClip().getItemAt(0).getText().toString(); - } else { - return null; - } - } - } - - public static void writeTextToClipboard(@NonNull Context context, @NonNull String text) { - writeTextToClipboard(context, context.getString(R.string.app_name), text); - } - - public static void writeTextToClipboard(@NonNull Context context, @NonNull String label, @NonNull String text) { - ClipboardManager clipboard = (ClipboardManager) context.getSystemService(Context.CLIPBOARD_SERVICE); - ClipData clip = ClipData.newPlainText(label, text); - clipboard.setPrimaryClip(clip); - } - - public static int toIntExact(long value) { - if ((int)value != value) { - throw new ArithmeticException("integer overflow"); - } - return (int)value; - } - - public static boolean isEquals(@Nullable Long first, long second) { - return first != null && first == second; - } - - @SafeVarargs - public static List concatenatedList(Collection ... items) { - final List concat = new ArrayList<>(Stream.of(items).reduce(0, (sum, list) -> sum + list.size())); - - for (Collection list : items) { - concat.addAll(list); - } - - return concat; - } - - public static boolean isLong(String value) { - try { - Long.parseLong(value); - return true; - } catch (NumberFormatException e) { - return false; - } - } - - public static int parseInt(String integer, int defaultValue) { - try { - return Integer.parseInt(integer); - } catch (NumberFormatException e) { - return defaultValue; - } - } -} diff --git a/app/src/main/java/org/thoughtcrime/securesms/conversation/v2/Util.kt b/app/src/main/java/org/thoughtcrime/securesms/conversation/v2/Util.kt new file mode 100644 index 0000000000..ff33a58e91 --- /dev/null +++ b/app/src/main/java/org/thoughtcrime/securesms/conversation/v2/Util.kt @@ -0,0 +1,384 @@ +/* + * Copyright (C) 2011 Whisper Systems + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * 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 . + */ +package org.thoughtcrime.securesms.conversation.v2 + +import android.annotation.TargetApi +import android.app.ActivityManager +import android.content.ClipData +import android.content.ClipboardManager +import android.content.Context +import android.graphics.Typeface +import android.net.Uri +import android.os.Build.VERSION +import android.os.Build.VERSION_CODES +import android.text.Spannable +import android.text.SpannableString +import android.text.TextUtils +import android.text.style.StyleSpan +import android.view.View +import com.annimon.stream.Stream +import com.google.android.mms.pdu_alt.CharacterSets +import com.google.android.mms.pdu_alt.EncodedStringValue +import java.io.ByteArrayOutputStream +import java.io.IOException +import java.io.UnsupportedEncodingException +import java.security.SecureRandom +import java.util.Arrays +import java.util.Collections +import java.util.concurrent.TimeUnit +import kotlin.math.max +import kotlin.math.min +import network.loki.messenger.R +import org.session.libsignal.utilities.Log +import org.thoughtcrime.securesms.components.ComposeText + +object Util { + private val TAG: String = Log.tag(Util::class.java) + + private val BUILD_LIFESPAN = TimeUnit.DAYS.toMillis(90) + + fun asList(vararg elements: T): List { + val result = mutableListOf() // LinkedList() + Collections.addAll(result, *elements) + return result + } + + fun join(list: Array, delimiter: String?): String { + return join(listOf(*list), delimiter) + } + + fun join(list: Collection, delimiter: String?): String { + val result = StringBuilder() + var i = 0 + + for (item in list) { + result.append(item) + if (++i < list.size) result.append(delimiter) + } + + return result.toString() + } + + fun join(list: LongArray, delimeter: String?): String { + val boxed: MutableList = ArrayList(list.size) + + for (i in list.indices) { + boxed.add(list[i]) + } + + return join(boxed, delimeter) + } + + @SafeVarargs + fun join(vararg lists: List): List { + val totalSize = Stream.of(*lists).reduce(0) { sum: Int, list: List -> sum + list.size } + val joined: MutableList = ArrayList(totalSize) + + for (list in lists) { + joined.addAll(list) + } + + return joined + } + + fun join(list: List, delimeter: String?): String { + val sb = StringBuilder() + + for (j in list.indices) { + if (j != 0) sb.append(delimeter) + sb.append(list[j]) + } + + return sb.toString() + } + + fun rightPad(value: String, length: Int): String { + if (value.length >= length) { + return value + } + + val out = StringBuilder(value) + while (out.length < length) { + out.append(" ") + } + + return out.toString() + } + + fun isEmpty(value: Array?): Boolean { + return value == null || value.size == 0 + } + + fun isEmpty(value: ComposeText?): Boolean { + return value == null || value.text == null || TextUtils.isEmpty(value.textTrimmed) + } + + fun isEmpty(collection: Collection<*>?): Boolean { + return collection == null || collection.isEmpty() + } + + fun isEmpty(charSequence: CharSequence?): Boolean { + return charSequence == null || charSequence.length == 0 + } + + fun hasItems(collection: Collection<*>?): Boolean { + return collection != null && !collection.isEmpty() + } + + fun getOrDefault(map: Map, key: K, defaultValue: V): V? { + return if (map.containsKey(key)) map[key] else defaultValue + } + + fun getFirstNonEmpty(vararg values: String?): String { + for (value in values) { + if (!value.isNullOrEmpty()) { return value } + } + return "" + } + + fun emptyIfNull(value: String?): String { + return value ?: "" + } + + fun emptyIfNull(value: CharSequence?): CharSequence { + return value ?: "" + } + + fun getBoldedString(value: String?): CharSequence { + val spanned = SpannableString(value) + spanned.setSpan( + StyleSpan(Typeface.BOLD), 0, + spanned.length, + Spannable.SPAN_EXCLUSIVE_EXCLUSIVE + ) + + return spanned + } + + fun toIsoString(bytes: ByteArray?): String { + try { + return String(bytes!!, charset(CharacterSets.MIMENAME_ISO_8859_1)) + } catch (e: UnsupportedEncodingException) { + throw AssertionError("ISO_8859_1 must be supported!") + } + } + + fun toIsoBytes(isoString: String): ByteArray { + try { + return isoString.toByteArray(charset(CharacterSets.MIMENAME_ISO_8859_1)) + } catch (e: UnsupportedEncodingException) { + throw AssertionError("ISO_8859_1 must be supported!") + } + } + + fun toUtf8Bytes(utf8String: String): ByteArray { + try { + return utf8String.toByteArray(charset(CharacterSets.MIMENAME_UTF_8)) + } catch (e: UnsupportedEncodingException) { + throw AssertionError("UTF_8 must be supported!") + } + } + + fun wait(lock: Any, timeout: Long) { + try { + (lock as Object).wait(timeout) + } catch (ie: InterruptedException) { + throw AssertionError(ie) + } + } + + fun split(source: String, delimiter: String): List { + val results = mutableListOf() + + if (TextUtils.isEmpty(source)) { + return results + } + + val elements = + source.split(delimiter.toRegex()).dropLastWhile { it.isEmpty() }.toTypedArray() + Collections.addAll(results, *elements) + + return results + } + + fun split(input: ByteArray?, firstLength: Int, secondLength: Int): Array { + val parts = arrayOfNulls(2) + + parts[0] = ByteArray(firstLength) + System.arraycopy(input, 0, parts[0], 0, firstLength) + + parts[1] = ByteArray(secondLength) + System.arraycopy(input, firstLength, parts[1], 0, secondLength) + + return parts + } + + fun combine(vararg elements: ByteArray?): ByteArray { + try { + val baos = ByteArrayOutputStream() + + for (element in elements) { + baos.write(element) + } + + return baos.toByteArray() + } catch (e: IOException) { + throw AssertionError(e) + } + } + + fun trim(input: ByteArray?, length: Int): ByteArray { + val result = ByteArray(length) + System.arraycopy(input, 0, result, 0, result.size) + + return result + } + + fun getSecretBytes(size: Int): ByteArray { + return getSecretBytes(SecureRandom(), size) + } + + fun getSecretBytes(secureRandom: SecureRandom, size: Int): ByteArray { + val secret = ByteArray(size) + secureRandom.nextBytes(secret) + return secret + } + + fun getRandomElement(elements: Array): T { + return elements[SecureRandom().nextInt(elements.size)] + } + + fun getRandomElement(elements: List): T { + return elements[SecureRandom().nextInt(elements.size)] + } + + fun equals(a: Any?, b: Any?): Boolean { + return a === b || (a != null && a == b) + } + + fun hashCode(vararg objects: Any?): Int { + return objects.contentHashCode() + } + + fun uri(uri: String?): Uri? { + return if (uri == null) null + else Uri.parse(uri) + } + + @TargetApi(VERSION_CODES.KITKAT) + fun isLowMemory(context: Context): Boolean { + val activityManager = context.getSystemService(Context.ACTIVITY_SERVICE) as ActivityManager + + return (VERSION.SDK_INT >= VERSION_CODES.KITKAT && activityManager.isLowRamDevice) || + activityManager.largeMemoryClass <= 64 + } + + fun clamp(value: Int, min: Int, max: Int): Int { + return min(max(value.toDouble(), min.toDouble()), max.toDouble()).toInt() + } + + fun clamp(value: Long, min: Long, max: Long): Long { + return min(max(value.toDouble(), min.toDouble()), max.toDouble()).toLong() + } + + fun clamp(value: Float, min: Float, max: Float): Float { + return min(max(value.toDouble(), min.toDouble()), max.toDouble()).toFloat() + } + + /** + * Returns half of the difference between the given length, and the length when scaled by the + * given scale. + */ + fun halfOffsetFromScale(length: Int, scale: Float): Float { + val scaledLength = length * scale + return (length - scaledLength) / 2 + } + + fun readTextFromClipboard(context: Context): String? { + run { + val clipboardManager = + context.getSystemService(Context.CLIPBOARD_SERVICE) as ClipboardManager + return if (clipboardManager.hasPrimaryClip() && clipboardManager.primaryClip!!.itemCount > 0) { + clipboardManager.primaryClip!!.getItemAt(0).text.toString() + } else { + null + } + } + } + + fun writeTextToClipboard(context: Context, text: String) { + writeTextToClipboard(context, context.getString(R.string.app_name), text) + } + + fun writeTextToClipboard(context: Context, label: String, text: String) { + val clipboard = context.getSystemService(Context.CLIPBOARD_SERVICE) as ClipboardManager + val clip = ClipData.newPlainText(label, text) + clipboard.setPrimaryClip(clip) + } + + fun toIntExact(value: Long): Int { + if (value.toInt().toLong() != value) { + throw ArithmeticException("integer overflow") + } + return value.toInt() + } + + fun isEquals(first: Long?, second: Long): Boolean { + return first != null && first == second + } + + @SafeVarargs + fun concatenatedList(vararg items: Collection): List { + val concat: MutableList = ArrayList( + Stream.of(*items).reduce(0) { sum: Int, list: Collection -> sum + list.size }) + + for (list in items) { + concat.addAll(list) + } + + return concat + } + + fun isLong(value: String): Boolean { + try { + value.toLong() + return true + } catch (e: NumberFormatException) { + return false + } + } + + fun parseInt(integer: String, defaultValue: Int): Int { + return try { + integer.toInt() + } catch (e: NumberFormatException) { + defaultValue + } + } + + // Method to determine if we're currently in a left-to-right or right-to-left language like Arabic + fun usingRightToLeftLanguage(context: Context): Boolean { + val config = context.resources.configuration + return config.layoutDirection == View.LAYOUT_DIRECTION_RTL + } + + // Method to determine if we're currently in a left-to-right or right-to-left language like Arabic + fun usingLeftToRightLanguage(context: Context): Boolean { + val config = context.resources.configuration + return config.layoutDirection == View.LAYOUT_DIRECTION_LTR + } +} \ No newline at end of file diff --git a/app/src/main/java/org/thoughtcrime/securesms/mms/Slide.java b/app/src/main/java/org/thoughtcrime/securesms/mms/Slide.java deleted file mode 100644 index 2c92121cf2..0000000000 --- a/app/src/main/java/org/thoughtcrime/securesms/mms/Slide.java +++ /dev/null @@ -1,209 +0,0 @@ -/** - * Copyright (C) 2011 Whisper Systems - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * 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 . - */ -package org.thoughtcrime.securesms.mms; - -import static org.session.libsession.utilities.StringSubstitutionConstants.EMOJI_KEY; - -import android.content.Context; -import android.content.res.Resources.Theme; -import android.net.Uri; -import androidx.annotation.DrawableRes; -import androidx.annotation.NonNull; -import androidx.annotation.Nullable; -import com.squareup.phrase.Phrase; -import java.security.SecureRandom; -import network.loki.messenger.R; -import org.session.libsession.messaging.sending_receiving.attachments.Attachment; -import org.session.libsession.messaging.sending_receiving.attachments.AttachmentTransferProgress; -import org.session.libsession.messaging.sending_receiving.attachments.UriAttachment; -import org.session.libsession.utilities.Util; -import org.session.libsignal.utilities.guava.Optional; -import org.thoughtcrime.securesms.util.MediaUtil; - -public abstract class Slide { - - protected final Attachment attachment; - protected final Context context; - - public Slide(@NonNull Context context, @NonNull Attachment attachment) { - this.context = context; - this.attachment = attachment; - } - - public String getContentType() { - return attachment.getContentType(); - } - - @Nullable - public Uri getUri() { - return attachment.getDataUri(); - } - - @Nullable - public Uri getThumbnailUri() { - return attachment.getThumbnailUri(); - } - - @NonNull - public Optional getBody() { - String attachmentString = context.getString(R.string.attachment); - - if (MediaUtil.isAudio(attachment)) { - // A missing file name is the legacy way to determine if an audio attachment is - // a voice note vs. other arbitrary audio attachments. - if (attachment.isVoiceNote() || attachment.getFileName() == null || - attachment.getFileName().isEmpty()) { - attachmentString = context.getString(R.string.attachment_type_voice_message); - return Optional.fromNullable("🎤 " + attachmentString); - } - } - String txt = Phrase.from(context, R.string.attachmentsNotification) - .put(EMOJI_KEY, emojiForMimeType()) - .format().toString(); - return Optional.fromNullable(txt); - } - - private String emojiForMimeType() { - if (MediaUtil.isImage(attachment)) { - return "📷"; - } else if (MediaUtil.isVideo(attachment)) { - return "🎥"; - } else if (MediaUtil.isAudio(attachment)) { - return "🎧"; - } else if (MediaUtil.isFile(attachment)) { - return "📎"; - } else { - return "🎡"; // `isGif` - } - } - - @NonNull - public Optional getCaption() { - return Optional.fromNullable(attachment.getCaption()); - } - - @NonNull - public Optional getFileName() { - return Optional.fromNullable(attachment.getFileName()); - } - - @Nullable - public String getFastPreflightId() { - return attachment.getFastPreflightId(); - } - - public long getFileSize() { - return attachment.getSize(); - } - - public boolean hasImage() { - return false; - } - - public boolean hasVideo() { - return false; - } - - public boolean hasAudio() { - return false; - } - - public boolean hasDocument() { - return false; - } - - public @NonNull String getContentDescription() { return ""; } - - public @NonNull Attachment asAttachment() { - return attachment; - } - - public boolean isInProgress() { - return attachment.isInProgress(); - } - - public boolean isPendingDownload() { - return getTransferState() == AttachmentTransferProgress.TRANSFER_PROGRESS_FAILED || - getTransferState() == AttachmentTransferProgress.TRANSFER_PROGRESS_PENDING; - } - - public int getTransferState() { - return attachment.getTransferState(); - } - - public @DrawableRes int getPlaceholderRes(Theme theme) { - throw new AssertionError("getPlaceholderRes() called for non-drawable slide"); - } - - public boolean hasPlaceholder() { - return false; - } - - public boolean hasPlayOverlay() { - return false; - } - - protected static Attachment constructAttachmentFromUri(@NonNull Context context, - @NonNull Uri uri, - @NonNull String defaultMime, - long size, - int width, - int height, - boolean hasThumbnail, - @Nullable String fileName, - @Nullable String caption, - boolean voiceNote, - boolean quote) - { - String resolvedType = Optional.fromNullable(MediaUtil.getMimeType(context, uri)).or(defaultMime); - String fastPreflightId = String.valueOf(new SecureRandom().nextLong()); - return new UriAttachment(uri, - hasThumbnail ? uri : null, - resolvedType, - AttachmentTransferProgress.TRANSFER_PROGRESS_STARTED, - size, - width, - height, - fileName, - fastPreflightId, - voiceNote, - quote, - caption); - } - - @Override - public boolean equals(Object other) { - if (other == null) return false; - if (!(other instanceof Slide)) return false; - - Slide that = (Slide)other; - - return Util.equals(this.getContentType(), that.getContentType()) && - this.hasAudio() == that.hasAudio() && - this.hasImage() == that.hasImage() && - this.hasVideo() == that.hasVideo() && - this.getTransferState() == that.getTransferState() && - Util.equals(this.getUri(), that.getUri()) && - Util.equals(this.getThumbnailUri(), that.getThumbnailUri()); - } - - @Override - public int hashCode() { - return Util.hashCode(getContentType(), hasAudio(), hasImage(), - hasVideo(), getUri(), getThumbnailUri(), getTransferState()); - } -} diff --git a/app/src/main/java/org/thoughtcrime/securesms/mms/Slide.kt b/app/src/main/java/org/thoughtcrime/securesms/mms/Slide.kt new file mode 100644 index 0000000000..e284c1ce22 --- /dev/null +++ b/app/src/main/java/org/thoughtcrime/securesms/mms/Slide.kt @@ -0,0 +1,180 @@ +/** + * Copyright (C) 2011 Whisper Systems + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * 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 //www.gnu.org/licenses/>. + */ +package org.thoughtcrime.securesms.mms + +import android.content.Context +import android.content.res.Resources +import android.net.Uri +import androidx.annotation.DrawableRes +import com.squareup.phrase.Phrase +import java.security.SecureRandom +import network.loki.messenger.R +import org.session.libsession.messaging.sending_receiving.attachments.Attachment +import org.session.libsession.messaging.sending_receiving.attachments.AttachmentTransferProgress +import org.session.libsession.messaging.sending_receiving.attachments.UriAttachment +import org.session.libsession.utilities.StringSubstitutionConstants.EMOJI_KEY +import org.session.libsession.utilities.Util.equals +import org.session.libsession.utilities.Util.hashCode +import org.session.libsignal.utilities.guava.Optional +import org.thoughtcrime.securesms.conversation.v2.Util +import org.thoughtcrime.securesms.util.MediaUtil + +abstract class Slide(@JvmField protected val context: Context, protected val attachment: Attachment) { + val contentType: String + get() = attachment.contentType + + val uri: Uri? + get() = attachment.dataUri + + open val thumbnailUri: Uri? + get() = attachment.thumbnailUri + + val body: Optional + get() { + if (MediaUtil.isAudio(attachment)) { + // A missing file name is the legacy way to determine if an audio attachment is + // a voice note vs. other arbitrary audio attachments. + if (attachment.isVoiceNote || attachment.fileName.isNullOrEmpty()) { + val baseString = context.getString(R.string.attachment_type_voice_message) + val languageIsLTR = Util.usingLeftToRightLanguage(context) + val attachmentString = if (languageIsLTR) { + "🎙 $baseString" + } else { + "$baseString 🎙" + } + return Optional.fromNullable(attachmentString) + } + } + val txt = Phrase.from(context, R.string.attachmentsNotification) + .put(EMOJI_KEY, emojiForMimeType()) + .format().toString() + return Optional.fromNullable(txt) + } + + private fun emojiForMimeType(): String { + return if (MediaUtil.isGif(attachment)) { + "🎡" + } else if (MediaUtil.isImage(attachment)) { + "📷" + } else if (MediaUtil.isVideo(attachment)) { + "🎥" + } else if (MediaUtil.isAudio(attachment)) { + "🎧" + } else if (MediaUtil.isFile(attachment)) { + "📎" + } else { + // We don't provide emojis for other mime-types such as VCARD + "" + } + } + + val caption: Optional + get() = Optional.fromNullable(attachment.caption) + + val fileName: Optional + get() = Optional.fromNullable(attachment.fileName) + + val fastPreflightId: String? + get() = attachment.fastPreflightId + + val fileSize: Long + get() = attachment.size + + open fun hasImage(): Boolean { return false } + + open fun hasVideo(): Boolean { return false } + + open fun hasAudio(): Boolean { return false } + + open fun hasDocument(): Boolean { return false } + + open val contentDescription: String + get() = "" + + fun asAttachment(): Attachment { return attachment } + + val isInProgress: Boolean + get() = attachment.isInProgress + + val isPendingDownload: Boolean + get() = transferState == AttachmentTransferProgress.TRANSFER_PROGRESS_FAILED || + transferState == AttachmentTransferProgress.TRANSFER_PROGRESS_PENDING + + val transferState: Int + get() = attachment.transferState + + @DrawableRes + open fun getPlaceholderRes(theme: Resources.Theme?): Int { + throw AssertionError("getPlaceholderRes() called for non-drawable slide") + } + + open fun hasPlaceholder(): Boolean { return false } + + open fun hasPlayOverlay(): Boolean { return false } + + override fun equals(other: Any?): Boolean { + if (other == null) return false + if (other !is Slide) return false + + return (equals(this.contentType, other.contentType) && + hasAudio() == other.hasAudio() && + hasImage() == other.hasImage() && + hasVideo() == other.hasVideo()) && + this.transferState == other.transferState && + equals(this.uri, other.uri) && + equals(this.thumbnailUri, other.thumbnailUri) + } + + override fun hashCode(): Int { + return hashCode(contentType, hasAudio(), hasImage(), hasVideo(), uri, thumbnailUri, transferState) + } + + companion object { + @JvmStatic + protected fun constructAttachmentFromUri( + context: Context, + uri: Uri, + defaultMime: String, + size: Long, + width: Int, + height: Int, + hasThumbnail: Boolean, + fileName: String?, + caption: String?, + voiceNote: Boolean, + quote: Boolean + ): Attachment { + val resolvedType = + Optional.fromNullable(MediaUtil.getMimeType(context, uri)).or(defaultMime) + val fastPreflightId = SecureRandom().nextLong().toString() + return UriAttachment( + uri, + if (hasThumbnail) uri else null, + resolvedType!!, + AttachmentTransferProgress.TRANSFER_PROGRESS_STARTED, + size, + width, + height, + fileName, + fastPreflightId, + voiceNote, + quote, + caption + ) + } + } +} \ No newline at end of file