diff --git a/apps/multiplatform/android/src/main/java/chat/simplex/app/MainActivity.kt b/apps/multiplatform/android/src/main/java/chat/simplex/app/MainActivity.kt index 3d6b891af2..946973d973 100644 --- a/apps/multiplatform/android/src/main/java/chat/simplex/app/MainActivity.kt +++ b/apps/multiplatform/android/src/main/java/chat/simplex/app/MainActivity.kt @@ -24,7 +24,7 @@ import androidx.fragment.app.FragmentActivity import androidx.lifecycle.* import chat.simplex.app.MainActivity.Companion.enteredBackground import chat.simplex.app.model.* -import chat.simplex.app.model.NtfManager.Companion.getUserIdFromIntent +import chat.simplex.app.model.NtfManager.getUserIdFromIntent import chat.simplex.app.ui.theme.* import chat.simplex.app.views.SplashView import chat.simplex.app.views.call.ActiveCallView @@ -42,6 +42,7 @@ import chat.simplex.app.views.onboarding.* import chat.simplex.app.views.usersettings.LAMode import kotlinx.coroutines.* import kotlinx.coroutines.flow.distinctUntilChanged +import java.lang.ref.WeakReference class MainActivity: FragmentActivity() { companion object { @@ -65,6 +66,7 @@ class MainActivity: FragmentActivity() { override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) + SimplexApp.context.mainActivity = WeakReference(this) // testJson() val m = vm.chatModel applyAppLocale(m.controller.appPrefs.appLanguage) @@ -181,7 +183,6 @@ class MainActivity: FragmentActivity() { else generalGetString(R.string.auth_unlock), selfDestruct = true, - this@MainActivity, completed = { laResult -> when (laResult) { LAResult.Success -> @@ -250,7 +251,6 @@ class MainActivity: FragmentActivity() { authenticate( generalGetString(R.string.auth_enable_simplex_lock), generalGetString(R.string.auth_confirm_credential), - activity = activity, completed = { laResult -> when (laResult) { LAResult.Success -> { @@ -317,7 +317,6 @@ class MainActivity: FragmentActivity() { generalGetString(R.string.auth_confirm_credential) else "", - activity = activity, completed = { laResult -> val prefPerformLA = m.controller.appPrefs.performLA when (laResult) { @@ -353,7 +352,6 @@ class MainActivity: FragmentActivity() { generalGetString(R.string.auth_confirm_credential) else generalGetString(R.string.auth_disable_simplex_lock), - activity = activity, completed = { laResult -> val prefPerformLA = m.controller.appPrefs.performLA val selfDestructPref = m.controller.appPrefs.selfDestruct diff --git a/apps/multiplatform/android/src/main/java/chat/simplex/app/SimplexApp.kt b/apps/multiplatform/android/src/main/java/chat/simplex/app/SimplexApp.kt index 7a2aecaae6..29a20ff768 100644 --- a/apps/multiplatform/android/src/main/java/chat/simplex/app/SimplexApp.kt +++ b/apps/multiplatform/android/src/main/java/chat/simplex/app/SimplexApp.kt @@ -14,6 +14,7 @@ import com.jakewharton.processphoenix.ProcessPhoenix import kotlinx.coroutines.* import kotlinx.serialization.decodeFromString import java.io.* +import java.lang.ref.WeakReference import java.util.* import java.util.concurrent.Semaphore import java.util.concurrent.TimeUnit @@ -37,13 +38,20 @@ external fun chatParseServer(str: String): String external fun chatPasswordHash(pwd: String, salt: String): String class SimplexApp: Application(), LifecycleEventObserver { + var mainActivity: WeakReference = WeakReference(null) + val chatModel: ChatModel + get() = chatController.chatModel + val appPreferences: AppPreferences + get() = chatController.appPrefs + + val chatController: ChatController = ChatController var isAppOnForeground: Boolean = false val defaultLocale: Locale = Locale.getDefault() suspend fun initChatController(useKey: String? = null, confirmMigrations: MigrationConfirmation? = null, startChat: Boolean = true) { val dbKey = useKey ?: DatabaseUtils.useDatabaseKey() - val dbAbsolutePathPrefix = getFilesDirectory(SimplexApp.context) + val dbAbsolutePathPrefix = getFilesDirectory() val confirm = confirmMigrations ?: if (appPreferences.confirmDBUpgrades.get()) MigrationConfirmation.Error else MigrationConfirmation.YesUp val migrated: Array = chatMigrateInit(dbAbsolutePathPrefix, dbKey, confirm.value) val res: DBMigrationResult = kotlin.runCatching { @@ -84,21 +92,6 @@ class SimplexApp: Application(), LifecycleEventObserver { } } - val chatModel: ChatModel - get() = chatController.chatModel - - private val ntfManager: NtfManager by lazy { - NtfManager(applicationContext, appPreferences) - } - - private val appPreferences: AppPreferences by lazy { - AppPreferences(applicationContext) - } - - val chatController: ChatController by lazy { - ChatController(0L, ntfManager, applicationContext, appPreferences) - } - override fun onCreate() { super.onCreate() @@ -162,12 +155,12 @@ class SimplexApp: Application(), LifecycleEventObserver { fun allowToStartServiceAfterAppExit() = with(chatModel.controller) { appPrefs.notificationsMode.get() == NotificationsMode.SERVICE.name && - (!NotificationsMode.SERVICE.requiresIgnoringBattery || isIgnoringBatteryOptimizations(chatModel.controller.appContext)) + (!NotificationsMode.SERVICE.requiresIgnoringBattery || isIgnoringBatteryOptimizations()) } private fun allowToStartPeriodically() = with(chatModel.controller) { appPrefs.notificationsMode.get() == NotificationsMode.PERIODIC.name && - (!NotificationsMode.PERIODIC.requiresIgnoringBattery || isIgnoringBatteryOptimizations(chatModel.controller.appContext)) + (!NotificationsMode.PERIODIC.requiresIgnoringBattery || isIgnoringBatteryOptimizations()) } /* diff --git a/apps/multiplatform/android/src/main/java/chat/simplex/app/model/ChatModel.kt b/apps/multiplatform/android/src/main/java/chat/simplex/app/model/ChatModel.kt index 3d9de2d35f..b480814144 100644 --- a/apps/multiplatform/android/src/main/java/chat/simplex/app/model/ChatModel.kt +++ b/apps/multiplatform/android/src/main/java/chat/simplex/app/model/ChatModel.kt @@ -34,7 +34,8 @@ import kotlin.time.* * Without this annotation an animation from ChatList to ChatView has 1 frame per the whole animation. Don't delete it * */ @Stable -class ChatModel(val controller: ChatController) { +object ChatModel { + val controller: ChatController = ChatController val onboardingStage = mutableStateOf(null) val currentUser = mutableStateOf(null) val users = mutableStateListOf() @@ -65,11 +66,11 @@ class ChatModel(val controller: ChatController) { val appOpenUrl = mutableStateOf(null) // preferences - val notificationsMode = mutableStateOf(NotificationsMode.default) - var notificationPreviewMode = mutableStateOf(NotificationPreviewMode.default) - val performLA = mutableStateOf(false) + val notificationsMode by lazy { mutableStateOf(NotificationsMode.values().firstOrNull { it.name == controller.appPrefs.notificationsMode.get() } ?: NotificationsMode.default) } + val notificationPreviewMode by lazy { mutableStateOf(NotificationPreviewMode.values().firstOrNull { it.name == controller.appPrefs.notificationPreviewMode.get() } ?: NotificationPreviewMode.default) } + val performLA by lazy { mutableStateOf(controller.appPrefs.performLA.get()) } val showAdvertiseLAUnavailableAlert = mutableStateOf(false) - var incognito = mutableStateOf(false) + val incognito by lazy { mutableStateOf(controller.appPrefs.incognito.get()) } // current WebRTC call val callManager = CallManager(this) @@ -91,7 +92,7 @@ class ChatModel(val controller: ChatController) { val sharedContent = mutableStateOf(null as SharedContent?) val filesToDelete = mutableSetOf() - val simplexLinkMode = mutableStateOf(controller.appPrefs.simplexLinkMode.get()) + val simplexLinkMode by lazy { mutableStateOf(controller.appPrefs.simplexLinkMode.get()) } fun getUser(userId: Long): User? = if (currentUser.value?.userId == userId) { currentUser.value diff --git a/apps/multiplatform/android/src/main/java/chat/simplex/app/model/NtfManager.kt b/apps/multiplatform/android/src/main/java/chat/simplex/app/model/NtfManager.kt index bc5985c5e5..1a52030c84 100644 --- a/apps/multiplatform/android/src/main/java/chat/simplex/app/model/NtfManager.kt +++ b/apps/multiplatform/android/src/main/java/chat/simplex/app/model/NtfManager.kt @@ -1,15 +1,17 @@ package chat.simplex.app.model +import android.Manifest import android.app.* +import android.app.TaskStackBuilder import android.content.* +import android.content.pm.PackageManager import android.graphics.BitmapFactory import android.hardware.display.DisplayManager import android.media.AudioAttributes import android.net.Uri import android.util.Log import android.view.Display -import androidx.core.app.NotificationCompat -import androidx.core.app.NotificationManagerCompat +import androidx.core.app.* import chat.simplex.app.* import chat.simplex.app.views.call.* import chat.simplex.app.views.chatlist.acceptContactRequest @@ -17,29 +19,29 @@ import chat.simplex.app.views.helpers.* import chat.simplex.app.views.usersettings.NotificationPreviewMode import kotlinx.datetime.Clock -class NtfManager(val context: Context, private val appPreferences: AppPreferences) { - companion object { - const val MessageChannel: String = "chat.simplex.app.MESSAGE_NOTIFICATION" - const val MessageGroup: String = "chat.simplex.app.MESSAGE_NOTIFICATION" - const val OpenChatAction: String = "chat.simplex.app.OPEN_CHAT" - const val ShowChatsAction: String = "chat.simplex.app.SHOW_CHATS" +object NtfManager { + const val MessageChannel: String = "chat.simplex.app.MESSAGE_NOTIFICATION" + const val MessageGroup: String = "chat.simplex.app.MESSAGE_NOTIFICATION" + const val OpenChatAction: String = "chat.simplex.app.OPEN_CHAT" + const val ShowChatsAction: String = "chat.simplex.app.SHOW_CHATS" - // DO NOT change notification channel settings / names - const val CallChannel: String = "chat.simplex.app.CALL_NOTIFICATION_1" - const val AcceptCallAction: String = "chat.simplex.app.ACCEPT_CALL" - const val RejectCallAction: String = "chat.simplex.app.REJECT_CALL" - const val CallNotificationId: Int = -1 + // DO NOT change notification channel settings / names + const val CallChannel: String = "chat.simplex.app.CALL_NOTIFICATION_1" + const val AcceptCallAction: String = "chat.simplex.app.ACCEPT_CALL" + const val RejectCallAction: String = "chat.simplex.app.REJECT_CALL" + const val CallNotificationId: Int = -1 + private const val UserIdKey: String = "userId" + private const val ChatIdKey: String = "chatId" + private val appPreferences: AppPreferences by lazy { ChatController.appPrefs } + private val context: Context + get() = SimplexApp.context - private const val UserIdKey: String = "userId" - private const val ChatIdKey: String = "chatId" - - fun getUserIdFromIntent(intent: Intent?): Long? { - val userId = intent?.getLongExtra(UserIdKey, -1L) - return if (userId == -1L || userId == null) null else userId - } + fun getUserIdFromIntent(intent: Intent?): Long? { + val userId = intent?.getLongExtra(UserIdKey, -1L) + return if (userId == -1L || userId == null) null else userId } - private val manager: NotificationManager = context.getSystemService(Context.NOTIFICATION_SERVICE) as NotificationManager + private val manager: NotificationManager = SimplexApp.context.getSystemService(Context.NOTIFICATION_SERVICE) as NotificationManager private var prevNtfTime = mutableMapOf() private val msgNtfTimeoutMs = 30000L @@ -58,7 +60,7 @@ class NtfManager(val context: Context, private val appPreferences: AppPreference .setUsage(AudioAttributes.USAGE_NOTIFICATION_RINGTONE) .build() val soundUri = Uri.parse(ContentResolver.SCHEME_ANDROID_RESOURCE + "://" + context.packageName + "/" + R.raw.ring_once) - Log.d(TAG,"callNotificationChannel sound: $soundUri") + Log.d(TAG, "callNotificationChannel sound: $soundUri") callChannel.setSound(soundUri, attrs) callChannel.enableVibration(true) // the numbers below are explained here: https://developer.android.com/reference/android/os/Vibrator @@ -70,8 +72,8 @@ class NtfManager(val context: Context, private val appPreferences: AppPreference fun cancelNotificationsForChat(chatId: String) { prevNtfTime.remove(chatId) manager.cancel(chatId.hashCode()) - val msgNtfs = manager.activeNotifications.filter { - ntf -> ntf.notification.channelId == MessageChannel + val msgNtfs = manager.activeNotifications.filter { ntf -> + ntf.notification.channelId == MessageChannel } if (msgNtfs.count() == 1) { // Have a group notification with no children so cancel it @@ -110,7 +112,6 @@ class NtfManager(val context: Context, private val appPreferences: AppPreference val now = Clock.System.now().toEpochMilliseconds() val recentNotification = (now - prevNtfTime.getOrDefault(chatId, 0) < msgNtfTimeoutMs) prevNtfTime[chatId] = now - val previewMode = appPreferences.notificationPreviewMode.get() val title = if (previewMode == NotificationPreviewMode.HIDDEN.name) generalGetString(R.string.notification_preview_somebody) else displayName val content = if (previewMode != NotificationPreviewMode.MESSAGE.name) generalGetString(R.string.notification_preview_new_message) else msgText @@ -145,7 +146,6 @@ class NtfManager(val context: Context, private val appPreferences: AppPreference } builder.addAction(0, actionButton, actionPendingIntent) } - val summary = NotificationCompat.Builder(context, MessageChannel) .setSmallIcon(R.drawable.ntf_icon) .setColor(0x88FFFF) @@ -157,14 +157,17 @@ class NtfManager(val context: Context, private val appPreferences: AppPreference with(NotificationManagerCompat.from(context)) { // using cInfo.id only shows one notification per chat and updates it when the message arrives - notify(chatId.hashCode(), builder.build()) - notify(0, summary) + if (ActivityCompat.checkSelfPermission(SimplexApp.context, Manifest.permission.POST_NOTIFICATIONS) == PackageManager.PERMISSION_GRANTED) { + notify(chatId.hashCode(), builder.build()) + notify(0, summary) + } } } fun notifyCallInvitation(invitation: RcvCallInvitation) { val keyguardManager = getKeyguardManager(context) - Log.d(TAG, + Log.d( + TAG, "notifyCallInvitation pre-requests: " + "keyguard locked ${keyguardManager.isKeyguardLocked}, " + "callOnLockScreen ${appPreferences.callOnLockScreen.get()}, " + @@ -223,7 +226,9 @@ class NtfManager(val context: Context, private val appPreferences: AppPreference // This makes notification sound and vibration repeat endlessly notification.flags = notification.flags or NotificationCompat.FLAG_INSISTENT with(NotificationManagerCompat.from(context)) { - notify(CallNotificationId, notification) + if (ActivityCompat.checkSelfPermission(SimplexApp.context, Manifest.permission.POST_NOTIFICATIONS) == PackageManager.PERMISSION_GRANTED) { + notify(CallNotificationId, notification) + } } } @@ -237,7 +242,7 @@ class NtfManager(val context: Context, private val appPreferences: AppPreference fun hasNotificationsForChat(chatId: String): Boolean = manager.activeNotifications.any { it.id == chatId.hashCode() } - private fun hideSecrets(cItem: ChatItem) : String { + private fun hideSecrets(cItem: ChatItem): String { val md = cItem.formattedText return if (md != null) { var res = "" @@ -302,14 +307,16 @@ class NtfManager(val context: Context, private val appPreferences: AppPreference } val apiId = chatId.replace("<@", "").toLongOrNull() ?: return acceptContactRequest(apiId, cInfo, isCurrentUser, m) - m.controller.ntfManager.cancelNotificationsForChat(chatId) + cancelNotificationsForChat(chatId) } + RejectCallAction -> { val invitation = m.callInvitations[chatId] if (invitation != null) { m.callManager.endCall(invitation = invitation) } } + else -> { Log.e(TAG, "Unknown action. Make sure you provide action from NotificationAction enum") } diff --git a/apps/multiplatform/android/src/main/java/chat/simplex/app/model/SimpleXAPI.kt b/apps/multiplatform/android/src/main/java/chat/simplex/app/model/SimpleXAPI.kt index e1fbbaa0ce..c55f697048 100644 --- a/apps/multiplatform/android/src/main/java/chat/simplex/app/model/SimpleXAPI.kt +++ b/apps/multiplatform/android/src/main/java/chat/simplex/app/model/SimpleXAPI.kt @@ -58,9 +58,9 @@ enum class SimplexLinkMode { } } -class AppPreferences(val context: Context) { - private val sharedPreferences: SharedPreferences = context.getSharedPreferences(SHARED_PREFS_ID, Context.MODE_PRIVATE) - private val sharedPreferencesThemes: SharedPreferences = context.getSharedPreferences(SHARED_PREFS_THEMES_ID, Context.MODE_PRIVATE) +class AppPreferences { + private val sharedPreferences: SharedPreferences = SimplexApp.context.getSharedPreferences(SHARED_PREFS_ID, Context.MODE_PRIVATE) + private val sharedPreferencesThemes: SharedPreferences = SimplexApp.context.getSharedPreferences(SHARED_PREFS_THEMES_ID, Context.MODE_PRIVATE) // deprecated, remove in 2024 private val runServiceInBackground = mkBoolPreference(SHARED_PREFS_RUN_SERVICE_IN_BACKGROUND, true) @@ -298,21 +298,16 @@ class AppPreferences(val context: Context) { private const val MESSAGE_TIMEOUT: Int = 15_000_000 -open class ChatController(var ctrl: ChatCtrl?, val ntfManager: NtfManager, val appContext: Context, val appPrefs: AppPreferences) { - val chatModel = ChatModel(this) +object ChatController { + var ctrl: ChatCtrl? = -1 + val appPrefs: AppPreferences by lazy { AppPreferences() } + val ntfManager by lazy { NtfManager } + + val chatModel = ChatModel private var receiverStarted = false var lastMsgReceivedTimestamp: Long = System.currentTimeMillis() private set - init { - chatModel.notificationsMode.value = - kotlin.runCatching { NotificationsMode.valueOf(appPrefs.notificationsMode.get()!!) }.getOrDefault(NotificationsMode.default) - chatModel.notificationPreviewMode.value = - kotlin.runCatching { NotificationPreviewMode.valueOf(appPrefs.notificationPreviewMode.get()!!) }.getOrDefault(NotificationPreviewMode.default) - chatModel.performLA.value = appPrefs.performLA.get() - chatModel.incognito.value = appPrefs.incognito.get() - } - private fun currentUserId(funcName: String): Long { val userId = chatModel.currentUser.value?.userId if (userId == null) { @@ -328,8 +323,8 @@ open class ChatController(var ctrl: ChatCtrl?, val ntfManager: NtfManager, val a try { if (chatModel.chatRunning.value == true) return apiSetNetworkConfig(getNetCfg()) - apiSetTempFolder(getTempFilesDirectory(appContext)) - apiSetFilesFolder(getAppFilesDirectory(appContext)) + apiSetTempFolder(getTempFilesDirectory()) + apiSetFilesFolder(getAppFilesDirectory()) apiSetXFTPConfig(getXFTPCfg()) val justStarted = apiStartChat() val users = listUsers() @@ -1602,7 +1597,7 @@ open class ChatController(var ctrl: ChatCtrl?, val ntfManager: NtfManager, val a mc is MsgContent.MCFile && fileName != null ) { - removeFile(appContext, fileName) + removeFile(fileName) } } @@ -1671,7 +1666,7 @@ open class ChatController(var ctrl: ChatCtrl?, val ntfManager: NtfManager, val a if (!appPrefs.backgroundServiceNoticeShown.get()) { // the branch for the new users who have never seen service notice - if (!mode.requiresIgnoringBattery || isIgnoringBatteryOptimizations(appContext)) { + if (!mode.requiresIgnoringBattery || isIgnoringBatteryOptimizations()) { showBGServiceNotice(mode) } else { showBGServiceNoticeIgnoreOptimization(mode) @@ -1679,7 +1674,7 @@ open class ChatController(var ctrl: ChatCtrl?, val ntfManager: NtfManager, val a // set both flags, so that if the user doesn't allow ignoring optimizations, the service will be disabled without additional notice appPrefs.backgroundServiceNoticeShown.set(true) appPrefs.backgroundServiceBatteryNoticeShown.set(true) - } else if (mode.requiresIgnoringBattery && !isIgnoringBatteryOptimizations(appContext)) { + } else if (mode.requiresIgnoringBattery && !isIgnoringBatteryOptimizations()) { // the branch for users who have app installed, and have seen the service notice, // but the battery optimization for the app is on (Android 12) AND the service is running if (appPrefs.backgroundServiceBatteryNoticeShown.get()) { @@ -1738,7 +1733,7 @@ open class ChatController(var ctrl: ChatCtrl?, val ntfManager: NtfManager, val a private fun showBGServiceNoticeIgnoreOptimization(mode: NotificationsMode) = AlertManager.shared.showAlert { val ignoreOptimization = { AlertManager.shared.hideAlert() - askAboutIgnoringBatteryOptimization(appContext) + askAboutIgnoringBatteryOptimization() } AlertDialog( onDismissRequest = ignoreOptimization, @@ -1800,19 +1795,19 @@ open class ChatController(var ctrl: ChatCtrl?, val ntfManager: NtfManager, val a ) } - fun isIgnoringBatteryOptimizations(context: Context): Boolean { - val powerManager = context.getSystemService(Application.POWER_SERVICE) as PowerManager - return powerManager.isIgnoringBatteryOptimizations(context.packageName) + fun isIgnoringBatteryOptimizations(): Boolean { + val powerManager = SimplexApp.context.getSystemService(Application.POWER_SERVICE) as PowerManager + return powerManager.isIgnoringBatteryOptimizations(SimplexApp.context.packageName) } - private fun askAboutIgnoringBatteryOptimization(context: Context) { + private fun askAboutIgnoringBatteryOptimization() { Intent().apply { @SuppressLint("BatteryLife") action = Settings.ACTION_REQUEST_IGNORE_BATTERY_OPTIMIZATIONS - data = Uri.parse("package:${context.packageName}") + data = Uri.parse("package:${SimplexApp.context.packageName}") // This flag is needed when you start a new activity from non-Activity context addFlags(Intent.FLAG_ACTIVITY_NEW_TASK) - context.startActivity(this) + SimplexApp.context.startActivity(this) } } diff --git a/apps/multiplatform/android/src/main/java/chat/simplex/app/views/TerminalView.kt b/apps/multiplatform/android/src/main/java/chat/simplex/app/views/TerminalView.kt index cb00859314..264c243ebd 100644 --- a/apps/multiplatform/android/src/main/java/chat/simplex/app/views/TerminalView.kt +++ b/apps/multiplatform/android/src/main/java/chat/simplex/app/views/TerminalView.kt @@ -128,7 +128,7 @@ fun TerminalLog(terminalItems: List) { modifier = Modifier .fillMaxWidth() .clickable { - ModalManager.shared.showModal(endButtons = { ShareButton { shareText(context, item.details) } }) { + ModalManager.shared.showModal(endButtons = { ShareButton { shareText(item.details) } }) { SelectionContainer(modifier = Modifier.verticalScroll(rememberScrollState())) { Text(item.details, modifier = Modifier.padding(horizontal = DEFAULT_PADDING).padding(bottom = DEFAULT_PADDING)) } diff --git a/apps/multiplatform/android/src/main/java/chat/simplex/app/views/call/IncomingCallActivity.kt b/apps/multiplatform/android/src/main/java/chat/simplex/app/views/call/IncomingCallActivity.kt index d5282f3c37..5708759ca9 100644 --- a/apps/multiplatform/android/src/main/java/chat/simplex/app/views/call/IncomingCallActivity.kt +++ b/apps/multiplatform/android/src/main/java/chat/simplex/app/views/call/IncomingCallActivity.kt @@ -33,7 +33,7 @@ import androidx.compose.ui.unit.sp import chat.simplex.app.* import chat.simplex.app.R import chat.simplex.app.model.* -import chat.simplex.app.model.NtfManager.Companion.OpenChatAction +import chat.simplex.app.model.NtfManager.OpenChatAction import chat.simplex.app.ui.theme.* import chat.simplex.app.views.helpers.ProfileImage import kotlinx.datetime.Clock diff --git a/apps/multiplatform/android/src/main/java/chat/simplex/app/views/call/IncomingCallAlertView.kt b/apps/multiplatform/android/src/main/java/chat/simplex/app/views/call/IncomingCallAlertView.kt index 6afe1b3492..a8548fadac 100644 --- a/apps/multiplatform/android/src/main/java/chat/simplex/app/views/call/IncomingCallAlertView.kt +++ b/apps/multiplatform/android/src/main/java/chat/simplex/app/views/call/IncomingCallAlertView.kt @@ -28,7 +28,7 @@ fun IncomingCallAlertView(invitation: RcvCallInvitation, chatModel: ChatModel) { val cm = chatModel.callManager val cxt = LocalContext.current val scope = rememberCoroutineScope() - LaunchedEffect(true) { SoundPlayer.shared.start(cxt, scope, sound = !chatModel.showCallView.value) } + LaunchedEffect(true) { SoundPlayer.shared.start(scope, sound = !chatModel.showCallView.value) } DisposableEffect(true) { onDispose { SoundPlayer.shared.stop() } } IncomingCallAlertLayout( invitation, diff --git a/apps/multiplatform/android/src/main/java/chat/simplex/app/views/call/SoundPlayer.kt b/apps/multiplatform/android/src/main/java/chat/simplex/app/views/call/SoundPlayer.kt index 456f50d0bd..314032984a 100644 --- a/apps/multiplatform/android/src/main/java/chat/simplex/app/views/call/SoundPlayer.kt +++ b/apps/multiplatform/android/src/main/java/chat/simplex/app/views/call/SoundPlayer.kt @@ -16,7 +16,7 @@ class SoundPlayer { private var player: MediaPlayer? = null var playing = false - fun start(cxt: Context, scope: CoroutineScope, sound: Boolean) { + fun start(scope: CoroutineScope, sound: Boolean) { player?.reset() player = MediaPlayer().apply { setAudioAttributes( @@ -28,7 +28,7 @@ class SoundPlayer { setDataSource(SimplexApp.context, Uri.parse("android.resource://" + SimplexApp.context.packageName + "/" + R.raw.ring_once)) prepare() } - val vibrator = ContextCompat.getSystemService(cxt, Vibrator::class.java) + val vibrator = ContextCompat.getSystemService(SimplexApp.context, Vibrator::class.java) val effect = VibrationEffect.createOneShot(250, VibrationEffect.DEFAULT_AMPLITUDE) playing = true withScope(scope) { diff --git a/apps/multiplatform/android/src/main/java/chat/simplex/app/views/chat/ChatInfoView.kt b/apps/multiplatform/android/src/main/java/chat/simplex/app/views/chat/ChatInfoView.kt index 75edc77c05..2c105155a9 100644 --- a/apps/multiplatform/android/src/main/java/chat/simplex/app/views/chat/ChatInfoView.kt +++ b/apps/multiplatform/android/src/main/java/chat/simplex/app/views/chat/ChatInfoView.kt @@ -211,10 +211,9 @@ fun ChatInfoLayout( SectionDividerSpaced() if (contact.contactLink != null) { - val context = LocalContext.current SectionView(stringResource(R.string.address_section_title).uppercase()) { QRCode(contact.contactLink, Modifier.padding(horizontal = DEFAULT_PADDING, vertical = DEFAULT_PADDING_HALF).aspectRatio(1f)) - ShareAddressButton { shareText(context, contact.contactLink) } + ShareAddressButton { shareText(contact.contactLink) } SectionTextFooter(stringResource(R.string.you_can_share_this_address_with_your_contacts).format(contact.displayName)) } SectionDividerSpaced() diff --git a/apps/multiplatform/android/src/main/java/chat/simplex/app/views/chat/ChatItemInfoView.kt b/apps/multiplatform/android/src/main/java/chat/simplex/app/views/chat/ChatItemInfoView.kt index 8ef98933da..a6dcaa97be 100644 --- a/apps/multiplatform/android/src/main/java/chat/simplex/app/views/chat/ChatItemInfoView.kt +++ b/apps/multiplatform/android/src/main/java/chat/simplex/app/views/chat/ChatItemInfoView.kt @@ -12,7 +12,6 @@ import androidx.compose.material.Text import androidx.compose.runtime.* import androidx.compose.ui.Modifier import androidx.compose.ui.draw.clip -import androidx.compose.ui.platform.LocalContext import androidx.compose.ui.platform.LocalUriHandler import androidx.compose.ui.res.painterResource import androidx.compose.ui.res.stringResource @@ -32,7 +31,6 @@ fun ChatItemInfoView(ci: ChatItem, ciInfo: ChatItemInfo, devTools: Boolean) { val sent = ci.chatDir.sent val appColors = CurrentColors.collectAsState().value.appColors val itemColor = if (sent) appColors.sentMessage else appColors.receivedMessage - val context = LocalContext.current val uriHandler = LocalUriHandler.current @Composable @@ -83,11 +81,11 @@ fun ChatItemInfoView(ci: ChatItem, ciInfo: ChatItemInfo, devTools: Boolean) { if (text != "") { DefaultDropdownMenu(showMenu) { ItemAction(stringResource(R.string.share_verb), painterResource(R.drawable.ic_share), onClick = { - shareText(context, text) + shareText(text) showMenu.value = false }) ItemAction(stringResource(R.string.copy_verb), painterResource(R.drawable.ic_content_copy), onClick = { - copyText(context, text) + copyText(text) showMenu.value = false }) } diff --git a/apps/multiplatform/android/src/main/java/chat/simplex/app/views/chat/ChatView.kt b/apps/multiplatform/android/src/main/java/chat/simplex/app/views/chat/ChatView.kt index 7681b79b73..809f529844 100644 --- a/apps/multiplatform/android/src/main/java/chat/simplex/app/views/chat/ChatView.kt +++ b/apps/multiplatform/android/src/main/java/chat/simplex/app/views/chat/ChatView.kt @@ -278,7 +278,7 @@ fun ChatView(chatId: String, chatModel: ChatModel, onComposed: () -> Unit) { val ciInfo = chatModel.controller.apiGetChatItemInfo(cInfo.chatType, cInfo.apiId, cItem.id) if (ciInfo != null) { ModalManager.shared.showModal(endButtons = { ShareButton { - shareText(context, itemInfoShareText(cItem, ciInfo, chatModel.controller.appPrefs.developerTools.get())) + shareText(itemInfoShareText(cItem, ciInfo, chatModel.controller.appPrefs.developerTools.get())) } }) { ChatItemInfoView(cItem, ciInfo, devTools = chatModel.controller.appPrefs.developerTools.get()) } @@ -976,7 +976,7 @@ private fun providerForGallery( scrollTo: (Int) -> Unit ): ImageGalleryProvider { fun canShowMedia(item: ChatItem): Boolean = - (item.content.msgContent is MsgContent.MCImage || item.content.msgContent is MsgContent.MCVideo) && (item.file?.loaded == true && getLoadedFilePath(SimplexApp.context, item.file) != null) + (item.content.msgContent is MsgContent.MCImage || item.content.msgContent is MsgContent.MCVideo) && (item.file?.loaded == true && getLoadedFilePath(item.file) != null) fun item(skipInternalIndex: Int, initialChatId: Long): Pair? { var processedInternalIndex = -skipInternalIndex.sign @@ -1003,15 +1003,15 @@ private fun providerForGallery( val item = item(internalIndex, initialChatId)?.second ?: return null return when (item.content.msgContent) { is MsgContent.MCImage -> { - val imageBitmap: Bitmap? = getLoadedImage(SimplexApp.context, item.file) - val filePath = getLoadedFilePath(SimplexApp.context, item.file) + val imageBitmap: Bitmap? = getLoadedImage(item.file) + val filePath = getLoadedFilePath(item.file) if (imageBitmap != null && filePath != null) { val uri = FileProvider.getUriForFile(SimplexApp.context, "${BuildConfig.APPLICATION_ID}.provider", File(filePath)) ProviderMedia.Image(uri, imageBitmap) } else null } is MsgContent.MCVideo -> { - val filePath = getLoadedFilePath(SimplexApp.context, item.file) + val filePath = getLoadedFilePath(item.file) if (filePath != null) { val uri = FileProvider.getUriForFile(SimplexApp.context, "${BuildConfig.APPLICATION_ID}.provider", File(filePath)) ProviderMedia.Video(uri, (item.content.msgContent as MsgContent.MCVideo).image) diff --git a/apps/multiplatform/android/src/main/java/chat/simplex/app/views/chat/ComposeView.kt b/apps/multiplatform/android/src/main/java/chat/simplex/app/views/chat/ComposeView.kt index ffdb4afa8e..8b43cb39ad 100644 --- a/apps/multiplatform/android/src/main/java/chat/simplex/app/views/chat/ComposeView.kt +++ b/apps/multiplatform/android/src/main/java/chat/simplex/app/views/chat/ComposeView.kt @@ -201,7 +201,7 @@ fun ComposeView( val imagesPreview = ArrayList() uris.forEach { uri -> var bitmap: Bitmap? = null - val isImage = MimeTypeMap.getSingleton().getMimeTypeFromExtension(getFileName(SimplexApp.context, uri)?.split(".")?.last())?.contains("image/") == true + val isImage = MimeTypeMap.getSingleton().getMimeTypeFromExtension(getFileName(uri)?.split(".")?.last())?.contains("image/") == true when { isImage -> { // Image @@ -209,10 +209,10 @@ fun ComposeView( bitmap = if (drawable != null) getBitmapFromUri(uri) else null val isAnimNewApi = Build.VERSION.SDK_INT >= 28 && drawable is AnimatedImageDrawable val isAnimOldApi = Build.VERSION.SDK_INT < 28 && - (getFileName(SimplexApp.context, uri)?.endsWith(".gif") == true || getFileName(SimplexApp.context, uri)?.endsWith(".webp") == true) + (getFileName(uri)?.endsWith(".gif") == true || getFileName(uri)?.endsWith(".webp") == true) if (isAnimNewApi || isAnimOldApi) { // It's a gif or webp - val fileSize = getFileSize(context, uri) + val fileSize = getFileSize(uri) if (fileSize != null && fileSize <= maxFileSize) { content.add(UploadContent.AnimatedImage(uri)) } else { @@ -244,9 +244,9 @@ fun ComposeView( } val processPickedFile = { uri: Uri?, text: String? -> if (uri != null) { - val fileSize = getFileSize(context, uri) + val fileSize = getFileSize(uri) if (fileSize != null && fileSize <= maxFileSize) { - val fileName = getFileName(SimplexApp.context, uri) + val fileName = getFileName(uri) if (fileName != null) { composeState.value = composeState.value.copy(message = text ?: composeState.value.message, preview = ComposePreview.FilePreview(fileName, uri)) } @@ -381,7 +381,7 @@ fun ComposeView( chatModel.addChatItem(cInfo, aChatItem.chatItem) return aChatItem.chatItem } - if (file != null) removeFile(context, file) + if (file != null) removeFile(file) return null } @@ -460,9 +460,9 @@ fun ComposeView( is ComposePreview.MediaPreview -> { preview.content.forEachIndexed { index, it -> val file = when (it) { - is UploadContent.SimpleImage -> saveImage(context, it.uri) - is UploadContent.AnimatedImage -> saveAnimImage(context, it.uri) - is UploadContent.Video -> saveFileFromUri(context, it.uri) + is UploadContent.SimpleImage -> saveImage(it.uri) + is UploadContent.AnimatedImage -> saveAnimImage(it.uri) + is UploadContent.Video -> saveFileFromUri(it.uri) } if (file != null) { files.add(file) @@ -477,7 +477,7 @@ fun ComposeView( is ComposePreview.VoicePreview -> { val tmpFile = File(preview.voice) AudioPlayer.stop(tmpFile.absolutePath) - val actualFile = File(getAppFilePath(SimplexApp.context, tmpFile.name.replaceAfter(RecorderNative.extension, ""))) + val actualFile = File(getAppFilePath(tmpFile.name.replaceAfter(RecorderNative.extension, ""))) withContext(Dispatchers.IO) { Files.move(tmpFile.toPath(), actualFile.toPath()) } @@ -486,7 +486,7 @@ fun ComposeView( msgs.add(MsgContent.MCVoice(if (msgs.isEmpty()) msgText else "", preview.durationMs / 1000)) } is ComposePreview.FilePreview -> { - val file = saveFileFromUri(context, preview.uri) + val file = saveFileFromUri(preview.uri) if (file != null) { files.add((file)) msgs.add(MsgContent.MCFile(if (msgs.isEmpty()) msgText else "")) diff --git a/apps/multiplatform/android/src/main/java/chat/simplex/app/views/chat/VerifyCodeView.kt b/apps/multiplatform/android/src/main/java/chat/simplex/app/views/chat/VerifyCodeView.kt index 12e8ab1aa9..472883d556 100644 --- a/apps/multiplatform/android/src/main/java/chat/simplex/app/views/chat/VerifyCodeView.kt +++ b/apps/multiplatform/android/src/main/java/chat/simplex/app/views/chat/VerifyCodeView.kt @@ -10,7 +10,6 @@ import androidx.compose.material.* import androidx.compose.runtime.* import androidx.compose.ui.Alignment import androidx.compose.ui.Modifier -import androidx.compose.ui.platform.LocalContext import androidx.compose.ui.res.painterResource import androidx.compose.ui.res.stringResource import androidx.compose.ui.text.font.FontFamily @@ -86,9 +85,8 @@ private fun VerifyCodeLayout( maxLines = 20 ) } - val context = LocalContext.current Box(Modifier.weight(1f)) { - IconButton({ shareText(context, connectionCode) }, Modifier.size(20.dp).align(Alignment.CenterStart)) { + IconButton({ shareText(connectionCode) }, Modifier.size(20.dp).align(Alignment.CenterStart)) { Icon(painterResource(R.drawable.ic_share_filled), null, tint = MaterialTheme.colors.primary) } } diff --git a/apps/multiplatform/android/src/main/java/chat/simplex/app/views/chat/group/GroupLinkView.kt b/apps/multiplatform/android/src/main/java/chat/simplex/app/views/chat/group/GroupLinkView.kt index 42a893ce99..45ccb4ca56 100644 --- a/apps/multiplatform/android/src/main/java/chat/simplex/app/views/chat/group/GroupLinkView.kt +++ b/apps/multiplatform/android/src/main/java/chat/simplex/app/views/chat/group/GroupLinkView.kt @@ -10,7 +10,6 @@ import androidx.compose.runtime.saveable.rememberSaveable import androidx.compose.ui.Alignment import androidx.compose.ui.Modifier import androidx.compose.ui.graphics.Color -import androidx.compose.ui.platform.LocalContext import androidx.compose.ui.res.painterResource import androidx.compose.ui.res.stringResource import androidx.compose.ui.unit.dp @@ -26,7 +25,6 @@ fun GroupLinkView(chatModel: ChatModel, groupInfo: GroupInfo, connReqContact: St var groupLink by rememberSaveable { mutableStateOf(connReqContact) } val groupLinkMemberRole = rememberSaveable { mutableStateOf(memberRole) } var creatingLink by rememberSaveable { mutableStateOf(false) } - val cxt = LocalContext.current fun createLink() { creatingLink = true withApi { @@ -50,7 +48,7 @@ fun GroupLinkView(chatModel: ChatModel, groupInfo: GroupInfo, connReqContact: St groupLinkMemberRole, creatingLink, createLink = ::createLink, - share = { shareText(cxt, groupLink ?: return@GroupLinkLayout) }, + share = { shareText(groupLink ?: return@GroupLinkLayout) }, updateLink = { val role = groupLinkMemberRole.value if (role != null) { diff --git a/apps/multiplatform/android/src/main/java/chat/simplex/app/views/chat/group/GroupMemberInfoView.kt b/apps/multiplatform/android/src/main/java/chat/simplex/app/views/chat/group/GroupMemberInfoView.kt index 1ed56a8874..79a8162892 100644 --- a/apps/multiplatform/android/src/main/java/chat/simplex/app/views/chat/group/GroupMemberInfoView.kt +++ b/apps/multiplatform/android/src/main/java/chat/simplex/app/views/chat/group/GroupMemberInfoView.kt @@ -220,7 +220,7 @@ fun GroupMemberInfoLayout( val context = LocalContext.current SectionView(stringResource(R.string.address_section_title).uppercase()) { QRCode(member.contactLink, Modifier.padding(horizontal = DEFAULT_PADDING, vertical = DEFAULT_PADDING_HALF).aspectRatio(1f)) - ShareAddressButton { shareText(context, member.contactLink) } + ShareAddressButton { shareText(member.contactLink) } if (contactId != null) { if (knownDirectChat(contactId) == null && !groupInfo.fullGroupPreferences.directMessages.on) { ConnectViaAddressButton(onClick = { connectViaAddress(member.contactLink) }) diff --git a/apps/multiplatform/android/src/main/java/chat/simplex/app/views/chat/group/WelcomeMessageView.kt b/apps/multiplatform/android/src/main/java/chat/simplex/app/views/chat/group/WelcomeMessageView.kt index db3fd4daa1..91a999c6f7 100644 --- a/apps/multiplatform/android/src/main/java/chat/simplex/app/views/chat/group/WelcomeMessageView.kt +++ b/apps/multiplatform/android/src/main/java/chat/simplex/app/views/chat/group/WelcomeMessageView.kt @@ -98,7 +98,7 @@ private fun GroupWelcomeLayout( }, wt.value.isEmpty() ) - CopyTextButton { copyText(SimplexApp.context, wt.value) } + CopyTextButton { copyText(wt.value) } SectionDividerSpaced(maxBottomPadding = false) SaveButton( save = save, @@ -106,7 +106,7 @@ private fun GroupWelcomeLayout( ) } else { TextPreview(wt.value, linkMode) - CopyTextButton { copyText(SimplexApp.context, wt.value) } + CopyTextButton { copyText(wt.value) } } SectionBottomSpacer() } diff --git a/apps/multiplatform/android/src/main/java/chat/simplex/app/views/chat/item/CIFileView.kt b/apps/multiplatform/android/src/main/java/chat/simplex/app/views/chat/item/CIFileView.kt index 52a4a417f9..98aedc361e 100644 --- a/apps/multiplatform/android/src/main/java/chat/simplex/app/views/chat/item/CIFileView.kt +++ b/apps/multiplatform/android/src/main/java/chat/simplex/app/views/chat/item/CIFileView.kt @@ -32,7 +32,7 @@ fun CIFileView( receiveFile: (Long) -> Unit ) { val context = LocalContext.current - val saveFileLauncher = rememberSaveFileLauncher(cxt = context, ciFile = file) + val saveFileLauncher = rememberSaveFileLauncher(ciFile = file) @Composable fun fileIcon( @@ -95,7 +95,7 @@ fun CIFileView( ) } is CIFileStatus.RcvComplete -> { - val filePath = getLoadedFilePath(context, file) + val filePath = getLoadedFilePath(file) if (filePath != null) { saveFileLauncher.launch(file.fileName) } else { diff --git a/apps/multiplatform/android/src/main/java/chat/simplex/app/views/chat/item/CIImageView.kt b/apps/multiplatform/android/src/main/java/chat/simplex/app/views/chat/item/CIImageView.kt index c4368e1b7a..4c94bed48d 100644 --- a/apps/multiplatform/android/src/main/java/chat/simplex/app/views/chat/item/CIImageView.kt +++ b/apps/multiplatform/android/src/main/java/chat/simplex/app/views/chat/item/CIImageView.kt @@ -139,8 +139,8 @@ fun CIImageView( } fun imageAndFilePath(file: CIFile?): Pair { - val imageBitmap: Bitmap? = getLoadedImage(SimplexApp.context, file) - val filePath = getLoadedFilePath(SimplexApp.context, file) + val imageBitmap: Bitmap? = getLoadedImage(file) + val filePath = getLoadedFilePath(file) return imageBitmap to filePath } @@ -160,7 +160,7 @@ fun CIImageView( val view = LocalView.current imageView(imagePainter, onClick = { hideKeyboard(view) - if (getLoadedFilePath(context, file) != null) { + if (getLoadedFilePath(file) != null) { ModalManager.shared.showCustomModal(animated = false) { close -> ImageFullScreenView(imageProvider, close) } diff --git a/apps/multiplatform/android/src/main/java/chat/simplex/app/views/chat/item/CIInvalidJSONView.kt b/apps/multiplatform/android/src/main/java/chat/simplex/app/views/chat/item/CIInvalidJSONView.kt index f22fcf46a2..34ea631c72 100644 --- a/apps/multiplatform/android/src/main/java/chat/simplex/app/views/chat/item/CIInvalidJSONView.kt +++ b/apps/multiplatform/android/src/main/java/chat/simplex/app/views/chat/item/CIInvalidJSONView.kt @@ -1,6 +1,5 @@ package chat.simplex.app.views.chat.item -import SectionSpacer import SectionView import androidx.compose.foundation.* import androidx.compose.foundation.layout.* @@ -8,7 +7,6 @@ import androidx.compose.material.* import androidx.compose.runtime.Composable import androidx.compose.ui.Modifier import androidx.compose.ui.graphics.Color -import androidx.compose.ui.platform.LocalContext import androidx.compose.ui.res.painterResource import androidx.compose.ui.res.stringResource import androidx.compose.ui.text.font.FontStyle @@ -33,9 +31,8 @@ fun InvalidJSONView(json: String) { Column { Spacer(Modifier.height(DEFAULT_PADDING)) SectionView { - val context = LocalContext.current SettingsActionItem(painterResource(R.drawable.ic_share), generalGetString(R.string.share_verb), click = { - shareText(context, json) + shareText(json) }) } Column(Modifier.padding(DEFAULT_PADDING).fillMaxWidth().verticalScroll(rememberScrollState())) { diff --git a/apps/multiplatform/android/src/main/java/chat/simplex/app/views/chat/item/CIVideoView.kt b/apps/multiplatform/android/src/main/java/chat/simplex/app/views/chat/item/CIVideoView.kt index 63d554b9bb..cfbfb714b0 100644 --- a/apps/multiplatform/android/src/main/java/chat/simplex/app/views/chat/item/CIVideoView.kt +++ b/apps/multiplatform/android/src/main/java/chat/simplex/app/views/chat/item/CIVideoView.kt @@ -46,7 +46,7 @@ fun CIVideoView( contentAlignment = Alignment.TopEnd ) { val context = LocalContext.current - val filePath = remember(file) { getLoadedFilePath(SimplexApp.context, file) } + val filePath = remember(file) { getLoadedFilePath(file) } val preview = remember(image) { base64ToBitmap(image) } if (file != null && filePath != null) { val uri = remember(filePath) { FileProvider.getUriForFile(context, "${BuildConfig.APPLICATION_ID}.provider", File(filePath)) } @@ -100,7 +100,7 @@ fun CIVideoView( @Composable private fun VideoView(uri: Uri, file: CIFile, defaultPreview: Bitmap, defaultDuration: Long, showMenu: MutableState, onClick: () -> Unit) { val context = LocalContext.current - val player = remember(uri) { VideoPlayer.getOrCreate(uri, false, defaultPreview, defaultDuration, true, context) } + val player = remember(uri) { VideoPlayer.getOrCreate(uri, false, defaultPreview, defaultDuration, true) } val videoPlaying = remember(uri.path) { player.videoPlaying } val progress = remember(uri.path) { player.progress } val duration = remember(uri.path) { player.duration } diff --git a/apps/multiplatform/android/src/main/java/chat/simplex/app/views/chat/item/CIVoiceView.kt b/apps/multiplatform/android/src/main/java/chat/simplex/app/views/chat/item/CIVoiceView.kt index 00f96c60f7..840ba7b03f 100644 --- a/apps/multiplatform/android/src/main/java/chat/simplex/app/views/chat/item/CIVoiceView.kt +++ b/apps/multiplatform/android/src/main/java/chat/simplex/app/views/chat/item/CIVoiceView.kt @@ -44,7 +44,7 @@ fun CIVoiceView( ) { if (file != null) { val context = LocalContext.current - val filePath = remember(file.filePath, file.fileStatus) { getLoadedFilePath(context, file) } + val filePath = remember(file.filePath, file.fileStatus) { getLoadedFilePath(file) } var brokenAudio by rememberSaveable(file.filePath) { mutableStateOf(false) } val audioPlaying = rememberSaveable(file.filePath) { mutableStateOf(false) } val progress = rememberSaveable(file.filePath) { mutableStateOf(0) } diff --git a/apps/multiplatform/android/src/main/java/chat/simplex/app/views/chat/item/ChatItemView.kt b/apps/multiplatform/android/src/main/java/chat/simplex/app/views/chat/item/ChatItemView.kt index cd9dad7086..582d50b489 100644 --- a/apps/multiplatform/android/src/main/java/chat/simplex/app/views/chat/item/ChatItemView.kt +++ b/apps/multiplatform/android/src/main/java/chat/simplex/app/views/chat/item/ChatItemView.kt @@ -3,7 +3,6 @@ package chat.simplex.app.views.chat.item import android.Manifest import android.os.Build import androidx.compose.foundation.* -import androidx.compose.foundation.gestures.scrollable import androidx.compose.foundation.layout.* import androidx.compose.foundation.shape.RoundedCornerShape import androidx.compose.material.* @@ -21,13 +20,11 @@ import androidx.compose.ui.text.font.FontWeight import androidx.compose.ui.tooling.preview.Preview import androidx.compose.ui.unit.dp import androidx.compose.ui.unit.sp -import chat.simplex.app.* import chat.simplex.app.R import chat.simplex.app.model.* import chat.simplex.app.ui.theme.* import chat.simplex.app.views.chat.* import chat.simplex.app.views.helpers.* -import chat.simplex.app.views.usersettings.IncognitoView import com.google.accompanist.permissions.rememberPermissionState import kotlinx.datetime.Clock @@ -59,7 +56,7 @@ fun ChatItemView( val showMenu = remember { mutableStateOf(false) } val revealed = remember { mutableStateOf(false) } val fullDeleteAllowed = remember(cInfo) { cInfo.featureEnabled(ChatFeature.FullDelete) } - val saveFileLauncher = rememberSaveFileLauncher(cxt = context, ciFile = cItem.file) + val saveFileLauncher = rememberSaveFileLauncher(ciFile = cItem.file) val onLinkLongClick = { _: String -> showMenu.value = true } val live = composeState.value.liveMessage != null @@ -176,26 +173,26 @@ fun ChatItemView( }) } ItemAction(stringResource(R.string.share_verb), painterResource(R.drawable.ic_share), onClick = { - val filePath = getLoadedFilePath(SimplexApp.context, cItem.file) + val filePath = getLoadedFilePath(cItem.file) when { - filePath != null -> shareFile(context, cItem.text, filePath) - else -> shareText(context, cItem.content.text) + filePath != null -> shareFile(cItem.text, filePath) + else -> shareText(cItem.content.text) } showMenu.value = false }) ItemAction(stringResource(R.string.copy_verb), painterResource(R.drawable.ic_content_copy), onClick = { - copyText(context, cItem.content.text) + copyText(cItem.content.text) showMenu.value = false }) if (cItem.content.msgContent is MsgContent.MCImage || cItem.content.msgContent is MsgContent.MCVideo || cItem.content.msgContent is MsgContent.MCFile || cItem.content.msgContent is MsgContent.MCVoice) { - val filePath = getLoadedFilePath(context, cItem.file) + val filePath = getLoadedFilePath(cItem.file) if (filePath != null) { val writePermissionState = rememberPermissionState(permission = Manifest.permission.WRITE_EXTERNAL_STORAGE) ItemAction(stringResource(R.string.save_verb), painterResource(R.drawable.ic_download), onClick = { when (cItem.content.msgContent) { is MsgContent.MCImage -> { if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.R || writePermissionState.hasPermission) { - saveImage(context, cItem.file) + saveImage(cItem.file) } else { writePermissionState.launchPermissionRequest() } diff --git a/apps/multiplatform/android/src/main/java/chat/simplex/app/views/chat/item/ImageFullScreenView.kt b/apps/multiplatform/android/src/main/java/chat/simplex/app/views/chat/item/ImageFullScreenView.kt index d916e4cb94..a0279d0220 100644 --- a/apps/multiplatform/android/src/main/java/chat/simplex/app/views/chat/item/ImageFullScreenView.kt +++ b/apps/multiplatform/android/src/main/java/chat/simplex/app/views/chat/item/ImageFullScreenView.kt @@ -178,7 +178,7 @@ fun ImageFullScreenView(imageProvider: () -> ImageGalleryProvider, close: () -> @Composable private fun VideoView(modifier: Modifier, uri: Uri, defaultPreview: Bitmap, currentPage: Boolean) { val context = LocalContext.current - val player = remember(uri) { VideoPlayer.getOrCreate(uri, true, defaultPreview, 0L, true, context) } + val player = remember(uri) { VideoPlayer.getOrCreate(uri, true, defaultPreview, 0L, true) } val isCurrentPage = rememberUpdatedState(currentPage) val play = { player.play(true) diff --git a/apps/multiplatform/android/src/main/java/chat/simplex/app/views/database/ChatArchiveView.kt b/apps/multiplatform/android/src/main/java/chat/simplex/app/views/database/ChatArchiveView.kt index 6df30f9170..6bafc207c1 100644 --- a/apps/multiplatform/android/src/main/java/chat/simplex/app/views/database/ChatArchiveView.kt +++ b/apps/multiplatform/android/src/main/java/chat/simplex/app/views/database/ChatArchiveView.kt @@ -14,15 +14,14 @@ import androidx.activity.result.contract.ActivityResultContracts import androidx.compose.foundation.layout.* import androidx.compose.material.* import androidx.compose.runtime.* -import androidx.compose.ui.Alignment import androidx.compose.ui.Modifier import androidx.compose.ui.graphics.Color import androidx.compose.ui.platform.LocalContext import androidx.compose.ui.res.painterResource import androidx.compose.ui.res.stringResource import androidx.compose.ui.tooling.preview.Preview +import chat.simplex.app.* import chat.simplex.app.R -import chat.simplex.app.TAG import chat.simplex.app.model.ChatModel import chat.simplex.app.ui.theme.SimpleXTheme import chat.simplex.app.views.helpers.* @@ -36,8 +35,8 @@ import java.util.* @Composable fun ChatArchiveView(m: ChatModel, title: String, archiveName: String, archiveTime: Instant) { val context = LocalContext.current - val archivePath = "${getFilesDirectory(context)}/$archiveName" - val saveArchiveLauncher = rememberSaveArchiveLauncher(cxt = context, archivePath) + val archivePath = "${getFilesDirectory()}/$archiveName" + val saveArchiveLauncher = rememberSaveArchiveLauncher(archivePath) ChatArchiveLayout( title, archiveTime, @@ -82,10 +81,11 @@ fun ChatArchiveLayout( } @Composable -private fun rememberSaveArchiveLauncher(cxt: Context, chatArchivePath: String): ManagedActivityResultLauncher = +private fun rememberSaveArchiveLauncher(chatArchivePath: String): ManagedActivityResultLauncher = rememberLauncherForActivityResult( contract = ActivityResultContracts.CreateDocument(), onResult = { destination -> + val cxt = SimplexApp.context try { destination?.let { val contentResolver = cxt.contentResolver diff --git a/apps/multiplatform/android/src/main/java/chat/simplex/app/views/database/DatabaseEncryptionView.kt b/apps/multiplatform/android/src/main/java/chat/simplex/app/views/database/DatabaseEncryptionView.kt index 8923962bcd..642d855ac1 100644 --- a/apps/multiplatform/android/src/main/java/chat/simplex/app/views/database/DatabaseEncryptionView.kt +++ b/apps/multiplatform/android/src/main/java/chat/simplex/app/views/database/DatabaseEncryptionView.kt @@ -497,7 +497,7 @@ fun PreviewDatabaseEncryptionLayout() { SimpleXTheme { DatabaseEncryptionLayout( useKeychain = remember { mutableStateOf(true) }, - prefs = AppPreferences(SimplexApp.context), + prefs = AppPreferences(), chatDbEncrypted = true, currentKey = remember { mutableStateOf("") }, newKey = remember { mutableStateOf("") }, diff --git a/apps/multiplatform/android/src/main/java/chat/simplex/app/views/database/DatabaseErrorView.kt b/apps/multiplatform/android/src/main/java/chat/simplex/app/views/database/DatabaseErrorView.kt index c1aa1fe5a6..a8192e3d89 100644 --- a/apps/multiplatform/android/src/main/java/chat/simplex/app/views/database/DatabaseErrorView.kt +++ b/apps/multiplatform/android/src/main/java/chat/simplex/app/views/database/DatabaseErrorView.kt @@ -42,7 +42,7 @@ fun DatabaseErrorView( var storedDBKey by remember { mutableStateOf(DatabaseUtils.ksDatabasePassword.get()) } var useKeychain by remember { mutableStateOf(appPreferences.storeDBPassphrase.get()) } val context = LocalContext.current - val restoreDbFromBackup = remember { mutableStateOf(shouldShowRestoreDbButton(appPreferences, context)) } + val restoreDbFromBackup = remember { mutableStateOf(shouldShowRestoreDbButton(appPreferences)) } fun callRunChat(confirmMigrations: MigrationConfirmation? = null) { val useKey = if (useKeychain) null else dbKey.value @@ -164,7 +164,7 @@ fun DatabaseErrorView( title = generalGetString(R.string.restore_database_alert_title), text = generalGetString(R.string.restore_database_alert_desc), confirmText = generalGetString(R.string.restore_database_alert_confirm), - onConfirm = { restoreDb(restoreDbFromBackup, appPreferences, context) }, + onConfirm = { restoreDb(restoreDbFromBackup, appPreferences) }, destructive = true, ) } @@ -226,7 +226,8 @@ private fun runChat( } } -private fun shouldShowRestoreDbButton(prefs: AppPreferences, context: Context): Boolean { +private fun shouldShowRestoreDbButton(prefs: AppPreferences): Boolean { + val context = SimplexApp.context val startedAt = prefs.encryptionStartedAt.get() ?: return false /** Just in case there is any small difference between reported Java's [Clock.System.now] and Linux's time on a file */ val safeDiffInTime = 10_000L @@ -238,7 +239,8 @@ private fun shouldShowRestoreDbButton(prefs: AppPreferences, context: Context): startedAt.toEpochMilliseconds() - safeDiffInTime <= filesAgent.lastModified() } -private fun restoreDb(restoreDbFromBackup: MutableState, prefs: AppPreferences, context: Context) { +private fun restoreDb(restoreDbFromBackup: MutableState, prefs: AppPreferences) { + val context = SimplexApp.context val filesChatBase = context.dataDir.absolutePath + File.separator + "files_chat.db" val filesAgentBase = context.dataDir.absolutePath + File.separator + "files_agent.db" try { @@ -299,7 +301,7 @@ fun PreviewChatInfoLayout() { SimpleXTheme { DatabaseErrorView( remember { mutableStateOf(DBMigrationResult.ErrorNotADatabase("simplex_v1_chat.db")) }, - AppPreferences(SimplexApp.context) + AppPreferences() ) } } diff --git a/apps/multiplatform/android/src/main/java/chat/simplex/app/views/database/DatabaseView.kt b/apps/multiplatform/android/src/main/java/chat/simplex/app/views/database/DatabaseView.kt index 99293f76ef..41b19cb60c 100644 --- a/apps/multiplatform/android/src/main/java/chat/simplex/app/views/database/DatabaseView.kt +++ b/apps/multiplatform/android/src/main/java/chat/simplex/app/views/database/DatabaseView.kt @@ -57,11 +57,11 @@ fun DatabaseView( val chatArchiveTime = remember { mutableStateOf(prefs.chatArchiveTime.get()) } val chatLastStart = remember { mutableStateOf(prefs.chatLastStart.get()) } val chatArchiveFile = remember { mutableStateOf(null) } - val saveArchiveLauncher = rememberSaveArchiveLauncher(cxt = context, chatArchiveFile) - val appFilesCountAndSize = remember { mutableStateOf(directoryFileCountAndSize(getAppFilesDirectory(context))) } + val saveArchiveLauncher = rememberSaveArchiveLauncher(chatArchiveFile) + val appFilesCountAndSize = remember { mutableStateOf(directoryFileCountAndSize(getAppFilesDirectory())) } val importArchiveLauncher = rememberGetContentLauncher { uri: Uri? -> if (uri != null) { - importArchiveAlert(m, context, uri, appFilesCountAndSize, progressIndicator) + importArchiveAlert(m, uri, appFilesCountAndSize, progressIndicator) } } LaunchedEffect(m.chatRunning) { @@ -88,17 +88,17 @@ fun DatabaseView( m.currentUser.value, m.users, startChat = { startChat(m, runChat, chatLastStart, m.chatDbChanged) }, - stopChatAlert = { stopChatAlert(m, runChat, context) }, - exportArchive = { exportArchive(context, m, progressIndicator, chatArchiveName, chatArchiveTime, chatArchiveFile, saveArchiveLauncher) }, + stopChatAlert = { stopChatAlert(m, runChat) }, + exportArchive = { exportArchive(m, progressIndicator, chatArchiveName, chatArchiveTime, chatArchiveFile, saveArchiveLauncher) }, deleteChatAlert = { deleteChatAlert(m, progressIndicator) }, - deleteAppFilesAndMedia = { deleteFilesAndMediaAlert(context, appFilesCountAndSize) }, + deleteAppFilesAndMedia = { deleteFilesAndMediaAlert(appFilesCountAndSize) }, onChatItemTTLSelected = { val oldValue = chatItemTTL.value chatItemTTL.value = it if (it < oldValue) { - setChatItemTTLAlert(m, chatItemTTL, progressIndicator, appFilesCountAndSize, context) + setChatItemTTLAlert(m, chatItemTTL, progressIndicator, appFilesCountAndSize) } else if (it != oldValue) { - setCiTTL(m, chatItemTTL, progressIndicator, appFilesCountAndSize, context) + setCiTTL(m, chatItemTTL, progressIndicator, appFilesCountAndSize) } }, showSettingsModal @@ -283,13 +283,12 @@ private fun setChatItemTTLAlert( m: ChatModel, selectedChatItemTTL: MutableState, progressIndicator: MutableState, appFilesCountAndSize: MutableState>, - context: Context ) { AlertManager.shared.showAlertDialog( title = generalGetString(R.string.enable_automatic_deletion_question), text = generalGetString(R.string.enable_automatic_deletion_message), confirmText = generalGetString(R.string.delete_messages), - onConfirm = { setCiTTL(m, selectedChatItemTTL, progressIndicator, appFilesCountAndSize, context) }, + onConfirm = { setCiTTL(m, selectedChatItemTTL, progressIndicator, appFilesCountAndSize) }, onDismiss = { selectedChatItemTTL.value = m.chatItemTTL.value }, destructive = true, ) @@ -387,12 +386,12 @@ private fun startChat(m: ChatModel, runChat: MutableState, chatLastSta } } -private fun stopChatAlert(m: ChatModel, runChat: MutableState, context: Context) { +private fun stopChatAlert(m: ChatModel, runChat: MutableState) { AlertManager.shared.showAlertDialog( title = generalGetString(R.string.stop_chat_question), text = generalGetString(R.string.stop_chat_to_export_import_or_delete_chat_database), confirmText = generalGetString(R.string.stop_chat_confirmation), - onConfirm = { authStopChat(m, runChat, context) }, + onConfirm = { authStopChat(m, runChat) }, onDismiss = { runChat.value = true } ) } @@ -404,16 +403,15 @@ private fun exportProhibitedAlert() { ) } -private fun authStopChat(m: ChatModel, runChat: MutableState, context: Context) { +private fun authStopChat(m: ChatModel, runChat: MutableState) { if (m.controller.appPrefs.performLA.get()) { authenticate( generalGetString(R.string.auth_stop_chat), generalGetString(R.string.auth_log_in_using_credential), - activity = context as FragmentActivity, completed = { laResult -> when (laResult) { LAResult.Success, is LAResult.Unavailable -> { - stopChat(m, runChat, context) + stopChat(m, runChat) } is LAResult.Error -> { runChat.value = true @@ -425,11 +423,11 @@ private fun authStopChat(m: ChatModel, runChat: MutableState, context: } ) } else { - stopChat(m, runChat, context) + stopChat(m, runChat) } } -private fun stopChat(m: ChatModel, runChat: MutableState, context: Context) { +private fun stopChat(m: ChatModel, runChat: MutableState) { withApi { try { runChat.value = false @@ -455,7 +453,6 @@ suspend fun deleteChatAsync(m: ChatModel) { } private fun exportArchive( - context: Context, m: ChatModel, progressIndicator: MutableState, chatArchiveName: MutableState, @@ -466,7 +463,7 @@ private fun exportArchive( progressIndicator.value = true withApi { try { - val archiveFile = exportChatArchive(m, context, chatArchiveName, chatArchiveTime, chatArchiveFile) + val archiveFile = exportChatArchive(m, chatArchiveName, chatArchiveTime, chatArchiveFile) chatArchiveFile.value = archiveFile saveArchiveLauncher.launch(archiveFile.substringAfterLast("/")) progressIndicator.value = false @@ -479,7 +476,6 @@ private fun exportArchive( private suspend fun exportChatArchive( m: ChatModel, - context: Context, chatArchiveName: MutableState, chatArchiveTime: MutableState, chatArchiveFile: MutableState @@ -487,10 +483,10 @@ private suspend fun exportChatArchive( val archiveTime = Clock.System.now() val ts = SimpleDateFormat("yyyy-MM-dd'T'HHmmss", Locale.US).format(Date.from(archiveTime.toJavaInstant())) val archiveName = "simplex-chat.$ts.zip" - val archivePath = "${getFilesDirectory(context)}/$archiveName" - val config = ArchiveConfig(archivePath, parentTempDirectory = context.cacheDir.toString()) + val archivePath = "${getFilesDirectory()}/$archiveName" + val config = ArchiveConfig(archivePath, parentTempDirectory = SimplexApp.context.cacheDir.toString()) m.controller.apiExportArchive(config) - deleteOldArchive(m, context) + deleteOldArchive(m) m.controller.appPrefs.chatArchiveName.set(archiveName) chatArchiveName.value = archiveName m.controller.appPrefs.chatArchiveTime.set(archiveTime) @@ -499,10 +495,10 @@ private suspend fun exportChatArchive( return archivePath } -private fun deleteOldArchive(m: ChatModel, context: Context) { +private fun deleteOldArchive(m: ChatModel) { val chatArchiveName = m.controller.appPrefs.chatArchiveName.get() if (chatArchiveName != null) { - val file = File("${getFilesDirectory(context)}/$chatArchiveName") + val file = File("${getFilesDirectory()}/$chatArchiveName") val fileDeleted = file.delete() if (fileDeleted) { m.controller.appPrefs.chatArchiveName.set(null) @@ -514,15 +510,16 @@ private fun deleteOldArchive(m: ChatModel, context: Context) { } @Composable -private fun rememberSaveArchiveLauncher(cxt: Context, chatArchiveFile: MutableState): ManagedActivityResultLauncher = +private fun rememberSaveArchiveLauncher(chatArchiveFile: MutableState): ManagedActivityResultLauncher = rememberLauncherForActivityResult( contract = ActivityResultContracts.CreateDocument(), onResult = { destination -> + val cxt = SimplexApp.context try { destination?.let { val filePath = chatArchiveFile.value if (filePath != null) { - val contentResolver = cxt.contentResolver + val contentResolver = SimplexApp.context.contentResolver contentResolver.openOutputStream(destination)?.let { stream -> val outputStream = BufferedOutputStream(stream) File(filePath).inputStream().use { it.copyTo(outputStream) } @@ -544,7 +541,6 @@ private fun rememberSaveArchiveLauncher(cxt: Context, chatArchiveFile: MutableSt private fun importArchiveAlert( m: ChatModel, - context: Context, importedArchiveUri: Uri, appFilesCountAndSize: MutableState>, progressIndicator: MutableState @@ -553,29 +549,28 @@ private fun importArchiveAlert( title = generalGetString(R.string.import_database_question), text = generalGetString(R.string.your_current_chat_database_will_be_deleted_and_replaced_with_the_imported_one), confirmText = generalGetString(R.string.import_database_confirmation), - onConfirm = { importArchive(m, context, importedArchiveUri, appFilesCountAndSize, progressIndicator) }, + onConfirm = { importArchive(m, importedArchiveUri, appFilesCountAndSize, progressIndicator) }, destructive = true, ) } private fun importArchive( m: ChatModel, - context: Context, importedArchiveUri: Uri, appFilesCountAndSize: MutableState>, progressIndicator: MutableState ) { progressIndicator.value = true - val archivePath = saveArchiveFromUri(context, importedArchiveUri) + val archivePath = saveArchiveFromUri(importedArchiveUri) if (archivePath != null) { withApi { try { m.controller.apiDeleteStorage() try { - val config = ArchiveConfig(archivePath, parentTempDirectory = context.cacheDir.toString()) + val config = ArchiveConfig(archivePath, parentTempDirectory = SimplexApp.context.cacheDir.toString()) val archiveErrors = m.controller.apiImportArchive(config) DatabaseUtils.ksDatabasePassword.remove() - appFilesCountAndSize.value = directoryFileCountAndSize(getAppFilesDirectory(context)) + appFilesCountAndSize.value = directoryFileCountAndSize(getAppFilesDirectory()) if (archiveErrors.isEmpty()) { operationEnded(m, progressIndicator) { AlertManager.shared.showAlertMsg(generalGetString(R.string.chat_database_imported), text = generalGetString(R.string.restart_the_app_to_use_imported_chat_database)) @@ -601,12 +596,12 @@ private fun importArchive( } } -private fun saveArchiveFromUri(context: Context, importedArchiveUri: Uri): String? { +private fun saveArchiveFromUri(importedArchiveUri: Uri): String? { return try { - val inputStream = context.contentResolver.openInputStream(importedArchiveUri) - val archiveName = getFileName(context, importedArchiveUri) + val inputStream = SimplexApp.context.contentResolver.openInputStream(importedArchiveUri) + val archiveName = getFileName(importedArchiveUri) if (inputStream != null && archiveName != null) { - val archivePath = "${context.cacheDir}/$archiveName" + val archivePath = "${SimplexApp.context.cacheDir}/$archiveName" val destFile = File(archivePath) IOUtils.copy(inputStream, FileOutputStream(destFile)) archivePath @@ -651,7 +646,6 @@ private fun setCiTTL( chatItemTTL: MutableState, progressIndicator: MutableState, appFilesCountAndSize: MutableState>, - context: Context ) { Log.d(TAG, "DatabaseView setChatItemTTL ${chatItemTTL.value.seconds ?: -1}") progressIndicator.value = true @@ -660,11 +654,11 @@ private fun setCiTTL( m.controller.setChatItemTTL(chatItemTTL.value) // Update model on success m.chatItemTTL.value = chatItemTTL.value - afterSetCiTTL(m, progressIndicator, appFilesCountAndSize, context) + afterSetCiTTL(m, progressIndicator, appFilesCountAndSize) } catch (e: Exception) { // Rollback to model's value chatItemTTL.value = m.chatItemTTL.value - afterSetCiTTL(m, progressIndicator, appFilesCountAndSize, context) + afterSetCiTTL(m, progressIndicator, appFilesCountAndSize) AlertManager.shared.showAlertMsg(generalGetString(R.string.error_changing_message_deletion), e.stackTraceToString()) } } @@ -673,11 +667,10 @@ private fun setCiTTL( private fun afterSetCiTTL( m: ChatModel, progressIndicator: MutableState, - appFilesCountAndSize: MutableState>, - context: Context + appFilesCountAndSize: MutableState> ) { progressIndicator.value = false - appFilesCountAndSize.value = directoryFileCountAndSize(getAppFilesDirectory(context)) + appFilesCountAndSize.value = directoryFileCountAndSize(getAppFilesDirectory()) withApi { try { val chats = m.controller.apiGetChats() @@ -688,19 +681,19 @@ private fun afterSetCiTTL( } } -private fun deleteFilesAndMediaAlert(context: Context, appFilesCountAndSize: MutableState>) { +private fun deleteFilesAndMediaAlert(appFilesCountAndSize: MutableState>) { AlertManager.shared.showAlertDialog( title = generalGetString(R.string.delete_files_and_media_question), text = generalGetString(R.string.delete_files_and_media_desc), confirmText = generalGetString(R.string.delete_verb), - onConfirm = { deleteFiles(appFilesCountAndSize, context) }, + onConfirm = { deleteFiles(appFilesCountAndSize) }, destructive = true ) } -private fun deleteFiles(appFilesCountAndSize: MutableState>, context: Context) { - deleteAppFiles(context) - appFilesCountAndSize.value = directoryFileCountAndSize(getAppFilesDirectory(context)) +private fun deleteFiles(appFilesCountAndSize: MutableState>) { + deleteAppFiles() + appFilesCountAndSize.value = directoryFileCountAndSize(getAppFilesDirectory()) } private fun operationEnded(m: ChatModel, progressIndicator: MutableState, alert: () -> Unit) { diff --git a/apps/multiplatform/android/src/main/java/chat/simplex/app/views/helpers/DatabaseUtils.kt b/apps/multiplatform/android/src/main/java/chat/simplex/app/views/helpers/DatabaseUtils.kt index 68829da559..f200d4ab69 100644 --- a/apps/multiplatform/android/src/main/java/chat/simplex/app/views/helpers/DatabaseUtils.kt +++ b/apps/multiplatform/android/src/main/java/chat/simplex/app/views/helpers/DatabaseUtils.kt @@ -2,8 +2,7 @@ package chat.simplex.app.views.helpers import android.util.Log import chat.simplex.app.* -import chat.simplex.app.model.AppPreferences -import chat.simplex.app.model.SharedPreference +import chat.simplex.app.model.* import chat.simplex.app.views.usersettings.Cryptor import kotlinx.serialization.* import java.io.File @@ -13,7 +12,7 @@ object DatabaseUtils { private val cryptor = Cryptor() private val appPreferences: AppPreferences by lazy { - AppPreferences(SimplexApp.context) + ChatController.appPrefs } private const val DATABASE_PASSWORD_ALIAS: String = "databasePassword" diff --git a/apps/multiplatform/android/src/main/java/chat/simplex/app/views/helpers/GetImageView.kt b/apps/multiplatform/android/src/main/java/chat/simplex/app/views/helpers/GetImageView.kt index 9ad3180ae5..0ea74b4b5f 100644 --- a/apps/multiplatform/android/src/main/java/chat/simplex/app/views/helpers/GetImageView.kt +++ b/apps/multiplatform/android/src/main/java/chat/simplex/app/views/helpers/GetImageView.kt @@ -111,7 +111,7 @@ fun base64ToBitmap(base64ImageString: String): Bitmap { class CustomTakePicturePreview(var uri: Uri?, var tmpFile: File?): ActivityResultContract() { @CallSuper override fun createIntent(context: Context, input: Void?): Intent { - tmpFile = File.createTempFile("image", ".bmp", File(getAppFilesDirectory(SimplexApp.context))) + tmpFile = File.createTempFile("image", ".bmp", File(getAppFilesDirectory())) // Since the class should return Uri, the file should be deleted somewhere else. And in order to be sure, delegate this to system tmpFile?.deleteOnExit() uri = FileProvider.getUriForFile(context, "${BuildConfig.APPLICATION_ID}.provider", tmpFile!!) diff --git a/apps/multiplatform/android/src/main/java/chat/simplex/app/views/helpers/LocalAuthentication.kt b/apps/multiplatform/android/src/main/java/chat/simplex/app/views/helpers/LocalAuthentication.kt index 74c8cdedd0..ed406c32fd 100644 --- a/apps/multiplatform/android/src/main/java/chat/simplex/app/views/helpers/LocalAuthentication.kt +++ b/apps/multiplatform/android/src/main/java/chat/simplex/app/views/helpers/LocalAuthentication.kt @@ -40,10 +40,10 @@ fun authenticate( promptTitle: String, promptSubtitle: String, selfDestruct: Boolean = false, - activity: FragmentActivity, usingLAMode: LAMode = SimplexApp.context.chatModel.controller.appPrefs.laMode.get(), completed: (LAResult) -> Unit ) { + val activity = SimplexApp.context.mainActivity.get() ?: return completed(LAResult.Error("")) when (usingLAMode) { LAMode.SYSTEM -> when { SDK_INT in 28..29 -> diff --git a/apps/multiplatform/android/src/main/java/chat/simplex/app/views/helpers/RecAndPlay.kt b/apps/multiplatform/android/src/main/java/chat/simplex/app/views/helpers/RecAndPlay.kt index 63da5f5c24..e902f3dc37 100644 --- a/apps/multiplatform/android/src/main/java/chat/simplex/app/views/helpers/RecAndPlay.kt +++ b/apps/multiplatform/android/src/main/java/chat/simplex/app/views/helpers/RecAndPlay.kt @@ -51,7 +51,7 @@ class RecorderNative(): Recorder { rec.setAudioEncodingBitRate(32000) rec.setMaxDuration(MAX_VOICE_MILLIS_FOR_SENDING) val tmpDir = SimplexApp.context.getDir("temp", Application.MODE_PRIVATE) - val fileToSave = File.createTempFile(generateNewFileName(SimplexApp.context, "voice", "${extension}_"), ".tmp", tmpDir) + val fileToSave = File.createTempFile(generateNewFileName("voice", "${extension}_"), ".tmp", tmpDir) fileToSave.deleteOnExit() val path = fileToSave.absolutePath filePath = path diff --git a/apps/multiplatform/android/src/main/java/chat/simplex/app/views/helpers/Share.kt b/apps/multiplatform/android/src/main/java/chat/simplex/app/views/helpers/Share.kt index 015f05f0e6..0affca2e34 100644 --- a/apps/multiplatform/android/src/main/java/chat/simplex/app/views/helpers/Share.kt +++ b/apps/multiplatform/android/src/main/java/chat/simplex/app/views/helpers/Share.kt @@ -18,17 +18,19 @@ import chat.simplex.app.model.CIFile import java.io.BufferedOutputStream import java.io.File -fun shareText(cxt: Context, text: String) { +fun shareText(text: String) { val sendIntent: Intent = Intent().apply { action = Intent.ACTION_SEND putExtra(Intent.EXTRA_TEXT, text) type = "text/plain" } val shareIntent = Intent.createChooser(sendIntent, null) - cxt.startActivity(shareIntent) + // This flag is needed when you start a new activity from non-Activity context + shareIntent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK) + SimplexApp.context.startActivity(shareIntent) } -fun shareFile(cxt: Context, text: String, filePath: String) { +fun shareFile(text: String, filePath: String) { val uri = FileProvider.getUriForFile(SimplexApp.context, "${BuildConfig.APPLICATION_ID}.provider", File(filePath)) val ext = filePath.substringAfterLast(".") val mimeType = MimeTypeMap.getSingleton().getMimeTypeFromExtension(ext) ?: return @@ -41,32 +43,37 @@ fun shareFile(cxt: Context, text: String, filePath: String) { type = mimeType } val shareIntent = Intent.createChooser(sendIntent, null) - cxt.startActivity(shareIntent) + // This flag is needed when you start a new activity from non-Activity context + shareIntent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK) + SimplexApp.context.startActivity(shareIntent) } -fun copyText(cxt: Context, text: String) { - val clipboard = ContextCompat.getSystemService(cxt, ClipboardManager::class.java) +fun copyText(text: String) { + val clipboard = ContextCompat.getSystemService(SimplexApp.context, ClipboardManager::class.java) clipboard?.setPrimaryClip(ClipData.newPlainText("text", text)) } -fun sendEmail(context: Context, subject: String, body: CharSequence) { +fun sendEmail(subject: String, body: CharSequence) { val emailIntent = Intent(Intent.ACTION_SENDTO, Uri.parse("mailto:")) emailIntent.putExtra(Intent.EXTRA_SUBJECT, subject) emailIntent.putExtra(Intent.EXTRA_TEXT, body) + // This flag is needed when you start a new activity from non-Activity context + emailIntent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK) try { - context.startActivity(emailIntent) + SimplexApp.context.startActivity(emailIntent) } catch (e: ActivityNotFoundException) { Log.e(TAG, "No activity was found for handling email intent") } } @Composable -fun rememberSaveFileLauncher(cxt: Context, ciFile: CIFile?): ManagedActivityResultLauncher = +fun rememberSaveFileLauncher(ciFile: CIFile?): ManagedActivityResultLauncher = rememberLauncherForActivityResult( contract = ActivityResultContracts.CreateDocument(), onResult = { destination -> destination?.let { - val filePath = getLoadedFilePath(cxt, ciFile) + val cxt = SimplexApp.context + val filePath = getLoadedFilePath(ciFile) if (filePath != null) { val contentResolver = cxt.contentResolver contentResolver.openOutputStream(destination)?.let { stream -> @@ -95,8 +102,9 @@ fun imageMimeType(fileName: String): String { } /** Before calling, make sure the user allows to write to external storage [Manifest.permission.WRITE_EXTERNAL_STORAGE] */ -fun saveImage(cxt: Context, ciFile: CIFile?) { - val filePath = getLoadedFilePath(cxt, ciFile) +fun saveImage(ciFile: CIFile?) { + val cxt = SimplexApp.context + val filePath = getLoadedFilePath(ciFile) val fileName = ciFile?.fileName if (filePath != null && fileName != null) { val values = ContentValues() diff --git a/apps/multiplatform/android/src/main/java/chat/simplex/app/views/helpers/Util.kt b/apps/multiplatform/android/src/main/java/chat/simplex/app/views/helpers/Util.kt index 1f6d3e7f0b..dea06315a7 100644 --- a/apps/multiplatform/android/src/main/java/chat/simplex/app/views/helpers/Util.kt +++ b/apps/multiplatform/android/src/main/java/chat/simplex/app/views/helpers/Util.kt @@ -247,30 +247,30 @@ const val MAX_FILE_SIZE_SMP: Long = 8000000 const val MAX_FILE_SIZE_XFTP: Long = 1_073_741_824 // 1GB -fun getFilesDirectory(context: Context): String { - return context.filesDir.toString() +fun getFilesDirectory(): String { + return SimplexApp.context.filesDir.toString() } -fun getTempFilesDirectory(context: Context): String { - return "${getFilesDirectory(context)}/temp_files" +fun getTempFilesDirectory(): String { + return "${getFilesDirectory()}/temp_files" } -fun getAppFilesDirectory(context: Context): String { - return "${getFilesDirectory(context)}/app_files" +fun getAppFilesDirectory(): String { + return "${getFilesDirectory()}/app_files" } -fun getAppFilePath(context: Context, fileName: String): String { - return "${getAppFilesDirectory(context)}/$fileName" +fun getAppFilePath(fileName: String): String { + return "${getAppFilesDirectory()}/$fileName" } fun getAppFileUri(fileName: String): Uri { - return Uri.parse("${getAppFilesDirectory(SimplexApp.context)}/$fileName") + return Uri.parse("${getAppFilesDirectory()}/$fileName") } -fun getLoadedFilePath(context: Context, file: CIFile?): String? { +fun getLoadedFilePath(file: CIFile?): String? { return if (file?.filePath != null && file.loaded) { - val filePath = getAppFilePath(context, file.filePath) + val filePath = getAppFilePath(file.filePath) if (File(filePath).exists()) filePath else null } else { null @@ -278,12 +278,12 @@ fun getLoadedFilePath(context: Context, file: CIFile?): String? { } // https://developer.android.com/training/data-storage/shared/documents-files#bitmap -fun getLoadedImage(context: Context, file: CIFile?): Bitmap? { - val filePath = getLoadedFilePath(context, file) +fun getLoadedImage(file: CIFile?): Bitmap? { + val filePath = getLoadedFilePath(file) return if (filePath != null) { try { - val uri = FileProvider.getUriForFile(context, "${BuildConfig.APPLICATION_ID}.provider", File(filePath)) - val parcelFileDescriptor = context.contentResolver.openFileDescriptor(uri, "r") + val uri = FileProvider.getUriForFile(SimplexApp.context, "${BuildConfig.APPLICATION_ID}.provider", File(filePath)) + val parcelFileDescriptor = SimplexApp.context.contentResolver.openFileDescriptor(uri, "r") val fileDescriptor = parcelFileDescriptor?.fileDescriptor val image = decodeSampledBitmapFromFileDescriptor(fileDescriptor, 1000, 1000) parcelFileDescriptor?.close() @@ -329,24 +329,24 @@ private fun calculateInSampleSize(options: BitmapFactory.Options, reqWidth: Int, return inSampleSize } -fun getFileName(context: Context, uri: Uri): String? { - return context.contentResolver.query(uri, null, null, null, null)?.use { cursor -> +fun getFileName(uri: Uri): String? { + return SimplexApp.context.contentResolver.query(uri, null, null, null, null)?.use { cursor -> val nameIndex = cursor.getColumnIndex(OpenableColumns.DISPLAY_NAME) cursor.moveToFirst() cursor.getString(nameIndex) } } -fun getAppFilePath(context: Context, uri: Uri): String? { - return context.contentResolver.query(uri, null, null, null, null)?.use { cursor -> +fun getAppFilePath(uri: Uri): String? { + return SimplexApp.context.contentResolver.query(uri, null, null, null, null)?.use { cursor -> val nameIndex = cursor.getColumnIndex(OpenableColumns.DISPLAY_NAME) cursor.moveToFirst() - getAppFilePath(context, cursor.getString(nameIndex)) + getAppFilePath(cursor.getString(nameIndex)) } } -fun getFileSize(context: Context, uri: Uri): Long? { - return context.contentResolver.query(uri, null, null, null, null)?.use { cursor -> +fun getFileSize(uri: Uri): Long? { + return SimplexApp.context.contentResolver.query(uri, null, null, null, null)?.use { cursor -> val sizeIndex = cursor.getColumnIndex(OpenableColumns.SIZE) cursor.moveToFirst() cursor.getLong(sizeIndex) @@ -369,7 +369,7 @@ fun getBitmapFromUri(uri: Uri, withAlertOnException: Boolean = true): Bitmap? { null } } else { - BitmapFactory.decodeFile(getAppFilePath(SimplexApp.context, uri)) + BitmapFactory.decodeFile(getAppFilePath(uri)) } } @@ -389,7 +389,7 @@ fun getDrawableFromUri(uri: Uri, withAlertOnException: Boolean = true): Drawable null } } else { - Drawable.createFromPath(getAppFilePath(SimplexApp.context, uri)) + Drawable.createFromPath(getAppFilePath(uri)) } } @@ -409,17 +409,17 @@ fun getThemeFromUri(uri: Uri, withAlertOnException: Boolean = true): ThemeOverri return null } -fun saveImage(context: Context, uri: Uri): String? { +fun saveImage(uri: Uri): String? { val bitmap = getBitmapFromUri(uri) ?: return null - return saveImage(context, bitmap) + return saveImage(bitmap) } -fun saveImage(context: Context, image: Bitmap): String? { +fun saveImage(image: Bitmap): String? { return try { val ext = if (image.hasAlpha()) "png" else "jpg" val dataResized = resizeImageToDataSize(image, ext == "png", maxDataSize = MAX_IMAGE_SIZE) - val fileToSave = generateNewFileName(context, "IMG", ext) - val file = File(getAppFilePath(context, fileToSave)) + val fileToSave = generateNewFileName("IMG", ext) + val file = File(getAppFilePath(fileToSave)) val output = FileOutputStream(file) dataResized.writeTo(output) output.flush() @@ -431,9 +431,9 @@ fun saveImage(context: Context, image: Bitmap): String? { } } -fun saveAnimImage(context: Context, uri: Uri): String? { +fun saveAnimImage(uri: Uri): String? { return try { - val filename = getFileName(context, uri)?.lowercase() + val filename = getFileName(uri)?.lowercase() var ext = when { // remove everything but extension filename?.contains(".") == true -> filename.replaceBeforeLast('.', "").replace(".", "") @@ -441,10 +441,10 @@ fun saveAnimImage(context: Context, uri: Uri): String? { } // Just in case the image has a strange extension if (ext.length < 3 || ext.length > 4) ext = "gif" - val fileToSave = generateNewFileName(context, "IMG", ext) - val file = File(getAppFilePath(context, fileToSave)) + val fileToSave = generateNewFileName("IMG", ext) + val file = File(getAppFilePath(fileToSave)) val output = FileOutputStream(file) - context.contentResolver.openInputStream(uri)!!.use { input -> + SimplexApp.context.contentResolver.openInputStream(uri)!!.use { input -> output.use { output -> input.copyTo(output) } @@ -460,7 +460,7 @@ fun saveTempImageUncompressed(image: Bitmap, asPng: Boolean): File? { return try { val ext = if (asPng) "png" else "jpg" val tmpDir = SimplexApp.context.getDir("temp", Application.MODE_PRIVATE) - return File(tmpDir.absolutePath + File.separator + generateNewFileName(SimplexApp.context, "IMG", ext)).apply { + return File(tmpDir.absolutePath + File.separator + generateNewFileName("IMG", ext)).apply { outputStream().use { out -> image.compress(if (asPng) Bitmap.CompressFormat.PNG else Bitmap.CompressFormat.JPEG, 85, out) out.flush() @@ -474,13 +474,13 @@ fun saveTempImageUncompressed(image: Bitmap, asPng: Boolean): File? { } } -fun saveFileFromUri(context: Context, uri: Uri): String? { +fun saveFileFromUri(uri: Uri): String? { return try { - val inputStream = context.contentResolver.openInputStream(uri) - val fileToSave = getFileName(context, uri) + val inputStream = SimplexApp.context.contentResolver.openInputStream(uri) + val fileToSave = getFileName(uri) if (inputStream != null && fileToSave != null) { - val destFileName = uniqueCombine(context, fileToSave) - val destFile = File(getAppFilePath(context, destFileName)) + val destFileName = uniqueCombine(fileToSave) + val destFile = File(getAppFilePath(destFileName)) IOUtils.copy(inputStream, FileOutputStream(destFile)) destFileName } else { @@ -493,21 +493,21 @@ fun saveFileFromUri(context: Context, uri: Uri): String? { } } -fun generateNewFileName(context: Context, prefix: String, ext: String): String { +fun generateNewFileName(prefix: String, ext: String): String { val sdf = SimpleDateFormat("yyyyMMdd_HHmmss", Locale.US) sdf.timeZone = TimeZone.getTimeZone("GMT") val timestamp = sdf.format(Date()) - return uniqueCombine(context, "${prefix}_$timestamp.$ext") + return uniqueCombine("${prefix}_$timestamp.$ext") } -fun uniqueCombine(context: Context, fileName: String): String { +fun uniqueCombine(fileName: String): String { val orig = File(fileName) val name = orig.nameWithoutExtension val ext = orig.extension fun tryCombine(n: Int): String { val suffix = if (n == 0) "" else "_$n" val f = "$name$suffix.$ext" - return if (File(getAppFilePath(context, f)).exists()) tryCombine(n + 1) else f + return if (File(getAppFilePath(f)).exists()) tryCombine(n + 1) else f } return tryCombine(0) } @@ -530,8 +530,8 @@ fun formatBytes(bytes: Long): String { } } -fun removeFile(context: Context, fileName: String): Boolean { - val file = File(getAppFilePath(context, fileName)) +fun removeFile(fileName: String): Boolean { + val file = File(getAppFilePath(fileName)) val fileDeleted = file.delete() if (!fileDeleted) { Log.e(chat.simplex.app.TAG, "Util.kt removeFile error") @@ -539,11 +539,11 @@ fun removeFile(context: Context, fileName: String): Boolean { return fileDeleted } -fun deleteAppFiles(context: Context) { - val dir = File(getAppFilesDirectory(context)) +fun deleteAppFiles() { + val dir = File(getAppFilesDirectory()) try { dir.list()?.forEach { - removeFile(context, it) + removeFile(it) } } catch (e: java.lang.Exception) { Log.e(TAG, "Util deleteAppFiles error: ${e.stackTraceToString()}") diff --git a/apps/multiplatform/android/src/main/java/chat/simplex/app/views/helpers/VideoPlayer.kt b/apps/multiplatform/android/src/main/java/chat/simplex/app/views/helpers/VideoPlayer.kt index dc0744a752..25f5d6aacf 100644 --- a/apps/multiplatform/android/src/main/java/chat/simplex/app/views/helpers/VideoPlayer.kt +++ b/apps/multiplatform/android/src/main/java/chat/simplex/app/views/helpers/VideoPlayer.kt @@ -24,7 +24,6 @@ class VideoPlayer private constructor( private val defaultPreview: Bitmap, defaultDuration: Long, soundEnabled: Boolean, - context: Context ) { companion object { private val players: MutableMap, VideoPlayer> = mutableMapOf() @@ -36,9 +35,8 @@ class VideoPlayer private constructor( defaultPreview: Bitmap, defaultDuration: Long, soundEnabled: Boolean, - context: Context ): VideoPlayer = - players.getOrPut(uri to gallery) { VideoPlayer(uri, gallery, defaultPreview, defaultDuration, soundEnabled, context) } + players.getOrPut(uri to gallery) { VideoPlayer(uri, gallery, defaultPreview, defaultDuration, soundEnabled) } fun enableSound(enable: Boolean, fileName: String?, gallery: Boolean): Boolean = player(fileName, gallery)?.enableSound(enable) == true @@ -76,8 +74,8 @@ class VideoPlayer private constructor( setPreviewAndDuration() } - val player = ExoPlayer.Builder(context, - DefaultRenderersFactory(context)) + val player = ExoPlayer.Builder(SimplexApp.context, + DefaultRenderersFactory(SimplexApp.context)) /*.setLoadControl(DefaultLoadControl.Builder() .setPrioritizeTimeOverSizeThresholds(false) // Could probably save some megabytes in memory in case it will be needed .createDefaultLoadControl())*/ @@ -108,7 +106,7 @@ class VideoPlayer private constructor( } private fun start(seek: Long? = null, onProgressUpdate: (position: Long?, state: TrackState) -> Unit): Boolean { - val filepath = getAppFilePath(SimplexApp.context, uri) + val filepath = getAppFilePath(uri) if (filepath == null || !File(filepath).exists()) { Log.e(TAG, "No such file: $uri") brokenVideo.value = true diff --git a/apps/multiplatform/android/src/main/java/chat/simplex/app/views/newchat/AddContactView.kt b/apps/multiplatform/android/src/main/java/chat/simplex/app/views/newchat/AddContactView.kt index 99eab4584d..cc5117bb9a 100644 --- a/apps/multiplatform/android/src/main/java/chat/simplex/app/views/newchat/AddContactView.kt +++ b/apps/multiplatform/android/src/main/java/chat/simplex/app/views/newchat/AddContactView.kt @@ -12,7 +12,6 @@ import androidx.compose.ui.res.painterResource import androidx.compose.runtime.* import androidx.compose.ui.Alignment import androidx.compose.ui.Modifier -import androidx.compose.ui.platform.LocalContext import androidx.compose.ui.res.stringResource import androidx.compose.ui.text.style.TextAlign import androidx.compose.ui.tooling.preview.Preview @@ -24,11 +23,10 @@ import chat.simplex.app.views.usersettings.SettingsActionItem @Composable fun AddContactView(connReqInvitation: String, connIncognito: Boolean) { - val cxt = LocalContext.current AddContactLayout( connReq = connReqInvitation, connIncognito = connIncognito, - share = { shareText(cxt, connReqInvitation) }, + share = { shareText(connReqInvitation) }, learnMore = { ModalManager.shared.showModal { Column( diff --git a/apps/multiplatform/android/src/main/java/chat/simplex/app/views/newchat/ContactConnectionInfoView.kt b/apps/multiplatform/android/src/main/java/chat/simplex/app/views/newchat/ContactConnectionInfoView.kt index fa7ec6f01f..50ad56a5eb 100644 --- a/apps/multiplatform/android/src/main/java/chat/simplex/app/views/newchat/ContactConnectionInfoView.kt +++ b/apps/multiplatform/android/src/main/java/chat/simplex/app/views/newchat/ContactConnectionInfoView.kt @@ -11,7 +11,6 @@ import androidx.compose.material.* import androidx.compose.runtime.* import androidx.compose.ui.Modifier import androidx.compose.ui.graphics.Color -import androidx.compose.ui.platform.LocalContext import androidx.compose.ui.res.painterResource import androidx.compose.ui.res.stringResource import androidx.compose.ui.tooling.preview.Preview @@ -45,7 +44,6 @@ fun ContactConnectionInfoView( } } } - val context = LocalContext.current ContactConnectionInfoLayout( connReq = connReqInvitation, contactConnection, @@ -53,7 +51,7 @@ fun ContactConnectionInfoView( focusAlias, deleteConnection = { deleteContactConnectionAlert(contactConnection, chatModel, close) }, onLocalAliasChanged = { setContactAlias(contactConnection, it, chatModel) }, - share = { if (connReqInvitation != null) shareText(context, connReqInvitation) }, + share = { if (connReqInvitation != null) shareText(connReqInvitation) }, learnMore = { ModalManager.shared.showModal { Column( diff --git a/apps/multiplatform/android/src/main/java/chat/simplex/app/views/newchat/QRCode.kt b/apps/multiplatform/android/src/main/java/chat/simplex/app/views/newchat/QRCode.kt index 1f190b4216..62c1f4f72e 100644 --- a/apps/multiplatform/android/src/main/java/chat/simplex/app/views/newchat/QRCode.kt +++ b/apps/multiplatform/android/src/main/java/chat/simplex/app/views/newchat/QRCode.kt @@ -27,7 +27,6 @@ fun QRCode( tintColor: Color = Color(0xff062d56), withLogo: Boolean = true ) { - val context = LocalContext.current val scope = rememberCoroutineScope() BoxWithConstraints { @@ -47,7 +46,7 @@ fun QRCode( .let { if (withLogo) it.addLogo() else it } val file = saveTempImageUncompressed(image, false) if (file != null) { - shareFile(context, "", file.absolutePath) + shareFile("", file.absolutePath) } } } diff --git a/apps/multiplatform/android/src/main/java/chat/simplex/app/views/onboarding/CreateSimpleXAddress.kt b/apps/multiplatform/android/src/main/java/chat/simplex/app/views/onboarding/CreateSimpleXAddress.kt index 60dcb04838..f536039542 100644 --- a/apps/multiplatform/android/src/main/java/chat/simplex/app/views/onboarding/CreateSimpleXAddress.kt +++ b/apps/multiplatform/android/src/main/java/chat/simplex/app/views/onboarding/CreateSimpleXAddress.kt @@ -9,7 +9,6 @@ import androidx.compose.material.* import androidx.compose.runtime.* import androidx.compose.ui.Alignment import androidx.compose.ui.Modifier -import androidx.compose.ui.platform.LocalContext import androidx.compose.ui.res.painterResource import androidx.compose.ui.res.stringResource import androidx.compose.ui.text.style.TextAlign @@ -24,16 +23,14 @@ import chat.simplex.app.views.newchat.QRCode @Composable fun CreateSimpleXAddress(m: ChatModel) { - val context = LocalContext.current var progressIndicator by remember { mutableStateOf(false) } val userAddress = remember { m.userAddress } CreateSimpleXAddressLayout( userAddress.value, - share = { address: String -> shareText(context, address) }, + share = { address: String -> shareText(address) }, sendEmail = { address -> sendEmail( - context, generalGetString(R.string.email_invite_subject), generalGetString(R.string.email_invite_body).format(address.connReqContact) ) diff --git a/apps/multiplatform/android/src/main/java/chat/simplex/app/views/usersettings/Appearance.kt b/apps/multiplatform/android/src/main/java/chat/simplex/app/views/usersettings/Appearance.kt index 8530394170..5233192e2d 100644 --- a/apps/multiplatform/android/src/main/java/chat/simplex/app/views/usersettings/Appearance.kt +++ b/apps/multiplatform/android/src/main/java/chat/simplex/app/views/usersettings/Appearance.kt @@ -236,9 +236,8 @@ fun CustomizeThemeView(editColor: (ThemeColor, Color) -> Unit) { } SectionSpacer() SectionView { - val context = LocalContext.current val theme = remember { mutableStateOf(null as String?) } - val exportThemeLauncher = rememberSaveThemeLauncher(context, theme) + val exportThemeLauncher = rememberSaveThemeLauncher(theme) SectionItemView({ val overrides = ThemeManager.currentThemeOverridesForExport(isInDarkTheme) theme.value = yaml.encodeToString(overrides) @@ -375,10 +374,11 @@ private fun DarkThemeSelector(state: State, onSelected: (String) -> Uni //} @Composable -private fun rememberSaveThemeLauncher(cxt: Context, theme: MutableState): ManagedActivityResultLauncher = +private fun rememberSaveThemeLauncher(theme: MutableState): ManagedActivityResultLauncher = rememberLauncherForActivityResult( contract = ActivityResultContracts.CreateDocument(), onResult = { destination -> + val cxt = SimplexApp.context try { destination?.let { val theme = theme.value diff --git a/apps/multiplatform/android/src/main/java/chat/simplex/app/views/usersettings/NotificationsSettingsView.kt b/apps/multiplatform/android/src/main/java/chat/simplex/app/views/usersettings/NotificationsSettingsView.kt index e80283ecba..8bd917e7a5 100644 --- a/apps/multiplatform/android/src/main/java/chat/simplex/app/views/usersettings/NotificationsSettingsView.kt +++ b/apps/multiplatform/android/src/main/java/chat/simplex/app/views/usersettings/NotificationsSettingsView.kt @@ -188,7 +188,7 @@ fun notificationPreviewModes(): List> { fun changeNotificationsMode(mode: NotificationsMode, chatModel: ChatModel) { chatModel.controller.appPrefs.notificationsMode.set(mode.name) - if (mode.requiresIgnoringBattery && !chatModel.controller.isIgnoringBatteryOptimizations(chatModel.controller.appContext)) { + if (mode.requiresIgnoringBattery && !chatModel.controller.isIgnoringBatteryOptimizations()) { chatModel.controller.appPrefs.backgroundServiceNoticeShown.set(false) } chatModel.notificationsMode.value = mode diff --git a/apps/multiplatform/android/src/main/java/chat/simplex/app/views/usersettings/PrivacySettings.kt b/apps/multiplatform/android/src/main/java/chat/simplex/app/views/usersettings/PrivacySettings.kt index c73bd6e7ce..b961ba58a2 100644 --- a/apps/multiplatform/android/src/main/java/chat/simplex/app/views/usersettings/PrivacySettings.kt +++ b/apps/multiplatform/android/src/main/java/chat/simplex/app/views/usersettings/PrivacySettings.kt @@ -143,7 +143,7 @@ fun SimplexLockView( } else { generalGetString(R.string.chat_lock) }, - generalGetString(R.string.change_lock_mode), activity = activity + generalGetString(R.string.change_lock_mode) ) { laResult -> when (laResult) { is LAResult.Error -> { @@ -153,7 +153,7 @@ fun SimplexLockView( LAResult.Success -> { when (toLAMode) { LAMode.SYSTEM -> { - authenticate(generalGetString(R.string.auth_enable_simplex_lock), promptSubtitle = "", activity = activity, usingLAMode = toLAMode) { laResult -> + authenticate(generalGetString(R.string.auth_enable_simplex_lock), promptSubtitle = "", usingLAMode = toLAMode) { laResult -> when (laResult) { LAResult.Success -> { currentLAMode.set(toLAMode) @@ -189,7 +189,7 @@ fun SimplexLockView( } fun toggleSelfDestruct(selfDestruct: SharedPreference) { - authenticate(generalGetString(R.string.la_current_app_passcode), generalGetString(R.string.change_self_destruct_mode), activity = activity) { laResult -> + authenticate(generalGetString(R.string.la_current_app_passcode), generalGetString(R.string.change_self_destruct_mode)) { laResult -> when (laResult) { is LAResult.Error -> laFailedAlert() is LAResult.Failed -> { /* Can be called multiple times on every failure */ } @@ -208,7 +208,7 @@ fun SimplexLockView( } fun changeLAPassword() { - authenticate(generalGetString(R.string.la_current_app_passcode), generalGetString(R.string.la_change_app_passcode), activity = activity) { laResult -> + authenticate(generalGetString(R.string.la_current_app_passcode), generalGetString(R.string.la_change_app_passcode)) { laResult -> when (laResult) { LAResult.Success -> { ModalManager.shared.showCustomModal { close -> @@ -231,7 +231,7 @@ fun SimplexLockView( } fun changeSelfDestructPassword() { - authenticate(generalGetString(R.string.la_current_app_passcode), generalGetString(R.string.change_self_destruct_passcode), activity = activity) { laResult -> + authenticate(generalGetString(R.string.la_current_app_passcode), generalGetString(R.string.change_self_destruct_passcode)) { laResult -> when (laResult) { LAResult.Success -> { ModalManager.shared.showCustomModal { close -> diff --git a/apps/multiplatform/android/src/main/java/chat/simplex/app/views/usersettings/SettingsView.kt b/apps/multiplatform/android/src/main/java/chat/simplex/app/views/usersettings/SettingsView.kt index 682c44a12a..2aae79fa8a 100644 --- a/apps/multiplatform/android/src/main/java/chat/simplex/app/views/usersettings/SettingsView.kt +++ b/apps/multiplatform/android/src/main/java/chat/simplex/app/views/usersettings/SettingsView.kt @@ -6,7 +6,6 @@ import SectionItemView import SectionItemViewWithIcon import SectionView import TextIconSpaced -import android.content.* import android.content.res.Configuration import androidx.compose.foundation.* import androidx.compose.foundation.layout.* @@ -45,7 +44,6 @@ fun SettingsView(chatModel: ChatModel, setPerformLA: (Boolean, FragmentActivity) if (user != null) { val requireAuth = remember { chatModel.controller.appPrefs.performLA.state } - val context = LocalContext.current SettingsLayout( profile = user.profile, stopped, @@ -92,7 +90,7 @@ fun SettingsView(chatModel: ChatModel, setPerformLA: (Boolean, FragmentActivity) LaunchedEffect(Unit) { if (autoShow) { autoShow = false - runAuth(title, desc, context, onFinishAuth) + runAuth(title, desc, onFinishAuth) } } Box( @@ -103,7 +101,7 @@ fun SettingsView(chatModel: ChatModel, setPerformLA: (Boolean, FragmentActivity) stringResource(R.string.auth_unlock), icon = painterResource(R.drawable.ic_lock), click = { - runAuth(title, desc, context, onFinishAuth) + runAuth(title, desc, onFinishAuth) } ) } @@ -481,11 +479,10 @@ fun PreferenceToggleWithIcon( } } -private fun runAuth(title: String, desc: String, context: Context, onFinish: (success: Boolean) -> Unit) { +private fun runAuth(title: String, desc: String, onFinish: (success: Boolean) -> Unit) { authenticate( title, desc, - activity = context as FragmentActivity, completed = { laResult -> onFinish(laResult == LAResult.Success || laResult is LAResult.Unavailable) } diff --git a/apps/multiplatform/android/src/main/java/chat/simplex/app/views/usersettings/UserAddressView.kt b/apps/multiplatform/android/src/main/java/chat/simplex/app/views/usersettings/UserAddressView.kt index c9ce92f10b..8f4b467dab 100644 --- a/apps/multiplatform/android/src/main/java/chat/simplex/app/views/usersettings/UserAddressView.kt +++ b/apps/multiplatform/android/src/main/java/chat/simplex/app/views/usersettings/UserAddressView.kt @@ -16,7 +16,6 @@ import androidx.compose.runtime.* import androidx.compose.runtime.saveable.rememberSaveable import androidx.compose.ui.Alignment import androidx.compose.ui.Modifier -import androidx.compose.ui.platform.LocalContext import androidx.compose.ui.res.painterResource import androidx.compose.ui.res.stringResource import androidx.compose.ui.tooling.preview.Preview @@ -36,7 +35,6 @@ fun UserAddressView( shareViaProfile: Boolean = false, close: () -> Unit ) { - val context = LocalContext.current val shareViaProfile = remember { mutableStateOf(shareViaProfile) } var progressIndicator by remember { mutableStateOf(false) } val onCloseHandler: MutableState<(close: () -> Unit) -> Unit> = remember { mutableStateOf({ _ -> }) } @@ -94,10 +92,9 @@ fun UserAddressView( } } }, - share = { userAddress: String -> shareText(context, userAddress) }, + share = { userAddress: String -> shareText(userAddress) }, sendEmail = { userAddress -> sendEmail( - context, generalGetString(R.string.email_invite_subject), generalGetString(R.string.email_invite_body).format(userAddress.connReqContact) )