mirror of
https://github.com/oxen-io/session-android.git
synced 2024-11-24 02:25:19 +00:00
Merge branch 'dev' into strings-squashed
This commit is contained in:
commit
c3c35de408
@ -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<SearchResult>()
|
||||
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.
|
||||
* */
|
||||
|
@ -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
|
||||
@ -294,4 +291,3 @@ private val MediaOverviewTab.titleResId: Int
|
||||
MediaOverviewTab.Media -> R.string.media
|
||||
MediaOverviewTab.Documents -> R.string.document
|
||||
}
|
||||
|
||||
|
@ -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
|
||||
}
|
||||
)
|
||||
}
|
||||
|
||||
|
@ -5,46 +5,65 @@ 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<String, String>()
|
||||
|
||||
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')
|
||||
}
|
||||
}
|
||||
|
||||
private val ipv4ToCountry by lazy {
|
||||
// Handle the last octet
|
||||
result = result or (currentValue shl (8 * (3 - octetIndex)))
|
||||
|
||||
return result.toInt()
|
||||
}
|
||||
|
||||
private val ipv4ToCountry: TreeMap<Int, Int?> 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<Int, String> 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
|
||||
companion object {
|
||||
@ -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
|
||||
}
|
||||
|
||||
|
@ -2,10 +2,7 @@ package org.thoughtcrime.securesms.util;
|
||||
|
||||
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 final class ListUtil {
|
||||
|
||||
return chunks;
|
||||
}
|
||||
|
||||
@SafeVarargs
|
||||
public static <T> List<T> concat(Collection<T>... items) {
|
||||
//noinspection Convert2MethodRef
|
||||
final List<T> concat = new ArrayList<>(Stream.of(items).map(Collection::size).reduce(0, (lhs, rhs) -> lhs+rhs));
|
||||
|
||||
for (Collection<T> list : items) {
|
||||
concat.addAll(list);
|
||||
}
|
||||
|
||||
return concat;
|
||||
}
|
||||
}
|
||||
|
@ -1,9 +0,0 @@
|
||||
<vector xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:width="24dp"
|
||||
android:height="24dp"
|
||||
android:viewportWidth="24"
|
||||
android:viewportHeight="24">
|
||||
<path
|
||||
android:fillColor="@android:color/white"
|
||||
android:pathData="M20,2H4C2.9,2 2,2.9 2,4V22L6,18H20C21.1,18 22,17.1 22,16V4C22,2.9 21.1,2 20,2M20,16H5.2L4,17.2V4H20V16M12.2,5.5C11.3,5.5 10.6,5.7 10.1,6C9.5,6.4 9.2,7 9.3,7.7H11.3C11.3,7.4 11.4,7.2 11.6,7.1C11.8,7 12,6.9 12.3,6.9C12.6,6.9 12.9,7 13.1,7.2C13.3,7.4 13.4,7.6 13.4,7.9C13.4,8.2 13.3,8.4 13.2,8.6C13,8.8 12.8,9 12.6,9.1C12.1,9.4 11.7,9.7 11.5,9.9C11.1,10.2 11,10.5 11,11H13C13,10.7 13.1,10.5 13.1,10.3C13.2,10.1 13.4,10 13.6,9.8C14.1,9.6 14.4,9.3 14.7,8.9C15,8.5 15.1,8.1 15.1,7.7C15.1,7 14.8,6.4 14.3,6C13.9,5.7 13.1,5.5 12.2,5.5M11,12V14H13V12H11Z" />
|
||||
</vector>
|
@ -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"
|
||||
|
@ -58,6 +58,9 @@
|
||||
<item name="attachment_document_icon_small">@drawable/ic_document_small_dark</item>
|
||||
<item name="attachment_document_icon_large">@drawable/ic_document_large_dark</item>
|
||||
<item name="colorError">?danger</item>
|
||||
|
||||
<item name="conversation_icon_attach_audio">@drawable/ic_audio_dark</item>
|
||||
<item name="conversation_icon_attach_video">@drawable/ic_video_dark</item>
|
||||
</style>
|
||||
|
||||
<!-- This should be the default theme for the application. -->
|
||||
@ -206,9 +209,6 @@
|
||||
<item name="linkpreview_secondary_text_color">?android:textColorPrimary</item>
|
||||
<item name="linkpreview_divider_color">@color/transparent</item>
|
||||
|
||||
<item name="conversation_icon_attach_audio">@drawable/ic_audio_dark</item>
|
||||
<item name="conversation_icon_attach_video">@drawable/ic_video_dark</item>
|
||||
|
||||
<item name="sticker_management_icon">@drawable/sticker_button_dark</item>
|
||||
<item name="sticker_management_divider_color">@color/core_grey_75</item>
|
||||
<item name="sticker_management_empty_background_color">@color/core_grey_85</item>
|
||||
|
Loading…
Reference in New Issue
Block a user