diff --git a/app/src/main/java/org/thoughtcrime/securesms/home/search/GlobalSearchViewModel.kt b/app/src/main/java/org/thoughtcrime/securesms/home/search/GlobalSearchViewModel.kt index fb4a61a5587..4a25556a42f 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/home/search/GlobalSearchViewModel.kt +++ b/app/src/main/java/org/thoughtcrime/securesms/home/search/GlobalSearchViewModel.kt @@ -10,21 +10,19 @@ import kotlinx.coroutines.delay import kotlinx.coroutines.flow.Flow import kotlinx.coroutines.flow.MutableSharedFlow import kotlinx.coroutines.flow.MutableStateFlow -import kotlinx.coroutines.flow.SharingStarted import kotlinx.coroutines.flow.buffer import kotlinx.coroutines.flow.flatMapLatest import kotlinx.coroutines.flow.flowOf import kotlinx.coroutines.flow.map import kotlinx.coroutines.flow.mapLatest import kotlinx.coroutines.flow.merge -import kotlinx.coroutines.flow.stateIn import kotlinx.coroutines.launch import kotlinx.coroutines.plus -import org.session.libsignal.utilities.SettableFuture import org.thoughtcrime.securesms.search.SearchRepository import org.thoughtcrime.securesms.search.model.SearchResult -import java.util.concurrent.TimeUnit import javax.inject.Inject +import kotlin.coroutines.resume +import kotlin.coroutines.suspendCoroutine @OptIn(ExperimentalCoroutinesApi::class) @HiltViewModel @@ -47,11 +45,8 @@ class GlobalSearchViewModel @Inject constructor( // User input delay in case we get a new query within a few hundred ms this // coroutine will be cancelled and the expensive query will not be run. delay(300) - val settableFuture = SettableFuture() - searchRepository.query(query.toString(), settableFuture::set) try { - // search repository doesn't play nicely with suspend functions (yet) - settableFuture.get(10_000, TimeUnit.MILLISECONDS).toGlobalSearchResult() + searchRepository.suspendQuery(query.toString()).toGlobalSearchResult() } catch (e: Exception) { GlobalSearchResult(query.toString()) } @@ -69,6 +64,12 @@ class GlobalSearchViewModel @Inject constructor( } } +private suspend fun SearchRepository.suspendQuery(query: String): SearchResult { + return suspendCoroutine { cont -> + query(query, cont::resume) + } +} + /** * Re-emit whenever refreshes emits. * */ diff --git a/app/src/main/java/org/thoughtcrime/securesms/media/MediaOverviewScreen.kt b/app/src/main/java/org/thoughtcrime/securesms/media/MediaOverviewScreen.kt index 3aab6ae061f..ce479848a08 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/media/MediaOverviewScreen.kt +++ b/app/src/main/java/org/thoughtcrime/securesms/media/MediaOverviewScreen.kt @@ -17,7 +17,6 @@ import androidx.compose.foundation.layout.fillMaxWidth import androidx.compose.foundation.layout.padding import androidx.compose.foundation.pager.HorizontalPager import androidx.compose.foundation.pager.rememberPagerState -import androidx.compose.foundation.shape.RoundedCornerShape import androidx.compose.material3.BasicAlertDialog import androidx.compose.material3.CircularProgressIndicator import androidx.compose.material3.ExperimentalMaterial3Api @@ -29,7 +28,6 @@ import androidx.compose.material3.rememberTopAppBarState import androidx.compose.runtime.Composable import androidx.compose.runtime.LaunchedEffect import androidx.compose.runtime.collectAsState -import androidx.compose.runtime.derivedStateOf import androidx.compose.runtime.getValue import androidx.compose.runtime.mutableStateOf import androidx.compose.runtime.remember @@ -40,7 +38,6 @@ import androidx.compose.ui.hapticfeedback.HapticFeedbackType import androidx.compose.ui.input.nestedscroll.nestedScroll import androidx.compose.ui.platform.LocalContext import androidx.compose.ui.platform.LocalHapticFeedback -import androidx.compose.ui.unit.dp import network.loki.messenger.R import org.thoughtcrime.securesms.ui.AlertDialog import org.thoughtcrime.securesms.ui.DialogButtonModel @@ -293,5 +290,4 @@ private val MediaOverviewTab.titleResId: Int get() = when (this) { MediaOverviewTab.Media -> R.string.media MediaOverviewTab.Documents -> R.string.document - } - + } \ No newline at end of file diff --git a/app/src/main/java/org/thoughtcrime/securesms/media/MediaPage.kt b/app/src/main/java/org/thoughtcrime/securesms/media/MediaPage.kt index ef6361198ca..35479ae5036 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/media/MediaPage.kt +++ b/app/src/main/java/org/thoughtcrime/securesms/media/MediaPage.kt @@ -152,12 +152,30 @@ private fun ThumbnailRow( ) { it.diskCacheStrategy(DiskCacheStrategy.NONE) } - } else if (item.hasPlaceholder) { + } else { + // The resource given by the placeholder needs tinting according to our theme. + // But the missing thumbnail picture does not. + var (placeholder, shouldTint) = if (item.hasPlaceholder) { + item.placeholder(LocalContext.current) to true + } else { + R.drawable.ic_missing_thumbnail_picture to false + } + + if (placeholder == 0) { + placeholder = R.drawable.ic_missing_thumbnail_picture + shouldTint = false + } + Image( - painter = painterResource(item.placeholder(LocalContext.current)), + painter = painterResource(placeholder), contentDescription = null, modifier = Modifier.fillMaxSize(), - contentScale = ContentScale.Inside + contentScale = ContentScale.Inside, + colorFilter = if (shouldTint) { + ColorFilter.tint(LocalColors.current.textSecondary) + } else { + null + } ) } diff --git a/app/src/main/java/org/thoughtcrime/securesms/util/IP2Country.kt b/app/src/main/java/org/thoughtcrime/securesms/util/IP2Country.kt index 004dec8a5ec..446f0286c51 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/util/IP2Country.kt +++ b/app/src/main/java/org/thoughtcrime/securesms/util/IP2Country.kt @@ -5,45 +5,64 @@ import android.content.Context import android.content.Intent import android.content.IntentFilter import androidx.localbroadcastmanager.content.LocalBroadcastManager -import org.session.libsignal.utilities.Log import com.opencsv.CSVReader import org.session.libsession.snode.OnionRequestAPI +import org.session.libsignal.utilities.Log import org.session.libsignal.utilities.ThreadUtils import java.io.File import java.io.FileOutputStream import java.io.FileReader +import java.util.SortedMap +import java.util.TreeMap class IP2Country private constructor(private val context: Context) { private val pathsBuiltEventReceiver: BroadcastReceiver val countryNamesCache = mutableMapOf() - private fun Ipv4Int(ip:String) = ip.takeWhile { it != '/' }.split('.').foldIndexed(0L) { i, acc, s -> - val asInt = s.toLong() - acc + (asInt shl (8 * (3-i))) + private fun Ipv4Int(ip: String): Int { + var result = 0L + var currentValue = 0L + var octetIndex = 0 + + for (char in ip) { + if (char == '.' || char == '/') { + result = result or (currentValue shl (8 * (3 - octetIndex))) + currentValue = 0 + octetIndex++ + if (char == '/') break + } else { + currentValue = currentValue * 10 + (char - '0') + } + } + + // Handle the last octet + result = result or (currentValue shl (8 * (3 - octetIndex))) + + return result.toInt() } - private val ipv4ToCountry by lazy { + private val ipv4ToCountry: TreeMap by lazy { val file = loadFile("geolite2_country_blocks_ipv4.csv") - val csv = CSVReader(FileReader(file.absoluteFile)).apply { - skip(1) - } + CSVReader(FileReader(file.absoluteFile)).use { csv -> + csv.skip(1) - csv.readAll() - .associate { cols -> - Ipv4Int(cols[0]) to cols[1].toIntOrNull() - } + csv.asSequence().associateTo(TreeMap()) { cols -> + Ipv4Int(cols[0]).toInt() to cols[1].toIntOrNull() + } + } } - private val countryToNames by lazy { + private val countryToNames: Map by lazy { val file = loadFile("geolite2_country_locations_english.csv") - val csv = CSVReader(FileReader(file.absoluteFile)).apply { - skip(1) - } - csv.readAll() + CSVReader(FileReader(file.absoluteFile)).use { csv -> + csv.skip(1) + + csv.asSequence() .filter { cols -> !cols[0].isNullOrEmpty() && !cols[1].isNullOrEmpty() } .associate { cols -> cols[0].toInt() to cols[5] } + } } // region Initialization @@ -95,9 +114,8 @@ class IP2Country private constructor(private val context: Context) { // return early if cached countryNamesCache[ip]?.let { return it } - val comps = ipv4ToCountry.asSequence() - - val bestMatchCountry = comps.lastOrNull { it.key <= Ipv4Int(ip) }?.let { (_, code) -> + val ipInt = Ipv4Int(ip) + val bestMatchCountry = ipv4ToCountry.floorEntry(ipInt)?.let { (_, code) -> if (code != null) { countryToNames[code] } else { @@ -127,3 +145,4 @@ class IP2Country private constructor(private val context: Context) { } // endregion } + diff --git a/app/src/main/java/org/thoughtcrime/securesms/util/ListUtil.java b/app/src/main/java/org/thoughtcrime/securesms/util/ListUtil.java index 20dd6d68cf0..321a3abfc07 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/util/ListUtil.java +++ b/app/src/main/java/org/thoughtcrime/securesms/util/ListUtil.java @@ -2,10 +2,7 @@ import androidx.annotation.NonNull; -import com.annimon.stream.Stream; - import java.util.ArrayList; -import java.util.Collection; import java.util.List; public final class ListUtil { @@ -21,16 +18,4 @@ public static List> chunk(@NonNull List list, int chunkSize) { return chunks; } - - @SafeVarargs - public static List concat(Collection... items) { - //noinspection Convert2MethodRef - final List concat = new ArrayList<>(Stream.of(items).map(Collection::size).reduce(0, (lhs, rhs) -> lhs+rhs)); - - for (Collection list : items) { - concat.addAll(list); - } - - return concat; - } } diff --git a/app/src/main/res/drawable/ic_outline_message_requests_24.xml b/app/src/main/res/drawable/ic_outline_message_requests_24.xml deleted file mode 100644 index e01bceed1ca..00000000000 --- a/app/src/main/res/drawable/ic_outline_message_requests_24.xml +++ /dev/null @@ -1,9 +0,0 @@ - - - diff --git a/app/src/main/res/layout/view_message_request_banner.xml b/app/src/main/res/layout/view_message_request_banner.xml index 4e2e060e1a7..415bf7b4179 100644 --- a/app/src/main/res/layout/view_message_request_banner.xml +++ b/app/src/main/res/layout/view_message_request_banner.xml @@ -18,7 +18,7 @@ android:layout_marginVertical="@dimen/medium_spacing" android:layout_marginStart="@dimen/medium_spacing" android:padding="10dp" - android:src="@drawable/ic_outline_message_requests_24" + android:src="@drawable/ic_message_requests" android:tint="?unreadIndicatorTextColor" app:circleColor="?unreadIndicatorBackgroundColor" app:layout_constraintBottom_toBottomOf="parent" diff --git a/app/src/main/res/values/themes.xml b/app/src/main/res/values/themes.xml index 37f0e517ac5..9f81cd153ee 100644 --- a/app/src/main/res/values/themes.xml +++ b/app/src/main/res/values/themes.xml @@ -58,7 +58,10 @@ @drawable/ic_document_small_dark @drawable/ic_document_large_dark ?danger - + + @drawable/ic_audio_dark + @drawable/ic_video_dark +