mirror of
https://github.com/simplex-chat/simplex-chat.git
synced 2024-12-17 17:20:21 +01:00
Merge branch 'master' into master-ios
This commit is contained in:
@@ -1,20 +0,0 @@
|
||||
*.iml
|
||||
.gradle
|
||||
/local.properties
|
||||
/.idea/caches
|
||||
/.idea/libraries
|
||||
/.idea/modules.xml
|
||||
/.idea/workspace.xml
|
||||
/.idea/navEditor.xml
|
||||
/.idea/assetWizardSettings.xml
|
||||
/.idea/deploymentTargetDropDown.xml
|
||||
/.idea/misc.xml
|
||||
/.idea/uiDesigner.xml
|
||||
/.idea/kotlinc.xml
|
||||
.DS_Store
|
||||
/build
|
||||
/captures
|
||||
.externalNativeBuild
|
||||
.cxx
|
||||
local.properties
|
||||
app/src/main/cpp/libs/
|
||||
Generated
-1
@@ -1 +0,0 @@
|
||||
SimpleX
|
||||
Generated
-6
@@ -1,6 +0,0 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<project version="4">
|
||||
<component name="CompilerConfiguration">
|
||||
<bytecodeTargetLevel target="11" />
|
||||
</component>
|
||||
</project>
|
||||
Generated
-19
@@ -1,19 +0,0 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<project version="4">
|
||||
<component name="GradleMigrationSettings" migrationVersion="1" />
|
||||
<component name="GradleSettings">
|
||||
<option name="linkedExternalProjectsSettings">
|
||||
<GradleProjectSettings>
|
||||
<option name="testRunner" value="GRADLE" />
|
||||
<option name="distributionType" value="DEFAULT_WRAPPED" />
|
||||
<option name="externalProjectPath" value="$PROJECT_DIR$" />
|
||||
<option name="modules">
|
||||
<set>
|
||||
<option value="$PROJECT_DIR$" />
|
||||
<option value="$PROJECT_DIR$/app" />
|
||||
</set>
|
||||
</option>
|
||||
</GradleProjectSettings>
|
||||
</option>
|
||||
</component>
|
||||
</project>
|
||||
@@ -1,20 +0,0 @@
|
||||
<component name="InspectionProjectProfileManager">
|
||||
<profile version="1.0">
|
||||
<option name="myName" value="Project Default" />
|
||||
<inspection_tool class="PreviewAnnotationInFunctionWithParameters" enabled="true" level="ERROR" enabled_by_default="true">
|
||||
<option name="previewFile" value="true" />
|
||||
</inspection_tool>
|
||||
<inspection_tool class="PreviewDimensionRespectsLimit" enabled="true" level="WARNING" enabled_by_default="true">
|
||||
<option name="previewFile" value="true" />
|
||||
</inspection_tool>
|
||||
<inspection_tool class="PreviewMultipleParameterProviders" enabled="true" level="ERROR" enabled_by_default="true">
|
||||
<option name="previewFile" value="true" />
|
||||
</inspection_tool>
|
||||
<inspection_tool class="PreviewMustBeTopLevelFunction" enabled="true" level="ERROR" enabled_by_default="true">
|
||||
<option name="previewFile" value="true" />
|
||||
</inspection_tool>
|
||||
<inspection_tool class="PreviewNeedsComposableAnnotation" enabled="true" level="ERROR" enabled_by_default="true">
|
||||
<option name="previewFile" value="true" />
|
||||
</inspection_tool>
|
||||
</profile>
|
||||
</component>
|
||||
Generated
-6
@@ -1,6 +0,0 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<project version="4">
|
||||
<component name="VcsDirectoryMappings">
|
||||
<mapping directory="$PROJECT_DIR$/../.." vcs="Git" />
|
||||
</component>
|
||||
</project>
|
||||
@@ -1,243 +0,0 @@
|
||||
plugins {
|
||||
id 'com.android.application'
|
||||
id 'org.jetbrains.kotlin.android'
|
||||
id 'org.jetbrains.kotlin.plugin.serialization'
|
||||
}
|
||||
|
||||
android {
|
||||
compileSdk 32
|
||||
|
||||
defaultConfig {
|
||||
applicationId "chat.simplex.app"
|
||||
minSdk 26
|
||||
targetSdk 32
|
||||
// !!!
|
||||
// skip version code after release to F-Droid, as it uses two version codes
|
||||
versionCode 127
|
||||
versionName "5.1.3"
|
||||
|
||||
testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"
|
||||
vectorDrawables {
|
||||
useSupportLibrary true
|
||||
}
|
||||
externalNativeBuild {
|
||||
cmake {
|
||||
cppFlags ''
|
||||
}
|
||||
}
|
||||
manifestPlaceholders.app_name = "@string/app_name"
|
||||
manifestPlaceholders.provider_authorities = "chat.simplex.app.provider"
|
||||
manifestPlaceholders.extract_native_libs = compression_level != "0"
|
||||
}
|
||||
|
||||
buildTypes {
|
||||
debug {
|
||||
applicationIdSuffix "$application_id_suffix"
|
||||
debuggable new Boolean("$enable_debuggable")
|
||||
manifestPlaceholders.app_name = "$app_name"
|
||||
// Provider can't be the same for different apps on the same device
|
||||
manifestPlaceholders.provider_authorities = "chat.simplex.app${application_id_suffix}.provider"
|
||||
}
|
||||
release {
|
||||
minifyEnabled false
|
||||
proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro'
|
||||
}
|
||||
}
|
||||
compileOptions {
|
||||
sourceCompatibility JavaVersion.VERSION_1_8
|
||||
targetCompatibility JavaVersion.VERSION_1_8
|
||||
}
|
||||
kotlinOptions {
|
||||
jvmTarget = '1.8'
|
||||
freeCompilerArgs += "-opt-in=kotlinx.coroutines.DelicateCoroutinesApi"
|
||||
freeCompilerArgs += "-opt-in=androidx.compose.foundation.ExperimentalFoundationApi"
|
||||
freeCompilerArgs += "-opt-in=androidx.compose.ui.text.ExperimentalTextApi"
|
||||
freeCompilerArgs += "-opt-in=androidx.compose.material.ExperimentalMaterialApi"
|
||||
freeCompilerArgs += "-opt-in=com.google.accompanist.insets.ExperimentalAnimatedInsets"
|
||||
freeCompilerArgs += "-opt-in=com.google.accompanist.permissions.ExperimentalPermissionsApi"
|
||||
freeCompilerArgs += "-opt-in=kotlinx.serialization.InternalSerializationApi"
|
||||
freeCompilerArgs += "-opt-in=kotlinx.serialization.ExperimentalSerializationApi"
|
||||
}
|
||||
externalNativeBuild {
|
||||
cmake {
|
||||
path file('src/main/cpp/CMakeLists.txt')
|
||||
}
|
||||
}
|
||||
buildFeatures {
|
||||
compose true
|
||||
}
|
||||
composeOptions {
|
||||
kotlinCompilerExtensionVersion compose_version
|
||||
}
|
||||
packagingOptions {
|
||||
resources {
|
||||
excludes += '/META-INF/{AL2.0,LGPL2.1}'
|
||||
}
|
||||
jniLibs.useLegacyPackaging = compression_level != "0"
|
||||
}
|
||||
def isRelease = gradle.getStartParameter().taskNames.find({ it.toLowerCase().contains("release") }) != null
|
||||
def isBundle = gradle.getStartParameter().taskNames.find({ it.toLowerCase().contains("bundle") }) != null
|
||||
// if (isRelease) {
|
||||
// Comma separated list of languages that will be included in the apk
|
||||
android.defaultConfig.resConfigs(
|
||||
"en",
|
||||
"cs",
|
||||
"de",
|
||||
"es",
|
||||
"fr",
|
||||
"it",
|
||||
"ja",
|
||||
"nl",
|
||||
"pl",
|
||||
"pt-rBR",
|
||||
"ru",
|
||||
"zh-rCN"
|
||||
)
|
||||
// }
|
||||
if (isBundle) {
|
||||
defaultConfig.ndk.abiFilters 'arm64-v8a', 'armeabi-v7a'
|
||||
} else {
|
||||
splits {
|
||||
abi {
|
||||
enable true
|
||||
reset()
|
||||
if (isRelease) {
|
||||
include 'arm64-v8a', 'armeabi-v7a'
|
||||
} else {
|
||||
include 'arm64-v8a', 'armeabi-v7a'
|
||||
universalApk false
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
dependencies {
|
||||
implementation 'androidx.core:core-ktx:1.7.0'
|
||||
implementation "androidx.compose.ui:ui:$compose_version"
|
||||
implementation "androidx.compose.material:material:$compose_version"
|
||||
implementation "androidx.compose.ui:ui-tooling-preview:$compose_version"
|
||||
implementation 'androidx.lifecycle:lifecycle-runtime-ktx:2.4.1'
|
||||
implementation 'androidx.lifecycle:lifecycle-process:2.4.1'
|
||||
implementation 'androidx.activity:activity-compose:1.4.0'
|
||||
implementation 'androidx.fragment:fragment:1.4.1'
|
||||
implementation 'org.jetbrains.kotlinx:kotlinx-datetime:0.3.2'
|
||||
implementation 'org.jetbrains.kotlinx:kotlinx-serialization-json:1.3.2'
|
||||
implementation 'com.charleskorn.kaml:kaml:0.43.0'
|
||||
//implementation "androidx.compose.material:material-icons-extended:$compose_version"
|
||||
implementation "androidx.compose.ui:ui-util:$compose_version"
|
||||
implementation "androidx.navigation:navigation-compose:2.4.1"
|
||||
implementation "com.google.accompanist:accompanist-insets:0.23.0"
|
||||
implementation 'androidx.webkit:webkit:1.4.0'
|
||||
implementation "com.godaddy.android.colorpicker:compose-color-picker:0.4.2"
|
||||
|
||||
def work_version = "2.7.1"
|
||||
implementation "androidx.work:work-runtime-ktx:$work_version"
|
||||
implementation "androidx.work:work-multiprocess:$work_version"
|
||||
|
||||
def camerax_version = "1.1.0-beta01"
|
||||
implementation "androidx.camera:camera-core:${camerax_version}"
|
||||
implementation "androidx.camera:camera-camera2:${camerax_version}"
|
||||
implementation "androidx.camera:camera-lifecycle:${camerax_version}"
|
||||
implementation "androidx.camera:camera-view:${camerax_version}"
|
||||
|
||||
//Barcode
|
||||
implementation 'org.boofcv:boofcv-android:0.40.1'
|
||||
implementation 'org.boofcv:boofcv-core:0.40.1'
|
||||
|
||||
//Camera Permission
|
||||
implementation "com.google.accompanist:accompanist-permissions:0.23.0"
|
||||
implementation "com.google.accompanist:accompanist-pager:0.25.1"
|
||||
|
||||
// Link Previews
|
||||
implementation 'org.jsoup:jsoup:1.13.1'
|
||||
|
||||
// Biometric authentication
|
||||
implementation 'androidx.biometric:biometric:1.2.0-alpha04'
|
||||
|
||||
// GIFs support
|
||||
implementation "io.coil-kt:coil-compose:2.1.0"
|
||||
implementation "io.coil-kt:coil-gif:2.1.0"
|
||||
|
||||
// Video support
|
||||
implementation "com.google.android.exoplayer:exoplayer:2.17.1"
|
||||
|
||||
// Wheel picker
|
||||
implementation 'com.github.zj565061763:compose-wheel-picker:1.0.0-alpha10'
|
||||
|
||||
testImplementation 'junit:junit:4.13.2'
|
||||
androidTestImplementation 'androidx.test.ext:junit:1.1.3'
|
||||
androidTestImplementation 'androidx.test.espresso:espresso-core:3.4.0'
|
||||
androidTestImplementation "androidx.compose.ui:ui-test-junit4:$compose_version"
|
||||
debugImplementation "androidx.compose.ui:ui-tooling:$compose_version"
|
||||
implementation "com.jakewharton:process-phoenix:2.1.2"
|
||||
}
|
||||
|
||||
// Don't do anything if no compression is needed
|
||||
if (compression_level != "0") {
|
||||
tasks.whenTaskAdded { task ->
|
||||
if (task.name == 'packageDebug') {
|
||||
task.finalizedBy compressApk
|
||||
} else if (task.name == 'packageRelease') {
|
||||
task.finalizedBy compressApk
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
tasks.register("compressApk") {
|
||||
doLast {
|
||||
def isRelease = gradle.getStartParameter().taskNames.find({ it.toLowerCase().contains("release") }) != null
|
||||
def buildType
|
||||
if (isRelease) {
|
||||
buildType = "release"
|
||||
} else {
|
||||
buildType = "debug"
|
||||
}
|
||||
def javaHome = System.properties['java.home'] ?: org.gradle.internal.jvm.Jvm.current().getJavaHome()
|
||||
def sdkDir = android.getSdkDirectory().getAbsolutePath()
|
||||
def keyAlias = ""
|
||||
def keyPassword = ""
|
||||
def storeFile = ""
|
||||
def storePassword = ""
|
||||
if (project.properties['android.injected.signing.key.alias'] != null) {
|
||||
keyAlias = project.properties['android.injected.signing.key.alias']
|
||||
keyPassword = project.properties['android.injected.signing.key.password']
|
||||
storeFile = project.properties['android.injected.signing.store.file']
|
||||
storePassword = project.properties['android.injected.signing.store.password']
|
||||
} else if (android.signingConfigs.hasProperty(buildType)) {
|
||||
def gradleConfig = android.signingConfigs[buildType]
|
||||
keyAlias = gradleConfig.keyAlias
|
||||
keyPassword = gradleConfig.keyPassword
|
||||
storeFile = gradleConfig.storeFile
|
||||
storePassword = gradleConfig.storePassword
|
||||
} else {
|
||||
// There is no signing config for current build type, can't sign the apk
|
||||
println("No signing configs for this build type: $buildType")
|
||||
return
|
||||
}
|
||||
|
||||
def outputDir = tasks["package${buildType.capitalize()}"].outputs.files.last()
|
||||
|
||||
exec {
|
||||
workingDir '../../../scripts/android'
|
||||
setEnvironment(['JAVA_HOME': "$javaHome"])
|
||||
commandLine './compress-and-sign-apk.sh', \
|
||||
"$compression_level", \
|
||||
"$outputDir", \
|
||||
"$sdkDir", \
|
||||
"$storeFile", \
|
||||
"$storePassword", \
|
||||
"$keyAlias", \
|
||||
"$keyPassword"
|
||||
}
|
||||
|
||||
if (project.properties['android.injected.signing.key.alias'] != null && buildType == 'release') {
|
||||
new File(outputDir, "app-release.apk").renameTo(new File(outputDir, "simplex.apk"))
|
||||
new File(outputDir, "app-armeabi-v7a-release.apk").renameTo(new File(outputDir, "simplex-armv7a.apk"))
|
||||
new File(outputDir, "app-arm64-v8a-release.apk").renameTo(new File(outputDir, "simplex.apk"))
|
||||
}
|
||||
|
||||
// View all gradle properties set
|
||||
// project.properties.each { k, v -> println "$k -> $v" }
|
||||
}
|
||||
}
|
||||
@@ -1,50 +0,0 @@
|
||||
buildscript {
|
||||
Properties localProperties = new Properties()
|
||||
if (rootProject.file('local.properties').canRead()) {
|
||||
localProperties.load(rootProject.file("local.properties").newDataInputStream())
|
||||
}
|
||||
|
||||
ext {
|
||||
compose_version = localProperties['compose_version'] ?: '1.2.0-beta02'
|
||||
kotlin_version = localProperties['kotlin_version'] ?: '1.6.21'
|
||||
gradle_plugin_version = localProperties['gradle_plugin_version'] ?: '7.2.0'
|
||||
abi_filter = localProperties['abi_filter'] ?: 'arm64-v8a'
|
||||
|
||||
// Name that will be shown for debug build. By default it is from strings
|
||||
app_name = localProperties['app_name'] ?: "@string/app_name"
|
||||
// Whether the app is debuggable or not. Specify `false` if you want good performance in debug builds
|
||||
enable_debuggable = localProperties['debuggable'] ?: true
|
||||
// Ending part of package name.
|
||||
// Provide, for example, `application_id_suffix=.debug` in local.properties
|
||||
// to allow debug & release versions to coexist
|
||||
application_id_suffix = localProperties['application_id_suffix'] ?: ''
|
||||
|
||||
// Compression level for debug AND release apk. 0 = disable compression. Max is 9
|
||||
compression_level = localProperties['compression_level'] ?: '0'
|
||||
|
||||
// NOTE: If you need a different version of something, provide it in `local.properties`
|
||||
// like so: compose_version=123, or gradle_plugin_version=1.2.3, etc
|
||||
}
|
||||
repositories {
|
||||
google()
|
||||
mavenCentral()
|
||||
}
|
||||
dependencies {
|
||||
classpath "com.android.tools.build:gradle:$gradle_plugin_version"
|
||||
classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlin_version"
|
||||
classpath "org.jetbrains.kotlin:kotlin-serialization:1.3.2"
|
||||
|
||||
// NOTE: Do not place your application dependencies here; they belong
|
||||
// in the individual module build.gradle files
|
||||
}
|
||||
}// Top-level build file where you can add configuration options common to all sub-projects/modules.
|
||||
plugins {
|
||||
id 'com.android.application' version "$gradle_plugin_version" apply false
|
||||
id 'com.android.library' version "$gradle_plugin_version" apply false
|
||||
id 'org.jetbrains.kotlin.android' version "$kotlin_version" apply false
|
||||
id 'org.jetbrains.kotlin.plugin.serialization' version "$kotlin_version"
|
||||
}
|
||||
|
||||
task clean(type: Delete) {
|
||||
delete rootProject.buildDir
|
||||
}
|
||||
@@ -1,17 +0,0 @@
|
||||
pluginManagement {
|
||||
repositories {
|
||||
gradlePluginPortal()
|
||||
google()
|
||||
mavenCentral()
|
||||
}
|
||||
}
|
||||
dependencyResolutionManagement {
|
||||
repositoriesMode.set(RepositoriesMode.FAIL_ON_PROJECT_REPOS)
|
||||
repositories {
|
||||
google()
|
||||
mavenCentral()
|
||||
maven { url 'https://jitpack.io' }
|
||||
}
|
||||
}
|
||||
rootProject.name = "SimpleX"
|
||||
include ':app'
|
||||
@@ -115,11 +115,6 @@
|
||||
5CC2C0FC2809BF11000C35E3 /* Localizable.strings in Resources */ = {isa = PBXBuildFile; fileRef = 5CC2C0FA2809BF11000C35E3 /* Localizable.strings */; };
|
||||
5CC2C0FF2809BF11000C35E3 /* SimpleX--iOS--InfoPlist.strings in Resources */ = {isa = PBXBuildFile; fileRef = 5CC2C0FD2809BF11000C35E3 /* SimpleX--iOS--InfoPlist.strings */; };
|
||||
5CC868F329EB540C0017BBFD /* CIRcvDecryptionError.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5CC868F229EB540C0017BBFD /* CIRcvDecryptionError.swift */; };
|
||||
5CCAA6D52A4B38F700BAF93B /* libHSsimplex-chat-5.1.3.0-5OWMsZbPutPDzQF8wFktLQ.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 5CCAA6D02A4B38F600BAF93B /* libHSsimplex-chat-5.1.3.0-5OWMsZbPutPDzQF8wFktLQ.a */; };
|
||||
5CCAA6D62A4B38F700BAF93B /* libgmp.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 5CCAA6D12A4B38F600BAF93B /* libgmp.a */; };
|
||||
5CCAA6D72A4B38F700BAF93B /* libHSsimplex-chat-5.1.3.0-5OWMsZbPutPDzQF8wFktLQ-ghc8.10.7.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 5CCAA6D22A4B38F600BAF93B /* libHSsimplex-chat-5.1.3.0-5OWMsZbPutPDzQF8wFktLQ-ghc8.10.7.a */; };
|
||||
5CCAA6D82A4B38F700BAF93B /* libgmpxx.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 5CCAA6D32A4B38F600BAF93B /* libgmpxx.a */; };
|
||||
5CCAA6D92A4B38F700BAF93B /* libffi.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 5CCAA6D42A4B38F600BAF93B /* libffi.a */; };
|
||||
5CCB939C297EFCB100399E78 /* NavStackCompat.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5CCB939B297EFCB100399E78 /* NavStackCompat.swift */; };
|
||||
5CCD403427A5F6DF00368C90 /* AddContactView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5CCD403327A5F6DF00368C90 /* AddContactView.swift */; };
|
||||
5CCD403727A5F9A200368C90 /* ScanToConnectView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5CCD403627A5F9A200368C90 /* ScanToConnectView.swift */; };
|
||||
@@ -170,6 +165,11 @@
|
||||
648010AB281ADD15009009B9 /* CIFileView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 648010AA281ADD15009009B9 /* CIFileView.swift */; };
|
||||
649BCDA0280460FD00C3A862 /* ComposeImageView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 649BCD9F280460FD00C3A862 /* ComposeImageView.swift */; };
|
||||
649BCDA22805D6EF00C3A862 /* CIImageView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 649BCDA12805D6EF00C3A862 /* CIImageView.swift */; };
|
||||
64A353102A4C84CE007CD71D /* libgmp.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 64A3530B2A4C84CE007CD71D /* libgmp.a */; };
|
||||
64A353112A4C84CE007CD71D /* libHSsimplex-chat-5.2.0.0-ESKsZ4YorLH7yFQuFvHeIm.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 64A3530C2A4C84CE007CD71D /* libHSsimplex-chat-5.2.0.0-ESKsZ4YorLH7yFQuFvHeIm.a */; };
|
||||
64A353122A4C84CE007CD71D /* libffi.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 64A3530D2A4C84CE007CD71D /* libffi.a */; };
|
||||
64A353132A4C84CE007CD71D /* libHSsimplex-chat-5.2.0.0-ESKsZ4YorLH7yFQuFvHeIm-ghc8.10.7.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 64A3530E2A4C84CE007CD71D /* libHSsimplex-chat-5.2.0.0-ESKsZ4YorLH7yFQuFvHeIm-ghc8.10.7.a */; };
|
||||
64A353142A4C84CE007CD71D /* libgmpxx.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 64A3530F2A4C84CE007CD71D /* libgmpxx.a */; };
|
||||
64AA1C6927EE10C800AC7277 /* ContextItemView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 64AA1C6827EE10C800AC7277 /* ContextItemView.swift */; };
|
||||
64AA1C6C27F3537400AC7277 /* DeletedItemView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 64AA1C6B27F3537400AC7277 /* DeletedItemView.swift */; };
|
||||
64C06EB52A0A4A7C00792D4D /* ChatItemInfoView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 64C06EB42A0A4A7C00792D4D /* ChatItemInfoView.swift */; };
|
||||
@@ -253,7 +253,7 @@
|
||||
18415B08031E8FB0F7FC27F9 /* CallViewRenderers.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = CallViewRenderers.swift; sourceTree = "<group>"; };
|
||||
18415DAAAD1ADBEDB0EDA852 /* VideoPlayerView.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = VideoPlayerView.swift; sourceTree = "<group>"; };
|
||||
18415FD2E36F13F596A45BB4 /* CIVideoView.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = CIVideoView.swift; sourceTree = "<group>"; };
|
||||
3C714779281C0F6800CB4D4B /* www */ = {isa = PBXFileReference; lastKnownFileType = folder; name = www; path = ../android/app/src/main/assets/www; sourceTree = "<group>"; };
|
||||
3C714779281C0F6800CB4D4B /* www */ = {isa = PBXFileReference; lastKnownFileType = folder; name = www; path = ../multiplatform/android/src/main/assets/www; sourceTree = "<group>"; };
|
||||
3C8C548828133C84000A3EC7 /* PasteToConnectView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PasteToConnectView.swift; sourceTree = "<group>"; };
|
||||
3CDBCF4127FAE51000354CDD /* ComposeLinkView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ComposeLinkView.swift; sourceTree = "<group>"; };
|
||||
3CDBCF4727FF621E00354CDD /* CILinkView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CILinkView.swift; sourceTree = "<group>"; };
|
||||
@@ -392,11 +392,6 @@
|
||||
5CC2C0FB2809BF11000C35E3 /* ru */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = ru; path = ru.lproj/Localizable.strings; sourceTree = "<group>"; };
|
||||
5CC2C0FE2809BF11000C35E3 /* ru */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = ru; path = "ru.lproj/SimpleX--iOS--InfoPlist.strings"; sourceTree = "<group>"; };
|
||||
5CC868F229EB540C0017BBFD /* CIRcvDecryptionError.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CIRcvDecryptionError.swift; sourceTree = "<group>"; };
|
||||
5CCAA6D02A4B38F600BAF93B /* libHSsimplex-chat-5.1.3.0-5OWMsZbPutPDzQF8wFktLQ.a */ = {isa = PBXFileReference; lastKnownFileType = archive.ar; path = "libHSsimplex-chat-5.1.3.0-5OWMsZbPutPDzQF8wFktLQ.a"; sourceTree = "<group>"; };
|
||||
5CCAA6D12A4B38F600BAF93B /* libgmp.a */ = {isa = PBXFileReference; lastKnownFileType = archive.ar; path = libgmp.a; sourceTree = "<group>"; };
|
||||
5CCAA6D22A4B38F600BAF93B /* libHSsimplex-chat-5.1.3.0-5OWMsZbPutPDzQF8wFktLQ-ghc8.10.7.a */ = {isa = PBXFileReference; lastKnownFileType = archive.ar; path = "libHSsimplex-chat-5.1.3.0-5OWMsZbPutPDzQF8wFktLQ-ghc8.10.7.a"; sourceTree = "<group>"; };
|
||||
5CCAA6D32A4B38F600BAF93B /* libgmpxx.a */ = {isa = PBXFileReference; lastKnownFileType = archive.ar; path = libgmpxx.a; sourceTree = "<group>"; };
|
||||
5CCAA6D42A4B38F600BAF93B /* libffi.a */ = {isa = PBXFileReference; lastKnownFileType = archive.ar; path = libffi.a; sourceTree = "<group>"; };
|
||||
5CCB939B297EFCB100399E78 /* NavStackCompat.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NavStackCompat.swift; sourceTree = "<group>"; };
|
||||
5CCD403327A5F6DF00368C90 /* AddContactView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AddContactView.swift; sourceTree = "<group>"; };
|
||||
5CCD403627A5F9A200368C90 /* ScanToConnectView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ScanToConnectView.swift; sourceTree = "<group>"; };
|
||||
@@ -446,6 +441,11 @@
|
||||
6493D667280ED77F007A76FB /* en */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = en; path = en.lproj/Localizable.strings; sourceTree = "<group>"; };
|
||||
649BCD9F280460FD00C3A862 /* ComposeImageView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ComposeImageView.swift; sourceTree = "<group>"; };
|
||||
649BCDA12805D6EF00C3A862 /* CIImageView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CIImageView.swift; sourceTree = "<group>"; };
|
||||
64A3530B2A4C84CE007CD71D /* libgmp.a */ = {isa = PBXFileReference; lastKnownFileType = archive.ar; path = libgmp.a; sourceTree = "<group>"; };
|
||||
64A3530C2A4C84CE007CD71D /* libHSsimplex-chat-5.2.0.0-ESKsZ4YorLH7yFQuFvHeIm.a */ = {isa = PBXFileReference; lastKnownFileType = archive.ar; path = "libHSsimplex-chat-5.2.0.0-ESKsZ4YorLH7yFQuFvHeIm.a"; sourceTree = "<group>"; };
|
||||
64A3530D2A4C84CE007CD71D /* libffi.a */ = {isa = PBXFileReference; lastKnownFileType = archive.ar; path = libffi.a; sourceTree = "<group>"; };
|
||||
64A3530E2A4C84CE007CD71D /* libHSsimplex-chat-5.2.0.0-ESKsZ4YorLH7yFQuFvHeIm-ghc8.10.7.a */ = {isa = PBXFileReference; lastKnownFileType = archive.ar; path = "libHSsimplex-chat-5.2.0.0-ESKsZ4YorLH7yFQuFvHeIm-ghc8.10.7.a"; sourceTree = "<group>"; };
|
||||
64A3530F2A4C84CE007CD71D /* libgmpxx.a */ = {isa = PBXFileReference; lastKnownFileType = archive.ar; path = libgmpxx.a; sourceTree = "<group>"; };
|
||||
64AA1C6827EE10C800AC7277 /* ContextItemView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ContextItemView.swift; sourceTree = "<group>"; };
|
||||
64AA1C6B27F3537400AC7277 /* DeletedItemView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = DeletedItemView.swift; sourceTree = "<group>"; };
|
||||
64C06EB42A0A4A7C00792D4D /* ChatItemInfoView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ChatItemInfoView.swift; sourceTree = "<group>"; };
|
||||
@@ -497,13 +497,13 @@
|
||||
isa = PBXFrameworksBuildPhase;
|
||||
buildActionMask = 2147483647;
|
||||
files = (
|
||||
5CCAA6D82A4B38F700BAF93B /* libgmpxx.a in Frameworks */,
|
||||
5CE2BA93284534B000EC33A6 /* libiconv.tbd in Frameworks */,
|
||||
5CCAA6D72A4B38F700BAF93B /* libHSsimplex-chat-5.1.3.0-5OWMsZbPutPDzQF8wFktLQ-ghc8.10.7.a in Frameworks */,
|
||||
5CCAA6D52A4B38F700BAF93B /* libHSsimplex-chat-5.1.3.0-5OWMsZbPutPDzQF8wFktLQ.a in Frameworks */,
|
||||
5CCAA6D92A4B38F700BAF93B /* libffi.a in Frameworks */,
|
||||
5CCAA6D62A4B38F700BAF93B /* libgmp.a in Frameworks */,
|
||||
64A353132A4C84CE007CD71D /* libHSsimplex-chat-5.2.0.0-ESKsZ4YorLH7yFQuFvHeIm-ghc8.10.7.a in Frameworks */,
|
||||
64A353102A4C84CE007CD71D /* libgmp.a in Frameworks */,
|
||||
64A353112A4C84CE007CD71D /* libHSsimplex-chat-5.2.0.0-ESKsZ4YorLH7yFQuFvHeIm.a in Frameworks */,
|
||||
64A353122A4C84CE007CD71D /* libffi.a in Frameworks */,
|
||||
5CE2BA94284534BB00EC33A6 /* libz.tbd in Frameworks */,
|
||||
64A353142A4C84CE007CD71D /* libgmpxx.a in Frameworks */,
|
||||
);
|
||||
runOnlyForDeploymentPostprocessing = 0;
|
||||
};
|
||||
@@ -564,11 +564,11 @@
|
||||
5C764E5C279C70B7000C6508 /* Libraries */ = {
|
||||
isa = PBXGroup;
|
||||
children = (
|
||||
5CCAA6D42A4B38F600BAF93B /* libffi.a */,
|
||||
5CCAA6D12A4B38F600BAF93B /* libgmp.a */,
|
||||
5CCAA6D32A4B38F600BAF93B /* libgmpxx.a */,
|
||||
5CCAA6D22A4B38F600BAF93B /* libHSsimplex-chat-5.1.3.0-5OWMsZbPutPDzQF8wFktLQ-ghc8.10.7.a */,
|
||||
5CCAA6D02A4B38F600BAF93B /* libHSsimplex-chat-5.1.3.0-5OWMsZbPutPDzQF8wFktLQ.a */,
|
||||
64A3530D2A4C84CE007CD71D /* libffi.a */,
|
||||
64A3530B2A4C84CE007CD71D /* libgmp.a */,
|
||||
64A3530F2A4C84CE007CD71D /* libgmpxx.a */,
|
||||
64A3530E2A4C84CE007CD71D /* libHSsimplex-chat-5.2.0.0-ESKsZ4YorLH7yFQuFvHeIm-ghc8.10.7.a */,
|
||||
64A3530C2A4C84CE007CD71D /* libHSsimplex-chat-5.2.0.0-ESKsZ4YorLH7yFQuFvHeIm.a */,
|
||||
);
|
||||
path = Libraries;
|
||||
sourceTree = "<group>";
|
||||
@@ -1470,7 +1470,7 @@
|
||||
CLANG_ENABLE_MODULES = YES;
|
||||
CODE_SIGN_ENTITLEMENTS = "SimpleX (iOS).entitlements";
|
||||
CODE_SIGN_STYLE = Automatic;
|
||||
CURRENT_PROJECT_VERSION = 150;
|
||||
CURRENT_PROJECT_VERSION = 151;
|
||||
DEVELOPMENT_TEAM = 5NN7GUYB6T;
|
||||
ENABLE_BITCODE = NO;
|
||||
ENABLE_PREVIEWS = YES;
|
||||
@@ -1491,7 +1491,7 @@
|
||||
"$(inherited)",
|
||||
"@executable_path/Frameworks",
|
||||
);
|
||||
MARKETING_VERSION = 5.1.3;
|
||||
MARKETING_VERSION = 5.2;
|
||||
PRODUCT_BUNDLE_IDENTIFIER = chat.simplex.app;
|
||||
PRODUCT_NAME = SimpleX;
|
||||
SDKROOT = iphoneos;
|
||||
@@ -1512,7 +1512,7 @@
|
||||
CLANG_ENABLE_MODULES = YES;
|
||||
CODE_SIGN_ENTITLEMENTS = "SimpleX (iOS).entitlements";
|
||||
CODE_SIGN_STYLE = Automatic;
|
||||
CURRENT_PROJECT_VERSION = 150;
|
||||
CURRENT_PROJECT_VERSION = 151;
|
||||
DEVELOPMENT_TEAM = 5NN7GUYB6T;
|
||||
ENABLE_BITCODE = NO;
|
||||
ENABLE_PREVIEWS = YES;
|
||||
@@ -1533,7 +1533,7 @@
|
||||
"$(inherited)",
|
||||
"@executable_path/Frameworks",
|
||||
);
|
||||
MARKETING_VERSION = 5.1.3;
|
||||
MARKETING_VERSION = 5.2;
|
||||
PRODUCT_BUNDLE_IDENTIFIER = chat.simplex.app;
|
||||
PRODUCT_NAME = SimpleX;
|
||||
SDKROOT = iphoneos;
|
||||
@@ -1592,7 +1592,7 @@
|
||||
CODE_SIGN_ENTITLEMENTS = "SimpleX NSE/SimpleX NSE.entitlements";
|
||||
CODE_SIGN_IDENTITY = "Apple Development";
|
||||
CODE_SIGN_STYLE = Automatic;
|
||||
CURRENT_PROJECT_VERSION = 150;
|
||||
CURRENT_PROJECT_VERSION = 151;
|
||||
DEVELOPMENT_TEAM = 5NN7GUYB6T;
|
||||
ENABLE_BITCODE = NO;
|
||||
GENERATE_INFOPLIST_FILE = YES;
|
||||
@@ -1605,7 +1605,7 @@
|
||||
"@executable_path/Frameworks",
|
||||
"@executable_path/../../Frameworks",
|
||||
);
|
||||
MARKETING_VERSION = 5.1.3;
|
||||
MARKETING_VERSION = 5.2;
|
||||
PRODUCT_BUNDLE_IDENTIFIER = "chat.simplex.app.SimpleX-NSE";
|
||||
PRODUCT_NAME = "$(TARGET_NAME)";
|
||||
PROVISIONING_PROFILE_SPECIFIER = "";
|
||||
@@ -1624,7 +1624,7 @@
|
||||
CODE_SIGN_ENTITLEMENTS = "SimpleX NSE/SimpleX NSE.entitlements";
|
||||
CODE_SIGN_IDENTITY = "Apple Development";
|
||||
CODE_SIGN_STYLE = Automatic;
|
||||
CURRENT_PROJECT_VERSION = 150;
|
||||
CURRENT_PROJECT_VERSION = 151;
|
||||
DEVELOPMENT_TEAM = 5NN7GUYB6T;
|
||||
ENABLE_BITCODE = NO;
|
||||
GENERATE_INFOPLIST_FILE = YES;
|
||||
@@ -1637,7 +1637,7 @@
|
||||
"@executable_path/Frameworks",
|
||||
"@executable_path/../../Frameworks",
|
||||
);
|
||||
MARKETING_VERSION = 5.1.3;
|
||||
MARKETING_VERSION = 5.2;
|
||||
PRODUCT_BUNDLE_IDENTIFIER = "chat.simplex.app.SimpleX-NSE";
|
||||
PRODUCT_NAME = "$(TARGET_NAME)";
|
||||
PROVISIONING_PROFILE_SPECIFIER = "";
|
||||
|
||||
@@ -0,0 +1,19 @@
|
||||
*.iml
|
||||
.gradle
|
||||
/local.properties
|
||||
/.idea
|
||||
!/.idea/codeStyles/*
|
||||
.DS_Store
|
||||
/build
|
||||
/captures
|
||||
.externalNativeBuild
|
||||
.cxx
|
||||
local.properties
|
||||
common/src/commonMain/cpp/android/libs/
|
||||
common/src/commonMain/cpp/desktop/libs/
|
||||
common/src/commonMain/resources/libs/
|
||||
android/build
|
||||
android/release
|
||||
common/build
|
||||
desktop/build
|
||||
release
|
||||
Generated
@@ -0,0 +1,210 @@
|
||||
@file:Suppress("UnstableApiUsage")
|
||||
|
||||
plugins {
|
||||
id("com.android.application")
|
||||
id("org.jetbrains.compose")
|
||||
kotlin("android")
|
||||
id("org.jetbrains.kotlin.plugin.serialization")
|
||||
}
|
||||
|
||||
repositories {
|
||||
maven("https://jitpack.io")
|
||||
}
|
||||
|
||||
android {
|
||||
compileSdkVersion(33)
|
||||
|
||||
defaultConfig {
|
||||
applicationId = "chat.simplex.app"
|
||||
minSdkVersion(26)
|
||||
targetSdkVersion(33)
|
||||
// !!!
|
||||
// skip version code after release to F-Droid, as it uses two version codes
|
||||
versionCode = (extra["android.version_code"] as String).toInt()
|
||||
versionName = extra["android.version_name"] as String
|
||||
|
||||
testInstrumentationRunner = "android.support.test.runner.AndroidJUnitRunner"
|
||||
vectorDrawables {
|
||||
useSupportLibrary = true
|
||||
}
|
||||
externalNativeBuild {
|
||||
cmake {
|
||||
cppFlags("")
|
||||
}
|
||||
}
|
||||
manifestPlaceholders["app_name"] = "@string/app_name"
|
||||
manifestPlaceholders["provider_authorities"] = "chat.simplex.app.provider"
|
||||
manifestPlaceholders["extract_native_libs"] = rootProject.extra["compression.level"] as Int != 0
|
||||
}
|
||||
|
||||
buildTypes {
|
||||
debug {
|
||||
applicationIdSuffix = rootProject.extra["application_id.suffix"] as String
|
||||
isDebuggable = rootProject.extra["enable_debuggable"] as Boolean
|
||||
manifestPlaceholders["app_name"] = rootProject.extra["app.name"] as String
|
||||
// Provider can"t be the same for different apps on the same device
|
||||
manifestPlaceholders["provider_authorities"] = "chat.simplex.app${rootProject.extra["application_id.suffix"]}.provider"
|
||||
}
|
||||
release {
|
||||
isMinifyEnabled = false
|
||||
proguardFiles(getDefaultProguardFile("proguard-android-optimize.txt"), "proguard-rules.pro")
|
||||
}
|
||||
}
|
||||
compileOptions {
|
||||
sourceCompatibility = JavaVersion.VERSION_1_8
|
||||
targetCompatibility = JavaVersion.VERSION_1_8
|
||||
}
|
||||
kotlinOptions {
|
||||
jvmTarget = "1.8"
|
||||
freeCompilerArgs += "-opt-in=kotlinx.coroutines.DelicateCoroutinesApi"
|
||||
freeCompilerArgs += "-opt-in=androidx.compose.foundation.ExperimentalFoundationApi"
|
||||
freeCompilerArgs += "-opt-in=androidx.compose.ui.text.ExperimentalTextApi"
|
||||
freeCompilerArgs += "-opt-in=androidx.compose.material.ExperimentalMaterialApi"
|
||||
freeCompilerArgs += "-opt-in=com.google.accompanist.insets.ExperimentalAnimatedInsets"
|
||||
freeCompilerArgs += "-opt-in=com.google.accompanist.permissions.ExperimentalPermissionsApi"
|
||||
freeCompilerArgs += "-opt-in=kotlinx.serialization.InternalSerializationApi"
|
||||
freeCompilerArgs += "-opt-in=kotlinx.serialization.ExperimentalSerializationApi"
|
||||
}
|
||||
externalNativeBuild {
|
||||
cmake {
|
||||
path(File("../common/src/commonMain/cpp/android/CMakeLists.txt"))
|
||||
}
|
||||
}
|
||||
buildTypes {
|
||||
getByName("release") {
|
||||
isMinifyEnabled = false
|
||||
}
|
||||
}
|
||||
packagingOptions {
|
||||
resources {
|
||||
excludes += "/META-INF/{AL2.0,LGPL2.1}"
|
||||
}
|
||||
jniLibs.useLegacyPackaging = rootProject.extra["compression.level"] as Int != 0
|
||||
}
|
||||
val isRelease = gradle.startParameter.taskNames.find { it.toLowerCase().contains("release") } != null
|
||||
val isBundle = gradle.startParameter.taskNames.find { it.toLowerCase().contains("bundle") } != null
|
||||
// if (isRelease) {
|
||||
// Comma separated list of languages that will be included in the apk
|
||||
android.defaultConfig.resConfigs(
|
||||
"en",
|
||||
"cs",
|
||||
"de",
|
||||
"es",
|
||||
"fr",
|
||||
"it",
|
||||
"ja",
|
||||
"nl",
|
||||
"pl",
|
||||
"pt-rBR",
|
||||
"ru",
|
||||
"zh-rCN"
|
||||
)
|
||||
// }
|
||||
if (isBundle) {
|
||||
defaultConfig.ndk.abiFilters("arm64-v8a", "armeabi-v7a")
|
||||
} else {
|
||||
splits {
|
||||
abi {
|
||||
isEnable = true
|
||||
reset()
|
||||
if (isRelease) {
|
||||
include("arm64-v8a", "armeabi-v7a")
|
||||
} else {
|
||||
include("arm64-v8a", "armeabi-v7a")
|
||||
isUniversalApk = false
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
dependencies {
|
||||
implementation(project(":common"))
|
||||
implementation("androidx.core:core-ktx:1.7.0")
|
||||
//implementation("androidx.compose.ui:ui:${rootProject.extra["compose.version"] as String}")
|
||||
//implementation("androidx.compose.material:material:$compose_version")
|
||||
//implementation("androidx.compose.ui:ui-tooling-preview:$compose_version")
|
||||
implementation("androidx.lifecycle:lifecycle-runtime-ktx:2.4.1")
|
||||
implementation("androidx.lifecycle:lifecycle-process:2.4.1")
|
||||
implementation("androidx.activity:activity-compose:1.5.0")
|
||||
//implementation("androidx.compose.material:material-icons-extended:$compose_version")
|
||||
//implementation("androidx.compose.ui:ui-util:$compose_version")
|
||||
|
||||
implementation("com.google.accompanist:accompanist-pager:0.25.1")
|
||||
|
||||
testImplementation("junit:junit:4.13.2")
|
||||
androidTestImplementation("androidx.test.ext:junit:1.1.3")
|
||||
androidTestImplementation("androidx.test.espresso:espresso-core:3.4.0")
|
||||
//androidTestImplementation("androidx.compose.ui:ui-test-junit4:$compose_version")
|
||||
debugImplementation("androidx.compose.ui:ui-tooling:${rootProject.extra["compose.version"] as String}")
|
||||
}
|
||||
|
||||
tasks {
|
||||
val compressApk by creating {
|
||||
doLast {
|
||||
val isRelease = gradle.startParameter.taskNames.find { it.toLowerCase().contains("release") } != null
|
||||
val buildType: String = if (isRelease) "release" else "debug"
|
||||
val javaHome = System.getProperties()["java.home"] ?: org.gradle.internal.jvm.Jvm.current().javaHome
|
||||
val sdkDir = android.sdkDirectory.absolutePath
|
||||
var keyAlias = ""
|
||||
var keyPassword = ""
|
||||
var storeFile = ""
|
||||
var storePassword = ""
|
||||
if (project.properties["android.injected.signing.key.alias"] != null) {
|
||||
keyAlias = project.properties["android.injected.signing.key.alias"] as String
|
||||
keyPassword = project.properties["android.injected.signing.key.password"] as String
|
||||
storeFile = project.properties["android.injected.signing.store.file"] as String
|
||||
storePassword = project.properties["android.injected.signing.store.password"] as String
|
||||
} else {
|
||||
try {
|
||||
val gradleConfig = android.signingConfigs.getByName(buildType)
|
||||
keyAlias = gradleConfig.keyAlias!!
|
||||
keyPassword = gradleConfig.keyPassword!!
|
||||
storeFile = gradleConfig.storeFile!!.absolutePath
|
||||
storePassword = gradleConfig.storePassword!!
|
||||
} catch (e: UnknownDomainObjectException) {
|
||||
// There is no signing config for current build type, can"t sign the apk
|
||||
println("No signing configs for this build type: $buildType")
|
||||
return@doLast
|
||||
}
|
||||
}
|
||||
lateinit var outputDir: File
|
||||
named(if (isRelease) "packageRelease" else "packageDebug") {
|
||||
outputDir = outputs.files.files.last()
|
||||
}
|
||||
exec {
|
||||
workingDir("../../../scripts/android")
|
||||
setEnvironment(mapOf("JAVA_HOME" to "$javaHome"))
|
||||
commandLine = listOf(
|
||||
"./compress-and-sign-apk.sh",
|
||||
"${rootProject.extra["compression.level"]}",
|
||||
"$outputDir",
|
||||
"$sdkDir",
|
||||
"$storeFile",
|
||||
"$storePassword",
|
||||
"$keyAlias",
|
||||
"$keyPassword"
|
||||
)
|
||||
}
|
||||
|
||||
if (project.properties["android.injected.signing.key.alias"] != null && buildType == "release") {
|
||||
File(outputDir, "android-release.apk").renameTo(File(outputDir, "simplex.apk"))
|
||||
File(outputDir, "android-armeabi-v7a-release.apk").renameTo(File(outputDir, "simplex-armv7a.apk"))
|
||||
File(outputDir, "android-arm64-v8a-release.apk").renameTo(File(outputDir, "simplex.apk"))
|
||||
}
|
||||
// View all gradle properties set
|
||||
// project.properties.each { k, v -> println "$k -> $v" }
|
||||
}
|
||||
}
|
||||
|
||||
// Don"t do anything if no compression is needed
|
||||
if (rootProject.extra["compression.level"] as Int != 0) {
|
||||
whenTaskAdded {
|
||||
if (name == "packageDebug") {
|
||||
finalizedBy(compressApk)
|
||||
} else if (name == "packageRelease") {
|
||||
finalizedBy(compressApk)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
+1
@@ -6,6 +6,7 @@
|
||||
<uses-feature android:name="android.hardware.camera" />
|
||||
|
||||
<uses-permission android:name="android.permission.CAMERA" />
|
||||
<uses-permission android:name="android.permission.POST_NOTIFICATIONS" />
|
||||
<uses-permission android:name="android.permission.VIDEO_CAPTURE" />
|
||||
<uses-permission android:name="android.permission.RECORD_AUDIO" />
|
||||
<uses-permission android:name="android.permission.MODIFY_AUDIO_SETTINGS" />
|
||||
|
Before Width: | Height: | Size: 34 KiB After Width: | Height: | Size: 34 KiB |
|
Before Width: | Height: | Size: 32 KiB After Width: | Height: | Size: 32 KiB |
+3
-5
@@ -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
|
||||
+11
-18
@@ -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<MainActivity> = 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<Any> = 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())
|
||||
}
|
||||
|
||||
/*
|
||||
+7
-6
@@ -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<OnboardingStage?>(null)
|
||||
val currentUser = mutableStateOf<User?>(null)
|
||||
val users = mutableStateListOf<UserInfo>()
|
||||
@@ -65,11 +66,11 @@ class ChatModel(val controller: ChatController) {
|
||||
val appOpenUrl = mutableStateOf<Uri?>(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<File>()
|
||||
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
|
||||
+39
-32
@@ -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<String, Long>()
|
||||
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")
|
||||
}
|
||||
+21
-26
@@ -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)
|
||||
}
|
||||
}
|
||||
|
||||
+1
-2
@@ -16,7 +16,6 @@ import chat.simplex.app.views.helpers.*
|
||||
import kotlinx.coroutines.flow.MutableStateFlow
|
||||
import kotlinx.serialization.SerialName
|
||||
import kotlinx.serialization.Serializable
|
||||
import okhttp3.internal.toHexString
|
||||
|
||||
enum class DefaultTheme {
|
||||
SYSTEM, LIGHT, DARK, SIMPLEX;
|
||||
@@ -150,7 +149,7 @@ data class ThemeColors(
|
||||
private fun String.colorFromReadableHex(): Color =
|
||||
Color(this.replace("#", "").toLongOrNull(16) ?: Color.White.toArgb().toLong())
|
||||
|
||||
private fun Color.toReadableHex(): String = "#" + toArgb().toHexString()
|
||||
private fun Color.toReadableHex(): String = "#" + Integer.toHexString(toArgb())
|
||||
|
||||
@Serializable
|
||||
data class ThemeOverrides (
|
||||
+1
-2
@@ -7,7 +7,6 @@ import chat.simplex.app.R
|
||||
import chat.simplex.app.SimplexApp
|
||||
import chat.simplex.app.model.AppPreferences
|
||||
import chat.simplex.app.views.helpers.generalGetString
|
||||
import okhttp3.internal.toHexString
|
||||
|
||||
object ThemeManager {
|
||||
private val appPrefs: AppPreferences by lazy {
|
||||
@@ -149,4 +148,4 @@ object ThemeManager {
|
||||
}
|
||||
}
|
||||
|
||||
private fun Color.toReadableHex(): String = "#" + toArgb().toHexString()
|
||||
private fun Color.toReadableHex(): String = "#" + Integer.toHexString(toArgb())
|
||||
+1
-1
@@ -128,7 +128,7 @@ fun TerminalLog(terminalItems: List<TerminalItem>) {
|
||||
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))
|
||||
}
|
||||
+1
-1
@@ -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
|
||||
+1
-1
@@ -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,
|
||||
+2
-2
@@ -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) {
|
||||
+1
-2
@@ -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()
|
||||
+2
-4
@@ -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
|
||||
})
|
||||
}
|
||||
+5
-5
@@ -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<Int, ChatItem>? {
|
||||
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)
|
||||
+12
-12
@@ -201,7 +201,7 @@ fun ComposeView(
|
||||
val imagesPreview = ArrayList<String>()
|
||||
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 ""))
|
||||
@@ -594,7 +594,7 @@ fun ComposeView(
|
||||
suspend fun sendLiveMessage() {
|
||||
val cs = composeState.value
|
||||
val typedMsg = cs.message
|
||||
if ((cs.sendEnabled() || cs.contextItem is ComposeContextItem.QuotedItem) && (cs.liveMessage == null || !cs.liveMessage?.sent)) {
|
||||
if ((cs.sendEnabled() || cs.contextItem is ComposeContextItem.QuotedItem) && (cs.liveMessage == null || !cs.liveMessage.sent)) {
|
||||
val ci = sendMessageAsync(typedMsg, live = true, ttl = null)
|
||||
if (ci != null) {
|
||||
composeState.value = composeState.value.copy(liveMessage = LiveMessage(ci, typedMsg = typedMsg, sentMsg = typedMsg, sent = true))
|
||||
+1
-3
@@ -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)
|
||||
}
|
||||
}
|
||||
+1
-3
@@ -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) {
|
||||
+1
-1
@@ -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) })
|
||||
+2
-2
@@ -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()
|
||||
}
|
||||
+2
-2
@@ -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 {
|
||||
+3
-3
@@ -139,8 +139,8 @@ fun CIImageView(
|
||||
}
|
||||
|
||||
fun imageAndFilePath(file: CIFile?): Pair<Bitmap?, String?> {
|
||||
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)
|
||||
}
|
||||
+1
-4
@@ -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())) {
|
||||
+2
-2
@@ -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<Boolean>, 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 }
|
||||
+1
-1
@@ -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) }
|
||||
+7
-10
@@ -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()
|
||||
}
|
||||
+1
-2
@@ -21,7 +21,6 @@ import androidx.compose.ui.text.font.FontStyle
|
||||
import androidx.compose.ui.text.style.TextOverflow
|
||||
import androidx.compose.ui.tooling.preview.*
|
||||
import androidx.compose.ui.unit.*
|
||||
import androidx.compose.ui.util.fastMap
|
||||
import chat.simplex.app.R
|
||||
import chat.simplex.app.model.*
|
||||
import chat.simplex.app.ui.theme.*
|
||||
@@ -300,7 +299,7 @@ fun PriorityLayout(
|
||||
// Find important element which should tell what max width other elements can use
|
||||
// Expecting only one such element. Can be less than one but not more
|
||||
val imagePlaceable = measureable.firstOrNull { it.layoutId == priorityLayoutId }?.measure(constraints)
|
||||
val placeables: List<Placeable> = measureable.fastMap {
|
||||
val placeables: List<Placeable> = measureable.map {
|
||||
if (it.layoutId == priorityLayoutId)
|
||||
imagePlaceable!!
|
||||
else
|
||||
+1
-1
@@ -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)
|
||||
+5
-5
@@ -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<String, Uri?> =
|
||||
private fun rememberSaveArchiveLauncher(chatArchivePath: String): ManagedActivityResultLauncher<String, Uri?> =
|
||||
rememberLauncherForActivityResult(
|
||||
contract = ActivityResultContracts.CreateDocument(),
|
||||
onResult = { destination ->
|
||||
val cxt = SimplexApp.context
|
||||
try {
|
||||
destination?.let {
|
||||
val contentResolver = cxt.contentResolver
|
||||
+1
-1
@@ -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("") },
|
||||
+7
-5
@@ -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<Boolean>, prefs: AppPreferences, context: Context) {
|
||||
private fun restoreDb(restoreDbFromBackup: MutableState<Boolean>, 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()
|
||||
)
|
||||
}
|
||||
}
|
||||
+41
-48
@@ -57,11 +57,11 @@ fun DatabaseView(
|
||||
val chatArchiveTime = remember { mutableStateOf(prefs.chatArchiveTime.get()) }
|
||||
val chatLastStart = remember { mutableStateOf(prefs.chatLastStart.get()) }
|
||||
val chatArchiveFile = remember { mutableStateOf<String?>(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<ChatItemTTL>,
|
||||
progressIndicator: MutableState<Boolean>,
|
||||
appFilesCountAndSize: MutableState<Pair<Int, Long>>,
|
||||
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<Boolean?>, chatLastSta
|
||||
}
|
||||
}
|
||||
|
||||
private fun stopChatAlert(m: ChatModel, runChat: MutableState<Boolean?>, context: Context) {
|
||||
private fun stopChatAlert(m: ChatModel, runChat: MutableState<Boolean?>) {
|
||||
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<Boolean?>, context: Context) {
|
||||
private fun authStopChat(m: ChatModel, runChat: MutableState<Boolean?>) {
|
||||
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<Boolean?>, context:
|
||||
}
|
||||
)
|
||||
} else {
|
||||
stopChat(m, runChat, context)
|
||||
stopChat(m, runChat)
|
||||
}
|
||||
}
|
||||
|
||||
private fun stopChat(m: ChatModel, runChat: MutableState<Boolean?>, context: Context) {
|
||||
private fun stopChat(m: ChatModel, runChat: MutableState<Boolean?>) {
|
||||
withApi {
|
||||
try {
|
||||
runChat.value = false
|
||||
@@ -455,7 +453,6 @@ suspend fun deleteChatAsync(m: ChatModel) {
|
||||
}
|
||||
|
||||
private fun exportArchive(
|
||||
context: Context,
|
||||
m: ChatModel,
|
||||
progressIndicator: MutableState<Boolean>,
|
||||
chatArchiveName: MutableState<String?>,
|
||||
@@ -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<String?>,
|
||||
chatArchiveTime: MutableState<Instant?>,
|
||||
chatArchiveFile: MutableState<String?>
|
||||
@@ -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<String?>): ManagedActivityResultLauncher<String, Uri?> =
|
||||
private fun rememberSaveArchiveLauncher(chatArchiveFile: MutableState<String?>): ManagedActivityResultLauncher<String, Uri?> =
|
||||
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<Pair<Int, Long>>,
|
||||
progressIndicator: MutableState<Boolean>
|
||||
@@ -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<Pair<Int, Long>>,
|
||||
progressIndicator: MutableState<Boolean>
|
||||
) {
|
||||
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<ChatItemTTL>,
|
||||
progressIndicator: MutableState<Boolean>,
|
||||
appFilesCountAndSize: MutableState<Pair<Int, Long>>,
|
||||
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<Boolean>,
|
||||
appFilesCountAndSize: MutableState<Pair<Int, Long>>,
|
||||
context: Context
|
||||
appFilesCountAndSize: MutableState<Pair<Int, Long>>
|
||||
) {
|
||||
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<Pair<Int, Long>>) {
|
||||
private fun deleteFilesAndMediaAlert(appFilesCountAndSize: MutableState<Pair<Int, Long>>) {
|
||||
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<Pair<Int, Long>>, context: Context) {
|
||||
deleteAppFiles(context)
|
||||
appFilesCountAndSize.value = directoryFileCountAndSize(getAppFilesDirectory(context))
|
||||
private fun deleteFiles(appFilesCountAndSize: MutableState<Pair<Int, Long>>) {
|
||||
deleteAppFiles()
|
||||
appFilesCountAndSize.value = directoryFileCountAndSize(getAppFilesDirectory())
|
||||
}
|
||||
|
||||
private fun operationEnded(m: ChatModel, progressIndicator: MutableState<Boolean>, alert: () -> Unit) {
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user