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 1/3] 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 From c31e89a13de589636f2c4ecfd16db517723f4be6 Mon Sep 17 00:00:00 2001 From: AL-Session <160798022+AL-Session@users.noreply.github.com> Date: Thu, 18 Jul 2024 13:34:07 +1000 Subject: [PATCH 2/3] SES2405 - Mms Notification Adjustment (#1551) * Fixes implemented * Undid Java to Kotlin class conversion From 9e2b24f7b9f0e8a0caf3aa6b0a6f3fe70a1073fd Mon Sep 17 00:00:00 2001 From: ThomasSession Date: Thu, 18 Jul 2024 16:32:03 +1000 Subject: [PATCH 3/3] Feature/compose cleanup (#1543) * Moving color files * Moving theme classes into their own package * Only obtain new theme when required * UI Tweaks Using the proper color for danger as opposed to one hardcoded color Reusing BlackAlpha40 Using the right delete icon in settings * matching figma * Matching signature across buttons * Dialogs do not have a 'x' button by default * Updated typography Updated the typography in an composition local so it can be accessed from anyehere in compose and matching the figma declarations. * Centered text in control messages * Fixing new color access for action items * renaming spacing to be more general * Unifying dimensions and rectifying design inconsistencies Unifying spacing values and reusing common ones Identified spacing issues and inconsistencies in design and figma * Rounded corners for the new conversation sheet * Rounding sheets corners * Spacing fixes and UI consolidating Discussed with QA to make sure the 'new message' screen should indeed behave as the other screens and use disabled state instead of disappearing * Disappearing messages tweaks Re-using our new radio buttons in disappearing messages Tweaking UI to match designs * Cleaning up spaces * Migrating to Material3 * Fixing UI issues found * PR feedbacks --------- Co-authored-by: bemusementpark --- app/build.gradle | 2 +- .../securesms/SessionDialogBuilder.kt | 4 +- .../securesms/components/menu/ActionItem.kt | 4 +- .../components/menu/ContextMenuList.kt | 2 +- .../DisappearingMessages.kt | 3 +- .../disappearingmessages/ui/Adapter.kt | 4 +- .../ui/DisappearingMessages.kt | 32 ++- .../ui/DisappearingMessagesPreview.kt | 8 +- .../disappearingmessages/ui/UiState.kt | 8 +- .../start/home/StartConversation.kt | 39 +-- .../start/invitefriend/InviteFriend.kt | 34 +-- .../start/newmessage/NewMessage.kt | 65 ++--- .../start/newmessage/NewMessageViewModel.kt | 5 +- .../conversation/v2/ConversationActivityV2.kt | 4 +- .../v2/ConversationReactionOverlay.kt | 3 +- .../conversation/v2/MessageDetailActivity.kt | 59 +++-- .../v2/messages/VisibleMessageView.kt | 3 +- .../securesms/home/ConversationView.kt | 5 +- .../thoughtcrime/securesms/home/EmptyView.kt | 38 ++- .../securesms/home/SeedReminder.kt | 29 ++- .../messagerequests/MessageRequestsAdapter.kt | 6 +- .../OnboardingBackPressAlertDialog.kt | 2 +- .../securesms/onboarding/landing/Landing.kt | 55 ++--- .../onboarding/loadaccount/LoadAccount.kt | 27 +- .../securesms/onboarding/loading/Loading.kt | 13 +- .../MessageNotifications.kt | 50 ++-- .../onboarding/pickname/PickDisplayName.kt | 25 +- .../securesms/onboarding/ui/ContinueButton.kt | 6 +- .../securesms/preferences/QRCodeActivity.kt | 16 +- .../securesms/preferences/SettingsActivity.kt | 16 +- .../appearance/AppearanceSettingsViewModel.kt | 7 + .../recoverypassword/RecoveryPassword.kt | 47 ++-- .../RecoveryPasswordActivity.kt | 4 +- .../thoughtcrime/securesms/ui/AlertDialog.kt | 127 +++++++--- .../org/thoughtcrime/securesms/ui/Carousel.kt | 39 ++- .../thoughtcrime/securesms/ui/Components.kt | 170 ++++++------- .../thoughtcrime/securesms/ui/Dimensions.kt | 32 --- .../securesms/ui/SessionTypography.kt | 59 ----- .../org/thoughtcrime/securesms/ui/Util.kt | 1 + .../thoughtcrime/securesms/ui/color/Colors.kt | 233 ------------------ .../ui/color/ColorsFromPreferences.kt | 34 --- .../securesms/ui/components/AppBar.kt | 20 +- .../securesms/ui/components/Border.kt | 6 +- .../securesms/ui/components/Button.kt | 43 ++-- .../securesms/ui/components/ButtonStyle.kt | 31 ++- .../securesms/ui/components/ButtonType.kt | 25 +- .../components/CircularProgressIndicator.kt | 6 +- .../securesms/ui/components/QR.kt | 46 ++-- .../securesms/ui/components/QrImage.kt | 17 +- .../securesms/ui/components/RadioButton.kt | 126 +++++++++- .../securesms/ui/components/SessionTabRow.kt | 45 ++-- .../securesms/ui/components/Text.kt | 32 +-- .../{color/ColorDefs.kt => theme/Colors.kt} | 2 +- .../securesms/ui/theme/Dimensions.kt | 24 ++ .../securesms/ui/theme/SessionTypography.kt | 145 +++++++++++ .../securesms/ui/theme/ThemeColorSet.kt | 10 + .../securesms/ui/theme/ThemeColors.kt | 206 ++++++++++++++++ .../ui/theme/ThemeFromPreferences.kt | 65 +++++ .../securesms/ui/{ => theme}/Themes.kt | 66 +++-- ...tton_destructive.xml => button_danger.xml} | 2 +- .../state_list_call_action_mic_background.xml | 2 +- ...ml => danger_dialog_button_background.xml} | 4 +- ... danger_dialog_text_button_background.xml} | 0 ...nger_outline_button_medium_background.xml} | 4 +- app/src/main/res/drawable/ic_clear_data.xml | 11 - app/src/main/res/drawable/ic_delete_24.xml | 2 +- .../main/res/drawable/sheet_rounded_bg.xml | 5 + .../res/layout/activity_blocked_contacts.xml | 2 +- .../res/layout/activity_conversation_v2.xml | 6 +- .../res/layout/activity_message_requests.xml | 2 +- app/src/main/res/layout/activity_webrtc.xml | 4 +- .../layout/blocked_contacts_preference.xml | 2 +- .../main/res/layout/dialog_clear_all_data.xml | 2 +- app/src/main/res/layout/dialog_send_seed.xml | 2 +- .../res/layout/fragment_call_bottom_sheet.xml | 2 +- .../main/res/layout/fragment_create_group.xml | 2 +- .../fragment_delete_message_bottom_sheet.xml | 4 +- .../res/layout/fragment_join_community.xml | 2 +- .../fragment_modal_url_bottom_sheet.xml | 2 +- .../main/res/layout/fragment_new_message.xml | 2 +- .../layout/layout_conversation_block_icon.xml | 4 +- ..._sheet_dialog_fragment_recycler_header.xml | 2 +- .../main/res/layout/view_control_message.xml | 1 + .../res/layout/view_input_bar_recording.xml | 6 +- app/src/main/res/values/attrs.xml | 1 + app/src/main/res/values/colors.xml | 1 - app/src/main/res/values/styles.xml | 12 +- app/src/main/res/values/themes.xml | 7 +- .../DisappearingMessagesViewModelTest.kt | 32 +-- libsession/src/main/res/values/colors.xml | 1 - 90 files changed, 1308 insertions(+), 1058 deletions(-) delete mode 100644 app/src/main/java/org/thoughtcrime/securesms/ui/Dimensions.kt delete mode 100644 app/src/main/java/org/thoughtcrime/securesms/ui/SessionTypography.kt delete mode 100644 app/src/main/java/org/thoughtcrime/securesms/ui/color/Colors.kt delete mode 100644 app/src/main/java/org/thoughtcrime/securesms/ui/color/ColorsFromPreferences.kt rename app/src/main/java/org/thoughtcrime/securesms/ui/{color/ColorDefs.kt => theme/Colors.kt} (97%) create mode 100644 app/src/main/java/org/thoughtcrime/securesms/ui/theme/Dimensions.kt create mode 100644 app/src/main/java/org/thoughtcrime/securesms/ui/theme/SessionTypography.kt create mode 100644 app/src/main/java/org/thoughtcrime/securesms/ui/theme/ThemeColorSet.kt create mode 100644 app/src/main/java/org/thoughtcrime/securesms/ui/theme/ThemeColors.kt create mode 100644 app/src/main/java/org/thoughtcrime/securesms/ui/theme/ThemeFromPreferences.kt rename app/src/main/java/org/thoughtcrime/securesms/ui/{ => theme}/Themes.kt (55%) rename app/src/main/res/color/{button_destructive.xml => button_danger.xml} (81%) rename app/src/main/res/drawable/{destructive_dialog_button_background.xml => danger_dialog_button_background.xml} (78%) rename app/src/main/res/drawable/{destructive_dialog_text_button_background.xml => danger_dialog_text_button_background.xml} (100%) rename app/src/main/res/drawable/{destructive_outline_button_medium_background.xml => danger_outline_button_medium_background.xml} (79%) delete mode 100644 app/src/main/res/drawable/ic_clear_data.xml create mode 100644 app/src/main/res/drawable/sheet_rounded_bg.xml diff --git a/app/build.gradle b/app/build.gradle index c56a2c8e3d..4cd08255d6 100644 --- a/app/build.gradle +++ b/app/build.gradle @@ -381,7 +381,7 @@ dependencies { implementation "androidx.compose.ui:ui-tooling:$composeVersion" implementation "androidx.compose.runtime:runtime-livedata:$composeVersion" implementation "androidx.compose.foundation:foundation-layout:$composeVersion" - implementation "androidx.compose.material:material:$composeVersion" + implementation "androidx.compose.material3:material3:1.2.1" androidTestImplementation "androidx.compose.ui:ui-test-junit4-android:$composeVersion" debugImplementation "androidx.compose.ui:ui-test-manifest:$composeVersion" diff --git a/app/src/main/java/org/thoughtcrime/securesms/SessionDialogBuilder.kt b/app/src/main/java/org/thoughtcrime/securesms/SessionDialogBuilder.kt index fa9cb2e475..71e04230f2 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/SessionDialogBuilder.kt +++ b/app/src/main/java/org/thoughtcrime/securesms/SessionDialogBuilder.kt @@ -114,14 +114,14 @@ class SessionDialogBuilder(val context: Context) { options, ) { dialog, it -> onSelect(it); dialog.dismiss() } - fun destructiveButton( + fun dangerButton( @StringRes text: Int, @StringRes contentDescription: Int = text, listener: () -> Unit = {} ) = button( text, contentDescription, - R.style.Widget_Session_Button_Dialog_DestructiveText, + R.style.Widget_Session_Button_Dialog_DangerText, ) { listener() } fun okButton(listener: (() -> Unit) = {}) = button(android.R.string.ok) { listener() } diff --git a/app/src/main/java/org/thoughtcrime/securesms/components/menu/ActionItem.kt b/app/src/main/java/org/thoughtcrime/securesms/components/menu/ActionItem.kt index 700534fad1..af32b7f50f 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/components/menu/ActionItem.kt +++ b/app/src/main/java/org/thoughtcrime/securesms/components/menu/ActionItem.kt @@ -2,7 +2,7 @@ package org.thoughtcrime.securesms.components.menu import android.content.Context import androidx.annotation.AttrRes -import androidx.annotation.ColorRes +import androidx.annotation.ColorInt /** * Represents an action to be rendered @@ -13,5 +13,5 @@ data class ActionItem( val action: Runnable, val contentDescription: Int? = null, val subtitle: ((Context) -> CharSequence?)? = null, - @ColorRes val color: Int? = null, + @ColorInt val color: Int? = null, ) diff --git a/app/src/main/java/org/thoughtcrime/securesms/components/menu/ContextMenuList.kt b/app/src/main/java/org/thoughtcrime/securesms/components/menu/ContextMenuList.kt index 69dec0cdd6..7b92e505c6 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/components/menu/ContextMenuList.kt +++ b/app/src/main/java/org/thoughtcrime/securesms/components/menu/ContextMenuList.kt @@ -78,7 +78,7 @@ class ContextMenuList(recyclerView: RecyclerView, onItemClick: () -> Unit) { override fun bind(model: DisplayItem) { val item = model.item - val color = item.color?.let { ContextCompat.getColor(context, it) } + val color = item.color if (item.iconRes > 0) { val typedValue = TypedValue() diff --git a/app/src/main/java/org/thoughtcrime/securesms/conversation/disappearingmessages/DisappearingMessages.kt b/app/src/main/java/org/thoughtcrime/securesms/conversation/disappearingmessages/DisappearingMessages.kt index d336c967ce..38da11ae24 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/conversation/disappearingmessages/DisappearingMessages.kt +++ b/app/src/main/java/org/thoughtcrime/securesms/conversation/disappearingmessages/DisappearingMessages.kt @@ -14,7 +14,6 @@ import org.session.libsession.utilities.ExpirationUtil import org.session.libsession.utilities.SSKEnvironment.MessageExpirationManagerProtocol import org.session.libsession.utilities.TextSecurePreferences import org.session.libsession.utilities.getExpirationTypeDisplayValue -import org.thoughtcrime.securesms.database.ThreadDatabase import org.thoughtcrime.securesms.database.model.MessageRecord import org.thoughtcrime.securesms.showSessionDialog import org.thoughtcrime.securesms.util.ConfigurationMessageUtilities @@ -57,7 +56,7 @@ class DisappearingMessages @Inject constructor( context.getExpirationTypeDisplayValue(message.isNotDisappearAfterRead) ) }) - destructiveButton( + dangerButton( text = if (message.expiresIn == 0L) R.string.dialog_disappearing_messages_follow_setting_confirm else R.string.dialog_disappearing_messages_follow_setting_set, contentDescription = if (message.expiresIn == 0L) R.string.AccessibilityId_confirm else R.string.AccessibilityId_set_button ) { diff --git a/app/src/main/java/org/thoughtcrime/securesms/conversation/disappearingmessages/ui/Adapter.kt b/app/src/main/java/org/thoughtcrime/securesms/conversation/disappearingmessages/ui/Adapter.kt index 6ddc28c688..d78d33a2f9 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/conversation/disappearingmessages/ui/Adapter.kt +++ b/app/src/main/java/org/thoughtcrime/securesms/conversation/disappearingmessages/ui/Adapter.kt @@ -13,8 +13,8 @@ import kotlin.time.Duration.Companion.seconds fun State.toUiState() = UiState( cards = listOfNotNull( - typeOptions()?.let { ExpiryOptionsCard(GetString(R.string.activity_disappearing_messages_delete_type), it) }, - timeOptions()?.let { ExpiryOptionsCard(GetString(R.string.activity_disappearing_messages_timer), it) } + typeOptions()?.let { ExpiryOptionsCardData(GetString(R.string.activity_disappearing_messages_delete_type), it) }, + timeOptions()?.let { ExpiryOptionsCardData(GetString(R.string.activity_disappearing_messages_timer), it) } ), showGroupFooter = isGroup && isNewConfigEnabled, showSetButton = isSelfAdmin diff --git a/app/src/main/java/org/thoughtcrime/securesms/conversation/disappearingmessages/ui/DisappearingMessages.kt b/app/src/main/java/org/thoughtcrime/securesms/conversation/disappearingmessages/ui/DisappearingMessages.kt index 51ac1d445e..ae17e6a09b 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/conversation/disappearingmessages/ui/DisappearingMessages.kt +++ b/app/src/main/java/org/thoughtcrime/securesms/conversation/disappearingmessages/ui/DisappearingMessages.kt @@ -3,11 +3,13 @@ package org.thoughtcrime.securesms.conversation.disappearingmessages.ui import androidx.compose.foundation.layout.Arrangement import androidx.compose.foundation.layout.Box import androidx.compose.foundation.layout.Column +import androidx.compose.foundation.layout.Spacer import androidx.compose.foundation.layout.fillMaxWidth +import androidx.compose.foundation.layout.height import androidx.compose.foundation.layout.padding import androidx.compose.foundation.rememberScrollState import androidx.compose.foundation.verticalScroll -import androidx.compose.material.Text +import androidx.compose.material3.Text import androidx.compose.runtime.Composable import androidx.compose.ui.Alignment import androidx.compose.ui.Modifier @@ -18,15 +20,15 @@ import androidx.compose.ui.unit.dp import network.loki.messenger.R import network.loki.messenger.libsession_util.util.ExpiryMode import org.thoughtcrime.securesms.ui.Callbacks -import org.thoughtcrime.securesms.ui.LocalDimensions +import org.thoughtcrime.securesms.ui.theme.LocalDimensions import org.thoughtcrime.securesms.ui.NoOpCallbacks import org.thoughtcrime.securesms.ui.OptionsCard import org.thoughtcrime.securesms.ui.RadioOption -import org.thoughtcrime.securesms.ui.color.LocalColors +import org.thoughtcrime.securesms.ui.theme.LocalColors import org.thoughtcrime.securesms.ui.components.SlimOutlineButton import org.thoughtcrime.securesms.ui.contentDescription -import org.thoughtcrime.securesms.ui.extraSmall import org.thoughtcrime.securesms.ui.fadingEdges +import org.thoughtcrime.securesms.ui.theme.LocalType typealias ExpiryCallbacks = Callbacks typealias ExpiryRadioOption = RadioOption @@ -39,26 +41,32 @@ fun DisappearingMessages( ) { val scrollState = rememberScrollState() - Column(modifier = modifier.padding(horizontal = LocalDimensions.current.margin)) { + Column(modifier = modifier.padding(horizontal = LocalDimensions.current.spacing)) { Box(modifier = Modifier.weight(1f)) { Column( modifier = Modifier - .padding(bottom = 20.dp) + .padding(vertical = LocalDimensions.current.spacing) .verticalScroll(scrollState) .fadingEdges(scrollState), - verticalArrangement = Arrangement.spacedBy(LocalDimensions.current.smallItemSpacing) ) { - state.cards.forEach { - OptionsCard(it, callbacks) + state.cards.forEachIndexed { index, option -> + OptionsCard(option, callbacks) + + // add spacing if not the last item + if(index != state.cards.lastIndex){ + Spacer(modifier = Modifier.height(LocalDimensions.current.spacing)) + } } if (state.showGroupFooter) Text( text = stringResource(R.string.activity_disappearing_messages_group_footer), - style = extraSmall, + style = LocalType.current.extraSmall, fontWeight = FontWeight(400), color = LocalColors.current.textSecondary, textAlign = TextAlign.Center, - modifier = Modifier.fillMaxWidth() + modifier = Modifier + .fillMaxWidth() + .padding(top = LocalDimensions.current.xsSpacing) ) } } @@ -68,7 +76,7 @@ fun DisappearingMessages( modifier = Modifier .contentDescription(R.string.AccessibilityId_set_button) .align(Alignment.CenterHorizontally) - .padding(bottom = 20.dp), + .padding(bottom = LocalDimensions.current.spacing), onClick = callbacks::onSetClick ) } diff --git a/app/src/main/java/org/thoughtcrime/securesms/conversation/disappearingmessages/ui/DisappearingMessagesPreview.kt b/app/src/main/java/org/thoughtcrime/securesms/conversation/disappearingmessages/ui/DisappearingMessagesPreview.kt index 20f1821e4c..d043cc314f 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/conversation/disappearingmessages/ui/DisappearingMessagesPreview.kt +++ b/app/src/main/java/org/thoughtcrime/securesms/conversation/disappearingmessages/ui/DisappearingMessagesPreview.kt @@ -10,9 +10,9 @@ import androidx.compose.ui.unit.dp import network.loki.messenger.libsession_util.util.ExpiryMode import org.thoughtcrime.securesms.conversation.disappearingmessages.ExpiryType import org.thoughtcrime.securesms.conversation.disappearingmessages.State -import org.thoughtcrime.securesms.ui.PreviewTheme -import org.thoughtcrime.securesms.ui.color.Colors -import org.thoughtcrime.securesms.ui.SessionColorsParameterProvider +import org.thoughtcrime.securesms.ui.theme.PreviewTheme +import org.thoughtcrime.securesms.ui.theme.ThemeColors +import org.thoughtcrime.securesms.ui.theme.SessionColorsParameterProvider @Preview(widthDp = 450, heightDp = 700) @Composable @@ -51,7 +51,7 @@ class StatePreviewParameterProvider : PreviewParameterProvider { @Preview @Composable fun PreviewThemes( - @PreviewParameter(SessionColorsParameterProvider::class) colors: Colors + @PreviewParameter(SessionColorsParameterProvider::class) colors: ThemeColors ) { PreviewTheme(colors) { DisappearingMessages( diff --git a/app/src/main/java/org/thoughtcrime/securesms/conversation/disappearingmessages/ui/UiState.kt b/app/src/main/java/org/thoughtcrime/securesms/conversation/disappearingmessages/ui/UiState.kt index 40f917427c..47159571f8 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/conversation/disappearingmessages/ui/UiState.kt +++ b/app/src/main/java/org/thoughtcrime/securesms/conversation/disappearingmessages/ui/UiState.kt @@ -5,15 +5,15 @@ import network.loki.messenger.libsession_util.util.ExpiryMode import org.thoughtcrime.securesms.ui.GetString import org.thoughtcrime.securesms.ui.RadioOption -typealias ExpiryOptionsCard = OptionsCard +typealias ExpiryOptionsCardData = OptionsCardData data class UiState( - val cards: List = emptyList(), + val cards: List = emptyList(), val showGroupFooter: Boolean = false, val showSetButton: Boolean = true ) { constructor( - vararg cards: ExpiryOptionsCard, + vararg cards: ExpiryOptionsCardData, showGroupFooter: Boolean = false, showSetButton: Boolean = true, ): this( @@ -23,7 +23,7 @@ data class UiState( ) } -data class OptionsCard( +data class OptionsCardData( val title: GetString, val options: List> ) { diff --git a/app/src/main/java/org/thoughtcrime/securesms/conversation/start/home/StartConversation.kt b/app/src/main/java/org/thoughtcrime/securesms/conversation/start/home/StartConversation.kt index 452968429e..fcf13c9cf1 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/conversation/start/home/StartConversation.kt +++ b/app/src/main/java/org/thoughtcrime/securesms/conversation/start/home/StartConversation.kt @@ -7,8 +7,9 @@ import androidx.compose.foundation.layout.height import androidx.compose.foundation.layout.padding import androidx.compose.foundation.rememberScrollState import androidx.compose.foundation.verticalScroll -import androidx.compose.material.Surface -import androidx.compose.material.Text +import androidx.compose.material3.MaterialTheme +import androidx.compose.material3.Surface +import androidx.compose.material3.Text import androidx.compose.runtime.Composable import androidx.compose.ui.Modifier import androidx.compose.ui.input.nestedscroll.nestedScroll @@ -21,23 +22,25 @@ import org.thoughtcrime.securesms.conversation.start.NullStartConversationDelega import org.thoughtcrime.securesms.conversation.start.StartConversationDelegate import org.thoughtcrime.securesms.ui.Divider import org.thoughtcrime.securesms.ui.ItemButton -import org.thoughtcrime.securesms.ui.LocalDimensions -import org.thoughtcrime.securesms.ui.PreviewTheme -import org.thoughtcrime.securesms.ui.SessionColorsParameterProvider -import org.thoughtcrime.securesms.ui.color.Colors -import org.thoughtcrime.securesms.ui.color.LocalColors +import org.thoughtcrime.securesms.ui.theme.LocalDimensions +import org.thoughtcrime.securesms.ui.theme.PreviewTheme +import org.thoughtcrime.securesms.ui.theme.SessionColorsParameterProvider +import org.thoughtcrime.securesms.ui.theme.ThemeColors +import org.thoughtcrime.securesms.ui.theme.LocalColors import org.thoughtcrime.securesms.ui.components.AppBar import org.thoughtcrime.securesms.ui.components.QrImage import org.thoughtcrime.securesms.ui.contentDescription -import org.thoughtcrime.securesms.ui.small -import org.thoughtcrime.securesms.ui.xl +import org.thoughtcrime.securesms.ui.theme.LocalType @Composable internal fun StartConversationScreen( accountId: String, delegate: StartConversationDelegate ) { - Column(modifier = Modifier.background(LocalColors.current.backgroundSecondary)) { + Column(modifier = Modifier.background( + LocalColors.current.backgroundSecondary, + shape = MaterialTheme.shapes.small + )) { AppBar(stringResource(R.string.dialog_start_conversation_title), onClose = delegate::onDialogClosePressed) Surface( modifier = Modifier.nestedScroll(rememberNestedScrollInteropConnection()), @@ -74,18 +77,18 @@ internal fun StartConversationScreen( ) Column( modifier = Modifier - .padding(horizontal = LocalDimensions.current.margin) - .padding(top = LocalDimensions.current.itemSpacing) - .padding(bottom = LocalDimensions.current.margin) + .padding(horizontal = LocalDimensions.current.spacing) + .padding(top = LocalDimensions.current.spacing) + .padding(bottom = LocalDimensions.current.spacing) ) { - Text(stringResource(R.string.accountIdYours), style = xl) - Spacer(modifier = Modifier.height(LocalDimensions.current.xxxsItemSpacing)) + Text(stringResource(R.string.accountIdYours), style = LocalType.current.xl) + Spacer(modifier = Modifier.height(LocalDimensions.current.xxsSpacing)) Text( text = stringResource(R.string.qrYoursDescription), color = LocalColors.current.textSecondary, - style = small + style = LocalType.current.small ) - Spacer(modifier = Modifier.height(LocalDimensions.current.smallItemSpacing)) + Spacer(modifier = Modifier.height(LocalDimensions.current.smallSpacing)) QrImage( string = accountId, Modifier.contentDescription(R.string.AccessibilityId_qr_code), @@ -100,7 +103,7 @@ internal fun StartConversationScreen( @Preview @Composable private fun PreviewStartConversationScreen( - @PreviewParameter(SessionColorsParameterProvider::class) colors: Colors + @PreviewParameter(SessionColorsParameterProvider::class) colors: ThemeColors ) { PreviewTheme(colors) { StartConversationScreen( diff --git a/app/src/main/java/org/thoughtcrime/securesms/conversation/start/invitefriend/InviteFriend.kt b/app/src/main/java/org/thoughtcrime/securesms/conversation/start/invitefriend/InviteFriend.kt index 256756521e..54abf66303 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/conversation/start/invitefriend/InviteFriend.kt +++ b/app/src/main/java/org/thoughtcrime/securesms/conversation/start/invitefriend/InviteFriend.kt @@ -8,23 +8,23 @@ import androidx.compose.foundation.layout.Spacer import androidx.compose.foundation.layout.fillMaxWidth import androidx.compose.foundation.layout.height import androidx.compose.foundation.layout.padding -import androidx.compose.material.Text +import androidx.compose.material3.MaterialTheme +import androidx.compose.material3.Text import androidx.compose.runtime.Composable import androidx.compose.ui.Modifier import androidx.compose.ui.res.stringResource import androidx.compose.ui.text.style.TextAlign import androidx.compose.ui.tooling.preview.Preview import network.loki.messenger.R -import org.thoughtcrime.securesms.ui.LocalDimensions -import org.thoughtcrime.securesms.ui.PreviewTheme -import org.thoughtcrime.securesms.ui.base -import org.thoughtcrime.securesms.ui.color.LocalColors +import org.thoughtcrime.securesms.ui.theme.LocalDimensions +import org.thoughtcrime.securesms.ui.theme.PreviewTheme +import org.thoughtcrime.securesms.ui.theme.LocalColors import org.thoughtcrime.securesms.ui.components.AppBar import org.thoughtcrime.securesms.ui.components.SlimOutlineButton import org.thoughtcrime.securesms.ui.components.SlimOutlineCopyButton import org.thoughtcrime.securesms.ui.components.border import org.thoughtcrime.securesms.ui.contentDescription -import org.thoughtcrime.securesms.ui.small +import org.thoughtcrime.securesms.ui.theme.LocalType @Composable internal fun InviteFriend( @@ -34,10 +34,14 @@ internal fun InviteFriend( copyPublicKey: () -> Unit = {}, sendInvitation: () -> Unit = {}, ) { - Column(modifier = Modifier.background(LocalColors.current.backgroundSecondary)) { + Column(modifier = Modifier.background( + LocalColors.current.backgroundSecondary, + shape = MaterialTheme.shapes.small + )) { AppBar(stringResource(R.string.invite_a_friend), onBack = onBack, onClose = onClose) Column( - modifier = Modifier.padding(horizontal = LocalDimensions.current.itemSpacing), + modifier = Modifier.padding(horizontal = LocalDimensions.current.spacing) + .padding(top = LocalDimensions.current.spacing), ) { Text( accountId, @@ -45,24 +49,24 @@ internal fun InviteFriend( .contentDescription(R.string.AccessibilityId_account_id) .fillMaxWidth() .border() - .padding(LocalDimensions.current.smallMargin), + .padding(LocalDimensions.current.spacing), textAlign = TextAlign.Center, - style = base + style = LocalType.current.base ) - Spacer(modifier = Modifier.height(LocalDimensions.current.xsItemSpacing)) + Spacer(modifier = Modifier.height(LocalDimensions.current.xsSpacing)) Text( stringResource(R.string.invite_your_friend_to_chat_with_you_on_session_by_sharing_your_account_id_with_them), textAlign = TextAlign.Center, - style = small, + style = LocalType.current.small, color = LocalColors.current.textSecondary, - modifier = Modifier.padding(horizontal = LocalDimensions.current.smallItemSpacing) + modifier = Modifier.padding(horizontal = LocalDimensions.current.smallSpacing) ) - Spacer(modifier = Modifier.height(LocalDimensions.current.smallItemSpacing)) + Spacer(modifier = Modifier.height(LocalDimensions.current.smallSpacing)) - Row(horizontalArrangement = spacedBy(LocalDimensions.current.smallItemSpacing)) { + Row(horizontalArrangement = spacedBy(LocalDimensions.current.smallSpacing)) { SlimOutlineButton( stringResource(R.string.share), modifier = Modifier diff --git a/app/src/main/java/org/thoughtcrime/securesms/conversation/start/newmessage/NewMessage.kt b/app/src/main/java/org/thoughtcrime/securesms/conversation/start/newmessage/NewMessage.kt index 82f9d7ae1f..97740b2a26 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/conversation/start/newmessage/NewMessage.kt +++ b/app/src/main/java/org/thoughtcrime/securesms/conversation/start/newmessage/NewMessage.kt @@ -3,17 +3,19 @@ package org.thoughtcrime.securesms.conversation.start.newmessage import androidx.compose.animation.AnimatedVisibility import androidx.compose.foundation.ExperimentalFoundationApi import androidx.compose.foundation.background -import androidx.compose.foundation.layout.Arrangement import androidx.compose.foundation.layout.Column +import androidx.compose.foundation.layout.Spacer import androidx.compose.foundation.layout.fillMaxSize import androidx.compose.foundation.layout.fillMaxWidth +import androidx.compose.foundation.layout.height import androidx.compose.foundation.layout.imePadding import androidx.compose.foundation.layout.padding import androidx.compose.foundation.pager.HorizontalPager import androidx.compose.foundation.pager.rememberPagerState import androidx.compose.foundation.rememberScrollState import androidx.compose.foundation.verticalScroll -import androidx.compose.material.Text +import androidx.compose.material3.MaterialTheme +import androidx.compose.material3.Text import androidx.compose.runtime.Composable import androidx.compose.ui.Alignment import androidx.compose.ui.Modifier @@ -23,12 +25,13 @@ import androidx.compose.ui.tooling.preview.PreviewParameter import kotlinx.coroutines.flow.Flow import kotlinx.coroutines.flow.emptyFlow import network.loki.messenger.R +import org.thoughtcrime.securesms.onboarding.ui.ContinuePrimaryOutlineButton import org.thoughtcrime.securesms.ui.LoadingArcOr -import org.thoughtcrime.securesms.ui.LocalDimensions -import org.thoughtcrime.securesms.ui.PreviewTheme -import org.thoughtcrime.securesms.ui.SessionColorsParameterProvider -import org.thoughtcrime.securesms.ui.color.Colors -import org.thoughtcrime.securesms.ui.color.LocalColors +import org.thoughtcrime.securesms.ui.theme.LocalDimensions +import org.thoughtcrime.securesms.ui.theme.PreviewTheme +import org.thoughtcrime.securesms.ui.theme.SessionColorsParameterProvider +import org.thoughtcrime.securesms.ui.theme.ThemeColors +import org.thoughtcrime.securesms.ui.theme.LocalColors import org.thoughtcrime.securesms.ui.components.AppBar import org.thoughtcrime.securesms.ui.components.BorderlessButtonWithIcon import org.thoughtcrime.securesms.ui.components.MaybeScanQrCode @@ -36,7 +39,7 @@ import org.thoughtcrime.securesms.ui.components.PrimaryOutlineButton import org.thoughtcrime.securesms.ui.components.SessionOutlinedTextField import org.thoughtcrime.securesms.ui.components.SessionTabRow import org.thoughtcrime.securesms.ui.contentDescription -import org.thoughtcrime.securesms.ui.small +import org.thoughtcrime.securesms.ui.theme.LocalType private val TITLES = listOf(R.string.enter_account_id, R.string.qrScan) @@ -52,7 +55,10 @@ internal fun NewMessage( ) { val pagerState = rememberPagerState { TITLES.size } - Column(modifier = Modifier.background(LocalColors.current.backgroundSecondary)) { + Column(modifier = Modifier.background( + LocalColors.current.backgroundSecondary, + shape = MaterialTheme.shapes.small + )) { AppBar(stringResource(R.string.messageNew), onClose = onClose, onBack = onBack) SessionTabRow(pagerState, TITLES) HorizontalPager(pagerState) { @@ -70,7 +76,6 @@ private fun EnterAccountId( callbacks: Callbacks, onHelp: () -> Unit = {} ) { - Column( modifier = Modifier .fillMaxSize() @@ -78,14 +83,13 @@ private fun EnterAccountId( .imePadding() ) { Column( - modifier = Modifier.padding(horizontal = LocalDimensions.current.xxsMargin, vertical = LocalDimensions.current.xsMargin), + modifier = Modifier.padding(vertical = LocalDimensions.current.spacing), horizontalAlignment = Alignment.CenterHorizontally, - verticalArrangement = Arrangement.spacedBy(LocalDimensions.current.xsMargin) ) { SessionOutlinedTextField( text = state.newMessageIdOrOns, modifier = Modifier - .padding(horizontal = LocalDimensions.current.smallMargin), + .padding(horizontal = LocalDimensions.current.spacing), contentDescription = "Session id input box", placeholder = stringResource(R.string.accountIdOrOnsEnter), onChange = callbacks::onChange, @@ -94,31 +98,36 @@ private fun EnterAccountId( isTextErrorColor = state.isTextErrorColor ) + Spacer(modifier = Modifier.height(LocalDimensions.current.xxxsSpacing)) + BorderlessButtonWithIcon( text = stringResource(R.string.messageNewDescription), modifier = Modifier .contentDescription(R.string.AccessibilityId_help_desk_link) - .padding(horizontal = LocalDimensions.current.margin) + .padding(horizontal = LocalDimensions.current.mediumSpacing) .fillMaxWidth(), - style = small, + style = LocalType.current.small, color = LocalColors.current.textSecondary, iconRes = R.drawable.ic_circle_question_mark, onClick = onHelp ) } - AnimatedVisibility(state.isNextButtonVisible) { - PrimaryOutlineButton( - modifier = Modifier - .align(Alignment.CenterHorizontally) - .padding(horizontal = LocalDimensions.current.largeMargin) - .fillMaxWidth() - .contentDescription(R.string.next), - onClick = callbacks::onContinue - ) { - LoadingArcOr(state.loading) { - Text(stringResource(R.string.next)) - } + Spacer(modifier = Modifier.height(LocalDimensions.current.smallSpacing)) + Spacer(Modifier.weight(2f)) + + PrimaryOutlineButton( + modifier = Modifier + .align(Alignment.CenterHorizontally) + .padding(horizontal = LocalDimensions.current.xlargeSpacing) + .padding(bottom = LocalDimensions.current.smallSpacing) + .fillMaxWidth() + .contentDescription(R.string.next), + enabled = state.isNextButtonEnabled, + onClick = callbacks::onContinue + ) { + LoadingArcOr(state.loading) { + Text(stringResource(R.string.next)) } } } @@ -127,7 +136,7 @@ private fun EnterAccountId( @Preview @Composable private fun PreviewNewMessage( - @PreviewParameter(SessionColorsParameterProvider::class) colors: Colors + @PreviewParameter(SessionColorsParameterProvider::class) colors: ThemeColors ) { PreviewTheme(colors) { NewMessage(State("z")) diff --git a/app/src/main/java/org/thoughtcrime/securesms/conversation/start/newmessage/NewMessageViewModel.kt b/app/src/main/java/org/thoughtcrime/securesms/conversation/start/newmessage/NewMessageViewModel.kt index a08282da74..65c8dd539a 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/conversation/start/newmessage/NewMessageViewModel.kt +++ b/app/src/main/java/org/thoughtcrime/securesms/conversation/start/newmessage/NewMessageViewModel.kt @@ -6,7 +6,6 @@ import androidx.lifecycle.viewModelScope import dagger.hilt.android.lifecycle.HiltViewModel import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.Job -import kotlinx.coroutines.TimeoutCancellationException import kotlinx.coroutines.channels.BufferOverflow import kotlinx.coroutines.flow.MutableSharedFlow import kotlinx.coroutines.flow.MutableStateFlow @@ -22,8 +21,6 @@ import org.session.libsignal.utilities.timeout import org.thoughtcrime.securesms.ui.GetString import java.util.concurrent.TimeoutException import javax.inject.Inject -import kotlin.coroutines.cancellation.CancellationException -import kotlin.time.Duration.Companion.seconds @HiltViewModel internal class NewMessageViewModel @Inject constructor( @@ -112,7 +109,7 @@ internal data class State( val error: GetString? = null, val loading: Boolean = false ) { - val isNextButtonVisible: Boolean get() = newMessageIdOrOns.isNotBlank() + val isNextButtonEnabled: Boolean get() = newMessageIdOrOns.isNotBlank() } internal data class Success(val publicKey: String) diff --git a/app/src/main/java/org/thoughtcrime/securesms/conversation/v2/ConversationActivityV2.kt b/app/src/main/java/org/thoughtcrime/securesms/conversation/v2/ConversationActivityV2.kt index 4aeeb66fe8..62ea2e52d4 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/conversation/v2/ConversationActivityV2.kt +++ b/app/src/main/java/org/thoughtcrime/securesms/conversation/v2/ConversationActivityV2.kt @@ -1120,7 +1120,7 @@ class ConversationActivityV2 : PassphraseRequiredActionBarActivity(), InputBarDe showSessionDialog { title(R.string.RecipientPreferenceActivity_block_this_contact_question) text(R.string.RecipientPreferenceActivity_you_will_no_longer_receive_messages_and_calls_from_this_contact) - destructiveButton(R.string.RecipientPreferenceActivity_block, R.string.AccessibilityId_block_confirm) { + dangerButton(R.string.RecipientPreferenceActivity_block, R.string.AccessibilityId_block_confirm) { viewModel.block() if (deleteThread) { viewModel.deleteThread() @@ -1163,7 +1163,7 @@ class ConversationActivityV2 : PassphraseRequiredActionBarActivity(), InputBarDe showSessionDialog { title(R.string.ConversationActivity_unblock_this_contact_question) text(R.string.ConversationActivity_you_will_once_again_be_able_to_receive_messages_and_calls_from_this_contact) - destructiveButton( + dangerButton( R.string.ConversationActivity_unblock, R.string.AccessibilityId_block_confirm ) { viewModel.unblock() } diff --git a/app/src/main/java/org/thoughtcrime/securesms/conversation/v2/ConversationReactionOverlay.kt b/app/src/main/java/org/thoughtcrime/securesms/conversation/v2/ConversationReactionOverlay.kt index 56c6540ed5..9f2046334b 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/conversation/v2/ConversationReactionOverlay.kt +++ b/app/src/main/java/org/thoughtcrime/securesms/conversation/v2/ConversationReactionOverlay.kt @@ -545,7 +545,8 @@ class ConversationReactionOverlay : FrameLayout { } // Delete message if (userCanDeleteSelectedItems(context, message, openGroup, userPublicKey, blindedPublicKey)) { - items += ActionItem(R.attr.menu_trash_icon, R.string.delete, { handleActionItemClicked(Action.DELETE) }, R.string.AccessibilityId_delete_message, message.subtitle, R.color.destructive) + items += ActionItem(R.attr.menu_trash_icon, R.string.delete, { handleActionItemClicked(Action.DELETE) }, + R.string.AccessibilityId_delete_message, message.subtitle, ThemeUtil.getThemedColor(context, R.attr.danger)) } // Ban user if (userCanBanSelectedUsers(context, message, openGroup, userPublicKey, blindedPublicKey)) { diff --git a/app/src/main/java/org/thoughtcrime/securesms/conversation/v2/MessageDetailActivity.kt b/app/src/main/java/org/thoughtcrime/securesms/conversation/v2/MessageDetailActivity.kt index d43225b5d7..9514552d28 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/conversation/v2/MessageDetailActivity.kt +++ b/app/src/main/java/org/thoughtcrime/securesms/conversation/v2/MessageDetailActivity.kt @@ -27,9 +27,9 @@ import androidx.compose.foundation.pager.rememberPagerState import androidx.compose.foundation.rememberScrollState import androidx.compose.foundation.shape.CircleShape import androidx.compose.foundation.verticalScroll -import androidx.compose.material.Icon -import androidx.compose.material.Surface -import androidx.compose.material.Text +import androidx.compose.material3.Icon +import androidx.compose.material3.Surface +import androidx.compose.material3.Text import androidx.compose.runtime.Composable import androidx.compose.runtime.collectAsState import androidx.compose.runtime.getValue @@ -63,20 +63,19 @@ import org.thoughtcrime.securesms.ui.CellWithPaddingAndMargin import org.thoughtcrime.securesms.ui.Divider import org.thoughtcrime.securesms.ui.GetString import org.thoughtcrime.securesms.ui.HorizontalPagerIndicator -import org.thoughtcrime.securesms.ui.ItemButton import org.thoughtcrime.securesms.ui.LargeItemButton -import org.thoughtcrime.securesms.ui.LocalDimensions -import org.thoughtcrime.securesms.ui.PreviewTheme -import org.thoughtcrime.securesms.ui.SessionColorsParameterProvider +import org.thoughtcrime.securesms.ui.theme.LocalDimensions +import org.thoughtcrime.securesms.ui.theme.PreviewTheme +import org.thoughtcrime.securesms.ui.theme.SessionColorsParameterProvider import org.thoughtcrime.securesms.ui.TitledText -import org.thoughtcrime.securesms.ui.base -import org.thoughtcrime.securesms.ui.baseBold -import org.thoughtcrime.securesms.ui.baseMonospace -import org.thoughtcrime.securesms.ui.color.Colors -import org.thoughtcrime.securesms.ui.color.LocalColors -import org.thoughtcrime.securesms.ui.color.blackAlpha40 -import org.thoughtcrime.securesms.ui.color.destructiveButtonColors +import org.thoughtcrime.securesms.ui.theme.ThemeColors +import org.thoughtcrime.securesms.ui.theme.LocalColors +import org.thoughtcrime.securesms.ui.theme.blackAlpha40 +import org.thoughtcrime.securesms.ui.theme.dangerButtonColors import org.thoughtcrime.securesms.ui.setComposeContent +import org.thoughtcrime.securesms.ui.theme.LocalType +import org.thoughtcrime.securesms.ui.theme.bold +import org.thoughtcrime.securesms.ui.theme.monospace import javax.inject.Inject @AndroidEntryPoint @@ -152,12 +151,12 @@ fun MessageDetails( Column( modifier = Modifier .verticalScroll(rememberScrollState()) - .padding(vertical = LocalDimensions.current.smallItemSpacing), - verticalArrangement = Arrangement.spacedBy(LocalDimensions.current.smallItemSpacing) + .padding(vertical = LocalDimensions.current.smallSpacing), + verticalArrangement = Arrangement.spacedBy(LocalDimensions.current.smallSpacing) ) { state.record?.let { message -> AndroidView( - modifier = Modifier.padding(horizontal = LocalDimensions.current.margin), + modifier = Modifier.padding(horizontal = LocalDimensions.current.spacing), factory = { ViewVisibleMessageContentBinding.inflate(LayoutInflater.from(it)).mainContainerConstraint.apply { bind( @@ -193,7 +192,7 @@ fun CellMetadata( state.apply { if (listOfNotNull(sent, received, error, senderInfo).isEmpty()) return CellWithPaddingAndMargin { - Column(verticalArrangement = Arrangement.spacedBy(LocalDimensions.current.smallItemSpacing)) { + Column(verticalArrangement = Arrangement.spacedBy(LocalDimensions.current.smallSpacing)) { TitledText(sent) TitledText(received) TitledErrorText(error) @@ -237,7 +236,7 @@ fun CellButtons( LargeItemButton( R.string.delete, R.drawable.ic_message_details__trash, - colors = destructiveButtonColors(), + colors = dangerButtonColors(), onClick = onDelete ) } @@ -251,7 +250,7 @@ fun Carousel(attachments: List, onClick: (Int) -> Unit) { val pagerState = rememberPagerState { attachments.size } - Column(verticalArrangement = Arrangement.spacedBy(LocalDimensions.current.smallItemSpacing)) { + Column(verticalArrangement = Arrangement.spacedBy(LocalDimensions.current.smallSpacing)) { Row { CarouselPrevButton(pagerState) Box(modifier = Modifier.weight(1f)) { @@ -260,7 +259,7 @@ fun Carousel(attachments: List, onClick: (Int) -> Unit) { ExpandButton( modifier = Modifier .align(Alignment.BottomEnd) - .padding(LocalDimensions.current.xxsItemSpacing) + .padding(LocalDimensions.current.xxsSpacing) ) { onClick(pagerState.currentPage) } } CarouselNextButton(pagerState) @@ -313,7 +312,7 @@ fun ExpandButton(modifier: Modifier = Modifier, onClick: () -> Unit) { @Preview @Composable fun PreviewMessageDetails( - @PreviewParameter(SessionColorsParameterProvider::class) colors: Colors + @PreviewParameter(SessionColorsParameterProvider::class) colors: ThemeColors ) { PreviewTheme(colors) { MessageDetails( @@ -340,8 +339,8 @@ fun FileDetails(fileDetails: List) { Cell { FlowRow( - modifier = Modifier.padding(horizontal = LocalDimensions.current.xsItemSpacing, vertical = LocalDimensions.current.itemSpacing), - verticalArrangement = Arrangement.spacedBy(LocalDimensions.current.smallItemSpacing) + modifier = Modifier.padding(horizontal = LocalDimensions.current.xsSpacing, vertical = LocalDimensions.current.spacing), + verticalArrangement = Arrangement.spacedBy(LocalDimensions.current.smallSpacing) ) { fileDetails.forEach { BoxWithConstraints { @@ -349,7 +348,7 @@ fun FileDetails(fileDetails: List) { it, modifier = Modifier .widthIn(min = maxWidth.div(2)) - .padding(horizontal = LocalDimensions.current.xsItemSpacing) + .padding(horizontal = LocalDimensions.current.xsSpacing) .width(IntrinsicSize.Max) ) } @@ -362,7 +361,7 @@ fun FileDetails(fileDetails: List) { fun TitledErrorText(titledText: TitledText?) { TitledText( titledText, - style = base, + style = LocalType.current.base, color = LocalColors.current.danger ) } @@ -371,7 +370,7 @@ fun TitledErrorText(titledText: TitledText?) { fun TitledMonospaceText(titledText: TitledText?) { TitledText( titledText, - style = baseMonospace + style = LocalType.current.base.monospace() ) } @@ -379,7 +378,7 @@ fun TitledMonospaceText(titledText: TitledText?) { fun TitledText( titledText: TitledText?, modifier: Modifier = Modifier, - style: TextStyle = base, + style: TextStyle = LocalType.current.base, color: Color = Color.Unspecified ) { titledText?.apply { @@ -396,8 +395,8 @@ fun TitledText( @Composable fun TitledView(title: GetString, modifier: Modifier = Modifier, content: @Composable () -> Unit) { - Column(modifier = modifier, verticalArrangement = Arrangement.spacedBy(LocalDimensions.current.xxxsItemSpacing)) { - Text(title.string(), style = baseBold) + Column(modifier = modifier, verticalArrangement = Arrangement.spacedBy(LocalDimensions.current.xxxsSpacing)) { + Text(title.string(), style = LocalType.current.base.bold()) content() } } diff --git a/app/src/main/java/org/thoughtcrime/securesms/conversation/v2/messages/VisibleMessageView.kt b/app/src/main/java/org/thoughtcrime/securesms/conversation/v2/messages/VisibleMessageView.kt index 2019867f80..b2e3bba81b 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/conversation/v2/messages/VisibleMessageView.kt +++ b/app/src/main/java/org/thoughtcrime/securesms/conversation/v2/messages/VisibleMessageView.kt @@ -36,6 +36,7 @@ import org.session.libsession.messaging.contacts.Contact.ContactContext import org.session.libsession.messaging.open_groups.OpenGroupApi import org.session.libsession.messaging.sending_receiving.attachments.DatabaseAttachment import org.session.libsession.utilities.Address +import org.session.libsession.utilities.ThemeUtil.getThemedColor import org.session.libsession.utilities.ViewUtil import org.session.libsession.utilities.getColorFromAttr import org.session.libsession.utilities.modifyLayoutParams @@ -382,7 +383,7 @@ class VisibleMessageView : FrameLayout { private fun getMessageStatusInfo(message: MessageRecord): MessageStatusInfo = when { message.isFailed -> MessageStatusInfo(R.drawable.ic_delivery_status_failed, - resources.getColor(R.color.destructive, context.theme), + getThemedColor(context, R.attr.danger), R.string.delivery_status_failed ) message.isSyncFailed -> diff --git a/app/src/main/java/org/thoughtcrime/securesms/home/ConversationView.kt b/app/src/main/java/org/thoughtcrime/securesms/home/ConversationView.kt index 4647156a01..68aea84417 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/home/ConversationView.kt +++ b/app/src/main/java/org/thoughtcrime/securesms/home/ConversationView.kt @@ -15,6 +15,7 @@ import androidx.recyclerview.widget.RecyclerView import dagger.hilt.android.AndroidEntryPoint import network.loki.messenger.R import network.loki.messenger.databinding.ViewConversationBinding +import org.session.libsession.utilities.ThemeUtil import org.session.libsession.utilities.recipients.Recipient import org.thoughtcrime.securesms.conversation.v2.utilities.MentionUtilities.highlightMentions import org.thoughtcrime.securesms.database.RecipientDatabase.NOTIFY_TYPE_ALL @@ -67,7 +68,7 @@ class ConversationView : LinearLayout { } val unreadCount = thread.unreadCount if (thread.recipient.isBlocked) { - binding.accentView.setBackgroundResource(R.color.destructive) + binding.accentView.setBackgroundColor(ThemeUtil.getThemedColor(context, R.attr.danger)) binding.accentView.visibility = View.VISIBLE } else { val accentColor = context.getAccentColor() @@ -120,7 +121,7 @@ class ConversationView : LinearLayout { !thread.isOutgoing -> binding.statusIndicatorImageView.visibility = View.GONE thread.isFailed -> { val drawable = ContextCompat.getDrawable(context, R.drawable.ic_error)?.mutate() - drawable?.setTint(ContextCompat.getColor(context, R.color.destructive)) + drawable?.setTint(ThemeUtil.getThemedColor(context, R.attr.danger)) binding.statusIndicatorImageView.setImageDrawable(drawable) } thread.isPending -> binding.statusIndicatorImageView.setImageResource(R.drawable.ic_circle_dot_dot_dot) diff --git a/app/src/main/java/org/thoughtcrime/securesms/home/EmptyView.kt b/app/src/main/java/org/thoughtcrime/securesms/home/EmptyView.kt index 9d078df4f7..aa4e0d9017 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/home/EmptyView.kt +++ b/app/src/main/java/org/thoughtcrime/securesms/home/EmptyView.kt @@ -3,8 +3,8 @@ package org.thoughtcrime.securesms.home import androidx.compose.foundation.layout.Column import androidx.compose.foundation.layout.Spacer import androidx.compose.foundation.layout.padding -import androidx.compose.material.Icon -import androidx.compose.material.Text +import androidx.compose.material3.Icon +import androidx.compose.material3.Text import androidx.compose.runtime.Composable import androidx.compose.ui.Alignment import androidx.compose.ui.Modifier @@ -14,24 +14,22 @@ import androidx.compose.ui.res.stringResource import androidx.compose.ui.text.style.TextAlign import androidx.compose.ui.tooling.preview.Preview import androidx.compose.ui.tooling.preview.PreviewParameter +import androidx.compose.ui.unit.dp import network.loki.messenger.R import org.thoughtcrime.securesms.ui.Divider -import org.thoughtcrime.securesms.ui.LocalDimensions -import org.thoughtcrime.securesms.ui.PreviewTheme -import org.thoughtcrime.securesms.ui.SessionColorsParameterProvider -import org.thoughtcrime.securesms.ui.base -import org.thoughtcrime.securesms.ui.color.Colors -import org.thoughtcrime.securesms.ui.color.LocalColors -import org.thoughtcrime.securesms.ui.h4 -import org.thoughtcrime.securesms.ui.h8 -import org.thoughtcrime.securesms.ui.small +import org.thoughtcrime.securesms.ui.theme.LocalDimensions +import org.thoughtcrime.securesms.ui.theme.PreviewTheme +import org.thoughtcrime.securesms.ui.theme.SessionColorsParameterProvider +import org.thoughtcrime.securesms.ui.theme.ThemeColors +import org.thoughtcrime.securesms.ui.theme.LocalColors +import org.thoughtcrime.securesms.ui.theme.LocalType @Composable internal fun EmptyView(newAccount: Boolean) { Column( horizontalAlignment = Alignment.CenterHorizontally, modifier = Modifier - .padding(horizontal = LocalDimensions.current.homeEmptyViewMargin) + .padding(horizontal = 50.dp) ) { Spacer(modifier = Modifier.weight(1f)) Icon( @@ -42,27 +40,27 @@ internal fun EmptyView(newAccount: Boolean) { if (newAccount) { Text( stringResource(R.string.onboardingAccountCreated), - style = h4, + style = LocalType.current.h4, textAlign = TextAlign.Center ) Text( stringResource(R.string.welcome_to_session), - style = base, + style = LocalType.current.base, color = LocalColors.current.primary, textAlign = TextAlign.Center ) } - Divider(modifier = Modifier.padding(vertical = LocalDimensions.current.xsMargin)) + Divider(modifier = Modifier.padding(vertical = LocalDimensions.current.smallSpacing)) Text( stringResource(R.string.conversationsNone), - style = h8, + style = LocalType.current.h8, textAlign = TextAlign.Center, - modifier = Modifier.padding(bottom = LocalDimensions.current.xsItemSpacing)) + modifier = Modifier.padding(bottom = LocalDimensions.current.xsSpacing)) Text( stringResource(R.string.onboardingHitThePlusButton), - style = small, + style = LocalType.current.small, textAlign = TextAlign.Center ) Spacer(modifier = Modifier.weight(2f)) @@ -72,7 +70,7 @@ internal fun EmptyView(newAccount: Boolean) { @Preview @Composable fun PreviewEmptyView( - @PreviewParameter(SessionColorsParameterProvider::class) colors: Colors + @PreviewParameter(SessionColorsParameterProvider::class) colors: ThemeColors ) { PreviewTheme(colors) { EmptyView(newAccount = false) @@ -82,7 +80,7 @@ fun PreviewEmptyView( @Preview @Composable fun PreviewEmptyViewNew( - @PreviewParameter(SessionColorsParameterProvider::class) colors: Colors + @PreviewParameter(SessionColorsParameterProvider::class) colors: ThemeColors ) { PreviewTheme(colors) { EmptyView(newAccount = true) diff --git a/app/src/main/java/org/thoughtcrime/securesms/home/SeedReminder.kt b/app/src/main/java/org/thoughtcrime/securesms/home/SeedReminder.kt index 4dccc4224f..33bdd2f2f6 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/home/SeedReminder.kt +++ b/app/src/main/java/org/thoughtcrime/securesms/home/SeedReminder.kt @@ -10,7 +10,7 @@ import androidx.compose.foundation.layout.height import androidx.compose.foundation.layout.padding import androidx.compose.foundation.layout.requiredWidth import androidx.compose.foundation.layout.width -import androidx.compose.material.Text +import androidx.compose.material3.Text import androidx.compose.runtime.Composable import androidx.compose.ui.Alignment import androidx.compose.ui.Modifier @@ -18,16 +18,15 @@ import androidx.compose.ui.res.stringResource import androidx.compose.ui.tooling.preview.Preview import androidx.compose.ui.tooling.preview.PreviewParameter import network.loki.messenger.R -import org.thoughtcrime.securesms.ui.LocalDimensions -import org.thoughtcrime.securesms.ui.PreviewTheme -import org.thoughtcrime.securesms.ui.SessionColorsParameterProvider +import org.thoughtcrime.securesms.ui.theme.LocalDimensions +import org.thoughtcrime.securesms.ui.theme.PreviewTheme +import org.thoughtcrime.securesms.ui.theme.SessionColorsParameterProvider import org.thoughtcrime.securesms.ui.SessionShieldIcon -import org.thoughtcrime.securesms.ui.color.Colors -import org.thoughtcrime.securesms.ui.color.LocalColors +import org.thoughtcrime.securesms.ui.theme.ThemeColors +import org.thoughtcrime.securesms.ui.theme.LocalColors import org.thoughtcrime.securesms.ui.components.SlimPrimaryOutlineButton import org.thoughtcrime.securesms.ui.contentDescription -import org.thoughtcrime.securesms.ui.h8 -import org.thoughtcrime.securesms.ui.small +import org.thoughtcrime.securesms.ui.theme.LocalType @Composable internal fun SeedReminder(startRecoveryPasswordActivity: () -> Unit) { @@ -43,25 +42,25 @@ internal fun SeedReminder(startRecoveryPasswordActivity: () -> Unit) { Modifier .background(LocalColors.current.backgroundSecondary) .padding( - horizontal = LocalDimensions.current.smallMargin, - vertical = LocalDimensions.current.xsMargin + horizontal = LocalDimensions.current.spacing, + vertical = LocalDimensions.current.smallSpacing ) ) { Column(Modifier.weight(1f)) { Row { Text( stringResource(R.string.save_your_recovery_password), - style = h8 + style = LocalType.current.h8 ) - Spacer(Modifier.requiredWidth(LocalDimensions.current.xxsItemSpacing)) + Spacer(Modifier.requiredWidth(LocalDimensions.current.xxsSpacing)) SessionShieldIcon() } Text( stringResource(R.string.save_your_recovery_password_to_make_sure_you_don_t_lose_access_to_your_account), - style = small + style = LocalType.current.small ) } - Spacer(Modifier.width(LocalDimensions.current.xxsMargin)) + Spacer(Modifier.width(LocalDimensions.current.xsSpacing)) SlimPrimaryOutlineButton( text = stringResource(R.string.continue_2), modifier = Modifier @@ -76,7 +75,7 @@ internal fun SeedReminder(startRecoveryPasswordActivity: () -> Unit) { @Preview @Composable private fun PreviewSeedReminder( - @PreviewParameter(SessionColorsParameterProvider::class) colors: Colors + @PreviewParameter(SessionColorsParameterProvider::class) colors: ThemeColors ) { PreviewTheme(colors) { SeedReminder {} diff --git a/app/src/main/java/org/thoughtcrime/securesms/messagerequests/MessageRequestsAdapter.kt b/app/src/main/java/org/thoughtcrime/securesms/messagerequests/MessageRequestsAdapter.kt index 10142cc8fc..3040bb774f 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/messagerequests/MessageRequestsAdapter.kt +++ b/app/src/main/java/org/thoughtcrime/securesms/messagerequests/MessageRequestsAdapter.kt @@ -10,6 +10,7 @@ import android.view.ViewGroup import android.widget.PopupMenu import androidx.recyclerview.widget.RecyclerView import network.loki.messenger.R +import org.session.libsession.utilities.ThemeUtil import org.thoughtcrime.securesms.database.CursorRecyclerViewAdapter import org.thoughtcrime.securesms.database.model.ThreadRecord import org.thoughtcrime.securesms.dependencies.DatabaseComponent @@ -60,8 +61,9 @@ class MessageRequestsAdapter( for (i in 0 until popupMenu.menu.size()) { val item = popupMenu.menu.getItem(i) val s = SpannableString(item.title) - s.setSpan(ForegroundColorSpan(context.getColor(R.color.destructive)), 0, s.length, 0) - item.iconTintList = ColorStateList.valueOf(context.getColor(R.color.destructive)) + val danger = ThemeUtil.getThemedColor(context, R.attr.danger) + s.setSpan(ForegroundColorSpan(danger), 0, s.length, 0) + item.iconTintList = ColorStateList.valueOf(danger) item.title = s } popupMenu.setForceShowIcon(true) diff --git a/app/src/main/java/org/thoughtcrime/securesms/onboarding/OnboardingBackPressAlertDialog.kt b/app/src/main/java/org/thoughtcrime/securesms/onboarding/OnboardingBackPressAlertDialog.kt index c620f32b6a..2820a64a66 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/onboarding/OnboardingBackPressAlertDialog.kt +++ b/app/src/main/java/org/thoughtcrime/securesms/onboarding/OnboardingBackPressAlertDialog.kt @@ -7,7 +7,7 @@ import network.loki.messenger.R import org.thoughtcrime.securesms.ui.AlertDialog import org.thoughtcrime.securesms.ui.DialogButtonModel import org.thoughtcrime.securesms.ui.GetString -import org.thoughtcrime.securesms.ui.color.LocalColors +import org.thoughtcrime.securesms.ui.theme.LocalColors @Composable fun OnboardingBackPressAlertDialog( diff --git a/app/src/main/java/org/thoughtcrime/securesms/onboarding/landing/Landing.kt b/app/src/main/java/org/thoughtcrime/securesms/onboarding/landing/Landing.kt index 55d0cb14e2..e40328b1d8 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/onboarding/landing/Landing.kt +++ b/app/src/main/java/org/thoughtcrime/securesms/onboarding/landing/Landing.kt @@ -5,6 +5,7 @@ import androidx.compose.animation.AnimatedVisibility import androidx.compose.animation.core.tween import androidx.compose.animation.fadeIn import androidx.compose.animation.slideInVertically +import androidx.compose.foundation.background import androidx.compose.foundation.layout.Arrangement import androidx.compose.foundation.layout.Box import androidx.compose.foundation.layout.Column @@ -16,10 +17,8 @@ import androidx.compose.foundation.layout.padding import androidx.compose.foundation.lazy.LazyColumn import androidx.compose.foundation.lazy.items import androidx.compose.foundation.lazy.rememberLazyListState -import androidx.compose.foundation.shape.RoundedCornerShape -import androidx.compose.material.Card -import androidx.compose.material.MaterialTheme -import androidx.compose.material.Text +import androidx.compose.material3.MaterialTheme +import androidx.compose.material3.Text import androidx.compose.runtime.Composable import androidx.compose.runtime.LaunchedEffect import androidx.compose.runtime.getValue @@ -40,23 +39,22 @@ import network.loki.messenger.R import org.thoughtcrime.securesms.ui.AlertDialog import org.thoughtcrime.securesms.ui.DialogButtonModel import org.thoughtcrime.securesms.ui.GetString -import org.thoughtcrime.securesms.ui.LocalDimensions -import org.thoughtcrime.securesms.ui.PreviewTheme -import org.thoughtcrime.securesms.ui.SessionColorsParameterProvider -import org.thoughtcrime.securesms.ui.color.Colors -import org.thoughtcrime.securesms.ui.color.LocalColors import org.thoughtcrime.securesms.ui.components.BorderlessHtmlButton import org.thoughtcrime.securesms.ui.components.PrimaryFillButton import org.thoughtcrime.securesms.ui.components.PrimaryOutlineButton import org.thoughtcrime.securesms.ui.contentDescription -import org.thoughtcrime.securesms.ui.h4 -import org.thoughtcrime.securesms.ui.large +import org.thoughtcrime.securesms.ui.theme.LocalColors +import org.thoughtcrime.securesms.ui.theme.LocalDimensions +import org.thoughtcrime.securesms.ui.theme.LocalType +import org.thoughtcrime.securesms.ui.theme.PreviewTheme +import org.thoughtcrime.securesms.ui.theme.SessionColorsParameterProvider +import org.thoughtcrime.securesms.ui.theme.ThemeColors import kotlin.time.Duration.Companion.milliseconds @Preview @Composable private fun PreviewLandingScreen( - @PreviewParameter(SessionColorsParameterProvider::class) colors: Colors + @PreviewParameter(SessionColorsParameterProvider::class) colors: ThemeColors ) { PreviewTheme(colors) { LandingScreen({}, {}, {}, {}) @@ -80,6 +78,7 @@ internal fun LandingScreen( onDismissRequest = { isUrlDialogVisible = false }, title = stringResource(R.string.urlOpen), text = stringResource(R.string.urlOpenBrowser), + showCloseButton = true, // display the 'x' button buttons = listOf( DialogButtonModel( text = GetString(R.string.activity_landing_terms_of_service), @@ -107,24 +106,24 @@ internal fun LandingScreen( Column { Column(modifier = Modifier .weight(1f) - .padding(horizontal = LocalDimensions.current.onboardingMargin) + .padding(horizontal = LocalDimensions.current.mediumSpacing) ) { Spacer(modifier = Modifier.weight(1f)) Text( stringResource(R.string.onboardingBubblePrivacyInYourPocket), modifier = Modifier.align(Alignment.CenterHorizontally), - style = h4, + style = LocalType.current.h4, textAlign = TextAlign.Center ) - Spacer(modifier = Modifier.height(LocalDimensions.current.itemSpacing)) + Spacer(modifier = Modifier.height(LocalDimensions.current.spacing)) LazyColumn( state = listState, modifier = Modifier - .heightIn(min = LocalDimensions.current.minScrollableViewHeight) + .heightIn(min = 200.dp) .fillMaxWidth() .weight(3f), - verticalArrangement = Arrangement.spacedBy(LocalDimensions.current.smallItemSpacing) + verticalArrangement = Arrangement.spacedBy(LocalDimensions.current.smallSpacing) ) { items( MESSAGES.take(count), @@ -140,7 +139,7 @@ internal fun LandingScreen( Spacer(modifier = Modifier.weight(1f)) } - Column(modifier = Modifier.padding(horizontal = LocalDimensions.current.largeMargin)) { + Column(modifier = Modifier.padding(horizontal = LocalDimensions.current.xlargeSpacing)) { PrimaryFillButton( text = stringResource(R.string.onboardingAccountCreate), modifier = Modifier @@ -149,7 +148,7 @@ internal fun LandingScreen( .contentDescription(R.string.AccessibilityId_create_account_button), onClick = createAccount ) - Spacer(modifier = Modifier.height(LocalDimensions.current.smallItemSpacing)) + Spacer(modifier = Modifier.height(LocalDimensions.current.smallSpacing)) PrimaryOutlineButton( stringResource(R.string.onboardingAccountExists), modifier = Modifier @@ -166,7 +165,7 @@ internal fun LandingScreen( .contentDescription(R.string.AccessibilityId_open_url), onClick = { isUrlDialogVisible = true } ) - Spacer(modifier = Modifier.height(LocalDimensions.current.xxsItemSpacing)) + Spacer(modifier = Modifier.height(LocalDimensions.current.xxsSpacing)) } } } @@ -195,7 +194,7 @@ private fun MessageText(text: String, isOutgoing: Boolean, modifier: Modifier) { Box(modifier = modifier then Modifier.fillMaxWidth()) { MessageText( text, - color = if (isOutgoing) LocalColors.current.backgroundBubbleSent else LocalColors.current.backgroundBubbleReceived, + color = if (isOutgoing) LocalColors.current.primary else LocalColors.current.backgroundBubbleReceived, textColor = if (isOutgoing) LocalColors.current.textBubbleSent else LocalColors.current.textBubbleReceived, modifier = Modifier.align(if (isOutgoing) Alignment.TopEnd else Alignment.TopStart) ) @@ -209,19 +208,17 @@ private fun MessageText( modifier: Modifier = Modifier, textColor: Color = Color.Unspecified ) { - Card( - modifier = modifier.fillMaxWidth(0.666f), - shape = MaterialTheme.shapes.small, - backgroundColor = color, - elevation = 0.dp + Box( + modifier = modifier.fillMaxWidth(0.666f) + .background(color = color, shape = MaterialTheme.shapes.small) ) { Text( text, - style = large, + style = LocalType.current.large, color = textColor, modifier = Modifier.padding( - horizontal = LocalDimensions.current.smallItemSpacing, - vertical = LocalDimensions.current.xsItemSpacing + horizontal = LocalDimensions.current.smallSpacing, + vertical = LocalDimensions.current.xsSpacing ) ) } diff --git a/app/src/main/java/org/thoughtcrime/securesms/onboarding/loadaccount/LoadAccount.kt b/app/src/main/java/org/thoughtcrime/securesms/onboarding/loadaccount/LoadAccount.kt index 5fe7b64fce..cc1033ee96 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/onboarding/loadaccount/LoadAccount.kt +++ b/app/src/main/java/org/thoughtcrime/securesms/onboarding/loadaccount/LoadAccount.kt @@ -13,8 +13,8 @@ import androidx.compose.foundation.pager.HorizontalPager import androidx.compose.foundation.pager.rememberPagerState import androidx.compose.foundation.rememberScrollState import androidx.compose.foundation.verticalScroll -import androidx.compose.material.Icon -import androidx.compose.material.Text +import androidx.compose.material3.Icon +import androidx.compose.material3.Text import androidx.compose.runtime.Composable import androidx.compose.ui.Alignment import androidx.compose.ui.Modifier @@ -24,13 +24,12 @@ import androidx.compose.ui.tooling.preview.Preview import kotlinx.coroutines.flow.Flow import network.loki.messenger.R import org.thoughtcrime.securesms.onboarding.ui.ContinuePrimaryOutlineButton -import org.thoughtcrime.securesms.ui.LocalDimensions -import org.thoughtcrime.securesms.ui.PreviewTheme -import org.thoughtcrime.securesms.ui.base +import org.thoughtcrime.securesms.ui.theme.LocalDimensions +import org.thoughtcrime.securesms.ui.theme.PreviewTheme import org.thoughtcrime.securesms.ui.components.MaybeScanQrCode import org.thoughtcrime.securesms.ui.components.SessionOutlinedTextField import org.thoughtcrime.securesms.ui.components.SessionTabRow -import org.thoughtcrime.securesms.ui.h4 +import org.thoughtcrime.securesms.ui.theme.LocalType private val TITLES = listOf(R.string.sessionRecoveryPassword, R.string.qrScan) @@ -75,29 +74,29 @@ private fun RecoveryPassword(state: State, onChange: (String) -> Unit = {}, onCo .verticalScroll(rememberScrollState()) ) { Spacer(Modifier.weight(1f)) - Spacer(modifier = Modifier.height(LocalDimensions.current.smallItemSpacing)) + Spacer(modifier = Modifier.height(LocalDimensions.current.smallSpacing)) Column( - modifier = Modifier.padding(horizontal = LocalDimensions.current.largeMargin) + modifier = Modifier.padding(horizontal = LocalDimensions.current.mediumSpacing) ) { Row { Text( text = stringResource(R.string.sessionRecoveryPassword), - style = h4 + style = LocalType.current.h4 ) - Spacer(Modifier.width(LocalDimensions.current.xxsItemSpacing)) + Spacer(Modifier.width(LocalDimensions.current.xxsSpacing)) Icon( modifier = Modifier.align(Alignment.CenterVertically), painter = painterResource(id = R.drawable.ic_shield_outline), contentDescription = null, ) } - Spacer(Modifier.height(LocalDimensions.current.smallItemSpacing)) + Spacer(Modifier.height(LocalDimensions.current.smallSpacing)) Text( stringResource(R.string.activity_link_enter_your_recovery_password_to_load_your_account_if_you_haven_t_saved_it_you_can_find_it_in_your_app_settings), - style = base + style = LocalType.current.base ) - Spacer(Modifier.height(LocalDimensions.current.itemSpacing)) + Spacer(Modifier.height(LocalDimensions.current.spacing)) SessionOutlinedTextField( text = state.recoveryPhrase, modifier = Modifier.fillMaxWidth(), @@ -110,7 +109,7 @@ private fun RecoveryPassword(state: State, onChange: (String) -> Unit = {}, onCo ) } - Spacer(modifier = Modifier.height(LocalDimensions.current.smallItemSpacing)) + Spacer(modifier = Modifier.height(LocalDimensions.current.smallSpacing)) Spacer(Modifier.weight(2f)) ContinuePrimaryOutlineButton(modifier = Modifier.align(Alignment.CenterHorizontally), onContinue) diff --git a/app/src/main/java/org/thoughtcrime/securesms/onboarding/loading/Loading.kt b/app/src/main/java/org/thoughtcrime/securesms/onboarding/loading/Loading.kt index 30d9a5acdb..8c2e10e765 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/onboarding/loading/Loading.kt +++ b/app/src/main/java/org/thoughtcrime/securesms/onboarding/loading/Loading.kt @@ -3,17 +3,16 @@ package org.thoughtcrime.securesms.onboarding.loading import androidx.compose.foundation.layout.Column import androidx.compose.foundation.layout.Spacer import androidx.compose.foundation.layout.height -import androidx.compose.material.Text +import androidx.compose.material3.Text import androidx.compose.runtime.Composable import androidx.compose.ui.Alignment import androidx.compose.ui.Modifier import androidx.compose.ui.res.stringResource import network.loki.messenger.R -import org.thoughtcrime.securesms.ui.LocalDimensions +import org.thoughtcrime.securesms.ui.theme.LocalDimensions import org.thoughtcrime.securesms.ui.ProgressArc -import org.thoughtcrime.securesms.ui.base import org.thoughtcrime.securesms.ui.contentDescription -import org.thoughtcrime.securesms.ui.h7 +import org.thoughtcrime.securesms.ui.theme.LocalType @Composable internal fun LoadingScreen(progress: Float) { @@ -25,12 +24,12 @@ internal fun LoadingScreen(progress: Float) { ) Text( stringResource(R.string.waitOneMoment), - style = h7 + style = LocalType.current.h7 ) - Spacer(modifier = Modifier.height(LocalDimensions.current.xxxsItemSpacing)) + Spacer(modifier = Modifier.height(LocalDimensions.current.xxxsSpacing)) Text( stringResource(R.string.loadAccountProgressMessage), - style = base + style = LocalType.current.base ) Spacer(modifier = Modifier.weight(2f)) } diff --git a/app/src/main/java/org/thoughtcrime/securesms/onboarding/messagenotifications/MessageNotifications.kt b/app/src/main/java/org/thoughtcrime/securesms/onboarding/messagenotifications/MessageNotifications.kt index c8cc30ce79..e56b55aaab 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/onboarding/messagenotifications/MessageNotifications.kt +++ b/app/src/main/java/org/thoughtcrime/securesms/onboarding/messagenotifications/MessageNotifications.kt @@ -10,7 +10,7 @@ import androidx.compose.foundation.layout.fillMaxSize import androidx.compose.foundation.layout.height import androidx.compose.foundation.layout.padding import androidx.compose.foundation.shape.RoundedCornerShape -import androidx.compose.material.Text +import androidx.compose.material3.Text import androidx.compose.runtime.Composable import androidx.compose.ui.Alignment import androidx.compose.ui.Modifier @@ -22,22 +22,15 @@ import network.loki.messenger.R import org.thoughtcrime.securesms.onboarding.OnboardingBackPressAlertDialog import org.thoughtcrime.securesms.onboarding.messagenotifications.MessageNotificationsViewModel.UiState import org.thoughtcrime.securesms.onboarding.ui.ContinuePrimaryOutlineButton -import org.thoughtcrime.securesms.ui.AlertDialog -import org.thoughtcrime.securesms.ui.DialogButtonModel -import org.thoughtcrime.securesms.ui.GetString -import org.thoughtcrime.securesms.ui.LocalDimensions -import org.thoughtcrime.securesms.ui.PreviewTheme -import org.thoughtcrime.securesms.ui.SessionColorsParameterProvider -import org.thoughtcrime.securesms.ui.base -import org.thoughtcrime.securesms.ui.color.Colors -import org.thoughtcrime.securesms.ui.color.LocalColors +import org.thoughtcrime.securesms.ui.theme.LocalDimensions +import org.thoughtcrime.securesms.ui.theme.PreviewTheme +import org.thoughtcrime.securesms.ui.theme.SessionColorsParameterProvider +import org.thoughtcrime.securesms.ui.theme.ThemeColors +import org.thoughtcrime.securesms.ui.theme.LocalColors import org.thoughtcrime.securesms.ui.components.CircularProgressIndicator import org.thoughtcrime.securesms.ui.components.RadioButton import org.thoughtcrime.securesms.ui.contentDescription -import org.thoughtcrime.securesms.ui.h4 -import org.thoughtcrime.securesms.ui.h8 -import org.thoughtcrime.securesms.ui.h9 -import org.thoughtcrime.securesms.ui.small +import org.thoughtcrime.securesms.ui.theme.LocalType @Composable internal fun MessageNotificationsScreen( @@ -63,11 +56,11 @@ internal fun MessageNotificationsScreen( Column { Spacer(Modifier.weight(1f)) - Column(modifier = Modifier.padding(horizontal = LocalDimensions.current.onboardingMargin)) { - Text(stringResource(R.string.notificationsMessage), style = h4) - Spacer(Modifier.height(LocalDimensions.current.xsMargin)) - Text(stringResource(R.string.onboardingMessageNotificationExplaination), style = base) - Spacer(Modifier.height(LocalDimensions.current.itemSpacing)) + Column(modifier = Modifier.padding(horizontal = LocalDimensions.current.mediumSpacing)) { + Text(stringResource(R.string.notificationsMessage), style = LocalType.current.h4) + Spacer(Modifier.height(LocalDimensions.current.smallSpacing)) + Text(stringResource(R.string.onboardingMessageNotificationExplaination), style = LocalType.current.base) + Spacer(Modifier.height(LocalDimensions.current.spacing)) } NotificationRadioButton( @@ -107,8 +100,8 @@ private fun NotificationRadioButton( RadioButton( onClick = onClick, modifier = modifier, - checked = checked, - contentPadding = PaddingValues(horizontal = LocalDimensions.current.margin, vertical = 7.dp) + selected = checked, + contentPadding = PaddingValues(horizontal = LocalDimensions.current.mediumSpacing, vertical = 7.dp) ) { Box( modifier = Modifier @@ -120,17 +113,18 @@ private fun NotificationRadioButton( ), ) { Column(modifier = Modifier - .padding(horizontal = 15.dp) - .padding(top = 10.dp, bottom = 11.dp)) { - Text(stringResource(title), style = h8) + .padding(horizontal = LocalDimensions.current.smallSpacing, + vertical = LocalDimensions.current.xsSpacing) + ) { + Text(stringResource(title), style = LocalType.current.h8) - Text(stringResource(explanation), style = small, modifier = Modifier.padding(top = 7.dp)) + Text(stringResource(explanation), style = LocalType.current.small, modifier = Modifier.padding(top = LocalDimensions.current.xxsSpacing)) tag?.let { Text( stringResource(it), - modifier = Modifier.padding(top = 6.dp), + modifier = Modifier.padding(top = LocalDimensions.current.xxsSpacing), color = LocalColors.current.primary, - style = h9 + style = LocalType.current.h9 ) } } @@ -141,7 +135,7 @@ private fun NotificationRadioButton( @Preview @Composable private fun MessageNotificationsScreenPreview( - @PreviewParameter(SessionColorsParameterProvider::class) colors: Colors + @PreviewParameter(SessionColorsParameterProvider::class) colors: ThemeColors ) { PreviewTheme(colors) { MessageNotificationsScreen() diff --git a/app/src/main/java/org/thoughtcrime/securesms/onboarding/pickname/PickDisplayName.kt b/app/src/main/java/org/thoughtcrime/securesms/onboarding/pickname/PickDisplayName.kt index f5e6e1a9a4..04124a5bfd 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/onboarding/pickname/PickDisplayName.kt +++ b/app/src/main/java/org/thoughtcrime/securesms/onboarding/pickname/PickDisplayName.kt @@ -8,7 +8,7 @@ import androidx.compose.foundation.layout.height import androidx.compose.foundation.layout.padding import androidx.compose.foundation.rememberScrollState import androidx.compose.foundation.verticalScroll -import androidx.compose.material.Text +import androidx.compose.material3.Text import androidx.compose.runtime.Composable import androidx.compose.ui.Alignment import androidx.compose.ui.Modifier @@ -17,11 +17,10 @@ import androidx.compose.ui.tooling.preview.Preview import network.loki.messenger.R import org.thoughtcrime.securesms.onboarding.OnboardingBackPressAlertDialog import org.thoughtcrime.securesms.onboarding.ui.ContinuePrimaryOutlineButton -import org.thoughtcrime.securesms.ui.LocalDimensions -import org.thoughtcrime.securesms.ui.PreviewTheme -import org.thoughtcrime.securesms.ui.base +import org.thoughtcrime.securesms.ui.theme.LocalDimensions +import org.thoughtcrime.securesms.ui.theme.PreviewTheme import org.thoughtcrime.securesms.ui.components.SessionOutlinedTextField -import org.thoughtcrime.securesms.ui.h4 +import org.thoughtcrime.securesms.ui.theme.LocalType @Preview @Composable @@ -52,18 +51,18 @@ internal fun PickDisplayName( .verticalScroll(rememberScrollState()) ) { Spacer(Modifier.weight(1f)) - Spacer(modifier = Modifier.height(LocalDimensions.current.smallItemSpacing)) + Spacer(modifier = Modifier.height(LocalDimensions.current.smallSpacing)) Column( - modifier = Modifier.padding(horizontal = LocalDimensions.current.largeMargin) + modifier = Modifier.padding(horizontal = LocalDimensions.current.mediumSpacing) ) { - Text(stringResource(state.title), style = h4) - Spacer(Modifier.height(LocalDimensions.current.smallItemSpacing)) + Text(stringResource(state.title), style = LocalType.current.h4) + Spacer(Modifier.height(LocalDimensions.current.smallSpacing)) Text( stringResource(state.description), - style = base, - modifier = Modifier.padding(bottom = LocalDimensions.current.xsItemSpacing)) - Spacer(Modifier.height(LocalDimensions.current.itemSpacing)) + style = LocalType.current.base, + modifier = Modifier.padding(bottom = LocalDimensions.current.xsSpacing)) + Spacer(Modifier.height(LocalDimensions.current.spacing)) SessionOutlinedTextField( text = state.displayName, modifier = Modifier.fillMaxWidth(), @@ -76,7 +75,7 @@ internal fun PickDisplayName( ) } - Spacer(modifier = Modifier.height(LocalDimensions.current.smallItemSpacing)) + Spacer(modifier = Modifier.height(LocalDimensions.current.smallSpacing)) Spacer(Modifier.weight(2f)) ContinuePrimaryOutlineButton(modifier = Modifier.align(Alignment.CenterHorizontally), onContinue) diff --git a/app/src/main/java/org/thoughtcrime/securesms/onboarding/ui/ContinueButton.kt b/app/src/main/java/org/thoughtcrime/securesms/onboarding/ui/ContinueButton.kt index d48b5f56ab..0d31363d76 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/onboarding/ui/ContinueButton.kt +++ b/app/src/main/java/org/thoughtcrime/securesms/onboarding/ui/ContinueButton.kt @@ -6,7 +6,7 @@ import androidx.compose.runtime.Composable import androidx.compose.ui.Modifier import androidx.compose.ui.res.stringResource import network.loki.messenger.R -import org.thoughtcrime.securesms.ui.LocalDimensions +import org.thoughtcrime.securesms.ui.theme.LocalDimensions import org.thoughtcrime.securesms.ui.components.PrimaryOutlineButton import org.thoughtcrime.securesms.ui.contentDescription @@ -17,8 +17,8 @@ fun ContinuePrimaryOutlineButton(modifier: Modifier, onContinue: () -> Unit) { modifier = modifier .contentDescription(R.string.AccessibilityId_continue) .fillMaxWidth() - .padding(horizontal = LocalDimensions.current.largeMargin) - .padding(bottom = LocalDimensions.current.xxsMargin), + .padding(horizontal = LocalDimensions.current.xlargeSpacing) + .padding(bottom = LocalDimensions.current.smallSpacing), onClick = onContinue, ) } diff --git a/app/src/main/java/org/thoughtcrime/securesms/preferences/QRCodeActivity.kt b/app/src/main/java/org/thoughtcrime/securesms/preferences/QRCodeActivity.kt index ccf7f9c178..9778a1c8b8 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/preferences/QRCodeActivity.kt +++ b/app/src/main/java/org/thoughtcrime/securesms/preferences/QRCodeActivity.kt @@ -8,7 +8,7 @@ import androidx.compose.foundation.layout.fillMaxSize import androidx.compose.foundation.layout.padding import androidx.compose.foundation.pager.HorizontalPager import androidx.compose.foundation.pager.rememberPagerState -import androidx.compose.material.Text +import androidx.compose.material3.Text import androidx.compose.runtime.Composable import androidx.compose.ui.Modifier import androidx.compose.ui.res.stringResource @@ -25,14 +25,14 @@ import org.session.libsignal.utilities.PublicKeyValidation import org.thoughtcrime.securesms.PassphraseRequiredActionBarActivity import org.thoughtcrime.securesms.conversation.v2.ConversationActivityV2 import org.thoughtcrime.securesms.database.threadDatabase -import org.thoughtcrime.securesms.ui.LocalDimensions -import org.thoughtcrime.securesms.ui.color.LocalColors +import org.thoughtcrime.securesms.ui.theme.LocalDimensions +import org.thoughtcrime.securesms.ui.theme.LocalColors import org.thoughtcrime.securesms.ui.components.MaybeScanQrCode import org.thoughtcrime.securesms.ui.components.QrImage import org.thoughtcrime.securesms.ui.components.SessionTabRow import org.thoughtcrime.securesms.ui.contentDescription import org.thoughtcrime.securesms.ui.setComposeContent -import org.thoughtcrime.securesms.ui.small +import org.thoughtcrime.securesms.ui.theme.LocalType import org.thoughtcrime.securesms.util.start private val TITLES = listOf(R.string.view, R.string.scan) @@ -93,14 +93,14 @@ private fun Tabs(accountId: String, errors: Flow, onScan: (String) -> Un fun QrPage(string: String) { Column( modifier = Modifier - .background(LocalColors.current.backgroundSecondary) - .padding(horizontal = LocalDimensions.current.margin) + .background(LocalColors.current.background) + .padding(horizontal = LocalDimensions.current.mediumSpacing) .fillMaxSize() ) { QrImage( string = string, modifier = Modifier - .padding(top = LocalDimensions.current.margin, bottom = LocalDimensions.current.xxsMargin) + .padding(top = LocalDimensions.current.mediumSpacing, bottom = LocalDimensions.current.xsSpacing) .contentDescription(R.string.AccessibilityId_qr_code), icon = R.drawable.session ) @@ -109,7 +109,7 @@ fun QrPage(string: String) { text = stringResource(R.string.this_is_your_account_id_other_users_can_scan_it_to_start_a_conversation_with_you), color = LocalColors.current.textSecondary, textAlign = TextAlign.Center, - style = small + style = LocalType.current.small ) } } diff --git a/app/src/main/java/org/thoughtcrime/securesms/preferences/SettingsActivity.kt b/app/src/main/java/org/thoughtcrime/securesms/preferences/SettingsActivity.kt index ca69d37bc1..fc98070ca4 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/preferences/SettingsActivity.kt +++ b/app/src/main/java/org/thoughtcrime/securesms/preferences/SettingsActivity.kt @@ -77,8 +77,8 @@ import org.thoughtcrime.securesms.ui.Cell import org.thoughtcrime.securesms.ui.Divider import org.thoughtcrime.securesms.ui.LargeItemButton import org.thoughtcrime.securesms.ui.LargeItemButtonWithDrawable -import org.thoughtcrime.securesms.ui.LocalDimensions -import org.thoughtcrime.securesms.ui.color.destructiveButtonColors +import org.thoughtcrime.securesms.ui.theme.LocalDimensions +import org.thoughtcrime.securesms.ui.theme.dangerButtonColors import org.thoughtcrime.securesms.ui.components.PrimaryOutlineButton import org.thoughtcrime.securesms.ui.components.PrimaryOutlineCopyButton import org.thoughtcrime.securesms.ui.contentDescription @@ -448,9 +448,9 @@ class SettingsActivity : PassphraseRequiredActionBarActivity() { Column { Row( modifier = Modifier - .padding(horizontal = LocalDimensions.current.smallMargin) - .padding(top = LocalDimensions.current.xxxsMargin), - horizontalArrangement = Arrangement.spacedBy(LocalDimensions.current.smallItemSpacing), + .padding(horizontal = LocalDimensions.current.spacing) + .padding(top = LocalDimensions.current.xxsSpacing), + horizontalArrangement = Arrangement.spacedBy(LocalDimensions.current.smallSpacing), ) { PrimaryOutlineButton( stringResource(R.string.share), @@ -464,7 +464,7 @@ class SettingsActivity : PassphraseRequiredActionBarActivity() { ) } - Spacer(modifier = Modifier.height(LocalDimensions.current.itemSpacing)) + Spacer(modifier = Modifier.height(LocalDimensions.current.spacing)) val hasPaths by hasPaths().collectAsState(initial = false) @@ -492,7 +492,7 @@ class SettingsActivity : PassphraseRequiredActionBarActivity() { } LargeItemButton(R.string.activity_settings_help_button, R.drawable.ic_help, Modifier.contentDescription(R.string.AccessibilityId_help)) { show() } Divider() - LargeItemButton(R.string.activity_settings_clear_all_data_button_title, R.drawable.ic_clear_data, Modifier.contentDescription(R.string.AccessibilityId_clear_data), destructiveButtonColors()) { ClearAllDataDialog().show(supportFragmentManager, "Clear All Data Dialog") } + LargeItemButton(R.string.activity_settings_clear_all_data_button_title, R.drawable.ic_message_details__trash, Modifier.contentDescription(R.string.AccessibilityId_clear_data), dangerButtonColors()) { ClearAllDataDialog().show(supportFragmentManager, "Clear All Data Dialog") } } } } @@ -509,4 +509,4 @@ private fun LocalBroadcastManager.hasPaths(): Flow = callbackFlow { registerReceiver(receiver, IntentFilter("pathsBuilt")) awaitClose { unregisterReceiver(receiver) } -}.onStart { emit(Unit) }.map { OnionRequestAPI.paths.isNotEmpty() } +}.onStart { emit(Unit) }.map { OnionRequestAPI.paths.isNotEmpty() } \ No newline at end of file diff --git a/app/src/main/java/org/thoughtcrime/securesms/preferences/appearance/AppearanceSettingsViewModel.kt b/app/src/main/java/org/thoughtcrime/securesms/preferences/appearance/AppearanceSettingsViewModel.kt index c569441382..afe33400ff 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/preferences/appearance/AppearanceSettingsViewModel.kt +++ b/app/src/main/java/org/thoughtcrime/securesms/preferences/appearance/AppearanceSettingsViewModel.kt @@ -6,6 +6,7 @@ import dagger.hilt.android.lifecycle.HiltViewModel import kotlinx.coroutines.flow.MutableStateFlow import kotlinx.coroutines.flow.StateFlow import org.session.libsession.utilities.TextSecurePreferences +import org.thoughtcrime.securesms.ui.theme.selectedTheme import org.thoughtcrime.securesms.util.ThemeState import org.thoughtcrime.securesms.util.themeState import javax.inject.Inject @@ -26,11 +27,17 @@ class AppearanceSettingsViewModel @Inject constructor(private val prefs: TextSec prefs.setThemeStyle(newThemeStyle) // update UI state _uiState.value = prefs.themeState() + + // force compose to refresh its style reference + selectedTheme = null } fun setNewFollowSystemSettings(followSystemSettings: Boolean) { prefs.setFollowSystemSettings(followSystemSettings) _uiState.value = prefs.themeState() + + // force compose to refresh its style reference + selectedTheme = null } } \ No newline at end of file diff --git a/app/src/main/java/org/thoughtcrime/securesms/recoverypassword/RecoveryPassword.kt b/app/src/main/java/org/thoughtcrime/securesms/recoverypassword/RecoveryPassword.kt index 2868bfbfd9..5f59943432 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/recoverypassword/RecoveryPassword.kt +++ b/app/src/main/java/org/thoughtcrime/securesms/recoverypassword/RecoveryPassword.kt @@ -11,7 +11,7 @@ import androidx.compose.foundation.layout.width import androidx.compose.foundation.layout.wrapContentWidth import androidx.compose.foundation.rememberScrollState import androidx.compose.foundation.verticalScroll -import androidx.compose.material.Text +import androidx.compose.material3.Text import androidx.compose.runtime.Composable import androidx.compose.runtime.getValue import androidx.compose.runtime.mutableStateOf @@ -26,20 +26,19 @@ import androidx.compose.ui.tooling.preview.PreviewParameter import androidx.compose.ui.unit.dp import network.loki.messenger.R import org.thoughtcrime.securesms.ui.CellWithPaddingAndMargin -import org.thoughtcrime.securesms.ui.LocalDimensions -import org.thoughtcrime.securesms.ui.PreviewTheme -import org.thoughtcrime.securesms.ui.SessionColorsParameterProvider +import org.thoughtcrime.securesms.ui.theme.LocalDimensions +import org.thoughtcrime.securesms.ui.theme.PreviewTheme +import org.thoughtcrime.securesms.ui.theme.SessionColorsParameterProvider import org.thoughtcrime.securesms.ui.SessionShieldIcon -import org.thoughtcrime.securesms.ui.base -import org.thoughtcrime.securesms.ui.color.Colors -import org.thoughtcrime.securesms.ui.color.LocalColors +import org.thoughtcrime.securesms.ui.theme.ThemeColors +import org.thoughtcrime.securesms.ui.theme.LocalColors import org.thoughtcrime.securesms.ui.components.QrImage import org.thoughtcrime.securesms.ui.components.SlimOutlineButton import org.thoughtcrime.securesms.ui.components.SlimOutlineCopyButton import org.thoughtcrime.securesms.ui.components.border import org.thoughtcrime.securesms.ui.contentDescription -import org.thoughtcrime.securesms.ui.extraSmallMonospace -import org.thoughtcrime.securesms.ui.h8 +import org.thoughtcrime.securesms.ui.theme.LocalType +import org.thoughtcrime.securesms.ui.theme.monospace @Composable internal fun RecoveryPasswordScreen( @@ -49,11 +48,11 @@ internal fun RecoveryPasswordScreen( onHide:() -> Unit = {} ) { Column( - verticalArrangement = Arrangement.spacedBy(LocalDimensions.current.xsMargin), + verticalArrangement = Arrangement.spacedBy(LocalDimensions.current.smallSpacing), modifier = Modifier .contentDescription(R.string.AccessibilityId_recovery_password) .verticalScroll(rememberScrollState()) - .padding(bottom = LocalDimensions.current.xsMargin) + .padding(bottom = LocalDimensions.current.smallSpacing) ) { RecoveryPasswordCell(mnemonic, seed, copyMnemonic) HideRecoveryPasswordCell(onHide) @@ -75,17 +74,17 @@ private fun RecoveryPasswordCell( Row { Text( stringResource(R.string.sessionRecoveryPassword), - style = h8 + style = LocalType.current.h8 ) - Spacer(Modifier.width(LocalDimensions.current.xxsItemSpacing)) + Spacer(Modifier.width(LocalDimensions.current.xxsSpacing)) SessionShieldIcon() } - Spacer(modifier = Modifier.height(LocalDimensions.current.xxxsMargin)) + Spacer(modifier = Modifier.height(LocalDimensions.current.xxsSpacing)) Text( stringResource(R.string.recoveryPasswordDescription), - style = base + style = LocalType.current.base ) AnimatedVisibility(!showQr) { @@ -99,7 +98,7 @@ private fun RecoveryPasswordCell( QrImage( seed, modifier = Modifier - .padding(vertical = LocalDimensions.current.smallMargin) + .padding(vertical = LocalDimensions.current.spacing) .contentDescription(R.string.AccessibilityId_qr_code), contentPadding = 10.dp, icon = R.drawable.session_shield @@ -108,7 +107,7 @@ private fun RecoveryPasswordCell( AnimatedVisibility(!showQr) { Row( - horizontalArrangement = Arrangement.spacedBy(LocalDimensions.current.smallItemSpacing), + horizontalArrangement = Arrangement.spacedBy(LocalDimensions.current.smallSpacing), verticalAlignment = Alignment.CenterVertically ) { SlimOutlineCopyButton( @@ -138,11 +137,11 @@ private fun RecoveryPassword(mnemonic: String) { mnemonic, modifier = Modifier .contentDescription(R.string.AccessibilityId_recovery_password_container) - .padding(vertical = LocalDimensions.current.smallMargin) + .padding(vertical = LocalDimensions.current.spacing) .border() - .padding(LocalDimensions.current.smallMargin), + .padding(LocalDimensions.current.spacing), textAlign = TextAlign.Center, - style = extraSmallMonospace, + style = LocalType.current.extraSmall.monospace(), color = LocalColors.current.run { if (isLight) text else primary }, ) } @@ -156,14 +155,14 @@ private fun HideRecoveryPasswordCell(onHide: () -> Unit = {}) { ) { Text( stringResource(R.string.recoveryPasswordHideRecoveryPassword), - style = h8 + style = LocalType.current.h8 ) Text( stringResource(R.string.recoveryPasswordHideRecoveryPasswordDescription), - style = base + style = LocalType.current.base ) } - Spacer(modifier = Modifier.width(LocalDimensions.current.xxsMargin)) + Spacer(modifier = Modifier.width(LocalDimensions.current.xsSpacing)) SlimOutlineButton( text = stringResource(R.string.hide), modifier = Modifier @@ -180,7 +179,7 @@ private fun HideRecoveryPasswordCell(onHide: () -> Unit = {}) { @Preview @Composable private fun PreviewRecoveryPasswordScreen( - @PreviewParameter(SessionColorsParameterProvider::class) colors: Colors + @PreviewParameter(SessionColorsParameterProvider::class) colors: ThemeColors ) { PreviewTheme(colors) { RecoveryPasswordScreen(mnemonic = "voyage urban toyed maverick peculiar tuxedo penguin tree grass building listen speak withdraw terminal plane") diff --git a/app/src/main/java/org/thoughtcrime/securesms/recoverypassword/RecoveryPasswordActivity.kt b/app/src/main/java/org/thoughtcrime/securesms/recoverypassword/RecoveryPasswordActivity.kt index 8d1a80fb0c..a46b4a1d63 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/recoverypassword/RecoveryPasswordActivity.kt +++ b/app/src/main/java/org/thoughtcrime/securesms/recoverypassword/RecoveryPasswordActivity.kt @@ -34,7 +34,7 @@ class RecoveryPasswordActivity : BaseActionBarActivity() { showSessionDialog { title(R.string.recoveryPasswordHidePermanently) htmlText(R.string.recoveryPasswordHidePermanentlyDescription1) - destructiveButton(R.string.continue_2, R.string.AccessibilityId_continue) { onHideConfirm() } + dangerButton(R.string.continue_2, R.string.AccessibilityId_continue) { onHideConfirm() } cancelButton() } } @@ -44,7 +44,7 @@ class RecoveryPasswordActivity : BaseActionBarActivity() { title(R.string.recoveryPasswordHidePermanently) text(R.string.recoveryPasswordHidePermanentlyDescription2) cancelButton() - destructiveButton( + dangerButton( R.string.yes, contentDescription = R.string.AccessibilityId_confirm_button ) { diff --git a/app/src/main/java/org/thoughtcrime/securesms/ui/AlertDialog.kt b/app/src/main/java/org/thoughtcrime/securesms/ui/AlertDialog.kt index 121748809f..df66cef471 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/ui/AlertDialog.kt +++ b/app/src/main/java/org/thoughtcrime/securesms/ui/AlertDialog.kt @@ -1,5 +1,6 @@ package org.thoughtcrime.securesms.ui +import androidx.compose.foundation.background import androidx.compose.foundation.layout.Box import androidx.compose.foundation.layout.Column import androidx.compose.foundation.layout.IntrinsicSize @@ -8,11 +9,13 @@ import androidx.compose.foundation.layout.fillMaxHeight import androidx.compose.foundation.layout.fillMaxWidth import androidx.compose.foundation.layout.height import androidx.compose.foundation.layout.padding -import androidx.compose.material.Icon -import androidx.compose.material.IconButton -import androidx.compose.material.MaterialTheme -import androidx.compose.material.Text -import androidx.compose.material.TextButton +import androidx.compose.material3.BasicAlertDialog +import androidx.compose.material3.ExperimentalMaterial3Api +import androidx.compose.material3.Icon +import androidx.compose.material3.IconButton +import androidx.compose.material3.MaterialTheme +import androidx.compose.material3.Text +import androidx.compose.material3.TextButton import androidx.compose.runtime.Composable import androidx.compose.ui.Alignment import androidx.compose.ui.Modifier @@ -20,9 +23,16 @@ import androidx.compose.ui.graphics.Color import androidx.compose.ui.graphics.RectangleShape import androidx.compose.ui.graphics.takeOrElse import androidx.compose.ui.res.painterResource +import androidx.compose.ui.res.stringResource import androidx.compose.ui.text.style.TextAlign +import androidx.compose.ui.tooling.preview.Preview import network.loki.messenger.R -import org.thoughtcrime.securesms.ui.color.LocalColors +import org.thoughtcrime.securesms.ui.theme.LocalColors +import org.thoughtcrime.securesms.ui.theme.LocalDimensions +import org.thoughtcrime.securesms.ui.theme.LocalType +import org.thoughtcrime.securesms.ui.theme.PreviewTheme +import org.thoughtcrime.securesms.ui.theme.bold + class DialogButtonModel( val text: GetString, @@ -32,29 +42,35 @@ class DialogButtonModel( val onClick: () -> Unit = {}, ) +@OptIn(ExperimentalMaterial3Api::class) @Composable fun AlertDialog( onDismissRequest: () -> Unit, title: String? = null, text: String? = null, - content: @Composable () -> Unit = {}, - buttons: List? = null + buttons: List? = null, + showCloseButton: Boolean = false, + content: @Composable () -> Unit = {} ) { - androidx.compose.material.AlertDialog( - onDismissRequest, - shape = MaterialTheme.shapes.small, - backgroundColor = LocalColors.current.backgroundSecondary, - buttons = { - Box { - IconButton( - onClick = onDismissRequest, - modifier = Modifier.align(Alignment.TopEnd) - ) { - Icon( - painter = painterResource(id = R.drawable.ic_dialog_x), - tint = LocalColors.current.text, - contentDescription = "back" - ) + BasicAlertDialog( + onDismissRequest = onDismissRequest, + content = { + Box( + modifier = Modifier.background(color = LocalColors.current.backgroundSecondary, + shape = MaterialTheme.shapes.small) + ) { + // only show the 'x' button is required + if(showCloseButton) { + IconButton( + onClick = onDismissRequest, + modifier = Modifier.align(Alignment.TopEnd) + ) { + Icon( + painter = painterResource(id = R.drawable.ic_dialog_x), + tint = LocalColors.current.text, + contentDescription = "back" + ) + } } Column(modifier = Modifier.fillMaxWidth()) { @@ -62,23 +78,23 @@ fun AlertDialog( horizontalAlignment = Alignment.CenterHorizontally, modifier = Modifier .fillMaxWidth() - .padding(top = LocalDimensions.current.smallItemSpacing) - .padding(horizontal = LocalDimensions.current.smallItemSpacing) + .padding(top = LocalDimensions.current.smallSpacing) + .padding(horizontal = LocalDimensions.current.smallSpacing) ) { title?.let { Text( it, textAlign = TextAlign.Center, - style = h7, - modifier = Modifier.padding(bottom = LocalDimensions.current.xxsItemSpacing) + style = LocalType.current.h7, + modifier = Modifier.padding(bottom = LocalDimensions.current.xxsSpacing) ) } text?.let { Text( it, textAlign = TextAlign.Center, - style = large, - modifier = Modifier.padding(bottom = LocalDimensions.current.xxsItemSpacing) + style = LocalType.current.large, + modifier = Modifier.padding(bottom = LocalDimensions.current.xxsSpacing) ) } content() @@ -116,12 +132,59 @@ fun DialogButton(text: String, modifier: Modifier, color: Color = Color.Unspecif Text( text, color = color.takeOrElse { LocalColors.current.text }, - style = largeBold, + style = LocalType.current.large.bold(), textAlign = TextAlign.Center, modifier = Modifier.padding( - top = LocalDimensions.current.smallItemSpacing, - bottom = LocalDimensions.current.itemSpacing + top = LocalDimensions.current.smallSpacing, + bottom = LocalDimensions.current.spacing ) ) } } + +@Preview +@Composable +fun PreviewSimpleDialog(){ + PreviewTheme { + AlertDialog( + onDismissRequest = {}, + title = stringResource(R.string.warning), + text = stringResource(R.string.you_cannot_go_back_further_in_order_to_stop_loading_your_account_session_needs_to_quit), + buttons = listOf( + DialogButtonModel( + GetString(stringResource(R.string.quit)), + color = LocalColors.current.danger, + onClick = {} + ), + DialogButtonModel( + GetString(stringResource(R.string.cancel)) + ) + ) + ) + } +} + +@Preview +@Composable +fun PreviewXCloseDialog(){ + PreviewTheme { + AlertDialog( + title = stringResource(R.string.urlOpen), + text = stringResource(R.string.urlOpenBrowser), + showCloseButton = true, // display the 'x' button + buttons = listOf( + DialogButtonModel( + text = GetString(R.string.activity_landing_terms_of_service), + contentDescription = GetString(R.string.AccessibilityId_terms_of_service_button), + onClick = {} + ), + DialogButtonModel( + text = GetString(R.string.activity_landing_privacy_policy), + contentDescription = GetString(R.string.AccessibilityId_privacy_policy_button), + onClick = {} + ) + ), + onDismissRequest = {} + ) + } +} diff --git a/app/src/main/java/org/thoughtcrime/securesms/ui/Carousel.kt b/app/src/main/java/org/thoughtcrime/securesms/ui/Carousel.kt index e3ffff66d0..ea632d46e7 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/ui/Carousel.kt +++ b/app/src/main/java/org/thoughtcrime/securesms/ui/Carousel.kt @@ -16,12 +16,8 @@ import androidx.compose.foundation.layout.size import androidx.compose.foundation.layout.width import androidx.compose.foundation.pager.PagerState import androidx.compose.foundation.shape.CircleShape -import androidx.compose.material.Card -import androidx.compose.material.ContentAlpha -import androidx.compose.material.Icon -import androidx.compose.material.IconButton -import androidx.compose.material.LocalContentAlpha -import androidx.compose.material.LocalContentColor +import androidx.compose.material3.Icon +import androidx.compose.material3.IconButton import androidx.compose.runtime.Composable import androidx.compose.runtime.remember import androidx.compose.runtime.rememberCoroutineScope @@ -37,26 +33,27 @@ import androidx.compose.ui.unit.IntOffset import androidx.compose.ui.unit.dp import kotlinx.coroutines.launch import network.loki.messenger.R -import org.thoughtcrime.securesms.ui.color.LocalColors +import org.thoughtcrime.securesms.ui.theme.LocalColors +import org.thoughtcrime.securesms.ui.theme.LocalDimensions +import org.thoughtcrime.securesms.ui.theme.blackAlpha40 +import org.thoughtcrime.securesms.ui.theme.pillShape import kotlin.math.absoluteValue import kotlin.math.sign @OptIn(ExperimentalFoundationApi::class) @Composable fun BoxScope.HorizontalPagerIndicator(pagerState: PagerState) { - if (pagerState.pageCount >= 2) Card( - shape = pillShape, - backgroundColor = Color.Black.copy(alpha = 0.4f), + if (pagerState.pageCount >= 2) Box( modifier = Modifier + .background(color = blackAlpha40, shape = pillShape) .align(Alignment.BottomCenter) - .padding(8.dp) + .padding(LocalDimensions.current.xxsSpacing) ) { - Box(modifier = Modifier.padding(8.dp)) { + Box(modifier = Modifier.padding(LocalDimensions.current.xxsSpacing)) { ClickableHorizontalPagerIndicator( pagerState = pagerState, - pageCount = pagerState.pageCount, - activeColor = Color.White, - inactiveColor = LocalColors.current.textSecondary) + pageCount = pagerState.pageCount + ) } } } @@ -73,9 +70,9 @@ fun ClickableHorizontalPagerIndicator( pageCount: Int, modifier: Modifier = Modifier, pageIndexMapping: (Int) -> Int = { it }, - activeColor: Color = LocalContentColor.current.copy(alpha = LocalContentAlpha.current), - inactiveColor: Color = activeColor.copy(ContentAlpha.disabled), - indicatorWidth: Dp = 8.dp, + activeColor: Color = Color.White, + inactiveColor: Color = LocalColors.current.disabled, + indicatorWidth: Dp = LocalDimensions.current.xxsSpacing, indicatorHeight: Dp = indicatorWidth, spacing: Dp = indicatorWidth, indicatorShape: Shape = CircleShape, @@ -115,9 +112,9 @@ private fun HorizontalPagerIndicator( pageCount: Int, modifier: Modifier = Modifier, pageIndexMapping: (Int) -> Int = { it }, - activeColor: Color = LocalContentColor.current.copy(alpha = LocalContentAlpha.current), - inactiveColor: Color = activeColor.copy(ContentAlpha.disabled), - indicatorWidth: Dp = 8.dp, + activeColor: Color = Color.White, + inactiveColor: Color = LocalColors.current.disabled, + indicatorWidth: Dp = LocalDimensions.current.xxsSpacing, indicatorHeight: Dp = indicatorWidth, spacing: Dp = indicatorWidth, indicatorShape: Shape = CircleShape, diff --git a/app/src/main/java/org/thoughtcrime/securesms/ui/Components.kt b/app/src/main/java/org/thoughtcrime/securesms/ui/Components.kt index 31ce715a8f..daeb6b853d 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/ui/Components.kt +++ b/app/src/main/java/org/thoughtcrime/securesms/ui/Components.kt @@ -7,13 +7,13 @@ import androidx.compose.animation.AnimatedVisibility import androidx.compose.foundation.Canvas import androidx.compose.foundation.Image import androidx.compose.foundation.ScrollState -import androidx.compose.foundation.clickable -import androidx.compose.foundation.layout.Arrangement +import androidx.compose.foundation.background import androidx.compose.foundation.layout.Box import androidx.compose.foundation.layout.BoxScope import androidx.compose.foundation.layout.Column -import androidx.compose.foundation.layout.Row +import androidx.compose.foundation.layout.ColumnScope import androidx.compose.foundation.layout.RowScope +import androidx.compose.foundation.layout.Spacer import androidx.compose.foundation.layout.fillMaxWidth import androidx.compose.foundation.layout.height import androidx.compose.foundation.layout.heightIn @@ -24,14 +24,14 @@ import androidx.compose.foundation.layout.wrapContentHeight import androidx.compose.foundation.layout.wrapContentSize import androidx.compose.foundation.lazy.LazyColumn import androidx.compose.foundation.lazy.itemsIndexed -import androidx.compose.material.ButtonColors -import androidx.compose.material.Card -import androidx.compose.material.Icon -import androidx.compose.material.LocalContentColor -import androidx.compose.material.MaterialTheme -import androidx.compose.material.RadioButton -import androidx.compose.material.Text -import androidx.compose.material.TextButton +import androidx.compose.material3.ButtonColors +import androidx.compose.material3.Card +import androidx.compose.material3.HorizontalDivider +import androidx.compose.material3.Icon +import androidx.compose.material3.LocalContentColor +import androidx.compose.material3.MaterialTheme +import androidx.compose.material3.Text +import androidx.compose.material3.TextButton import androidx.compose.runtime.Composable import androidx.compose.runtime.LaunchedEffect import androidx.compose.runtime.rememberCoroutineScope @@ -52,6 +52,7 @@ import androidx.compose.ui.res.stringResource import androidx.compose.ui.semantics.contentDescription import androidx.compose.ui.semantics.semantics import androidx.compose.ui.text.TextStyle +import androidx.compose.ui.tooling.preview.Preview import androidx.compose.ui.unit.Dp import androidx.compose.ui.unit.dp import androidx.compose.ui.viewinterop.AndroidView @@ -61,14 +62,15 @@ import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.launch import network.loki.messenger.R import org.session.libsession.utilities.recipients.Recipient -import org.session.libsession.utilities.runIf import org.thoughtcrime.securesms.components.ProfilePictureView -import org.thoughtcrime.securesms.conversation.disappearingmessages.ui.OptionsCard -import org.thoughtcrime.securesms.ui.color.LocalColors -import org.thoughtcrime.securesms.ui.color.divider -import org.thoughtcrime.securesms.ui.color.radioButtonColors -import org.thoughtcrime.securesms.ui.color.transparentButtonColors +import org.thoughtcrime.securesms.conversation.disappearingmessages.ui.OptionsCardData import org.thoughtcrime.securesms.ui.components.SmallCircularProgressIndicator +import org.thoughtcrime.securesms.ui.components.TitledRadioButton +import org.thoughtcrime.securesms.ui.theme.LocalColors +import org.thoughtcrime.securesms.ui.theme.LocalDimensions +import org.thoughtcrime.securesms.ui.theme.LocalType +import org.thoughtcrime.securesms.ui.theme.PreviewTheme +import org.thoughtcrime.securesms.ui.theme.transparentButtonColors import kotlin.math.min import kotlin.math.roundToInt @@ -92,18 +94,25 @@ data class RadioOption( ) @Composable -fun OptionsCard(card: OptionsCard, callbacks: Callbacks) { - Text( - card.title(), - style = base - ) - CellNoMargin { - LazyColumn( - modifier = Modifier.heightIn(max = 5000.dp) - ) { - itemsIndexed(card.options) { i, it -> - if (i != 0) Divider() - TitledRadioButton(it) { callbacks.setValue(it.value) } +fun OptionsCard(card: OptionsCardData, callbacks: Callbacks) { + Column { + Text( + modifier = Modifier.padding(start = LocalDimensions.current.smallSpacing), + text = card.title(), + style = LocalType.current.base, + color = LocalColors.current.textSecondary + ) + + Spacer(modifier = Modifier.height(LocalDimensions.current.xsSpacing)) + + CellNoMargin { + LazyColumn( + modifier = Modifier.heightIn(max = 5000.dp) + ) { + itemsIndexed(card.options) { i, it -> + if (i != 0) Divider() + TitledRadioButton(option = it) { callbacks.setValue(it.value) } + } } } } @@ -117,7 +126,10 @@ fun LargeItemButtonWithDrawable( colors: ButtonColors = transparentButtonColors(), onClick: () -> Unit ) { - ItemButtonWithDrawable(textId, icon, modifier.heightIn(min = LocalDimensions.current.minLargeItemButtonHeight), h8, colors, onClick) + ItemButtonWithDrawable( + textId, icon, modifier.heightIn(min = LocalDimensions.current.minLargeItemButtonHeight), + LocalType.current.h8, colors, onClick + ) } @Composable @@ -125,7 +137,7 @@ fun ItemButtonWithDrawable( @StringRes textId: Int, @DrawableRes icon: Int, modifier: Modifier = Modifier, - textStyle: TextStyle = xl, + textStyle: TextStyle = LocalType.current.xl, colors: ButtonColors = transparentButtonColors(), onClick: () -> Unit ) { @@ -155,7 +167,10 @@ fun LargeItemButton( colors: ButtonColors = transparentButtonColors(), onClick: () -> Unit ) { - ItemButton(textId, icon, modifier.heightIn(min = LocalDimensions.current.minLargeItemButtonHeight), h8, colors, onClick) + ItemButton( + textId, icon, modifier.heightIn(min = LocalDimensions.current.minLargeItemButtonHeight), + LocalType.current.h8, colors, onClick + ) } /** @@ -166,7 +181,7 @@ fun ItemButton( @StringRes textId: Int, @DrawableRes icon: Int, modifier: Modifier = Modifier, - textStyle: TextStyle = xl, + textStyle: TextStyle = LocalType.current.xl, colors: ButtonColors = transparentButtonColors(), onClick: () -> Unit ) { @@ -196,7 +211,7 @@ fun ItemButton( text: String, icon: @Composable BoxScope.() -> Unit, modifier: Modifier = Modifier, - textStyle: TextStyle = xl, + textStyle: TextStyle = LocalType.current.xl, colors: ButtonColors = transparentButtonColors(), onClick: () -> Unit ) { @@ -208,27 +223,42 @@ fun ItemButton( ) { Box( modifier = Modifier - .width(80.dp) + .width(50.dp) .wrapContentHeight() .align(Alignment.CenterVertically) ) { icon() } + + Spacer(modifier = Modifier.width(LocalDimensions.current.smallSpacing)) + Text( text, Modifier .fillMaxWidth() - .padding(vertical = LocalDimensions.current.xsItemSpacing) + .padding(vertical = LocalDimensions.current.xsSpacing) .align(Alignment.CenterVertically), style = textStyle ) } } +@Preview +@Composable +fun PrewviewItemButton() { + PreviewTheme { + ItemButton( + textId = R.string.activity_create_group_title, + icon = R.drawable.ic_group, + onClick = {} + ) + } +} + @Composable fun Cell( padding: Dp = 0.dp, - margin: Dp = LocalDimensions.current.margin, + margin: Dp = LocalDimensions.current.spacing, content: @Composable () -> Unit ) { CellWithPaddingAndMargin(padding, margin) { content() } @@ -240,64 +270,22 @@ fun CellNoMargin(content: @Composable () -> Unit) { @Composable fun CellWithPaddingAndMargin( - padding: Dp = LocalDimensions.current.smallMargin, - margin: Dp = LocalDimensions.current.margin, + padding: Dp = LocalDimensions.current.spacing, + margin: Dp = LocalDimensions.current.spacing, content: @Composable () -> Unit ) { - Card( - backgroundColor = LocalColors.current.backgroundSecondary, - shape = MaterialTheme.shapes.medium, - elevation = 0.dp, + Box( modifier = Modifier + .padding(horizontal = margin) + .background(color = LocalColors.current.backgroundSecondary, + shape = MaterialTheme.shapes.small) .wrapContentHeight() - .fillMaxWidth() - .padding(horizontal = margin), + .fillMaxWidth(), ) { Box(Modifier.padding(padding)) { content() } } } -@Composable -fun TitledRadioButton(option: RadioOption, onClick: () -> Unit) { - val color = if (option.enabled) LocalColors.current.text else LocalColors.current.disabled - Row( - horizontalArrangement = Arrangement.spacedBy(LocalDimensions.current.smallItemSpacing), - modifier = Modifier - .runIf(option.enabled) { clickable { if (!option.selected) onClick() } } - .heightIn(min = 60.dp) - .padding(horizontal = LocalDimensions.current.margin) - .contentDescription(option.contentDescription) - ) { - Column(modifier = Modifier - .weight(1f) - .align(Alignment.CenterVertically)) { - Column { - Text( - text = option.title(), - style = large, - color = color - ) - option.subtitle?.let { - Text( - text = it(), - style = extraSmall, - color = color - ) - } - } - } - RadioButton( - selected = option.selected, - onClick = null, - modifier = Modifier - .height(26.dp) - .align(Alignment.CenterVertically), - enabled = option.enabled, - colors = LocalColors.current.radioButtonColors() - ) - } -} - @Composable fun Modifier.contentDescription(text: GetString?): Modifier { return text?.let { @@ -357,10 +345,10 @@ fun Modifier.fadingEdges( @Composable fun Divider(modifier: Modifier = Modifier, startIndent: Dp = 0.dp) { - androidx.compose.material.Divider( - modifier = modifier.padding(horizontal = LocalDimensions.current.xsMargin), - color = LocalColors.current.divider, - startIndent = startIndent + HorizontalDivider( + modifier = modifier.padding(horizontal = LocalDimensions.current.smallSpacing) + .padding(start = startIndent), + color = LocalColors.current.borders, ) } @@ -392,7 +380,7 @@ fun ProgressArc(progress: Float, modifier: Modifier = Modifier) { "${text}%", color = Color.White, modifier = Modifier.align(Alignment.Center), - style = h2 + style = LocalType.current.h2 ) } } diff --git a/app/src/main/java/org/thoughtcrime/securesms/ui/Dimensions.kt b/app/src/main/java/org/thoughtcrime/securesms/ui/Dimensions.kt deleted file mode 100644 index 0a04880e8a..0000000000 --- a/app/src/main/java/org/thoughtcrime/securesms/ui/Dimensions.kt +++ /dev/null @@ -1,32 +0,0 @@ -package org.thoughtcrime.securesms.ui - -import androidx.compose.runtime.staticCompositionLocalOf -import androidx.compose.ui.unit.Dp -import androidx.compose.ui.unit.dp - -val LocalDimensions = staticCompositionLocalOf { Dimensions() } - -data class Dimensions( - val xxxsItemSpacing: Dp = 4.dp, - val xxsItemSpacing: Dp = 8.dp, - val xsItemSpacing: Dp = 12.dp, - val smallItemSpacing: Dp = 16.dp, - val itemSpacing: Dp = 24.dp, - - val xxxsMargin: Dp = 8.dp, - val xxsMargin: Dp = 12.dp, - val xsMargin: Dp = 16.dp, - val smallMargin: Dp = 24.dp, - val margin: Dp = 32.dp, - val onboardingMargin: Dp = 36.dp, - val largeMargin: Dp = 64.dp, - val homeEmptyViewMargin: Dp = 50.dp, - - val dividerIndent: Dp = 80.dp, - val appBarHeight: Dp = 64.dp, - val minScrollableViewHeight: Dp = 200.dp, - val minLargeItemButtonHeight: Dp = 60.dp, - - val indicatorHeight: Dp = 4.dp, - val borderStroke: Dp = 1.dp -) diff --git a/app/src/main/java/org/thoughtcrime/securesms/ui/SessionTypography.kt b/app/src/main/java/org/thoughtcrime/securesms/ui/SessionTypography.kt deleted file mode 100644 index 426c6f8dc4..0000000000 --- a/app/src/main/java/org/thoughtcrime/securesms/ui/SessionTypography.kt +++ /dev/null @@ -1,59 +0,0 @@ -package org.thoughtcrime.securesms.ui - -import androidx.compose.material.Typography -import androidx.compose.ui.text.TextStyle -import androidx.compose.ui.text.font.FontFamily -import androidx.compose.ui.text.font.FontFamily.Companion.Monospace -import androidx.compose.ui.text.font.FontWeight -import androidx.compose.ui.unit.TextUnit -import androidx.compose.ui.unit.sp - -fun boldStyle(size: TextUnit) = TextStyle.Default.copy( - fontSize = size, - lineHeight = size * 1.2, - fontWeight = FontWeight.Bold, -) - -fun defaultStyle(size: TextUnit, fontFamily: FontFamily? = TextStyle.Default.fontFamily) = TextStyle.Default.copy( - fontSize = size, - lineHeight = size * 1.2, - fontFamily = fontFamily -) - -val xl = defaultStyle(18.sp) - -val large = defaultStyle(16.sp) -val largeBold = boldStyle(16.sp) - -val base = defaultStyle(14.sp) -val baseBold = boldStyle(14.sp) -val baseMonospace = defaultStyle(14.sp, fontFamily = Monospace) - -val small = defaultStyle(12.sp) -val smallBold = boldStyle(12.sp) -val smallMonospace = defaultStyle(12.sp, fontFamily = Monospace) - -val extraSmall = defaultStyle(11.sp) -val extraSmallBold = boldStyle(11.sp) -val extraSmallMonospace = defaultStyle(11.sp, fontFamily = Monospace) - -val fine = defaultStyle(9.sp) - -val h1 = boldStyle(36.sp) -val h2 = boldStyle(32.sp) -val h3 = boldStyle(29.sp) -val h4 = boldStyle(26.sp) -val h5 = boldStyle(23.sp) -val h6 = boldStyle(20.sp) -val h7 = boldStyle(18.sp) -val h8 = boldStyle(16.sp) -val h9 = boldStyle(14.sp) - -val sessionTypography = Typography( - h1 = h1, - h2 = h2, - h3 = h3, - h4 = h4, - h5 = h5, - h6 = h6, -) diff --git a/app/src/main/java/org/thoughtcrime/securesms/ui/Util.kt b/app/src/main/java/org/thoughtcrime/securesms/ui/Util.kt index d7aaf5430c..b49f9c6d60 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/ui/Util.kt +++ b/app/src/main/java/org/thoughtcrime/securesms/ui/Util.kt @@ -5,6 +5,7 @@ import android.content.Context import androidx.compose.runtime.Composable import androidx.compose.ui.platform.ComposeView import androidx.fragment.app.Fragment +import org.thoughtcrime.securesms.ui.theme.SessionMaterialTheme fun Activity.setComposeContent(content: @Composable () -> Unit) { ComposeView(this) diff --git a/app/src/main/java/org/thoughtcrime/securesms/ui/color/Colors.kt b/app/src/main/java/org/thoughtcrime/securesms/ui/color/Colors.kt deleted file mode 100644 index 9aab573cee..0000000000 --- a/app/src/main/java/org/thoughtcrime/securesms/ui/color/Colors.kt +++ /dev/null @@ -1,233 +0,0 @@ -package org.thoughtcrime.securesms.ui.color - -import androidx.compose.foundation.background -import androidx.compose.foundation.isSystemInDarkTheme -import androidx.compose.foundation.layout.Box -import androidx.compose.foundation.layout.Column -import androidx.compose.foundation.text.selection.TextSelectionColors -import androidx.compose.material.ButtonDefaults -import androidx.compose.material.MaterialTheme -import androidx.compose.material.RadioButtonDefaults -import androidx.compose.material.TabRowDefaults -import androidx.compose.material.Text -import androidx.compose.material.TextFieldDefaults -import androidx.compose.material.primarySurface -import androidx.compose.runtime.Composable -import androidx.compose.runtime.staticCompositionLocalOf -import androidx.compose.ui.Modifier -import androidx.compose.ui.graphics.Color -import androidx.compose.ui.graphics.takeOrElse -import androidx.compose.ui.tooling.preview.Preview -import androidx.compose.ui.tooling.preview.PreviewParameter -import org.thoughtcrime.securesms.ui.PreviewTheme -import org.thoughtcrime.securesms.ui.SessionColorsParameterProvider -import org.thoughtcrime.securesms.ui.base - -val LocalColors = staticCompositionLocalOf { ClassicDark() } - -interface Colors { - val isLight: Boolean - val primary: Color - val danger: Color - val disabled: Color - val background: Color - val backgroundSecondary: Color - val text: Color - val textSecondary: Color - val borders: Color - val textBubbleSent: Color - val backgroundBubbleReceived: Color - val textBubbleReceived: Color - val backgroundBubbleSent: Color get() = primary - val qrCodeContent: Color - val qrCodeBackground: Color - val primaryButtonFill: Color - val primaryButtonFillText: Color -} - -fun Colors.text(isError: Boolean): Color = if (isError) danger else text -fun Colors.textSecondary(isError: Boolean): Color = if (isError) danger else textSecondary -fun Colors.borders(isError: Boolean): Color = if (isError) danger else borders - -val Colors.textSelectionColors get() = TextSelectionColors( - handleColor = primary, - backgroundColor = primary.copy(alpha = 0.5f) -) - -data class ClassicDark(override val primary: Color = primaryGreen): Colors { - override val isLight = false - override val danger = dangerDark - override val disabled = disabledDark - override val background = classicDark0 - override val backgroundSecondary = classicDark1 - override val text = classicDark6 - override val textSecondary = classicDark5 - override val borders = classicDark3 - override val textBubbleSent = Color.Black - override val backgroundBubbleReceived = classicDark2 - override val textBubbleReceived = Color.White - override val qrCodeContent = background - override val qrCodeBackground = text - override val primaryButtonFill = primary - override val primaryButtonFillText = Color.Black -} - -data class ClassicLight(override val primary: Color = primaryGreen): Colors { - override val isLight = true - override val danger = dangerLight - override val disabled = disabledLight - override val background = classicLight6 - override val backgroundSecondary = classicLight5 - override val text = classicLight0 - override val textSecondary = classicLight1 - override val borders = classicLight3 - override val textBubbleSent = Color.Black - override val backgroundBubbleReceived = classicLight4 - override val textBubbleReceived = classicLight4 - override val qrCodeContent = text - override val qrCodeBackground = backgroundSecondary - override val primaryButtonFill = text - override val primaryButtonFillText = Color.White -} - -data class OceanDark(override val primary: Color = primaryBlue): Colors { - override val isLight = false - override val danger = dangerDark - override val disabled = disabledDark - override val background = oceanDark2 - override val backgroundSecondary = oceanDark1 - override val text = oceanDark7 - override val textSecondary = oceanDark5 - override val borders = oceanDark4 - override val textBubbleSent = Color.Black - override val backgroundBubbleReceived = oceanDark4 - override val textBubbleReceived = oceanDark4 - override val qrCodeContent = background - override val qrCodeBackground = text - override val primaryButtonFill = primary - override val primaryButtonFillText = Color.Black -} - -data class OceanLight(override val primary: Color = primaryBlue): Colors { - override val isLight = true - override val danger = dangerLight - override val disabled = disabledLight - override val background = oceanLight7 - override val backgroundSecondary = oceanLight6 - override val text = oceanLight1 - override val textSecondary = oceanLight2 - override val borders = oceanLight3 - override val textBubbleSent = oceanLight1 - override val backgroundBubbleReceived = oceanLight4 - override val textBubbleReceived = oceanLight1 - override val qrCodeContent = text - override val qrCodeBackground = backgroundSecondary - override val primaryButtonFill = text - override val primaryButtonFillText = Color.White -} - -@Composable -fun Colors(name: String, colors: List) { - Column { - colors.forEachIndexed { i, it -> - Box(Modifier.background(it)) { - Text("$name: $i") - } - } - } -} - -@Preview -@Composable -fun PreviewThemeColors( - @PreviewParameter(SessionColorsParameterProvider::class) colors: Colors -) { - PreviewTheme(colors) { ThemeColors() } -} - -@Composable -private fun ThemeColors() { - Column { - Box(Modifier.background(MaterialTheme.colors.primary)) { - Text("primary", style = base) - } - Box(Modifier.background(MaterialTheme.colors.primaryVariant)) { - Text("primaryVariant", style = base) - } - Box(Modifier.background(MaterialTheme.colors.secondary)) { - Text("secondary", style = base) - } - Box(Modifier.background(MaterialTheme.colors.secondaryVariant)) { - Text("secondaryVariant", style = base) - } - Box(Modifier.background(MaterialTheme.colors.surface)) { - Text("surface", style = base) - } - Box(Modifier.background(MaterialTheme.colors.primarySurface)) { - Text("primarySurface", style = base) - } - Box(Modifier.background(MaterialTheme.colors.background)) { - Text("background", style = base) - } - Box(Modifier.background(MaterialTheme.colors.error)) { - Text("error", style = base) - } - } -} - -@Composable -fun Colors.outlinedTextFieldColors( - isError: Boolean -) = TextFieldDefaults.outlinedTextFieldColors( - textColor = if (isError) danger else text, - cursorColor = if (isError) danger else text, - focusedBorderColor = borders, - unfocusedBorderColor = borders, - placeholderColor = if (isError) danger else textSecondary -) - -val Colors.divider get() = text.copy(alpha = TabRowDefaults.DividerOpacity) - -@Composable -fun Colors.radioButtonColors() = RadioButtonDefaults.colors( - selectedColor = primary, - unselectedColor = text, - disabledColor = disabled -) - -@Composable -fun transparentButtonColors() = ButtonDefaults.buttonColors(backgroundColor = Color.Transparent) - -@Composable -fun destructiveButtonColors() = ButtonDefaults.buttonColors(backgroundColor = Color.Transparent, contentColor = LocalColors.current.danger) - - -/** - * This class holds two instances of [Colors], [light] representing the [Colors] to use when the system is in a - * light theme, and [dark] representing the [Colors] to use when the system is in a dark theme. - * - * If the user has [followSystemSettings] turned on then [light] should be equal to [dark]. - */ -data class LightDarkColors( - val light: Colors, - val dark: Colors -) { - @Composable - fun colors() = if (light == dark || isSystemInDarkTheme()) dark else light -} - -/** - * Courtesy constructor that sets [light] and [dark] based on properties. - */ -fun LightDarkColors(isClassic: Boolean, isLight: Boolean, followSystemSettings: Boolean, primaryOrUnspecified: Color): LightDarkColors { - val primary = primaryOrUnspecified.takeOrElse { if (isClassic) primaryGreen else primaryBlue } - val light = when { - isLight || followSystemSettings -> if (isClassic) ClassicLight(primary) else OceanLight(primary) - else -> if (isClassic) ClassicDark(primary) else OceanDark(primary) - } - val dark = when { - isLight && !followSystemSettings -> if (isClassic) ClassicLight(primary) else OceanLight(primary) - else -> if (isClassic) ClassicDark(primary) else OceanDark(primary) - } - return LightDarkColors(light, dark) -} diff --git a/app/src/main/java/org/thoughtcrime/securesms/ui/color/ColorsFromPreferences.kt b/app/src/main/java/org/thoughtcrime/securesms/ui/color/ColorsFromPreferences.kt deleted file mode 100644 index a65c08cba0..0000000000 --- a/app/src/main/java/org/thoughtcrime/securesms/ui/color/ColorsFromPreferences.kt +++ /dev/null @@ -1,34 +0,0 @@ -package org.thoughtcrime.securesms.ui.color - -import androidx.compose.runtime.Composable -import androidx.compose.ui.graphics.Color -import org.session.libsession.utilities.TextSecurePreferences -import org.session.libsession.utilities.TextSecurePreferences.Companion.BLUE_ACCENT -import org.session.libsession.utilities.TextSecurePreferences.Companion.CLASSIC_DARK -import org.session.libsession.utilities.TextSecurePreferences.Companion.CLASSIC_LIGHT -import org.session.libsession.utilities.TextSecurePreferences.Companion.GREEN_ACCENT -import org.session.libsession.utilities.TextSecurePreferences.Companion.OCEAN_LIGHT -import org.session.libsession.utilities.TextSecurePreferences.Companion.ORANGE_ACCENT -import org.session.libsession.utilities.TextSecurePreferences.Companion.PINK_ACCENT -import org.session.libsession.utilities.TextSecurePreferences.Companion.PURPLE_ACCENT -import org.session.libsession.utilities.TextSecurePreferences.Companion.RED_ACCENT -import org.session.libsession.utilities.TextSecurePreferences.Companion.YELLOW_ACCENT - -/** - * Retrieve the current [Colors] from [TextSecurePreferences] and current system settings. - */ -@Composable -fun TextSecurePreferences.colors(): Colors = lightDarkColors().colors() -private fun TextSecurePreferences.lightDarkColors() = LightDarkColors(isClassic(), isLight(), getFollowSystemSettings(), primaryColor()) -private fun TextSecurePreferences.isLight(): Boolean = getThemeStyle() in setOf(CLASSIC_LIGHT, OCEAN_LIGHT) -private fun TextSecurePreferences.isClassic(): Boolean = getThemeStyle() in setOf(CLASSIC_DARK, CLASSIC_LIGHT) -private fun TextSecurePreferences.primaryColor(): Color = when(getSelectedAccentColor()) { - GREEN_ACCENT -> primaryGreen - BLUE_ACCENT -> primaryBlue - PURPLE_ACCENT -> primaryPurple - PINK_ACCENT -> primaryPink - RED_ACCENT -> primaryRed - ORANGE_ACCENT -> primaryOrange - YELLOW_ACCENT -> primaryYellow - else -> Color.Unspecified -} diff --git a/app/src/main/java/org/thoughtcrime/securesms/ui/components/AppBar.kt b/app/src/main/java/org/thoughtcrime/securesms/ui/components/AppBar.kt index 1069275056..b64a482d8a 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/ui/components/AppBar.kt +++ b/app/src/main/java/org/thoughtcrime/securesms/ui/components/AppBar.kt @@ -5,9 +5,9 @@ import androidx.compose.foundation.layout.Row import androidx.compose.foundation.layout.Spacer import androidx.compose.foundation.layout.height import androidx.compose.foundation.layout.size -import androidx.compose.material.Icon -import androidx.compose.material.IconButton -import androidx.compose.material.Text +import androidx.compose.material3.Icon +import androidx.compose.material3.IconButton +import androidx.compose.material3.Text import androidx.compose.runtime.Composable import androidx.compose.ui.Alignment import androidx.compose.ui.Modifier @@ -15,16 +15,16 @@ import androidx.compose.ui.res.painterResource import androidx.compose.ui.tooling.preview.Preview import androidx.compose.ui.tooling.preview.PreviewParameter import network.loki.messenger.R -import org.thoughtcrime.securesms.ui.LocalDimensions -import org.thoughtcrime.securesms.ui.PreviewTheme -import org.thoughtcrime.securesms.ui.SessionColorsParameterProvider -import org.thoughtcrime.securesms.ui.color.Colors -import org.thoughtcrime.securesms.ui.h4 +import org.thoughtcrime.securesms.ui.theme.LocalDimensions +import org.thoughtcrime.securesms.ui.theme.LocalType +import org.thoughtcrime.securesms.ui.theme.PreviewTheme +import org.thoughtcrime.securesms.ui.theme.SessionColorsParameterProvider +import org.thoughtcrime.securesms.ui.theme.ThemeColors @Preview @Composable fun AppBarPreview( - @PreviewParameter(SessionColorsParameterProvider::class) colors: Colors + @PreviewParameter(SessionColorsParameterProvider::class) colors: ThemeColors ) { PreviewTheme(colors) { AppBar(title = "Title", {}, {}) @@ -42,7 +42,7 @@ fun AppBar(title: String, onClose: () -> Unit = {}, onBack: (() -> Unit)? = null } } Spacer(modifier = Modifier.weight(1f)) - Text(text = title, style = h4) + Text(text = title, style = LocalType.current.h4) Spacer(modifier = Modifier.weight(1f)) Box(contentAlignment = Alignment.Center, modifier = Modifier.size(LocalDimensions.current.appBarHeight)) { IconButton(onClick = onClose) { diff --git a/app/src/main/java/org/thoughtcrime/securesms/ui/components/Border.kt b/app/src/main/java/org/thoughtcrime/securesms/ui/components/Border.kt index 4039f84ec6..4420e91e78 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/ui/components/Border.kt +++ b/app/src/main/java/org/thoughtcrime/securesms/ui/components/Border.kt @@ -1,12 +1,12 @@ package org.thoughtcrime.securesms.ui.components import androidx.compose.foundation.border -import androidx.compose.material.MaterialTheme +import androidx.compose.material3.MaterialTheme import androidx.compose.runtime.Composable import androidx.compose.ui.Modifier import androidx.compose.ui.graphics.SolidColor -import org.thoughtcrime.securesms.ui.LocalDimensions -import org.thoughtcrime.securesms.ui.color.LocalColors +import org.thoughtcrime.securesms.ui.theme.LocalDimensions +import org.thoughtcrime.securesms.ui.theme.LocalColors @Composable fun Modifier.border() = this.border( diff --git a/app/src/main/java/org/thoughtcrime/securesms/ui/components/Button.kt b/app/src/main/java/org/thoughtcrime/securesms/ui/components/Button.kt index 23aee8c011..5834f2f859 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/ui/components/Button.kt +++ b/app/src/main/java/org/thoughtcrime/securesms/ui/components/Button.kt @@ -16,8 +16,8 @@ import androidx.compose.foundation.layout.PaddingValues import androidx.compose.foundation.layout.RowScope import androidx.compose.foundation.layout.heightIn import androidx.compose.foundation.layout.padding -import androidx.compose.material.ButtonColors -import androidx.compose.material.Text +import androidx.compose.material3.ButtonColors +import androidx.compose.material3.Text import androidx.compose.runtime.Composable import androidx.compose.runtime.getValue import androidx.compose.runtime.mutableStateOf @@ -37,13 +37,14 @@ import kotlinx.coroutines.flow.collectLatest import kotlinx.coroutines.flow.filter import network.loki.messenger.R import org.thoughtcrime.securesms.ui.LaunchedEffectAsync -import org.thoughtcrime.securesms.ui.PreviewTheme -import org.thoughtcrime.securesms.ui.SessionColorsParameterProvider -import org.thoughtcrime.securesms.ui.baseBold -import org.thoughtcrime.securesms.ui.buttonShape -import org.thoughtcrime.securesms.ui.color.Colors -import org.thoughtcrime.securesms.ui.color.LocalColors import org.thoughtcrime.securesms.ui.contentDescription +import org.thoughtcrime.securesms.ui.theme.LocalColors +import org.thoughtcrime.securesms.ui.theme.LocalType +import org.thoughtcrime.securesms.ui.theme.PreviewTheme +import org.thoughtcrime.securesms.ui.theme.SessionColorsParameterProvider +import org.thoughtcrime.securesms.ui.theme.ThemeColors +import org.thoughtcrime.securesms.ui.theme.bold +import org.thoughtcrime.securesms.ui.theme.buttonShape import kotlin.time.Duration import kotlin.time.Duration.Companion.seconds @@ -65,16 +66,16 @@ fun Button( content: @Composable RowScope.() -> Unit ) { style.applyButtonConstraints { - androidx.compose.material.Button( - onClick, - modifier.heightIn(min = style.minHeight), - enabled, - interactionSource, + androidx.compose.material3.Button( + onClick = onClick, + modifier = modifier.heightIn(min = style.minHeight), + enabled = enabled, + interactionSource = interactionSource, elevation = null, - shape, - border, - colors, - contentPadding + shape = shape, + border = border, + colors = colors, + contentPadding = contentPadding ) { // Button sets LocalTextStyle, so text style is applied inside to override that. style.applyTextConstraints { @@ -129,11 +130,11 @@ fun Button( Button(text, onClick, ButtonType.Outline(LocalColors.current.primaryButtonFill), modifier, enabled) } -@Composable fun PrimaryOutlineButton(onClick: () -> Unit, modifier: Modifier = Modifier, enabled: Boolean = true, content: @Composable RowScope.() -> Unit) { +@Composable fun PrimaryOutlineButton(modifier: Modifier = Modifier, enabled: Boolean = true, onClick: () -> Unit, content: @Composable RowScope.() -> Unit) { Button(onClick, ButtonType.Outline(LocalColors.current.primaryButtonFill), modifier, enabled, content = content) } -@Composable fun SlimOutlineButton(onClick: () -> Unit, modifier: Modifier = Modifier, color: Color = LocalColors.current.text, enabled: Boolean = true, content: @Composable RowScope.() -> Unit) { +@Composable fun SlimOutlineButton(modifier: Modifier = Modifier, color: Color = LocalColors.current.text, enabled: Boolean = true, onClick: () -> Unit, content: @Composable RowScope.() -> Unit) { Button(onClick, ButtonType.Outline(color), modifier, enabled, ButtonStyle.Slim, content = content) } @@ -257,7 +258,7 @@ fun BorderlessButtonWithIcon( text: String, @DrawableRes iconRes: Int, modifier: Modifier = Modifier, - style: TextStyle = baseBold, + style: TextStyle = LocalType.current.base.bold(), color: Color = LocalColors.current.text, onClick: () -> Unit ) { @@ -292,7 +293,7 @@ val MutableInteractionSource.releases @Preview @Composable private fun VariousButtons( - @PreviewParameter(SessionColorsParameterProvider::class) colors: Colors + @PreviewParameter(SessionColorsParameterProvider::class) colors: ThemeColors ) { PreviewTheme(colors) { FlowRow( diff --git a/app/src/main/java/org/thoughtcrime/securesms/ui/components/ButtonStyle.kt b/app/src/main/java/org/thoughtcrime/securesms/ui/components/ButtonStyle.kt index 3f566911db..e8fcca06cf 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/ui/components/ButtonStyle.kt +++ b/app/src/main/java/org/thoughtcrime/securesms/ui/components/ButtonStyle.kt @@ -1,21 +1,20 @@ package org.thoughtcrime.securesms.ui.components import android.annotation.SuppressLint -import androidx.compose.material.ExperimentalMaterialApi -import androidx.compose.material.LocalMinimumInteractiveComponentEnforcement -import androidx.compose.material.LocalTextStyle +import androidx.compose.material3.ExperimentalMaterial3Api +import androidx.compose.material3.LocalMinimumInteractiveComponentEnforcement +import androidx.compose.material3.LocalTextStyle import androidx.compose.runtime.Composable import androidx.compose.runtime.CompositionLocalProvider import androidx.compose.ui.text.TextStyle import androidx.compose.ui.text.style.TextAlign import androidx.compose.ui.unit.Dp import androidx.compose.ui.unit.dp -import org.thoughtcrime.securesms.ui.baseBold -import org.thoughtcrime.securesms.ui.extraSmall -import org.thoughtcrime.securesms.ui.extraSmallBold +import org.thoughtcrime.securesms.ui.theme.LocalType +import org.thoughtcrime.securesms.ui.theme.bold interface ButtonStyle { - @OptIn(ExperimentalMaterialApi::class) + @OptIn(ExperimentalMaterial3Api::class) @SuppressLint("ComposableNaming") @Composable fun applyButtonConstraints(content: @Composable () -> Unit) { CompositionLocalProvider( @@ -27,26 +26,34 @@ interface ButtonStyle { @SuppressLint("ComposableNaming") @Composable fun applyTextConstraints(content: @Composable () -> Unit) { CompositionLocalProvider( - LocalTextStyle provides textStyle, + LocalTextStyle provides textStyle(), content = content ) } - val textStyle: TextStyle + @Composable + fun textStyle() : TextStyle + val minHeight: Dp object Large: ButtonStyle { - override val textStyle = baseBold.copy(textAlign = TextAlign.Center) + @Composable + override fun textStyle() = LocalType.current.base.bold() + .copy(textAlign = TextAlign.Center) override val minHeight = 41.dp } object Slim: ButtonStyle { - override val textStyle = extraSmallBold.copy(textAlign = TextAlign.Center) + @Composable + override fun textStyle() = LocalType.current.extraSmall.bold() + .copy(textAlign = TextAlign.Center) override val minHeight = 29.dp } object Borderless: ButtonStyle { - override val textStyle = extraSmall.copy(textAlign = TextAlign.Center) + @Composable + override fun textStyle() = LocalType.current.extraSmall + .copy(textAlign = TextAlign.Center) override val minHeight = 37.dp } } diff --git a/app/src/main/java/org/thoughtcrime/securesms/ui/components/ButtonType.kt b/app/src/main/java/org/thoughtcrime/securesms/ui/components/ButtonType.kt index e941ab4e83..54478e69b5 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/ui/components/ButtonType.kt +++ b/app/src/main/java/org/thoughtcrime/securesms/ui/components/ButtonType.kt @@ -2,13 +2,13 @@ package org.thoughtcrime.securesms.ui.components import androidx.compose.foundation.BorderStroke import androidx.compose.foundation.layout.PaddingValues -import androidx.compose.material.ButtonColors -import androidx.compose.material.ButtonDefaults +import androidx.compose.material3.ButtonColors +import androidx.compose.material3.ButtonDefaults import androidx.compose.runtime.Composable import androidx.compose.ui.graphics.Color import androidx.compose.ui.unit.dp -import org.thoughtcrime.securesms.ui.LocalDimensions -import org.thoughtcrime.securesms.ui.color.LocalColors +import org.thoughtcrime.securesms.ui.theme.LocalColors +import org.thoughtcrime.securesms.ui.theme.LocalDimensions private val disabledBorder @Composable get() = BorderStroke( width = LocalDimensions.current.borderStroke, @@ -35,9 +35,9 @@ interface ButtonType { @Composable override fun buttonColors() = ButtonDefaults.buttonColors( contentColor = contentColor, - backgroundColor = Color.Unspecified, + containerColor = Color.Transparent, disabledContentColor = LocalColors.current.disabled, - disabledBackgroundColor = Color.Unspecified + disabledContainerColor = Color.Transparent ) } @@ -47,9 +47,9 @@ interface ButtonType { @Composable override fun buttonColors() = ButtonDefaults.buttonColors( contentColor = LocalColors.current.background, - backgroundColor = LocalColors.current.text, + containerColor = LocalColors.current.text, disabledContentColor = LocalColors.current.disabled, - disabledBackgroundColor = Color.Unspecified + disabledContainerColor = Color.Transparent ) } @@ -59,9 +59,9 @@ interface ButtonType { @Composable override fun buttonColors() = ButtonDefaults.buttonColors( contentColor = LocalColors.current.primaryButtonFillText, - backgroundColor = LocalColors.current.primaryButtonFill, + containerColor = LocalColors.current.primaryButtonFill, disabledContentColor = LocalColors.current.disabled, - disabledBackgroundColor = Color.Unspecified + disabledContainerColor = Color.Transparent ) } @@ -73,8 +73,9 @@ interface ButtonType { @Composable override fun buttonColors() = ButtonDefaults.outlinedButtonColors( contentColor = color, - backgroundColor = Color.Transparent, - disabledContentColor = LocalColors.current.disabled + containerColor = Color.Transparent, + disabledContentColor = LocalColors.current.disabled, + disabledContainerColor = Color.Transparent ) } } diff --git a/app/src/main/java/org/thoughtcrime/securesms/ui/components/CircularProgressIndicator.kt b/app/src/main/java/org/thoughtcrime/securesms/ui/components/CircularProgressIndicator.kt index 259bbfe170..bbad82d29e 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/ui/components/CircularProgressIndicator.kt +++ b/app/src/main/java/org/thoughtcrime/securesms/ui/components/CircularProgressIndicator.kt @@ -1,7 +1,7 @@ package org.thoughtcrime.securesms.ui.components import androidx.compose.foundation.layout.size -import androidx.compose.material.LocalContentColor +import androidx.compose.material3.LocalContentColor import androidx.compose.runtime.Composable import androidx.compose.ui.Modifier import androidx.compose.ui.graphics.Color @@ -9,7 +9,7 @@ import androidx.compose.ui.unit.dp @Composable fun CircularProgressIndicator(color: Color = LocalContentColor.current) { - androidx.compose.material.CircularProgressIndicator( + androidx.compose.material3.CircularProgressIndicator( modifier = Modifier.size(40.dp), color = color, strokeWidth = 2.dp @@ -18,7 +18,7 @@ fun CircularProgressIndicator(color: Color = LocalContentColor.current) { @Composable fun SmallCircularProgressIndicator(color: Color = LocalContentColor.current) { - androidx.compose.material.CircularProgressIndicator( + androidx.compose.material3.CircularProgressIndicator( modifier = Modifier.size(20.dp), color = color, strokeWidth = 2.dp diff --git a/app/src/main/java/org/thoughtcrime/securesms/ui/components/QR.kt b/app/src/main/java/org/thoughtcrime/securesms/ui/components/QR.kt index 2d95a13d5c..c58f7dc97f 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/ui/components/QR.kt +++ b/app/src/main/java/org/thoughtcrime/securesms/ui/components/QR.kt @@ -22,11 +22,11 @@ import androidx.compose.foundation.layout.height import androidx.compose.foundation.layout.padding import androidx.compose.foundation.layout.size import androidx.compose.foundation.shape.RoundedCornerShape -import androidx.compose.material.Scaffold -import androidx.compose.material.Snackbar -import androidx.compose.material.SnackbarHost -import androidx.compose.material.Text -import androidx.compose.material.rememberScaffoldState +import androidx.compose.material3.Scaffold +import androidx.compose.material3.Snackbar +import androidx.compose.material3.SnackbarHost +import androidx.compose.material3.SnackbarHostState +import androidx.compose.material3.Text import androidx.compose.runtime.Composable import androidx.compose.runtime.DisposableEffect import androidx.compose.runtime.LaunchedEffect @@ -56,10 +56,9 @@ import kotlinx.coroutines.flow.Flow import kotlinx.coroutines.launch import network.loki.messenger.R import org.session.libsignal.utilities.Log -import org.thoughtcrime.securesms.ui.LocalDimensions -import org.thoughtcrime.securesms.ui.base -import org.thoughtcrime.securesms.ui.color.LocalColors -import org.thoughtcrime.securesms.ui.xl +import org.thoughtcrime.securesms.ui.theme.LocalColors +import org.thoughtcrime.securesms.ui.theme.LocalDimensions +import org.thoughtcrime.securesms.ui.theme.LocalType import java.util.concurrent.Executors private const val TAG = "NewMessageFragment" @@ -78,7 +77,6 @@ fun MaybeScanQrCode( Box( modifier = Modifier .fillMaxSize() - .background(LocalColors.current.background) ) { LocalSoftwareKeyboardController.current?.hide() @@ -94,10 +92,10 @@ fun MaybeScanQrCode( ) { Text( stringResource(R.string.activity_link_camera_permission_permanently_denied_configure_in_settings), - style = base, + style = LocalType.current.base, textAlign = TextAlign.Center ) - Spacer(modifier = Modifier.size(LocalDimensions.current.itemSpacing)) + Spacer(modifier = Modifier.size(LocalDimensions.current.spacing)) OutlineButton( stringResource(R.string.sessionSettings), modifier = Modifier.align(Alignment.CenterHorizontally), @@ -107,14 +105,14 @@ fun MaybeScanQrCode( } else { Column( modifier = Modifier - .background(color = LocalColors.current.backgroundSecondary) .fillMaxSize() - .padding(LocalDimensions.current.largeMargin), + .padding(LocalDimensions.current.xlargeSpacing), horizontalAlignment = Alignment.CenterHorizontally ) { Spacer(modifier = Modifier.weight(1f)) - Text(stringResource(R.string.fragment_scan_qr_code_camera_access_explanation), style = xl, textAlign = TextAlign.Center) - Spacer(modifier = Modifier.height(LocalDimensions.current.itemSpacing)) + Text(stringResource(R.string.fragment_scan_qr_code_camera_access_explanation), + style = LocalType.current.xl, textAlign = TextAlign.Center) + Spacer(modifier = Modifier.height(LocalDimensions.current.spacing)) PrimaryOutlineButton( stringResource(R.string.cameraGrantAccess), modifier = Modifier.fillMaxWidth(), @@ -158,13 +156,12 @@ fun ScanQrCode(errors: Flow, onScan: (String) -> Unit) { } } - val scaffoldState = rememberScaffoldState() - + val snackbarHostState = remember { SnackbarHostState() } val scope = rememberCoroutineScope() LaunchedEffect(Unit) { errors.collect { error -> - scaffoldState.snackbarHostState + snackbarHostState .takeIf { it.currentSnackbarData == null } ?.run { scope.launch { @@ -175,22 +172,21 @@ fun ScanQrCode(errors: Flow, onScan: (String) -> Unit) { // Don't use debounce() because many QR scans can come through each second, // and each scan could restart the timer which could mean no scan gets // through until the user stops scanning; quite perplexing. - showSnackbar(message = error) + snackbarHostState.showSnackbar(message = error) } } } } Scaffold( - scaffoldState = scaffoldState, snackbarHost = { SnackbarHost( - hostState = scaffoldState.snackbarHostState, - modifier = Modifier.padding(LocalDimensions.current.smallItemSpacing) + hostState = snackbarHostState, + modifier = Modifier.padding(LocalDimensions.current.smallSpacing) ) { data -> Snackbar( snackbarData = data, - modifier = Modifier.padding(LocalDimensions.current.smallItemSpacing) + modifier = Modifier.padding(LocalDimensions.current.smallSpacing) ) } } @@ -204,7 +200,7 @@ fun ScanQrCode(errors: Flow, onScan: (String) -> Unit) { Box( Modifier .aspectRatio(1f) - .padding(LocalDimensions.current.itemSpacing) + .padding(LocalDimensions.current.spacing) .clip(shape = RoundedCornerShape(26.dp)) .background(Color(0x33ffffff)) .align(Alignment.Center) diff --git a/app/src/main/java/org/thoughtcrime/securesms/ui/components/QrImage.kt b/app/src/main/java/org/thoughtcrime/securesms/ui/components/QrImage.kt index 48d81aa723..c5f927b4ab 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/ui/components/QrImage.kt +++ b/app/src/main/java/org/thoughtcrime/securesms/ui/components/QrImage.kt @@ -10,8 +10,8 @@ import androidx.compose.foundation.layout.aspectRatio import androidx.compose.foundation.layout.fillMaxWidth import androidx.compose.foundation.layout.padding import androidx.compose.foundation.layout.size -import androidx.compose.material.Card -import androidx.compose.material.Icon +import androidx.compose.material3.Icon +import androidx.compose.material3.MaterialTheme import androidx.compose.runtime.Composable import androidx.compose.runtime.LaunchedEffect import androidx.compose.runtime.getValue @@ -32,15 +32,15 @@ import androidx.compose.ui.unit.dp import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.launch import network.loki.messenger.R -import org.thoughtcrime.securesms.ui.LocalDimensions -import org.thoughtcrime.securesms.ui.color.LocalColors +import org.thoughtcrime.securesms.ui.theme.LocalColors +import org.thoughtcrime.securesms.ui.theme.LocalDimensions import org.thoughtcrime.securesms.util.QRCodeUtilities @Composable fun QrImage( string: String?, modifier: Modifier = Modifier, - contentPadding: Dp = LocalDimensions.current.smallItemSpacing, + contentPadding: Dp = LocalDimensions.current.smallSpacing, icon: Int = R.drawable.session_shield ) { var bitmap: Bitmap? by remember { @@ -56,10 +56,9 @@ fun QrImage( } } - Card( - backgroundColor = LocalColors.current.qrCodeBackground, - elevation = 0.dp, - modifier = modifier + Box( + modifier = modifier.background(color = LocalColors.current.qrCodeBackground, + shape = MaterialTheme.shapes.small) ) { Content(bitmap, icon, Modifier.padding(contentPadding), backgroundColor = LocalColors.current.qrCodeBackground) } } diff --git a/app/src/main/java/org/thoughtcrime/securesms/ui/components/RadioButton.kt b/app/src/main/java/org/thoughtcrime/securesms/ui/components/RadioButton.kt index 8058459a7c..154c9ea3a1 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/ui/components/RadioButton.kt +++ b/app/src/main/java/org/thoughtcrime/securesms/ui/components/RadioButton.kt @@ -1,40 +1,58 @@ package org.thoughtcrime.securesms.ui.components +import android.content.ContentProvider import androidx.compose.animation.AnimatedVisibility import androidx.compose.animation.scaleIn import androidx.compose.animation.scaleOut import androidx.compose.foundation.background import androidx.compose.foundation.border import androidx.compose.foundation.layout.Box +import androidx.compose.foundation.layout.Column import androidx.compose.foundation.layout.PaddingValues import androidx.compose.foundation.layout.RowScope import androidx.compose.foundation.layout.Spacer import androidx.compose.foundation.layout.aspectRatio import androidx.compose.foundation.layout.fillMaxSize import androidx.compose.foundation.layout.fillMaxWidth +import androidx.compose.foundation.layout.heightIn import androidx.compose.foundation.layout.padding import androidx.compose.foundation.layout.size import androidx.compose.foundation.layout.width import androidx.compose.foundation.selection.selectable import androidx.compose.foundation.shape.CircleShape -import androidx.compose.material.TextButton -import androidx.compose.material.ripple.rememberRipple +import androidx.compose.material3.LocalContentColor +import androidx.compose.material3.Text +import androidx.compose.material3.TextButton import androidx.compose.runtime.Composable +import androidx.compose.runtime.CompositionLocalProvider import androidx.compose.ui.Alignment import androidx.compose.ui.Modifier import androidx.compose.ui.draw.clip +import androidx.compose.ui.graphics.Color import androidx.compose.ui.graphics.RectangleShape import androidx.compose.ui.semantics.Role +import androidx.compose.ui.tooling.preview.Preview import androidx.compose.ui.unit.dp -import org.thoughtcrime.securesms.ui.LocalDimensions -import org.thoughtcrime.securesms.ui.color.LocalColors -import org.thoughtcrime.securesms.ui.color.transparentButtonColors +import network.loki.messenger.libsession_util.util.ExpiryMode +import org.thoughtcrime.securesms.conversation.disappearingmessages.ExpiryType +import org.thoughtcrime.securesms.ui.GetString +import org.thoughtcrime.securesms.ui.RadioOption +import org.thoughtcrime.securesms.ui.contentDescription +import org.thoughtcrime.securesms.ui.theme.LocalColors +import org.thoughtcrime.securesms.ui.theme.LocalDimensions +import org.thoughtcrime.securesms.ui.theme.LocalType +import org.thoughtcrime.securesms.ui.theme.PreviewTheme +import org.thoughtcrime.securesms.ui.theme.sessionTypography +import org.thoughtcrime.securesms.ui.theme.textEnabled +import org.thoughtcrime.securesms.ui.theme.transparentButtonColors +import kotlin.time.Duration.Companion.days @Composable fun RadioButton( onClick: () -> Unit = {}, modifier: Modifier = Modifier, - checked: Boolean = false, + selected: Boolean = false, + enabled: Boolean = true, contentPadding: PaddingValues = PaddingValues(), content: @Composable RowScope.() -> Unit = {} ) { @@ -42,20 +60,22 @@ fun RadioButton( modifier = modifier .fillMaxWidth() .selectable( - selected = checked, + selected = selected, enabled = true, role = Role.RadioButton, onClick = onClick ), + enabled = enabled, colors = transparentButtonColors(), onClick = onClick, shape = RectangleShape, contentPadding = contentPadding ) { content() + Spacer(modifier = Modifier.width(20.dp)) RadioButtonIndicator( - checked = checked, + selected = selected && enabled, // disabled radio shouldn't be selected modifier = Modifier .size(22.dp) .align(Alignment.CenterVertically) @@ -65,12 +85,12 @@ fun RadioButton( @Composable private fun RadioButtonIndicator( - checked: Boolean, + selected: Boolean, modifier: Modifier ) { Box(modifier = modifier) { AnimatedVisibility( - checked, + selected, modifier = Modifier .padding(2.5.dp) .clip(CircleShape), @@ -91,9 +111,93 @@ private fun RadioButtonIndicator( .aspectRatio(1f) .border( width = LocalDimensions.current.borderStroke, - color = LocalColors.current.text, + color = LocalContentColor.current, shape = CircleShape ) ) {} } } + +@Composable +fun TitledRadioButton( + modifier: Modifier = Modifier, + option: RadioOption, + onClick: () -> Unit +) { + RadioButton( + modifier = modifier.heightIn(min = 60.dp) + .contentDescription(option.contentDescription), + onClick = onClick, + selected = option.selected, + enabled = option.enabled, + contentPadding = PaddingValues(horizontal = LocalDimensions.current.spacing), + content = { + Column( + modifier = Modifier + .weight(1f) + .align(Alignment.CenterVertically) + ) { + Column { + Text( + text = option.title(), + style = LocalType.current.large + ) + option.subtitle?.let { + Text( + text = it(), + style = LocalType.current.extraSmall + ) + } + } + } + } + ) +} + +@Preview +@Composable +fun PreviewTextRadioButton() { + PreviewTheme { + TitledRadioButton( + option = RadioOption( + value = ExpiryType.AFTER_SEND.mode(7.days), + title = GetString(7.days), + subtitle = GetString("This is a subtitle"), + enabled = true, + selected = true + ) + ) {} + } +} + +@Preview +@Composable +fun PreviewDisabledTextRadioButton() { + PreviewTheme { + TitledRadioButton( + option = RadioOption( + value = ExpiryType.AFTER_SEND.mode(7.days), + title = GetString(7.days), + subtitle = GetString("This is a subtitle"), + enabled = false, + selected = true + ) + ) {} + } +} + +@Preview +@Composable +fun PreviewDeselectedTextRadioButton() { + PreviewTheme { + TitledRadioButton( + option = RadioOption( + value = ExpiryType.AFTER_SEND.mode(7.days), + title = GetString(7.days), + subtitle = GetString("This is a subtitle"), + enabled = true, + selected = false + ) + ) {} + } +} \ No newline at end of file diff --git a/app/src/main/java/org/thoughtcrime/securesms/ui/components/SessionTabRow.kt b/app/src/main/java/org/thoughtcrime/securesms/ui/components/SessionTabRow.kt index 5a3956922e..5dae714380 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/ui/components/SessionTabRow.kt +++ b/app/src/main/java/org/thoughtcrime/securesms/ui/components/SessionTabRow.kt @@ -1,15 +1,15 @@ package org.thoughtcrime.securesms.ui.components import androidx.compose.foundation.ExperimentalFoundationApi -import androidx.compose.foundation.background -import androidx.compose.foundation.layout.height +import androidx.compose.foundation.layout.heightIn import androidx.compose.foundation.pager.PagerState import androidx.compose.foundation.pager.rememberPagerState -import androidx.compose.material.Tab -import androidx.compose.material.TabRow -import androidx.compose.material.TabRowDefaults -import androidx.compose.material.TabRowDefaults.tabIndicatorOffset -import androidx.compose.material.Text +import androidx.compose.material3.HorizontalDivider +import androidx.compose.material3.Tab +import androidx.compose.material3.TabRow +import androidx.compose.material3.TabRowDefaults +import androidx.compose.material3.TabRowDefaults.tabIndicatorOffset +import androidx.compose.material3.Text import androidx.compose.runtime.Composable import androidx.compose.runtime.rememberCoroutineScope import androidx.compose.ui.Modifier @@ -19,13 +19,12 @@ import androidx.compose.ui.tooling.preview.PreviewParameter import androidx.compose.ui.unit.dp import kotlinx.coroutines.launch import network.loki.messenger.R -import org.thoughtcrime.securesms.ui.LocalDimensions -import org.thoughtcrime.securesms.ui.color.LocalColors -import org.thoughtcrime.securesms.ui.PreviewTheme -import org.thoughtcrime.securesms.ui.color.Colors -import org.thoughtcrime.securesms.ui.SessionColorsParameterProvider -import org.thoughtcrime.securesms.ui.color.divider -import org.thoughtcrime.securesms.ui.h8 +import org.thoughtcrime.securesms.ui.theme.LocalColors +import org.thoughtcrime.securesms.ui.theme.LocalDimensions +import org.thoughtcrime.securesms.ui.theme.LocalType +import org.thoughtcrime.securesms.ui.theme.PreviewTheme +import org.thoughtcrime.securesms.ui.theme.SessionColorsParameterProvider +import org.thoughtcrime.securesms.ui.theme.ThemeColors private val TITLES = listOf(R.string.sessionRecoveryPassword, R.string.qrScan) @@ -33,32 +32,30 @@ private val TITLES = listOf(R.string.sessionRecoveryPassword, R.string.qrScan) @Composable fun SessionTabRow(pagerState: PagerState, titles: List) { TabRow( - backgroundColor = Color.Unspecified, + containerColor = Color.Unspecified, selectedTabIndex = pagerState.currentPage, contentColor = LocalColors.current.text, indicator = { tabPositions -> - TabRowDefaults.Indicator( + TabRowDefaults.SecondaryIndicator( Modifier.tabIndicatorOffset(tabPositions[pagerState.currentPage]), color = LocalColors.current.primary, height = LocalDimensions.current.indicatorHeight ) }, - divider = { TabRowDefaults.Divider(color = LocalColors.current.divider) }, - modifier = Modifier - .height(48.dp) - .background(color = Color.Unspecified) + divider = { HorizontalDivider(color = LocalColors.current.borders) } ) { val animationScope = rememberCoroutineScope() titles.forEachIndexed { i, it -> Tab( - i == pagerState.currentPage, + modifier = Modifier.heightIn(min = 48.dp), + selected = i == pagerState.currentPage, onClick = { animationScope.launch { pagerState.animateScrollToPage(i) } }, selectedContentColor = LocalColors.current.text, unselectedContentColor = LocalColors.current.text, ) { Text( - stringResource(id = it), - style = h8 + text = stringResource(id = it), + style = LocalType.current.h8 ) } } @@ -69,7 +66,7 @@ fun SessionTabRow(pagerState: PagerState, titles: List) { @androidx.compose.ui.tooling.preview.Preview @Composable fun PreviewSessionTabRow( - @PreviewParameter(SessionColorsParameterProvider::class) colors: Colors + @PreviewParameter(SessionColorsParameterProvider::class) colors: ThemeColors ) { PreviewTheme(colors) { val pagerState = rememberPagerState { TITLES.size } diff --git a/app/src/main/java/org/thoughtcrime/securesms/ui/components/Text.kt b/app/src/main/java/org/thoughtcrime/securesms/ui/components/Text.kt index b6a12a792d..5e09f78b65 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/ui/components/Text.kt +++ b/app/src/main/java/org/thoughtcrime/securesms/ui/components/Text.kt @@ -17,9 +17,9 @@ import androidx.compose.foundation.text.InlineTextContent import androidx.compose.foundation.text.KeyboardActions import androidx.compose.foundation.text.KeyboardOptions import androidx.compose.foundation.text.appendInlineContent -import androidx.compose.material.Icon -import androidx.compose.material.MaterialTheme -import androidx.compose.material.Text +import androidx.compose.material3.Icon +import androidx.compose.material3.MaterialTheme +import androidx.compose.material3.Text import androidx.compose.runtime.Composable import androidx.compose.ui.Alignment import androidx.compose.ui.Modifier @@ -37,15 +37,15 @@ import androidx.compose.ui.unit.TextUnit import androidx.compose.ui.unit.dp import androidx.compose.ui.unit.sp import network.loki.messenger.R -import org.thoughtcrime.securesms.ui.LocalDimensions -import org.thoughtcrime.securesms.ui.PreviewTheme -import org.thoughtcrime.securesms.ui.base -import org.thoughtcrime.securesms.ui.baseBold -import org.thoughtcrime.securesms.ui.color.LocalColors -import org.thoughtcrime.securesms.ui.color.borders -import org.thoughtcrime.securesms.ui.color.text -import org.thoughtcrime.securesms.ui.color.textSecondary +import org.thoughtcrime.securesms.ui.theme.LocalDimensions +import org.thoughtcrime.securesms.ui.theme.PreviewTheme +import org.thoughtcrime.securesms.ui.theme.LocalColors +import org.thoughtcrime.securesms.ui.theme.borders +import org.thoughtcrime.securesms.ui.theme.text +import org.thoughtcrime.securesms.ui.theme.textSecondary import org.thoughtcrime.securesms.ui.contentDescription +import org.thoughtcrime.securesms.ui.theme.LocalType +import org.thoughtcrime.securesms.ui.theme.bold @Preview @Composable @@ -85,7 +85,7 @@ fun SessionOutlinedTextField( modifier: Modifier = Modifier, contentDescription: String? = null, onChange: (String) -> Unit = {}, - textStyle: TextStyle = base, + textStyle: TextStyle = LocalType.current.base, placeholder: String = "", onContinue: () -> Unit = {}, error: String? = null, @@ -106,7 +106,7 @@ fun SessionOutlinedTextField( if (text.isEmpty()) { Text( text = placeholder, - style = base, + style = LocalType.current.base, color = LocalColors.current.textSecondary(isTextErrorColor), modifier = Modifier.wrapContentSize() .align(Alignment.CenterStart) @@ -130,13 +130,13 @@ fun SessionOutlinedTextField( ) } error?.let { - Spacer(modifier = Modifier.height(LocalDimensions.current.xsItemSpacing)) + Spacer(modifier = Modifier.height(LocalDimensions.current.xsSpacing)) Text( it, modifier = Modifier.fillMaxWidth() .contentDescription(R.string.AccessibilityId_error_message), textAlign = TextAlign.Center, - style = baseBold, + style = LocalType.current.base.bold(), color = LocalColors.current.danger ) } @@ -148,7 +148,7 @@ fun AnnotatedTextWithIcon( text: String, @DrawableRes iconRes: Int, modifier: Modifier = Modifier, - style: TextStyle = base, + style: TextStyle = LocalType.current.base, color: Color = Color.Unspecified, iconSize: TextUnit = 12.sp ) { diff --git a/app/src/main/java/org/thoughtcrime/securesms/ui/color/ColorDefs.kt b/app/src/main/java/org/thoughtcrime/securesms/ui/theme/Colors.kt similarity index 97% rename from app/src/main/java/org/thoughtcrime/securesms/ui/color/ColorDefs.kt rename to app/src/main/java/org/thoughtcrime/securesms/ui/theme/Colors.kt index 3e23985af3..0b630ab233 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/ui/color/ColorDefs.kt +++ b/app/src/main/java/org/thoughtcrime/securesms/ui/theme/Colors.kt @@ -1,4 +1,4 @@ -package org.thoughtcrime.securesms.ui.color +package org.thoughtcrime.securesms.ui.theme import androidx.compose.ui.graphics.Color diff --git a/app/src/main/java/org/thoughtcrime/securesms/ui/theme/Dimensions.kt b/app/src/main/java/org/thoughtcrime/securesms/ui/theme/Dimensions.kt new file mode 100644 index 0000000000..66e5b74e8b --- /dev/null +++ b/app/src/main/java/org/thoughtcrime/securesms/ui/theme/Dimensions.kt @@ -0,0 +1,24 @@ +package org.thoughtcrime.securesms.ui.theme + +import androidx.compose.runtime.staticCompositionLocalOf +import androidx.compose.ui.unit.Dp +import androidx.compose.ui.unit.dp + +val LocalDimensions = staticCompositionLocalOf { Dimensions() } + +data class Dimensions( + val xxxsSpacing: Dp = 4.dp, + val xxsSpacing: Dp = 8.dp, + val xsSpacing: Dp = 12.dp, + val smallSpacing: Dp = 16.dp, + val spacing: Dp = 24.dp, + val mediumSpacing: Dp = 36.dp, + val xlargeSpacing: Dp = 64.dp, + + val dividerIndent: Dp = 60.dp, + val appBarHeight: Dp = 64.dp, + val minLargeItemButtonHeight: Dp = 60.dp, + + val indicatorHeight: Dp = 4.dp, + val borderStroke: Dp = 1.dp +) diff --git a/app/src/main/java/org/thoughtcrime/securesms/ui/theme/SessionTypography.kt b/app/src/main/java/org/thoughtcrime/securesms/ui/theme/SessionTypography.kt new file mode 100644 index 0000000000..602affa6af --- /dev/null +++ b/app/src/main/java/org/thoughtcrime/securesms/ui/theme/SessionTypography.kt @@ -0,0 +1,145 @@ +package org.thoughtcrime.securesms.ui.theme + +import androidx.compose.material3.Typography +import androidx.compose.runtime.Composable +import androidx.compose.ui.text.TextStyle +import androidx.compose.ui.text.font.FontFamily +import androidx.compose.ui.text.font.FontWeight +import androidx.compose.ui.unit.sp + + +fun TextStyle.bold() = TextStyle.Default.copy( + fontWeight = FontWeight.Bold +) + +fun TextStyle.monospace() = TextStyle.Default.copy( + fontFamily = FontFamily.Monospace +) + +val sessionTypography = SessionTypography() + +data class SessionTypography( + // Body + val xl: TextStyle = TextStyle( + fontSize = 18.sp, + lineHeight = 21.6.sp, + fontWeight = FontWeight.Normal + ), + + val large: TextStyle = TextStyle( + fontSize = 16.sp, + lineHeight = 19.2.sp, + fontWeight = FontWeight.Normal + ), + + val base: TextStyle = TextStyle( + fontSize = 14.sp, + lineHeight = 16.8.sp, + fontWeight = FontWeight.Normal + ), + + val small: TextStyle = TextStyle( + fontSize = 12.sp, + lineHeight = 14.4.sp, + fontWeight = FontWeight.Normal + ), + + val extraSmall: TextStyle = TextStyle( + fontSize = 11.sp, + lineHeight = 13.2.sp, + fontWeight = FontWeight.Normal + ), + + val fine: TextStyle = TextStyle( + fontSize = 9.sp, + lineHeight = 10.8.sp, + fontWeight = FontWeight.Normal + ), + + // Headings + val h1: TextStyle = TextStyle( + fontSize = 36.sp, + lineHeight = 43.2.sp, + fontWeight = FontWeight.Bold + ), + + val h2: TextStyle = TextStyle( + fontSize = 32.sp, + lineHeight = 38.4.sp, + fontWeight = FontWeight.Bold + ), + + val h3: TextStyle = TextStyle( + fontSize = 29.sp, + lineHeight = 34.8.sp, + fontWeight = FontWeight.Bold + ), + + val h4: TextStyle = TextStyle( + fontSize = 26.sp, + lineHeight = 31.2.sp, + fontWeight = FontWeight.Bold + ), + + val h5: TextStyle = TextStyle( + fontSize = 23.sp, + lineHeight = 27.6.sp, + fontWeight = FontWeight.Bold + ), + + val h6: TextStyle = TextStyle( + fontSize = 20.sp, + lineHeight = 24.sp, + fontWeight = FontWeight.Bold + ), + + val h7: TextStyle = TextStyle( + fontSize = 18.sp, + lineHeight = 21.6.sp, + fontWeight = FontWeight.Bold + ), + + val h8: TextStyle = TextStyle( + fontSize = 16.sp, + lineHeight = 19.2.sp, + fontWeight = FontWeight.Bold + ), + + val h9: TextStyle = TextStyle( + fontSize = 14.sp, + lineHeight = 16.8.sp, + fontWeight = FontWeight.Bold + ) +) { + + // An opinionated override of Material's defaults + @Composable + fun asMaterialTypography() = Typography( + // Display + displayLarge = h1, + displayMedium = h1, + displaySmall = h1, + + // Headline + headlineLarge = h2, + headlineMedium = h3, + headlineSmall = h4, + + // Title + titleLarge = h5, + titleMedium = h6, + titleSmall = h7, + + // Body + bodyLarge = large, + bodyMedium = base, + bodySmall = small, + + // Label + labelLarge = extraSmall, + labelMedium = fine, + labelSmall = fine + ) +} + + diff --git a/app/src/main/java/org/thoughtcrime/securesms/ui/theme/ThemeColorSet.kt b/app/src/main/java/org/thoughtcrime/securesms/ui/theme/ThemeColorSet.kt new file mode 100644 index 0000000000..3ff4d55b61 --- /dev/null +++ b/app/src/main/java/org/thoughtcrime/securesms/ui/theme/ThemeColorSet.kt @@ -0,0 +1,10 @@ +package org.thoughtcrime.securesms.ui.theme + +/** + * This class holds two instances of [ThemeColors], [light] representing the [ThemeColors] to use when the system is in a + * light theme, and [dark] representing the [ThemeColors] to use when the system is in a dark theme. + */ +data class ThemeColorSet( + val light: ThemeColors, + val dark: ThemeColors +) \ No newline at end of file diff --git a/app/src/main/java/org/thoughtcrime/securesms/ui/theme/ThemeColors.kt b/app/src/main/java/org/thoughtcrime/securesms/ui/theme/ThemeColors.kt new file mode 100644 index 0000000000..8d080b65b7 --- /dev/null +++ b/app/src/main/java/org/thoughtcrime/securesms/ui/theme/ThemeColors.kt @@ -0,0 +1,206 @@ +package org.thoughtcrime.securesms.ui.theme + +import androidx.compose.foundation.background +import androidx.compose.foundation.layout.Box +import androidx.compose.foundation.layout.Column +import androidx.compose.foundation.text.selection.TextSelectionColors +import androidx.compose.material3.ButtonDefaults +import androidx.compose.material3.Text +import androidx.compose.material3.darkColorScheme +import androidx.compose.material3.lightColorScheme +import androidx.compose.runtime.Composable +import androidx.compose.ui.Modifier +import androidx.compose.ui.graphics.Color +import androidx.compose.ui.tooling.preview.Preview +import androidx.compose.ui.tooling.preview.PreviewParameter + +interface ThemeColors { + // properties to override for each theme + val isLight: Boolean + val primary: Color + val danger: Color + val disabled: Color + val background: Color + val backgroundSecondary: Color + val text: Color + val textSecondary: Color + val borders: Color + val textBubbleSent: Color + val backgroundBubbleReceived: Color + val textBubbleReceived: Color + val qrCodeContent: Color + val qrCodeBackground: Color + val primaryButtonFill: Color + val primaryButtonFillText: Color +} + +// extra functions and properties that work for all themes +val ThemeColors.textSelectionColors + get() = TextSelectionColors( + handleColor = primary, + backgroundColor = primary.copy(alpha = 0.5f) + ) + +fun ThemeColors.text(isError: Boolean): Color = if (isError) danger else text +fun ThemeColors.textSecondary(isError: Boolean): Color = if (isError) danger else textSecondary +fun ThemeColors.textEnabled(enabled: Boolean) = if (enabled) text else disabled +fun ThemeColors.borders(isError: Boolean): Color = if (isError) danger else borders + +fun ThemeColors.toMaterialColors() = if (isLight) { + lightColorScheme( + primary = background, + secondary = backgroundSecondary, + tertiary = backgroundSecondary, + onPrimary = text, + onSecondary = text, + onTertiary = text, + background = background, + surface = background, + surfaceVariant = background, + onBackground = text, + onSurface = text, + scrim = blackAlpha40, + outline = text, + outlineVariant = text + ) +} else { + darkColorScheme( + primary = background, + secondary = backgroundSecondary, + tertiary = backgroundSecondary, + onPrimary = text, + onSecondary = text, + onTertiary = text, + background = background, + surface = background, + surfaceVariant = background, + onBackground = text, + onSurface = text, + scrim = blackAlpha40, + outline = text, + outlineVariant = text + ) +} + + +@Composable +fun transparentButtonColors() = ButtonDefaults.buttonColors( + containerColor = Color.Transparent, + disabledContainerColor = Color.Transparent, + disabledContentColor = LocalColors.current.disabled +) + +@Composable +fun dangerButtonColors() = ButtonDefaults.buttonColors( + containerColor = Color.Transparent, + contentColor = LocalColors.current.danger +) + + +// Our themes +data class ClassicDark(override val primary: Color = primaryGreen) : ThemeColors { + override val isLight = false + override val danger = dangerDark + override val disabled = disabledDark + override val background = classicDark0 + override val backgroundSecondary = classicDark1 + override val text = classicDark6 + override val textSecondary = classicDark5 + override val borders = classicDark3 + override val textBubbleSent = Color.Black + override val backgroundBubbleReceived = classicDark2 + override val textBubbleReceived = Color.White + override val qrCodeContent = background + override val qrCodeBackground = text + override val primaryButtonFill = primary + override val primaryButtonFillText = Color.Black +} + +data class ClassicLight(override val primary: Color = primaryGreen) : ThemeColors { + override val isLight = true + override val danger = dangerLight + override val disabled = disabledLight + override val background = classicLight6 + override val backgroundSecondary = classicLight5 + override val text = classicLight0 + override val textSecondary = classicLight1 + override val borders = classicLight3 + override val textBubbleSent = text + override val backgroundBubbleReceived = classicLight4 + override val textBubbleReceived = classicLight4 + override val qrCodeContent = text + override val qrCodeBackground = backgroundSecondary + override val primaryButtonFill = text + override val primaryButtonFillText = Color.White +} + +data class OceanDark(override val primary: Color = primaryBlue) : ThemeColors { + override val isLight = false + override val danger = dangerDark + override val disabled = disabledDark + override val background = oceanDark2 + override val backgroundSecondary = oceanDark1 + override val text = oceanDark7 + override val textSecondary = oceanDark5 + override val borders = oceanDark4 + override val textBubbleSent = Color.Black + override val backgroundBubbleReceived = oceanDark4 + override val textBubbleReceived = oceanDark4 + override val qrCodeContent = background + override val qrCodeBackground = text + override val primaryButtonFill = primary + override val primaryButtonFillText = Color.Black +} + +data class OceanLight(override val primary: Color = primaryBlue) : ThemeColors { + override val isLight = true + override val danger = dangerLight + override val disabled = disabledLight + override val background = oceanLight7 + override val backgroundSecondary = oceanLight6 + override val text = oceanLight1 + override val textSecondary = oceanLight2 + override val borders = oceanLight3 + override val textBubbleSent = text + override val backgroundBubbleReceived = oceanLight4 + override val textBubbleReceived = oceanLight1 + override val qrCodeContent = text + override val qrCodeBackground = backgroundSecondary + override val primaryButtonFill = text + override val primaryButtonFillText = Color.White +} + +@Preview +@Composable +fun PreviewThemeColors( + @PreviewParameter(SessionColorsParameterProvider::class) colors: ThemeColors +) { + PreviewTheme(colors) { ThemeColors() } +} + +@Composable +private fun ThemeColors() { + Column { + Box(Modifier.background(LocalColors.current.primary)) { + Text("primary", style = LocalType.current.base) + } + Box(Modifier.background(LocalColors.current.background)) { + Text("background", style = LocalType.current.base) + } + Box(Modifier.background(LocalColors.current.backgroundSecondary)) { + Text("backgroundSecondary", style = LocalType.current.base) + } + Box(Modifier.background(LocalColors.current.text)) { + Text("text", style = LocalType.current.base) + } + Box(Modifier.background(LocalColors.current.textSecondary)) { + Text("textSecondary", style = LocalType.current.base) + } + Box(Modifier.background(LocalColors.current.danger)) { + Text("danger", style = LocalType.current.base) + } + Box(Modifier.background(LocalColors.current.borders)) { + Text("border", style = LocalType.current.base) + } + } +} \ No newline at end of file diff --git a/app/src/main/java/org/thoughtcrime/securesms/ui/theme/ThemeFromPreferences.kt b/app/src/main/java/org/thoughtcrime/securesms/ui/theme/ThemeFromPreferences.kt new file mode 100644 index 0000000000..8988e84ca7 --- /dev/null +++ b/app/src/main/java/org/thoughtcrime/securesms/ui/theme/ThemeFromPreferences.kt @@ -0,0 +1,65 @@ +package org.thoughtcrime.securesms.ui.theme + +import androidx.compose.foundation.isSystemInDarkTheme +import androidx.compose.runtime.Composable +import androidx.compose.ui.graphics.Color +import org.session.libsession.utilities.TextSecurePreferences +import org.session.libsession.utilities.TextSecurePreferences.Companion.BLUE_ACCENT +import org.session.libsession.utilities.TextSecurePreferences.Companion.ORANGE_ACCENT +import org.session.libsession.utilities.TextSecurePreferences.Companion.PINK_ACCENT +import org.session.libsession.utilities.TextSecurePreferences.Companion.PURPLE_ACCENT +import org.session.libsession.utilities.TextSecurePreferences.Companion.RED_ACCENT +import org.session.libsession.utilities.TextSecurePreferences.Companion.YELLOW_ACCENT + + +/** + * Returns the compose theme based on saved preferences + * Some behaviour is hardcoded to cater for legacy usage of people with themes already set + * But future themes will be picked and set directly from the "Appearance" screen + */ +@Composable +fun TextSecurePreferences.getComposeTheme(): ThemeColors { + val selectedTheme = getThemeStyle() + + // get the chosen primary color from the preferences + val selectedPrimary = primaryColor() + + // create a theme set with the appropriate primary + val colorSet = when(selectedTheme){ + TextSecurePreferences.OCEAN_DARK, + TextSecurePreferences.OCEAN_LIGHT -> ThemeColorSet( + light = OceanLight(selectedPrimary), + dark = OceanDark(selectedPrimary) + ) + + else -> ThemeColorSet( + light = ClassicLight(selectedPrimary), + dark = ClassicDark(selectedPrimary) + ) + } + + // deliver the right set from the light/dark mode chosen + val theme = when{ + getFollowSystemSettings() -> if(isSystemInDarkTheme()) colorSet.dark else colorSet.light + + selectedTheme == TextSecurePreferences.CLASSIC_LIGHT || + selectedTheme == TextSecurePreferences.OCEAN_LIGHT -> colorSet.light + + else -> colorSet.dark + } + + return theme +} + +fun TextSecurePreferences.primaryColor(): Color = when(getSelectedAccentColor()) { + BLUE_ACCENT -> primaryBlue + PURPLE_ACCENT -> primaryPurple + PINK_ACCENT -> primaryPink + RED_ACCENT -> primaryRed + ORANGE_ACCENT -> primaryOrange + YELLOW_ACCENT -> primaryYellow + else -> primaryGreen +} + + + diff --git a/app/src/main/java/org/thoughtcrime/securesms/ui/Themes.kt b/app/src/main/java/org/thoughtcrime/securesms/ui/theme/Themes.kt similarity index 55% rename from app/src/main/java/org/thoughtcrime/securesms/ui/Themes.kt rename to app/src/main/java/org/thoughtcrime/securesms/ui/theme/Themes.kt index 22050a2764..87e91e0ab0 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/ui/Themes.kt +++ b/app/src/main/java/org/thoughtcrime/securesms/ui/theme/Themes.kt @@ -1,28 +1,26 @@ -package org.thoughtcrime.securesms.ui +package org.thoughtcrime.securesms.ui.theme -import android.content.Context import androidx.compose.foundation.background import androidx.compose.foundation.layout.Box import androidx.compose.foundation.shape.RoundedCornerShape import androidx.compose.foundation.text.selection.LocalTextSelectionColors -import androidx.compose.material.LocalContentColor -import androidx.compose.material.MaterialTheme -import androidx.compose.material.Shapes +import androidx.compose.material3.LocalContentColor +import androidx.compose.material3.MaterialTheme +import androidx.compose.material3.Shapes import androidx.compose.runtime.Composable import androidx.compose.runtime.CompositionLocalProvider +import androidx.compose.runtime.compositionLocalOf import androidx.compose.ui.Modifier import androidx.compose.ui.platform.LocalContext import androidx.compose.ui.tooling.preview.PreviewParameterProvider import androidx.compose.ui.unit.dp import org.session.libsession.utilities.AppTextSecurePreferences -import org.thoughtcrime.securesms.ui.color.ClassicDark -import org.thoughtcrime.securesms.ui.color.ClassicLight -import org.thoughtcrime.securesms.ui.color.Colors -import org.thoughtcrime.securesms.ui.color.LocalColors -import org.thoughtcrime.securesms.ui.color.OceanDark -import org.thoughtcrime.securesms.ui.color.OceanLight -import org.thoughtcrime.securesms.ui.color.colors -import org.thoughtcrime.securesms.ui.color.textSelectionColors + +// Globally accessible composition local objects +val LocalColors = compositionLocalOf { ClassicDark() } +val LocalType = compositionLocalOf { sessionTypography } + +var selectedTheme: ThemeColors? = null /** * Apply a Material2 compose theme based on user selections in SharedPreferences. @@ -31,24 +29,33 @@ import org.thoughtcrime.securesms.ui.color.textSelectionColors fun SessionMaterialTheme( content: @Composable () -> Unit ) { - SessionMaterialTheme(LocalContext.current.colors()) { content() } + // set the theme data if it hasn't been done yet + if(selectedTheme == null) { + // Some values can be set from the preferences, and if not should fallback to a default value + val context = LocalContext.current + val preferences = AppTextSecurePreferences(context) + selectedTheme = preferences.getComposeTheme() + } + + SessionMaterialTheme(colors = selectedTheme ?: ClassicDark()) { content() } } /** - * Apply a given [Colors], and our typography and shapes as a Material 2 Compose Theme. + * Apply a given [ThemeColors], and our typography and shapes as a Material 2 Compose Theme. **/ @Composable fun SessionMaterialTheme( - colors: Colors, + colors: ThemeColors, content: @Composable () -> Unit ) { MaterialTheme( - colors = colors.toMaterialColors(), - typography = sessionTypography, + colorScheme = colors.toMaterialColors(), + typography = sessionTypography.asMaterialTypography(), shapes = sessionShapes, ) { CompositionLocalProvider( LocalColors provides colors, + LocalType provides sessionTypography, LocalContentColor provides colors.text, LocalTextSelectionColors provides colors.textSelectionColors, ) { @@ -57,24 +64,6 @@ fun SessionMaterialTheme( } } -private fun Colors.toMaterialColors() = androidx.compose.material.Colors( - primary = background, - primaryVariant = backgroundSecondary, - secondary = background, - secondaryVariant = background, - background = background, - surface = background, - error = danger, - onPrimary = text, - onSecondary = text, - onBackground = text, - onSurface = text, - onError = text, - isLight = isLight -) - -@Composable private fun Context.colors() = AppTextSecurePreferences(this).colors() - val pillShape = RoundedCornerShape(percent = 50) val buttonShape = pillShape @@ -88,7 +77,7 @@ val sessionShapes = Shapes( */ @Composable fun PreviewTheme( - colors: Colors = LocalColors.current, + colors: ThemeColors = LocalColors.current, content: @Composable () -> Unit ) { SessionMaterialTheme(colors) { @@ -98,6 +87,7 @@ fun PreviewTheme( } } -class SessionColorsParameterProvider : PreviewParameterProvider { +// used for previews +class SessionColorsParameterProvider : PreviewParameterProvider { override val values = sequenceOf(ClassicDark(), ClassicLight(), OceanDark(), OceanLight()) } diff --git a/app/src/main/res/color/button_destructive.xml b/app/src/main/res/color/button_danger.xml similarity index 81% rename from app/src/main/res/color/button_destructive.xml rename to app/src/main/res/color/button_danger.xml index cefbfed23a..d1d41b95e5 100644 --- a/app/src/main/res/color/button_destructive.xml +++ b/app/src/main/res/color/button_danger.xml @@ -1,5 +1,5 @@ - + diff --git a/app/src/main/res/color/state_list_call_action_mic_background.xml b/app/src/main/res/color/state_list_call_action_mic_background.xml index 1e40a3a054..f8ec990e18 100644 --- a/app/src/main/res/color/state_list_call_action_mic_background.xml +++ b/app/src/main/res/color/state_list_call_action_mic_background.xml @@ -1,5 +1,5 @@ - + \ No newline at end of file diff --git a/app/src/main/res/drawable/destructive_dialog_button_background.xml b/app/src/main/res/drawable/danger_dialog_button_background.xml similarity index 78% rename from app/src/main/res/drawable/destructive_dialog_button_background.xml rename to app/src/main/res/drawable/danger_dialog_button_background.xml index 9d3ac00b07..0f72e98c74 100644 --- a/app/src/main/res/drawable/destructive_dialog_button_background.xml +++ b/app/src/main/res/drawable/danger_dialog_button_background.xml @@ -3,9 +3,9 @@ xmlns:android="http://schemas.android.com/apk/res/android" android:shape="rectangle"> - + - + \ No newline at end of file diff --git a/app/src/main/res/drawable/destructive_dialog_text_button_background.xml b/app/src/main/res/drawable/danger_dialog_text_button_background.xml similarity index 100% rename from app/src/main/res/drawable/destructive_dialog_text_button_background.xml rename to app/src/main/res/drawable/danger_dialog_text_button_background.xml diff --git a/app/src/main/res/drawable/destructive_outline_button_medium_background.xml b/app/src/main/res/drawable/danger_outline_button_medium_background.xml similarity index 79% rename from app/src/main/res/drawable/destructive_outline_button_medium_background.xml rename to app/src/main/res/drawable/danger_outline_button_medium_background.xml index c6e01ef98e..7894c24be8 100644 --- a/app/src/main/res/drawable/destructive_outline_button_medium_background.xml +++ b/app/src/main/res/drawable/danger_outline_button_medium_background.xml @@ -1,12 +1,12 @@ + android:color="@color/button_danger"> diff --git a/app/src/main/res/drawable/ic_clear_data.xml b/app/src/main/res/drawable/ic_clear_data.xml deleted file mode 100644 index 84465dd4cb..0000000000 --- a/app/src/main/res/drawable/ic_clear_data.xml +++ /dev/null @@ -1,11 +0,0 @@ - - - diff --git a/app/src/main/res/drawable/ic_delete_24.xml b/app/src/main/res/drawable/ic_delete_24.xml index 178806738a..48fa95783f 100644 --- a/app/src/main/res/drawable/ic_delete_24.xml +++ b/app/src/main/res/drawable/ic_delete_24.xml @@ -3,7 +3,7 @@ android:height="24dp" android:viewportWidth="24" android:viewportHeight="24" - android:tint="@color/destructive"> + android:tint="?danger"> diff --git a/app/src/main/res/drawable/sheet_rounded_bg.xml b/app/src/main/res/drawable/sheet_rounded_bg.xml new file mode 100644 index 0000000000..848b177f4f --- /dev/null +++ b/app/src/main/res/drawable/sheet_rounded_bg.xml @@ -0,0 +1,5 @@ + + + + + \ No newline at end of file diff --git a/app/src/main/res/layout/activity_blocked_contacts.xml b/app/src/main/res/layout/activity_blocked_contacts.xml index 8d960ad231..8bc458da02 100644 --- a/app/src/main/res/layout/activity_blocked_contacts.xml +++ b/app/src/main/res/layout/activity_blocked_contacts.xml @@ -39,7 +39,7 @@ /> @@ -300,7 +300,7 @@ android:layout_height="wrap_content" android:layout_gravity="center" android:contentDescription="@string/AccessibilityId_block_message_request_button" - android:textColor="@color/destructive" + android:textColor="?danger" android:paddingHorizontal="@dimen/massive_spacing" android:paddingVertical="@dimen/small_spacing" android:textSize="@dimen/text_size" @@ -334,7 +334,7 @@