diff --git a/apps/android/.gitignore b/apps/android/.gitignore deleted file mode 100644 index 9ddf412fc2..0000000000 --- a/apps/android/.gitignore +++ /dev/null @@ -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/ diff --git a/apps/android/.idea/.name b/apps/android/.idea/.name deleted file mode 100644 index ccb58e52e1..0000000000 --- a/apps/android/.idea/.name +++ /dev/null @@ -1 +0,0 @@ -SimpleX \ No newline at end of file diff --git a/apps/android/.idea/compiler.xml b/apps/android/.idea/compiler.xml deleted file mode 100644 index fb7f4a8a46..0000000000 --- a/apps/android/.idea/compiler.xml +++ /dev/null @@ -1,6 +0,0 @@ - - - - - - \ No newline at end of file diff --git a/apps/android/.idea/gradle.xml b/apps/android/.idea/gradle.xml deleted file mode 100644 index a2d7c21338..0000000000 --- a/apps/android/.idea/gradle.xml +++ /dev/null @@ -1,19 +0,0 @@ - - - - - - - \ No newline at end of file diff --git a/apps/android/.idea/inspectionProfiles/Project_Default.xml b/apps/android/.idea/inspectionProfiles/Project_Default.xml deleted file mode 100644 index 28422375b6..0000000000 --- a/apps/android/.idea/inspectionProfiles/Project_Default.xml +++ /dev/null @@ -1,20 +0,0 @@ - - - - \ No newline at end of file diff --git a/apps/android/.idea/vcs.xml b/apps/android/.idea/vcs.xml deleted file mode 100644 index b2bdec2d71..0000000000 --- a/apps/android/.idea/vcs.xml +++ /dev/null @@ -1,6 +0,0 @@ - - - - - - \ No newline at end of file diff --git a/apps/android/app/build.gradle b/apps/android/app/build.gradle deleted file mode 100644 index 4f8b6cbe30..0000000000 --- a/apps/android/app/build.gradle +++ /dev/null @@ -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" } - } -} diff --git a/apps/android/build.gradle b/apps/android/build.gradle deleted file mode 100644 index 08dbb3f98d..0000000000 --- a/apps/android/build.gradle +++ /dev/null @@ -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 -} \ No newline at end of file diff --git a/apps/android/settings.gradle b/apps/android/settings.gradle deleted file mode 100644 index 3621283ff6..0000000000 --- a/apps/android/settings.gradle +++ /dev/null @@ -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' diff --git a/apps/ios/SimpleX.xcodeproj/project.pbxproj b/apps/ios/SimpleX.xcodeproj/project.pbxproj index 4e6f128f9f..fdb2d5412b 100644 --- a/apps/ios/SimpleX.xcodeproj/project.pbxproj +++ b/apps/ios/SimpleX.xcodeproj/project.pbxproj @@ -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 = ""; }; 18415DAAAD1ADBEDB0EDA852 /* VideoPlayerView.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = VideoPlayerView.swift; sourceTree = ""; }; 18415FD2E36F13F596A45BB4 /* CIVideoView.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = CIVideoView.swift; sourceTree = ""; }; - 3C714779281C0F6800CB4D4B /* www */ = {isa = PBXFileReference; lastKnownFileType = folder; name = www; path = ../android/app/src/main/assets/www; sourceTree = ""; }; + 3C714779281C0F6800CB4D4B /* www */ = {isa = PBXFileReference; lastKnownFileType = folder; name = www; path = ../multiplatform/android/src/main/assets/www; sourceTree = ""; }; 3C8C548828133C84000A3EC7 /* PasteToConnectView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PasteToConnectView.swift; sourceTree = ""; }; 3CDBCF4127FAE51000354CDD /* ComposeLinkView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ComposeLinkView.swift; sourceTree = ""; }; 3CDBCF4727FF621E00354CDD /* CILinkView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CILinkView.swift; sourceTree = ""; }; @@ -392,11 +392,6 @@ 5CC2C0FB2809BF11000C35E3 /* ru */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = ru; path = ru.lproj/Localizable.strings; sourceTree = ""; }; 5CC2C0FE2809BF11000C35E3 /* ru */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = ru; path = "ru.lproj/SimpleX--iOS--InfoPlist.strings"; sourceTree = ""; }; 5CC868F229EB540C0017BBFD /* CIRcvDecryptionError.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CIRcvDecryptionError.swift; sourceTree = ""; }; - 5CCAA6D02A4B38F600BAF93B /* libHSsimplex-chat-5.1.3.0-5OWMsZbPutPDzQF8wFktLQ.a */ = {isa = PBXFileReference; lastKnownFileType = archive.ar; path = "libHSsimplex-chat-5.1.3.0-5OWMsZbPutPDzQF8wFktLQ.a"; sourceTree = ""; }; - 5CCAA6D12A4B38F600BAF93B /* libgmp.a */ = {isa = PBXFileReference; lastKnownFileType = archive.ar; path = libgmp.a; sourceTree = ""; }; - 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 = ""; }; - 5CCAA6D32A4B38F600BAF93B /* libgmpxx.a */ = {isa = PBXFileReference; lastKnownFileType = archive.ar; path = libgmpxx.a; sourceTree = ""; }; - 5CCAA6D42A4B38F600BAF93B /* libffi.a */ = {isa = PBXFileReference; lastKnownFileType = archive.ar; path = libffi.a; sourceTree = ""; }; 5CCB939B297EFCB100399E78 /* NavStackCompat.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NavStackCompat.swift; sourceTree = ""; }; 5CCD403327A5F6DF00368C90 /* AddContactView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AddContactView.swift; sourceTree = ""; }; 5CCD403627A5F9A200368C90 /* ScanToConnectView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ScanToConnectView.swift; sourceTree = ""; }; @@ -446,6 +441,11 @@ 6493D667280ED77F007A76FB /* en */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = en; path = en.lproj/Localizable.strings; sourceTree = ""; }; 649BCD9F280460FD00C3A862 /* ComposeImageView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ComposeImageView.swift; sourceTree = ""; }; 649BCDA12805D6EF00C3A862 /* CIImageView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CIImageView.swift; sourceTree = ""; }; + 64A3530B2A4C84CE007CD71D /* libgmp.a */ = {isa = PBXFileReference; lastKnownFileType = archive.ar; path = libgmp.a; sourceTree = ""; }; + 64A3530C2A4C84CE007CD71D /* libHSsimplex-chat-5.2.0.0-ESKsZ4YorLH7yFQuFvHeIm.a */ = {isa = PBXFileReference; lastKnownFileType = archive.ar; path = "libHSsimplex-chat-5.2.0.0-ESKsZ4YorLH7yFQuFvHeIm.a"; sourceTree = ""; }; + 64A3530D2A4C84CE007CD71D /* libffi.a */ = {isa = PBXFileReference; lastKnownFileType = archive.ar; path = libffi.a; sourceTree = ""; }; + 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 = ""; }; + 64A3530F2A4C84CE007CD71D /* libgmpxx.a */ = {isa = PBXFileReference; lastKnownFileType = archive.ar; path = libgmpxx.a; sourceTree = ""; }; 64AA1C6827EE10C800AC7277 /* ContextItemView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ContextItemView.swift; sourceTree = ""; }; 64AA1C6B27F3537400AC7277 /* DeletedItemView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = DeletedItemView.swift; sourceTree = ""; }; 64C06EB42A0A4A7C00792D4D /* ChatItemInfoView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ChatItemInfoView.swift; sourceTree = ""; }; @@ -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 = ""; @@ -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 = ""; diff --git a/apps/multiplatform/.gitignore b/apps/multiplatform/.gitignore new file mode 100644 index 0000000000..d8419431db --- /dev/null +++ b/apps/multiplatform/.gitignore @@ -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 \ No newline at end of file diff --git a/apps/android/.idea/codeStyles/Project.xml b/apps/multiplatform/.idea/codeStyles/Project.xml similarity index 100% rename from apps/android/.idea/codeStyles/Project.xml rename to apps/multiplatform/.idea/codeStyles/Project.xml diff --git a/apps/android/.idea/codeStyles/codeStyleConfig.xml b/apps/multiplatform/.idea/codeStyles/codeStyleConfig.xml similarity index 100% rename from apps/android/.idea/codeStyles/codeStyleConfig.xml rename to apps/multiplatform/.idea/codeStyles/codeStyleConfig.xml diff --git a/apps/android/README.md b/apps/multiplatform/README.md similarity index 100% rename from apps/android/README.md rename to apps/multiplatform/README.md diff --git a/apps/android/app/.gitignore b/apps/multiplatform/android/.gitignore similarity index 100% rename from apps/android/app/.gitignore rename to apps/multiplatform/android/.gitignore diff --git a/apps/multiplatform/android/build.gradle.kts b/apps/multiplatform/android/build.gradle.kts new file mode 100644 index 0000000000..5d54c5f7b6 --- /dev/null +++ b/apps/multiplatform/android/build.gradle.kts @@ -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) + } + } + } +} diff --git a/apps/android/app/proguard-rules.pro b/apps/multiplatform/android/proguard-rules.pro similarity index 100% rename from apps/android/app/proguard-rules.pro rename to apps/multiplatform/android/proguard-rules.pro diff --git a/apps/android/app/src/androidTest/java/chat/simplex/app/ExampleInstrumentedTest.kt b/apps/multiplatform/android/src/androidTest/java/chat/simplex/app/ExampleInstrumentedTest.kt similarity index 100% rename from apps/android/app/src/androidTest/java/chat/simplex/app/ExampleInstrumentedTest.kt rename to apps/multiplatform/android/src/androidTest/java/chat/simplex/app/ExampleInstrumentedTest.kt diff --git a/apps/android/app/src/main/AndroidManifest.xml b/apps/multiplatform/android/src/main/AndroidManifest.xml similarity index 98% rename from apps/android/app/src/main/AndroidManifest.xml rename to apps/multiplatform/android/src/main/AndroidManifest.xml index 6d090e381e..f3d48fe88f 100644 --- a/apps/android/app/src/main/AndroidManifest.xml +++ b/apps/multiplatform/android/src/main/AndroidManifest.xml @@ -6,6 +6,7 @@ + diff --git a/apps/android/app/src/main/assets/www/README.md b/apps/multiplatform/android/src/main/assets/www/README.md similarity index 100% rename from apps/android/app/src/main/assets/www/README.md rename to apps/multiplatform/android/src/main/assets/www/README.md diff --git a/apps/android/app/src/main/assets/www/call.html b/apps/multiplatform/android/src/main/assets/www/call.html similarity index 100% rename from apps/android/app/src/main/assets/www/call.html rename to apps/multiplatform/android/src/main/assets/www/call.html diff --git a/apps/android/app/src/main/assets/www/call.js b/apps/multiplatform/android/src/main/assets/www/call.js similarity index 100% rename from apps/android/app/src/main/assets/www/call.js rename to apps/multiplatform/android/src/main/assets/www/call.js diff --git a/apps/android/app/src/main/assets/www/lz-string.min.js b/apps/multiplatform/android/src/main/assets/www/lz-string.min.js similarity index 100% rename from apps/android/app/src/main/assets/www/lz-string.min.js rename to apps/multiplatform/android/src/main/assets/www/lz-string.min.js diff --git a/apps/android/app/src/main/assets/www/style.css b/apps/multiplatform/android/src/main/assets/www/style.css similarity index 100% rename from apps/android/app/src/main/assets/www/style.css rename to apps/multiplatform/android/src/main/assets/www/style.css diff --git a/apps/android/app/src/main/icon-playstore.png b/apps/multiplatform/android/src/main/icon-playstore.png similarity index 100% rename from apps/android/app/src/main/icon-playstore.png rename to apps/multiplatform/android/src/main/icon-playstore.png diff --git a/apps/android/app/src/main/icon_dark_blue-playstore.png b/apps/multiplatform/android/src/main/icon_dark_blue-playstore.png similarity index 100% rename from apps/android/app/src/main/icon_dark_blue-playstore.png rename to apps/multiplatform/android/src/main/icon_dark_blue-playstore.png diff --git a/apps/android/app/src/main/java/chat/simplex/app/BackupAgent.kt b/apps/multiplatform/android/src/main/java/chat/simplex/app/BackupAgent.kt similarity index 100% rename from apps/android/app/src/main/java/chat/simplex/app/BackupAgent.kt rename to apps/multiplatform/android/src/main/java/chat/simplex/app/BackupAgent.kt diff --git a/apps/android/app/src/main/java/chat/simplex/app/MainActivity.kt b/apps/multiplatform/android/src/main/java/chat/simplex/app/MainActivity.kt similarity index 99% rename from apps/android/app/src/main/java/chat/simplex/app/MainActivity.kt rename to apps/multiplatform/android/src/main/java/chat/simplex/app/MainActivity.kt index 3d6b891af2..946973d973 100644 --- a/apps/android/app/src/main/java/chat/simplex/app/MainActivity.kt +++ b/apps/multiplatform/android/src/main/java/chat/simplex/app/MainActivity.kt @@ -24,7 +24,7 @@ import androidx.fragment.app.FragmentActivity import androidx.lifecycle.* import chat.simplex.app.MainActivity.Companion.enteredBackground import chat.simplex.app.model.* -import chat.simplex.app.model.NtfManager.Companion.getUserIdFromIntent +import chat.simplex.app.model.NtfManager.getUserIdFromIntent import chat.simplex.app.ui.theme.* import chat.simplex.app.views.SplashView import chat.simplex.app.views.call.ActiveCallView @@ -42,6 +42,7 @@ import chat.simplex.app.views.onboarding.* import chat.simplex.app.views.usersettings.LAMode import kotlinx.coroutines.* import kotlinx.coroutines.flow.distinctUntilChanged +import java.lang.ref.WeakReference class MainActivity: FragmentActivity() { companion object { @@ -65,6 +66,7 @@ class MainActivity: FragmentActivity() { override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) + SimplexApp.context.mainActivity = WeakReference(this) // testJson() val m = vm.chatModel applyAppLocale(m.controller.appPrefs.appLanguage) @@ -181,7 +183,6 @@ class MainActivity: FragmentActivity() { else generalGetString(R.string.auth_unlock), selfDestruct = true, - this@MainActivity, completed = { laResult -> when (laResult) { LAResult.Success -> @@ -250,7 +251,6 @@ class MainActivity: FragmentActivity() { authenticate( generalGetString(R.string.auth_enable_simplex_lock), generalGetString(R.string.auth_confirm_credential), - activity = activity, completed = { laResult -> when (laResult) { LAResult.Success -> { @@ -317,7 +317,6 @@ class MainActivity: FragmentActivity() { generalGetString(R.string.auth_confirm_credential) else "", - activity = activity, completed = { laResult -> val prefPerformLA = m.controller.appPrefs.performLA when (laResult) { @@ -353,7 +352,6 @@ class MainActivity: FragmentActivity() { generalGetString(R.string.auth_confirm_credential) else generalGetString(R.string.auth_disable_simplex_lock), - activity = activity, completed = { laResult -> val prefPerformLA = m.controller.appPrefs.performLA val selfDestructPref = m.controller.appPrefs.selfDestruct diff --git a/apps/android/app/src/main/java/chat/simplex/app/SimplexApp.kt b/apps/multiplatform/android/src/main/java/chat/simplex/app/SimplexApp.kt similarity index 95% rename from apps/android/app/src/main/java/chat/simplex/app/SimplexApp.kt rename to apps/multiplatform/android/src/main/java/chat/simplex/app/SimplexApp.kt index 7a2aecaae6..29a20ff768 100644 --- a/apps/android/app/src/main/java/chat/simplex/app/SimplexApp.kt +++ b/apps/multiplatform/android/src/main/java/chat/simplex/app/SimplexApp.kt @@ -14,6 +14,7 @@ import com.jakewharton.processphoenix.ProcessPhoenix import kotlinx.coroutines.* import kotlinx.serialization.decodeFromString import java.io.* +import java.lang.ref.WeakReference import java.util.* import java.util.concurrent.Semaphore import java.util.concurrent.TimeUnit @@ -37,13 +38,20 @@ external fun chatParseServer(str: String): String external fun chatPasswordHash(pwd: String, salt: String): String class SimplexApp: Application(), LifecycleEventObserver { + var mainActivity: WeakReference = WeakReference(null) + val chatModel: ChatModel + get() = chatController.chatModel + val appPreferences: AppPreferences + get() = chatController.appPrefs + + val chatController: ChatController = ChatController var isAppOnForeground: Boolean = false val defaultLocale: Locale = Locale.getDefault() suspend fun initChatController(useKey: String? = null, confirmMigrations: MigrationConfirmation? = null, startChat: Boolean = true) { val dbKey = useKey ?: DatabaseUtils.useDatabaseKey() - val dbAbsolutePathPrefix = getFilesDirectory(SimplexApp.context) + val dbAbsolutePathPrefix = getFilesDirectory() val confirm = confirmMigrations ?: if (appPreferences.confirmDBUpgrades.get()) MigrationConfirmation.Error else MigrationConfirmation.YesUp val migrated: Array = chatMigrateInit(dbAbsolutePathPrefix, dbKey, confirm.value) val res: DBMigrationResult = kotlin.runCatching { @@ -84,21 +92,6 @@ class SimplexApp: Application(), LifecycleEventObserver { } } - val chatModel: ChatModel - get() = chatController.chatModel - - private val ntfManager: NtfManager by lazy { - NtfManager(applicationContext, appPreferences) - } - - private val appPreferences: AppPreferences by lazy { - AppPreferences(applicationContext) - } - - val chatController: ChatController by lazy { - ChatController(0L, ntfManager, applicationContext, appPreferences) - } - override fun onCreate() { super.onCreate() @@ -162,12 +155,12 @@ class SimplexApp: Application(), LifecycleEventObserver { fun allowToStartServiceAfterAppExit() = with(chatModel.controller) { appPrefs.notificationsMode.get() == NotificationsMode.SERVICE.name && - (!NotificationsMode.SERVICE.requiresIgnoringBattery || isIgnoringBatteryOptimizations(chatModel.controller.appContext)) + (!NotificationsMode.SERVICE.requiresIgnoringBattery || isIgnoringBatteryOptimizations()) } private fun allowToStartPeriodically() = with(chatModel.controller) { appPrefs.notificationsMode.get() == NotificationsMode.PERIODIC.name && - (!NotificationsMode.PERIODIC.requiresIgnoringBattery || isIgnoringBatteryOptimizations(chatModel.controller.appContext)) + (!NotificationsMode.PERIODIC.requiresIgnoringBattery || isIgnoringBatteryOptimizations()) } /* diff --git a/apps/android/app/src/main/java/chat/simplex/app/SimplexService.kt b/apps/multiplatform/android/src/main/java/chat/simplex/app/SimplexService.kt similarity index 100% rename from apps/android/app/src/main/java/chat/simplex/app/SimplexService.kt rename to apps/multiplatform/android/src/main/java/chat/simplex/app/SimplexService.kt diff --git a/apps/android/app/src/main/java/chat/simplex/app/model/ChatModel.kt b/apps/multiplatform/android/src/main/java/chat/simplex/app/model/ChatModel.kt similarity index 99% rename from apps/android/app/src/main/java/chat/simplex/app/model/ChatModel.kt rename to apps/multiplatform/android/src/main/java/chat/simplex/app/model/ChatModel.kt index 3d9de2d35f..b480814144 100644 --- a/apps/android/app/src/main/java/chat/simplex/app/model/ChatModel.kt +++ b/apps/multiplatform/android/src/main/java/chat/simplex/app/model/ChatModel.kt @@ -34,7 +34,8 @@ import kotlin.time.* * Without this annotation an animation from ChatList to ChatView has 1 frame per the whole animation. Don't delete it * */ @Stable -class ChatModel(val controller: ChatController) { +object ChatModel { + val controller: ChatController = ChatController val onboardingStage = mutableStateOf(null) val currentUser = mutableStateOf(null) val users = mutableStateListOf() @@ -65,11 +66,11 @@ class ChatModel(val controller: ChatController) { val appOpenUrl = mutableStateOf(null) // preferences - val notificationsMode = mutableStateOf(NotificationsMode.default) - var notificationPreviewMode = mutableStateOf(NotificationPreviewMode.default) - val performLA = mutableStateOf(false) + val notificationsMode by lazy { mutableStateOf(NotificationsMode.values().firstOrNull { it.name == controller.appPrefs.notificationsMode.get() } ?: NotificationsMode.default) } + val notificationPreviewMode by lazy { mutableStateOf(NotificationPreviewMode.values().firstOrNull { it.name == controller.appPrefs.notificationPreviewMode.get() } ?: NotificationPreviewMode.default) } + val performLA by lazy { mutableStateOf(controller.appPrefs.performLA.get()) } val showAdvertiseLAUnavailableAlert = mutableStateOf(false) - var incognito = mutableStateOf(false) + val incognito by lazy { mutableStateOf(controller.appPrefs.incognito.get()) } // current WebRTC call val callManager = CallManager(this) @@ -91,7 +92,7 @@ class ChatModel(val controller: ChatController) { val sharedContent = mutableStateOf(null as SharedContent?) val filesToDelete = mutableSetOf() - val simplexLinkMode = mutableStateOf(controller.appPrefs.simplexLinkMode.get()) + val simplexLinkMode by lazy { mutableStateOf(controller.appPrefs.simplexLinkMode.get()) } fun getUser(userId: Long): User? = if (currentUser.value?.userId == userId) { currentUser.value diff --git a/apps/android/app/src/main/java/chat/simplex/app/model/NtfManager.kt b/apps/multiplatform/android/src/main/java/chat/simplex/app/model/NtfManager.kt similarity index 86% rename from apps/android/app/src/main/java/chat/simplex/app/model/NtfManager.kt rename to apps/multiplatform/android/src/main/java/chat/simplex/app/model/NtfManager.kt index bc5985c5e5..1a52030c84 100644 --- a/apps/android/app/src/main/java/chat/simplex/app/model/NtfManager.kt +++ b/apps/multiplatform/android/src/main/java/chat/simplex/app/model/NtfManager.kt @@ -1,15 +1,17 @@ package chat.simplex.app.model +import android.Manifest import android.app.* +import android.app.TaskStackBuilder import android.content.* +import android.content.pm.PackageManager import android.graphics.BitmapFactory import android.hardware.display.DisplayManager import android.media.AudioAttributes import android.net.Uri import android.util.Log import android.view.Display -import androidx.core.app.NotificationCompat -import androidx.core.app.NotificationManagerCompat +import androidx.core.app.* import chat.simplex.app.* import chat.simplex.app.views.call.* import chat.simplex.app.views.chatlist.acceptContactRequest @@ -17,29 +19,29 @@ import chat.simplex.app.views.helpers.* import chat.simplex.app.views.usersettings.NotificationPreviewMode import kotlinx.datetime.Clock -class NtfManager(val context: Context, private val appPreferences: AppPreferences) { - companion object { - const val MessageChannel: String = "chat.simplex.app.MESSAGE_NOTIFICATION" - const val MessageGroup: String = "chat.simplex.app.MESSAGE_NOTIFICATION" - const val OpenChatAction: String = "chat.simplex.app.OPEN_CHAT" - const val ShowChatsAction: String = "chat.simplex.app.SHOW_CHATS" +object NtfManager { + const val MessageChannel: String = "chat.simplex.app.MESSAGE_NOTIFICATION" + const val MessageGroup: String = "chat.simplex.app.MESSAGE_NOTIFICATION" + const val OpenChatAction: String = "chat.simplex.app.OPEN_CHAT" + const val ShowChatsAction: String = "chat.simplex.app.SHOW_CHATS" - // DO NOT change notification channel settings / names - const val CallChannel: String = "chat.simplex.app.CALL_NOTIFICATION_1" - const val AcceptCallAction: String = "chat.simplex.app.ACCEPT_CALL" - const val RejectCallAction: String = "chat.simplex.app.REJECT_CALL" - const val CallNotificationId: Int = -1 + // DO NOT change notification channel settings / names + const val CallChannel: String = "chat.simplex.app.CALL_NOTIFICATION_1" + const val AcceptCallAction: String = "chat.simplex.app.ACCEPT_CALL" + const val RejectCallAction: String = "chat.simplex.app.REJECT_CALL" + const val CallNotificationId: Int = -1 + private const val UserIdKey: String = "userId" + private const val ChatIdKey: String = "chatId" + private val appPreferences: AppPreferences by lazy { ChatController.appPrefs } + private val context: Context + get() = SimplexApp.context - private const val UserIdKey: String = "userId" - private const val ChatIdKey: String = "chatId" - - fun getUserIdFromIntent(intent: Intent?): Long? { - val userId = intent?.getLongExtra(UserIdKey, -1L) - return if (userId == -1L || userId == null) null else userId - } + fun getUserIdFromIntent(intent: Intent?): Long? { + val userId = intent?.getLongExtra(UserIdKey, -1L) + return if (userId == -1L || userId == null) null else userId } - private val manager: NotificationManager = context.getSystemService(Context.NOTIFICATION_SERVICE) as NotificationManager + private val manager: NotificationManager = SimplexApp.context.getSystemService(Context.NOTIFICATION_SERVICE) as NotificationManager private var prevNtfTime = mutableMapOf() private val msgNtfTimeoutMs = 30000L @@ -58,7 +60,7 @@ class NtfManager(val context: Context, private val appPreferences: AppPreference .setUsage(AudioAttributes.USAGE_NOTIFICATION_RINGTONE) .build() val soundUri = Uri.parse(ContentResolver.SCHEME_ANDROID_RESOURCE + "://" + context.packageName + "/" + R.raw.ring_once) - Log.d(TAG,"callNotificationChannel sound: $soundUri") + Log.d(TAG, "callNotificationChannel sound: $soundUri") callChannel.setSound(soundUri, attrs) callChannel.enableVibration(true) // the numbers below are explained here: https://developer.android.com/reference/android/os/Vibrator @@ -70,8 +72,8 @@ class NtfManager(val context: Context, private val appPreferences: AppPreference fun cancelNotificationsForChat(chatId: String) { prevNtfTime.remove(chatId) manager.cancel(chatId.hashCode()) - val msgNtfs = manager.activeNotifications.filter { - ntf -> ntf.notification.channelId == MessageChannel + val msgNtfs = manager.activeNotifications.filter { ntf -> + ntf.notification.channelId == MessageChannel } if (msgNtfs.count() == 1) { // Have a group notification with no children so cancel it @@ -110,7 +112,6 @@ class NtfManager(val context: Context, private val appPreferences: AppPreference val now = Clock.System.now().toEpochMilliseconds() val recentNotification = (now - prevNtfTime.getOrDefault(chatId, 0) < msgNtfTimeoutMs) prevNtfTime[chatId] = now - val previewMode = appPreferences.notificationPreviewMode.get() val title = if (previewMode == NotificationPreviewMode.HIDDEN.name) generalGetString(R.string.notification_preview_somebody) else displayName val content = if (previewMode != NotificationPreviewMode.MESSAGE.name) generalGetString(R.string.notification_preview_new_message) else msgText @@ -145,7 +146,6 @@ class NtfManager(val context: Context, private val appPreferences: AppPreference } builder.addAction(0, actionButton, actionPendingIntent) } - val summary = NotificationCompat.Builder(context, MessageChannel) .setSmallIcon(R.drawable.ntf_icon) .setColor(0x88FFFF) @@ -157,14 +157,17 @@ class NtfManager(val context: Context, private val appPreferences: AppPreference with(NotificationManagerCompat.from(context)) { // using cInfo.id only shows one notification per chat and updates it when the message arrives - notify(chatId.hashCode(), builder.build()) - notify(0, summary) + if (ActivityCompat.checkSelfPermission(SimplexApp.context, Manifest.permission.POST_NOTIFICATIONS) == PackageManager.PERMISSION_GRANTED) { + notify(chatId.hashCode(), builder.build()) + notify(0, summary) + } } } fun notifyCallInvitation(invitation: RcvCallInvitation) { val keyguardManager = getKeyguardManager(context) - Log.d(TAG, + Log.d( + TAG, "notifyCallInvitation pre-requests: " + "keyguard locked ${keyguardManager.isKeyguardLocked}, " + "callOnLockScreen ${appPreferences.callOnLockScreen.get()}, " + @@ -223,7 +226,9 @@ class NtfManager(val context: Context, private val appPreferences: AppPreference // This makes notification sound and vibration repeat endlessly notification.flags = notification.flags or NotificationCompat.FLAG_INSISTENT with(NotificationManagerCompat.from(context)) { - notify(CallNotificationId, notification) + if (ActivityCompat.checkSelfPermission(SimplexApp.context, Manifest.permission.POST_NOTIFICATIONS) == PackageManager.PERMISSION_GRANTED) { + notify(CallNotificationId, notification) + } } } @@ -237,7 +242,7 @@ class NtfManager(val context: Context, private val appPreferences: AppPreference fun hasNotificationsForChat(chatId: String): Boolean = manager.activeNotifications.any { it.id == chatId.hashCode() } - private fun hideSecrets(cItem: ChatItem) : String { + private fun hideSecrets(cItem: ChatItem): String { val md = cItem.formattedText return if (md != null) { var res = "" @@ -302,14 +307,16 @@ class NtfManager(val context: Context, private val appPreferences: AppPreference } val apiId = chatId.replace("<@", "").toLongOrNull() ?: return acceptContactRequest(apiId, cInfo, isCurrentUser, m) - m.controller.ntfManager.cancelNotificationsForChat(chatId) + cancelNotificationsForChat(chatId) } + RejectCallAction -> { val invitation = m.callInvitations[chatId] if (invitation != null) { m.callManager.endCall(invitation = invitation) } } + else -> { Log.e(TAG, "Unknown action. Make sure you provide action from NotificationAction enum") } diff --git a/apps/android/app/src/main/java/chat/simplex/app/model/SimpleXAPI.kt b/apps/multiplatform/android/src/main/java/chat/simplex/app/model/SimpleXAPI.kt similarity index 99% rename from apps/android/app/src/main/java/chat/simplex/app/model/SimpleXAPI.kt rename to apps/multiplatform/android/src/main/java/chat/simplex/app/model/SimpleXAPI.kt index e1fbbaa0ce..c55f697048 100644 --- a/apps/android/app/src/main/java/chat/simplex/app/model/SimpleXAPI.kt +++ b/apps/multiplatform/android/src/main/java/chat/simplex/app/model/SimpleXAPI.kt @@ -58,9 +58,9 @@ enum class SimplexLinkMode { } } -class AppPreferences(val context: Context) { - private val sharedPreferences: SharedPreferences = context.getSharedPreferences(SHARED_PREFS_ID, Context.MODE_PRIVATE) - private val sharedPreferencesThemes: SharedPreferences = context.getSharedPreferences(SHARED_PREFS_THEMES_ID, Context.MODE_PRIVATE) +class AppPreferences { + private val sharedPreferences: SharedPreferences = SimplexApp.context.getSharedPreferences(SHARED_PREFS_ID, Context.MODE_PRIVATE) + private val sharedPreferencesThemes: SharedPreferences = SimplexApp.context.getSharedPreferences(SHARED_PREFS_THEMES_ID, Context.MODE_PRIVATE) // deprecated, remove in 2024 private val runServiceInBackground = mkBoolPreference(SHARED_PREFS_RUN_SERVICE_IN_BACKGROUND, true) @@ -298,21 +298,16 @@ class AppPreferences(val context: Context) { private const val MESSAGE_TIMEOUT: Int = 15_000_000 -open class ChatController(var ctrl: ChatCtrl?, val ntfManager: NtfManager, val appContext: Context, val appPrefs: AppPreferences) { - val chatModel = ChatModel(this) +object ChatController { + var ctrl: ChatCtrl? = -1 + val appPrefs: AppPreferences by lazy { AppPreferences() } + val ntfManager by lazy { NtfManager } + + val chatModel = ChatModel private var receiverStarted = false var lastMsgReceivedTimestamp: Long = System.currentTimeMillis() private set - init { - chatModel.notificationsMode.value = - kotlin.runCatching { NotificationsMode.valueOf(appPrefs.notificationsMode.get()!!) }.getOrDefault(NotificationsMode.default) - chatModel.notificationPreviewMode.value = - kotlin.runCatching { NotificationPreviewMode.valueOf(appPrefs.notificationPreviewMode.get()!!) }.getOrDefault(NotificationPreviewMode.default) - chatModel.performLA.value = appPrefs.performLA.get() - chatModel.incognito.value = appPrefs.incognito.get() - } - private fun currentUserId(funcName: String): Long { val userId = chatModel.currentUser.value?.userId if (userId == null) { @@ -328,8 +323,8 @@ open class ChatController(var ctrl: ChatCtrl?, val ntfManager: NtfManager, val a try { if (chatModel.chatRunning.value == true) return apiSetNetworkConfig(getNetCfg()) - apiSetTempFolder(getTempFilesDirectory(appContext)) - apiSetFilesFolder(getAppFilesDirectory(appContext)) + apiSetTempFolder(getTempFilesDirectory()) + apiSetFilesFolder(getAppFilesDirectory()) apiSetXFTPConfig(getXFTPCfg()) val justStarted = apiStartChat() val users = listUsers() @@ -1602,7 +1597,7 @@ open class ChatController(var ctrl: ChatCtrl?, val ntfManager: NtfManager, val a mc is MsgContent.MCFile && fileName != null ) { - removeFile(appContext, fileName) + removeFile(fileName) } } @@ -1671,7 +1666,7 @@ open class ChatController(var ctrl: ChatCtrl?, val ntfManager: NtfManager, val a if (!appPrefs.backgroundServiceNoticeShown.get()) { // the branch for the new users who have never seen service notice - if (!mode.requiresIgnoringBattery || isIgnoringBatteryOptimizations(appContext)) { + if (!mode.requiresIgnoringBattery || isIgnoringBatteryOptimizations()) { showBGServiceNotice(mode) } else { showBGServiceNoticeIgnoreOptimization(mode) @@ -1679,7 +1674,7 @@ open class ChatController(var ctrl: ChatCtrl?, val ntfManager: NtfManager, val a // set both flags, so that if the user doesn't allow ignoring optimizations, the service will be disabled without additional notice appPrefs.backgroundServiceNoticeShown.set(true) appPrefs.backgroundServiceBatteryNoticeShown.set(true) - } else if (mode.requiresIgnoringBattery && !isIgnoringBatteryOptimizations(appContext)) { + } else if (mode.requiresIgnoringBattery && !isIgnoringBatteryOptimizations()) { // the branch for users who have app installed, and have seen the service notice, // but the battery optimization for the app is on (Android 12) AND the service is running if (appPrefs.backgroundServiceBatteryNoticeShown.get()) { @@ -1738,7 +1733,7 @@ open class ChatController(var ctrl: ChatCtrl?, val ntfManager: NtfManager, val a private fun showBGServiceNoticeIgnoreOptimization(mode: NotificationsMode) = AlertManager.shared.showAlert { val ignoreOptimization = { AlertManager.shared.hideAlert() - askAboutIgnoringBatteryOptimization(appContext) + askAboutIgnoringBatteryOptimization() } AlertDialog( onDismissRequest = ignoreOptimization, @@ -1800,19 +1795,19 @@ open class ChatController(var ctrl: ChatCtrl?, val ntfManager: NtfManager, val a ) } - fun isIgnoringBatteryOptimizations(context: Context): Boolean { - val powerManager = context.getSystemService(Application.POWER_SERVICE) as PowerManager - return powerManager.isIgnoringBatteryOptimizations(context.packageName) + fun isIgnoringBatteryOptimizations(): Boolean { + val powerManager = SimplexApp.context.getSystemService(Application.POWER_SERVICE) as PowerManager + return powerManager.isIgnoringBatteryOptimizations(SimplexApp.context.packageName) } - private fun askAboutIgnoringBatteryOptimization(context: Context) { + private fun askAboutIgnoringBatteryOptimization() { Intent().apply { @SuppressLint("BatteryLife") action = Settings.ACTION_REQUEST_IGNORE_BATTERY_OPTIMIZATIONS - data = Uri.parse("package:${context.packageName}") + data = Uri.parse("package:${SimplexApp.context.packageName}") // This flag is needed when you start a new activity from non-Activity context addFlags(Intent.FLAG_ACTIVITY_NEW_TASK) - context.startActivity(this) + SimplexApp.context.startActivity(this) } } diff --git a/apps/android/app/src/main/java/chat/simplex/app/ui/theme/Color.kt b/apps/multiplatform/android/src/main/java/chat/simplex/app/ui/theme/Color.kt similarity index 100% rename from apps/android/app/src/main/java/chat/simplex/app/ui/theme/Color.kt rename to apps/multiplatform/android/src/main/java/chat/simplex/app/ui/theme/Color.kt diff --git a/apps/android/app/src/main/java/chat/simplex/app/ui/theme/Shape.kt b/apps/multiplatform/android/src/main/java/chat/simplex/app/ui/theme/Shape.kt similarity index 100% rename from apps/android/app/src/main/java/chat/simplex/app/ui/theme/Shape.kt rename to apps/multiplatform/android/src/main/java/chat/simplex/app/ui/theme/Shape.kt diff --git a/apps/android/app/src/main/java/chat/simplex/app/ui/theme/Theme.kt b/apps/multiplatform/android/src/main/java/chat/simplex/app/ui/theme/Theme.kt similarity index 98% rename from apps/android/app/src/main/java/chat/simplex/app/ui/theme/Theme.kt rename to apps/multiplatform/android/src/main/java/chat/simplex/app/ui/theme/Theme.kt index 24652c76ff..8f1ec7eab3 100644 --- a/apps/android/app/src/main/java/chat/simplex/app/ui/theme/Theme.kt +++ b/apps/multiplatform/android/src/main/java/chat/simplex/app/ui/theme/Theme.kt @@ -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 ( diff --git a/apps/android/app/src/main/java/chat/simplex/app/ui/theme/ThemeManager.kt b/apps/multiplatform/android/src/main/java/chat/simplex/app/ui/theme/ThemeManager.kt similarity index 98% rename from apps/android/app/src/main/java/chat/simplex/app/ui/theme/ThemeManager.kt rename to apps/multiplatform/android/src/main/java/chat/simplex/app/ui/theme/ThemeManager.kt index 3453dad7e9..b7df77b0ae 100644 --- a/apps/android/app/src/main/java/chat/simplex/app/ui/theme/ThemeManager.kt +++ b/apps/multiplatform/android/src/main/java/chat/simplex/app/ui/theme/ThemeManager.kt @@ -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()) diff --git a/apps/android/app/src/main/java/chat/simplex/app/ui/theme/Type.kt b/apps/multiplatform/android/src/main/java/chat/simplex/app/ui/theme/Type.kt similarity index 100% rename from apps/android/app/src/main/java/chat/simplex/app/ui/theme/Type.kt rename to apps/multiplatform/android/src/main/java/chat/simplex/app/ui/theme/Type.kt diff --git a/apps/android/app/src/main/java/chat/simplex/app/views/SplashView.kt b/apps/multiplatform/android/src/main/java/chat/simplex/app/views/SplashView.kt similarity index 100% rename from apps/android/app/src/main/java/chat/simplex/app/views/SplashView.kt rename to apps/multiplatform/android/src/main/java/chat/simplex/app/views/SplashView.kt diff --git a/apps/android/app/src/main/java/chat/simplex/app/views/TerminalView.kt b/apps/multiplatform/android/src/main/java/chat/simplex/app/views/TerminalView.kt similarity index 99% rename from apps/android/app/src/main/java/chat/simplex/app/views/TerminalView.kt rename to apps/multiplatform/android/src/main/java/chat/simplex/app/views/TerminalView.kt index cb00859314..264c243ebd 100644 --- a/apps/android/app/src/main/java/chat/simplex/app/views/TerminalView.kt +++ b/apps/multiplatform/android/src/main/java/chat/simplex/app/views/TerminalView.kt @@ -128,7 +128,7 @@ fun TerminalLog(terminalItems: List) { modifier = Modifier .fillMaxWidth() .clickable { - ModalManager.shared.showModal(endButtons = { ShareButton { shareText(context, item.details) } }) { + ModalManager.shared.showModal(endButtons = { ShareButton { shareText(item.details) } }) { SelectionContainer(modifier = Modifier.verticalScroll(rememberScrollState())) { Text(item.details, modifier = Modifier.padding(horizontal = DEFAULT_PADDING).padding(bottom = DEFAULT_PADDING)) } diff --git a/apps/android/app/src/main/java/chat/simplex/app/views/WelcomeView.kt b/apps/multiplatform/android/src/main/java/chat/simplex/app/views/WelcomeView.kt similarity index 100% rename from apps/android/app/src/main/java/chat/simplex/app/views/WelcomeView.kt rename to apps/multiplatform/android/src/main/java/chat/simplex/app/views/WelcomeView.kt diff --git a/apps/android/app/src/main/java/chat/simplex/app/views/call/CallManager.kt b/apps/multiplatform/android/src/main/java/chat/simplex/app/views/call/CallManager.kt similarity index 100% rename from apps/android/app/src/main/java/chat/simplex/app/views/call/CallManager.kt rename to apps/multiplatform/android/src/main/java/chat/simplex/app/views/call/CallManager.kt diff --git a/apps/android/app/src/main/java/chat/simplex/app/views/call/CallView.kt b/apps/multiplatform/android/src/main/java/chat/simplex/app/views/call/CallView.kt similarity index 100% rename from apps/android/app/src/main/java/chat/simplex/app/views/call/CallView.kt rename to apps/multiplatform/android/src/main/java/chat/simplex/app/views/call/CallView.kt diff --git a/apps/android/app/src/main/java/chat/simplex/app/views/call/IncomingCallActivity.kt b/apps/multiplatform/android/src/main/java/chat/simplex/app/views/call/IncomingCallActivity.kt similarity index 99% rename from apps/android/app/src/main/java/chat/simplex/app/views/call/IncomingCallActivity.kt rename to apps/multiplatform/android/src/main/java/chat/simplex/app/views/call/IncomingCallActivity.kt index d5282f3c37..5708759ca9 100644 --- a/apps/android/app/src/main/java/chat/simplex/app/views/call/IncomingCallActivity.kt +++ b/apps/multiplatform/android/src/main/java/chat/simplex/app/views/call/IncomingCallActivity.kt @@ -33,7 +33,7 @@ import androidx.compose.ui.unit.sp import chat.simplex.app.* import chat.simplex.app.R import chat.simplex.app.model.* -import chat.simplex.app.model.NtfManager.Companion.OpenChatAction +import chat.simplex.app.model.NtfManager.OpenChatAction import chat.simplex.app.ui.theme.* import chat.simplex.app.views.helpers.ProfileImage import kotlinx.datetime.Clock diff --git a/apps/android/app/src/main/java/chat/simplex/app/views/call/IncomingCallAlertView.kt b/apps/multiplatform/android/src/main/java/chat/simplex/app/views/call/IncomingCallAlertView.kt similarity index 97% rename from apps/android/app/src/main/java/chat/simplex/app/views/call/IncomingCallAlertView.kt rename to apps/multiplatform/android/src/main/java/chat/simplex/app/views/call/IncomingCallAlertView.kt index 6afe1b3492..a8548fadac 100644 --- a/apps/android/app/src/main/java/chat/simplex/app/views/call/IncomingCallAlertView.kt +++ b/apps/multiplatform/android/src/main/java/chat/simplex/app/views/call/IncomingCallAlertView.kt @@ -28,7 +28,7 @@ fun IncomingCallAlertView(invitation: RcvCallInvitation, chatModel: ChatModel) { val cm = chatModel.callManager val cxt = LocalContext.current val scope = rememberCoroutineScope() - LaunchedEffect(true) { SoundPlayer.shared.start(cxt, scope, sound = !chatModel.showCallView.value) } + LaunchedEffect(true) { SoundPlayer.shared.start(scope, sound = !chatModel.showCallView.value) } DisposableEffect(true) { onDispose { SoundPlayer.shared.stop() } } IncomingCallAlertLayout( invitation, diff --git a/apps/android/app/src/main/java/chat/simplex/app/views/call/SoundPlayer.kt b/apps/multiplatform/android/src/main/java/chat/simplex/app/views/call/SoundPlayer.kt similarity index 89% rename from apps/android/app/src/main/java/chat/simplex/app/views/call/SoundPlayer.kt rename to apps/multiplatform/android/src/main/java/chat/simplex/app/views/call/SoundPlayer.kt index 456f50d0bd..314032984a 100644 --- a/apps/android/app/src/main/java/chat/simplex/app/views/call/SoundPlayer.kt +++ b/apps/multiplatform/android/src/main/java/chat/simplex/app/views/call/SoundPlayer.kt @@ -16,7 +16,7 @@ class SoundPlayer { private var player: MediaPlayer? = null var playing = false - fun start(cxt: Context, scope: CoroutineScope, sound: Boolean) { + fun start(scope: CoroutineScope, sound: Boolean) { player?.reset() player = MediaPlayer().apply { setAudioAttributes( @@ -28,7 +28,7 @@ class SoundPlayer { setDataSource(SimplexApp.context, Uri.parse("android.resource://" + SimplexApp.context.packageName + "/" + R.raw.ring_once)) prepare() } - val vibrator = ContextCompat.getSystemService(cxt, Vibrator::class.java) + val vibrator = ContextCompat.getSystemService(SimplexApp.context, Vibrator::class.java) val effect = VibrationEffect.createOneShot(250, VibrationEffect.DEFAULT_AMPLITUDE) playing = true withScope(scope) { diff --git a/apps/android/app/src/main/java/chat/simplex/app/views/call/WebRTC.kt b/apps/multiplatform/android/src/main/java/chat/simplex/app/views/call/WebRTC.kt similarity index 100% rename from apps/android/app/src/main/java/chat/simplex/app/views/call/WebRTC.kt rename to apps/multiplatform/android/src/main/java/chat/simplex/app/views/call/WebRTC.kt diff --git a/apps/android/app/src/main/java/chat/simplex/app/views/chat/ChatInfoView.kt b/apps/multiplatform/android/src/main/java/chat/simplex/app/views/chat/ChatInfoView.kt similarity index 99% rename from apps/android/app/src/main/java/chat/simplex/app/views/chat/ChatInfoView.kt rename to apps/multiplatform/android/src/main/java/chat/simplex/app/views/chat/ChatInfoView.kt index 75edc77c05..2c105155a9 100644 --- a/apps/android/app/src/main/java/chat/simplex/app/views/chat/ChatInfoView.kt +++ b/apps/multiplatform/android/src/main/java/chat/simplex/app/views/chat/ChatInfoView.kt @@ -211,10 +211,9 @@ fun ChatInfoLayout( SectionDividerSpaced() if (contact.contactLink != null) { - val context = LocalContext.current SectionView(stringResource(R.string.address_section_title).uppercase()) { QRCode(contact.contactLink, Modifier.padding(horizontal = DEFAULT_PADDING, vertical = DEFAULT_PADDING_HALF).aspectRatio(1f)) - ShareAddressButton { shareText(context, contact.contactLink) } + ShareAddressButton { shareText(contact.contactLink) } SectionTextFooter(stringResource(R.string.you_can_share_this_address_with_your_contacts).format(contact.displayName)) } SectionDividerSpaced() diff --git a/apps/android/app/src/main/java/chat/simplex/app/views/chat/ChatItemInfoView.kt b/apps/multiplatform/android/src/main/java/chat/simplex/app/views/chat/ChatItemInfoView.kt similarity index 97% rename from apps/android/app/src/main/java/chat/simplex/app/views/chat/ChatItemInfoView.kt rename to apps/multiplatform/android/src/main/java/chat/simplex/app/views/chat/ChatItemInfoView.kt index 8ef98933da..a6dcaa97be 100644 --- a/apps/android/app/src/main/java/chat/simplex/app/views/chat/ChatItemInfoView.kt +++ b/apps/multiplatform/android/src/main/java/chat/simplex/app/views/chat/ChatItemInfoView.kt @@ -12,7 +12,6 @@ import androidx.compose.material.Text import androidx.compose.runtime.* import androidx.compose.ui.Modifier import androidx.compose.ui.draw.clip -import androidx.compose.ui.platform.LocalContext import androidx.compose.ui.platform.LocalUriHandler import androidx.compose.ui.res.painterResource import androidx.compose.ui.res.stringResource @@ -32,7 +31,6 @@ fun ChatItemInfoView(ci: ChatItem, ciInfo: ChatItemInfo, devTools: Boolean) { val sent = ci.chatDir.sent val appColors = CurrentColors.collectAsState().value.appColors val itemColor = if (sent) appColors.sentMessage else appColors.receivedMessage - val context = LocalContext.current val uriHandler = LocalUriHandler.current @Composable @@ -83,11 +81,11 @@ fun ChatItemInfoView(ci: ChatItem, ciInfo: ChatItemInfo, devTools: Boolean) { if (text != "") { DefaultDropdownMenu(showMenu) { ItemAction(stringResource(R.string.share_verb), painterResource(R.drawable.ic_share), onClick = { - shareText(context, text) + shareText(text) showMenu.value = false }) ItemAction(stringResource(R.string.copy_verb), painterResource(R.drawable.ic_content_copy), onClick = { - copyText(context, text) + copyText(text) showMenu.value = false }) } diff --git a/apps/android/app/src/main/java/chat/simplex/app/views/chat/ChatView.kt b/apps/multiplatform/android/src/main/java/chat/simplex/app/views/chat/ChatView.kt similarity index 99% rename from apps/android/app/src/main/java/chat/simplex/app/views/chat/ChatView.kt rename to apps/multiplatform/android/src/main/java/chat/simplex/app/views/chat/ChatView.kt index 7681b79b73..809f529844 100644 --- a/apps/android/app/src/main/java/chat/simplex/app/views/chat/ChatView.kt +++ b/apps/multiplatform/android/src/main/java/chat/simplex/app/views/chat/ChatView.kt @@ -278,7 +278,7 @@ fun ChatView(chatId: String, chatModel: ChatModel, onComposed: () -> Unit) { val ciInfo = chatModel.controller.apiGetChatItemInfo(cInfo.chatType, cInfo.apiId, cItem.id) if (ciInfo != null) { ModalManager.shared.showModal(endButtons = { ShareButton { - shareText(context, itemInfoShareText(cItem, ciInfo, chatModel.controller.appPrefs.developerTools.get())) + shareText(itemInfoShareText(cItem, ciInfo, chatModel.controller.appPrefs.developerTools.get())) } }) { ChatItemInfoView(cItem, ciInfo, devTools = chatModel.controller.appPrefs.developerTools.get()) } @@ -976,7 +976,7 @@ private fun providerForGallery( scrollTo: (Int) -> Unit ): ImageGalleryProvider { fun canShowMedia(item: ChatItem): Boolean = - (item.content.msgContent is MsgContent.MCImage || item.content.msgContent is MsgContent.MCVideo) && (item.file?.loaded == true && getLoadedFilePath(SimplexApp.context, item.file) != null) + (item.content.msgContent is MsgContent.MCImage || item.content.msgContent is MsgContent.MCVideo) && (item.file?.loaded == true && getLoadedFilePath(item.file) != null) fun item(skipInternalIndex: Int, initialChatId: Long): Pair? { var processedInternalIndex = -skipInternalIndex.sign @@ -1003,15 +1003,15 @@ private fun providerForGallery( val item = item(internalIndex, initialChatId)?.second ?: return null return when (item.content.msgContent) { is MsgContent.MCImage -> { - val imageBitmap: Bitmap? = getLoadedImage(SimplexApp.context, item.file) - val filePath = getLoadedFilePath(SimplexApp.context, item.file) + val imageBitmap: Bitmap? = getLoadedImage(item.file) + val filePath = getLoadedFilePath(item.file) if (imageBitmap != null && filePath != null) { val uri = FileProvider.getUriForFile(SimplexApp.context, "${BuildConfig.APPLICATION_ID}.provider", File(filePath)) ProviderMedia.Image(uri, imageBitmap) } else null } is MsgContent.MCVideo -> { - val filePath = getLoadedFilePath(SimplexApp.context, item.file) + val filePath = getLoadedFilePath(item.file) if (filePath != null) { val uri = FileProvider.getUriForFile(SimplexApp.context, "${BuildConfig.APPLICATION_ID}.provider", File(filePath)) ProviderMedia.Video(uri, (item.content.msgContent as MsgContent.MCVideo).image) diff --git a/apps/android/app/src/main/java/chat/simplex/app/views/chat/ComposeFileView.kt b/apps/multiplatform/android/src/main/java/chat/simplex/app/views/chat/ComposeFileView.kt similarity index 100% rename from apps/android/app/src/main/java/chat/simplex/app/views/chat/ComposeFileView.kt rename to apps/multiplatform/android/src/main/java/chat/simplex/app/views/chat/ComposeFileView.kt diff --git a/apps/android/app/src/main/java/chat/simplex/app/views/chat/ComposeImageView.kt b/apps/multiplatform/android/src/main/java/chat/simplex/app/views/chat/ComposeImageView.kt similarity index 100% rename from apps/android/app/src/main/java/chat/simplex/app/views/chat/ComposeImageView.kt rename to apps/multiplatform/android/src/main/java/chat/simplex/app/views/chat/ComposeImageView.kt diff --git a/apps/android/app/src/main/java/chat/simplex/app/views/chat/ComposeView.kt b/apps/multiplatform/android/src/main/java/chat/simplex/app/views/chat/ComposeView.kt similarity index 97% rename from apps/android/app/src/main/java/chat/simplex/app/views/chat/ComposeView.kt rename to apps/multiplatform/android/src/main/java/chat/simplex/app/views/chat/ComposeView.kt index 189a8e4ad9..8b43cb39ad 100644 --- a/apps/android/app/src/main/java/chat/simplex/app/views/chat/ComposeView.kt +++ b/apps/multiplatform/android/src/main/java/chat/simplex/app/views/chat/ComposeView.kt @@ -201,7 +201,7 @@ fun ComposeView( val imagesPreview = ArrayList() uris.forEach { uri -> var bitmap: Bitmap? = null - val isImage = MimeTypeMap.getSingleton().getMimeTypeFromExtension(getFileName(SimplexApp.context, uri)?.split(".")?.last())?.contains("image/") == true + val isImage = MimeTypeMap.getSingleton().getMimeTypeFromExtension(getFileName(uri)?.split(".")?.last())?.contains("image/") == true when { isImage -> { // Image @@ -209,10 +209,10 @@ fun ComposeView( bitmap = if (drawable != null) getBitmapFromUri(uri) else null val isAnimNewApi = Build.VERSION.SDK_INT >= 28 && drawable is AnimatedImageDrawable val isAnimOldApi = Build.VERSION.SDK_INT < 28 && - (getFileName(SimplexApp.context, uri)?.endsWith(".gif") == true || getFileName(SimplexApp.context, uri)?.endsWith(".webp") == true) + (getFileName(uri)?.endsWith(".gif") == true || getFileName(uri)?.endsWith(".webp") == true) if (isAnimNewApi || isAnimOldApi) { // It's a gif or webp - val fileSize = getFileSize(context, uri) + val fileSize = getFileSize(uri) if (fileSize != null && fileSize <= maxFileSize) { content.add(UploadContent.AnimatedImage(uri)) } else { @@ -244,9 +244,9 @@ fun ComposeView( } val processPickedFile = { uri: Uri?, text: String? -> if (uri != null) { - val fileSize = getFileSize(context, uri) + val fileSize = getFileSize(uri) if (fileSize != null && fileSize <= maxFileSize) { - val fileName = getFileName(SimplexApp.context, uri) + val fileName = getFileName(uri) if (fileName != null) { composeState.value = composeState.value.copy(message = text ?: composeState.value.message, preview = ComposePreview.FilePreview(fileName, uri)) } @@ -381,7 +381,7 @@ fun ComposeView( chatModel.addChatItem(cInfo, aChatItem.chatItem) return aChatItem.chatItem } - if (file != null) removeFile(context, file) + if (file != null) removeFile(file) return null } @@ -460,9 +460,9 @@ fun ComposeView( is ComposePreview.MediaPreview -> { preview.content.forEachIndexed { index, it -> val file = when (it) { - is UploadContent.SimpleImage -> saveImage(context, it.uri) - is UploadContent.AnimatedImage -> saveAnimImage(context, it.uri) - is UploadContent.Video -> saveFileFromUri(context, it.uri) + is UploadContent.SimpleImage -> saveImage(it.uri) + is UploadContent.AnimatedImage -> saveAnimImage(it.uri) + is UploadContent.Video -> saveFileFromUri(it.uri) } if (file != null) { files.add(file) @@ -477,7 +477,7 @@ fun ComposeView( is ComposePreview.VoicePreview -> { val tmpFile = File(preview.voice) AudioPlayer.stop(tmpFile.absolutePath) - val actualFile = File(getAppFilePath(SimplexApp.context, tmpFile.name.replaceAfter(RecorderNative.extension, ""))) + val actualFile = File(getAppFilePath(tmpFile.name.replaceAfter(RecorderNative.extension, ""))) withContext(Dispatchers.IO) { Files.move(tmpFile.toPath(), actualFile.toPath()) } @@ -486,7 +486,7 @@ fun ComposeView( msgs.add(MsgContent.MCVoice(if (msgs.isEmpty()) msgText else "", preview.durationMs / 1000)) } is ComposePreview.FilePreview -> { - val file = saveFileFromUri(context, preview.uri) + val file = saveFileFromUri(preview.uri) if (file != null) { files.add((file)) msgs.add(MsgContent.MCFile(if (msgs.isEmpty()) msgText else "")) @@ -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)) diff --git a/apps/android/app/src/main/java/chat/simplex/app/views/chat/ComposeVoiceView.kt b/apps/multiplatform/android/src/main/java/chat/simplex/app/views/chat/ComposeVoiceView.kt similarity index 100% rename from apps/android/app/src/main/java/chat/simplex/app/views/chat/ComposeVoiceView.kt rename to apps/multiplatform/android/src/main/java/chat/simplex/app/views/chat/ComposeVoiceView.kt diff --git a/apps/android/app/src/main/java/chat/simplex/app/views/chat/ContactPreferences.kt b/apps/multiplatform/android/src/main/java/chat/simplex/app/views/chat/ContactPreferences.kt similarity index 100% rename from apps/android/app/src/main/java/chat/simplex/app/views/chat/ContactPreferences.kt rename to apps/multiplatform/android/src/main/java/chat/simplex/app/views/chat/ContactPreferences.kt diff --git a/apps/android/app/src/main/java/chat/simplex/app/views/chat/ContextItemView.kt b/apps/multiplatform/android/src/main/java/chat/simplex/app/views/chat/ContextItemView.kt similarity index 100% rename from apps/android/app/src/main/java/chat/simplex/app/views/chat/ContextItemView.kt rename to apps/multiplatform/android/src/main/java/chat/simplex/app/views/chat/ContextItemView.kt diff --git a/apps/android/app/src/main/java/chat/simplex/app/views/chat/ScanCodeView.kt b/apps/multiplatform/android/src/main/java/chat/simplex/app/views/chat/ScanCodeView.kt similarity index 100% rename from apps/android/app/src/main/java/chat/simplex/app/views/chat/ScanCodeView.kt rename to apps/multiplatform/android/src/main/java/chat/simplex/app/views/chat/ScanCodeView.kt diff --git a/apps/android/app/src/main/java/chat/simplex/app/views/chat/SendMsgView.kt b/apps/multiplatform/android/src/main/java/chat/simplex/app/views/chat/SendMsgView.kt similarity index 100% rename from apps/android/app/src/main/java/chat/simplex/app/views/chat/SendMsgView.kt rename to apps/multiplatform/android/src/main/java/chat/simplex/app/views/chat/SendMsgView.kt diff --git a/apps/android/app/src/main/java/chat/simplex/app/views/chat/VerifyCodeView.kt b/apps/multiplatform/android/src/main/java/chat/simplex/app/views/chat/VerifyCodeView.kt similarity index 95% rename from apps/android/app/src/main/java/chat/simplex/app/views/chat/VerifyCodeView.kt rename to apps/multiplatform/android/src/main/java/chat/simplex/app/views/chat/VerifyCodeView.kt index 12e8ab1aa9..472883d556 100644 --- a/apps/android/app/src/main/java/chat/simplex/app/views/chat/VerifyCodeView.kt +++ b/apps/multiplatform/android/src/main/java/chat/simplex/app/views/chat/VerifyCodeView.kt @@ -10,7 +10,6 @@ import androidx.compose.material.* import androidx.compose.runtime.* import androidx.compose.ui.Alignment import androidx.compose.ui.Modifier -import androidx.compose.ui.platform.LocalContext import androidx.compose.ui.res.painterResource import androidx.compose.ui.res.stringResource import androidx.compose.ui.text.font.FontFamily @@ -86,9 +85,8 @@ private fun VerifyCodeLayout( maxLines = 20 ) } - val context = LocalContext.current Box(Modifier.weight(1f)) { - IconButton({ shareText(context, connectionCode) }, Modifier.size(20.dp).align(Alignment.CenterStart)) { + IconButton({ shareText(connectionCode) }, Modifier.size(20.dp).align(Alignment.CenterStart)) { Icon(painterResource(R.drawable.ic_share_filled), null, tint = MaterialTheme.colors.primary) } } diff --git a/apps/android/app/src/main/java/chat/simplex/app/views/chat/group/AddGroupMembersView.kt b/apps/multiplatform/android/src/main/java/chat/simplex/app/views/chat/group/AddGroupMembersView.kt similarity index 100% rename from apps/android/app/src/main/java/chat/simplex/app/views/chat/group/AddGroupMembersView.kt rename to apps/multiplatform/android/src/main/java/chat/simplex/app/views/chat/group/AddGroupMembersView.kt diff --git a/apps/android/app/src/main/java/chat/simplex/app/views/chat/group/GroupChatInfoView.kt b/apps/multiplatform/android/src/main/java/chat/simplex/app/views/chat/group/GroupChatInfoView.kt similarity index 100% rename from apps/android/app/src/main/java/chat/simplex/app/views/chat/group/GroupChatInfoView.kt rename to apps/multiplatform/android/src/main/java/chat/simplex/app/views/chat/group/GroupChatInfoView.kt diff --git a/apps/android/app/src/main/java/chat/simplex/app/views/chat/group/GroupLinkView.kt b/apps/multiplatform/android/src/main/java/chat/simplex/app/views/chat/group/GroupLinkView.kt similarity index 97% rename from apps/android/app/src/main/java/chat/simplex/app/views/chat/group/GroupLinkView.kt rename to apps/multiplatform/android/src/main/java/chat/simplex/app/views/chat/group/GroupLinkView.kt index 42a893ce99..45ccb4ca56 100644 --- a/apps/android/app/src/main/java/chat/simplex/app/views/chat/group/GroupLinkView.kt +++ b/apps/multiplatform/android/src/main/java/chat/simplex/app/views/chat/group/GroupLinkView.kt @@ -10,7 +10,6 @@ import androidx.compose.runtime.saveable.rememberSaveable import androidx.compose.ui.Alignment import androidx.compose.ui.Modifier import androidx.compose.ui.graphics.Color -import androidx.compose.ui.platform.LocalContext import androidx.compose.ui.res.painterResource import androidx.compose.ui.res.stringResource import androidx.compose.ui.unit.dp @@ -26,7 +25,6 @@ fun GroupLinkView(chatModel: ChatModel, groupInfo: GroupInfo, connReqContact: St var groupLink by rememberSaveable { mutableStateOf(connReqContact) } val groupLinkMemberRole = rememberSaveable { mutableStateOf(memberRole) } var creatingLink by rememberSaveable { mutableStateOf(false) } - val cxt = LocalContext.current fun createLink() { creatingLink = true withApi { @@ -50,7 +48,7 @@ fun GroupLinkView(chatModel: ChatModel, groupInfo: GroupInfo, connReqContact: St groupLinkMemberRole, creatingLink, createLink = ::createLink, - share = { shareText(cxt, groupLink ?: return@GroupLinkLayout) }, + share = { shareText(groupLink ?: return@GroupLinkLayout) }, updateLink = { val role = groupLinkMemberRole.value if (role != null) { diff --git a/apps/android/app/src/main/java/chat/simplex/app/views/chat/group/GroupMemberInfoView.kt b/apps/multiplatform/android/src/main/java/chat/simplex/app/views/chat/group/GroupMemberInfoView.kt similarity index 99% rename from apps/android/app/src/main/java/chat/simplex/app/views/chat/group/GroupMemberInfoView.kt rename to apps/multiplatform/android/src/main/java/chat/simplex/app/views/chat/group/GroupMemberInfoView.kt index 1ed56a8874..79a8162892 100644 --- a/apps/android/app/src/main/java/chat/simplex/app/views/chat/group/GroupMemberInfoView.kt +++ b/apps/multiplatform/android/src/main/java/chat/simplex/app/views/chat/group/GroupMemberInfoView.kt @@ -220,7 +220,7 @@ fun GroupMemberInfoLayout( val context = LocalContext.current SectionView(stringResource(R.string.address_section_title).uppercase()) { QRCode(member.contactLink, Modifier.padding(horizontal = DEFAULT_PADDING, vertical = DEFAULT_PADDING_HALF).aspectRatio(1f)) - ShareAddressButton { shareText(context, member.contactLink) } + ShareAddressButton { shareText(member.contactLink) } if (contactId != null) { if (knownDirectChat(contactId) == null && !groupInfo.fullGroupPreferences.directMessages.on) { ConnectViaAddressButton(onClick = { connectViaAddress(member.contactLink) }) diff --git a/apps/android/app/src/main/java/chat/simplex/app/views/chat/group/GroupPreferences.kt b/apps/multiplatform/android/src/main/java/chat/simplex/app/views/chat/group/GroupPreferences.kt similarity index 100% rename from apps/android/app/src/main/java/chat/simplex/app/views/chat/group/GroupPreferences.kt rename to apps/multiplatform/android/src/main/java/chat/simplex/app/views/chat/group/GroupPreferences.kt diff --git a/apps/android/app/src/main/java/chat/simplex/app/views/chat/group/GroupProfileView.kt b/apps/multiplatform/android/src/main/java/chat/simplex/app/views/chat/group/GroupProfileView.kt similarity index 100% rename from apps/android/app/src/main/java/chat/simplex/app/views/chat/group/GroupProfileView.kt rename to apps/multiplatform/android/src/main/java/chat/simplex/app/views/chat/group/GroupProfileView.kt diff --git a/apps/android/app/src/main/java/chat/simplex/app/views/chat/group/WelcomeMessageView.kt b/apps/multiplatform/android/src/main/java/chat/simplex/app/views/chat/group/WelcomeMessageView.kt similarity index 97% rename from apps/android/app/src/main/java/chat/simplex/app/views/chat/group/WelcomeMessageView.kt rename to apps/multiplatform/android/src/main/java/chat/simplex/app/views/chat/group/WelcomeMessageView.kt index db3fd4daa1..91a999c6f7 100644 --- a/apps/android/app/src/main/java/chat/simplex/app/views/chat/group/WelcomeMessageView.kt +++ b/apps/multiplatform/android/src/main/java/chat/simplex/app/views/chat/group/WelcomeMessageView.kt @@ -98,7 +98,7 @@ private fun GroupWelcomeLayout( }, wt.value.isEmpty() ) - CopyTextButton { copyText(SimplexApp.context, wt.value) } + CopyTextButton { copyText(wt.value) } SectionDividerSpaced(maxBottomPadding = false) SaveButton( save = save, @@ -106,7 +106,7 @@ private fun GroupWelcomeLayout( ) } else { TextPreview(wt.value, linkMode) - CopyTextButton { copyText(SimplexApp.context, wt.value) } + CopyTextButton { copyText(wt.value) } } SectionBottomSpacer() } diff --git a/apps/android/app/src/main/java/chat/simplex/app/views/chat/item/CICallItemView.kt b/apps/multiplatform/android/src/main/java/chat/simplex/app/views/chat/item/CICallItemView.kt similarity index 100% rename from apps/android/app/src/main/java/chat/simplex/app/views/chat/item/CICallItemView.kt rename to apps/multiplatform/android/src/main/java/chat/simplex/app/views/chat/item/CICallItemView.kt diff --git a/apps/android/app/src/main/java/chat/simplex/app/views/chat/item/CIChatFeatureView.kt b/apps/multiplatform/android/src/main/java/chat/simplex/app/views/chat/item/CIChatFeatureView.kt similarity index 100% rename from apps/android/app/src/main/java/chat/simplex/app/views/chat/item/CIChatFeatureView.kt rename to apps/multiplatform/android/src/main/java/chat/simplex/app/views/chat/item/CIChatFeatureView.kt diff --git a/apps/android/app/src/main/java/chat/simplex/app/views/chat/item/CIEventView.kt b/apps/multiplatform/android/src/main/java/chat/simplex/app/views/chat/item/CIEventView.kt similarity index 100% rename from apps/android/app/src/main/java/chat/simplex/app/views/chat/item/CIEventView.kt rename to apps/multiplatform/android/src/main/java/chat/simplex/app/views/chat/item/CIEventView.kt diff --git a/apps/android/app/src/main/java/chat/simplex/app/views/chat/item/CIFeaturePreferenceView.kt b/apps/multiplatform/android/src/main/java/chat/simplex/app/views/chat/item/CIFeaturePreferenceView.kt similarity index 100% rename from apps/android/app/src/main/java/chat/simplex/app/views/chat/item/CIFeaturePreferenceView.kt rename to apps/multiplatform/android/src/main/java/chat/simplex/app/views/chat/item/CIFeaturePreferenceView.kt diff --git a/apps/android/app/src/main/java/chat/simplex/app/views/chat/item/CIFileView.kt b/apps/multiplatform/android/src/main/java/chat/simplex/app/views/chat/item/CIFileView.kt similarity index 98% rename from apps/android/app/src/main/java/chat/simplex/app/views/chat/item/CIFileView.kt rename to apps/multiplatform/android/src/main/java/chat/simplex/app/views/chat/item/CIFileView.kt index 52a4a417f9..98aedc361e 100644 --- a/apps/android/app/src/main/java/chat/simplex/app/views/chat/item/CIFileView.kt +++ b/apps/multiplatform/android/src/main/java/chat/simplex/app/views/chat/item/CIFileView.kt @@ -32,7 +32,7 @@ fun CIFileView( receiveFile: (Long) -> Unit ) { val context = LocalContext.current - val saveFileLauncher = rememberSaveFileLauncher(cxt = context, ciFile = file) + val saveFileLauncher = rememberSaveFileLauncher(ciFile = file) @Composable fun fileIcon( @@ -95,7 +95,7 @@ fun CIFileView( ) } is CIFileStatus.RcvComplete -> { - val filePath = getLoadedFilePath(context, file) + val filePath = getLoadedFilePath(file) if (filePath != null) { saveFileLauncher.launch(file.fileName) } else { diff --git a/apps/android/app/src/main/java/chat/simplex/app/views/chat/item/CIGroupInvitationView.kt b/apps/multiplatform/android/src/main/java/chat/simplex/app/views/chat/item/CIGroupInvitationView.kt similarity index 100% rename from apps/android/app/src/main/java/chat/simplex/app/views/chat/item/CIGroupInvitationView.kt rename to apps/multiplatform/android/src/main/java/chat/simplex/app/views/chat/item/CIGroupInvitationView.kt diff --git a/apps/android/app/src/main/java/chat/simplex/app/views/chat/item/CIImageView.kt b/apps/multiplatform/android/src/main/java/chat/simplex/app/views/chat/item/CIImageView.kt similarity index 97% rename from apps/android/app/src/main/java/chat/simplex/app/views/chat/item/CIImageView.kt rename to apps/multiplatform/android/src/main/java/chat/simplex/app/views/chat/item/CIImageView.kt index c4368e1b7a..4c94bed48d 100644 --- a/apps/android/app/src/main/java/chat/simplex/app/views/chat/item/CIImageView.kt +++ b/apps/multiplatform/android/src/main/java/chat/simplex/app/views/chat/item/CIImageView.kt @@ -139,8 +139,8 @@ fun CIImageView( } fun imageAndFilePath(file: CIFile?): Pair { - val imageBitmap: Bitmap? = getLoadedImage(SimplexApp.context, file) - val filePath = getLoadedFilePath(SimplexApp.context, file) + val imageBitmap: Bitmap? = getLoadedImage(file) + val filePath = getLoadedFilePath(file) return imageBitmap to filePath } @@ -160,7 +160,7 @@ fun CIImageView( val view = LocalView.current imageView(imagePainter, onClick = { hideKeyboard(view) - if (getLoadedFilePath(context, file) != null) { + if (getLoadedFilePath(file) != null) { ModalManager.shared.showCustomModal(animated = false) { close -> ImageFullScreenView(imageProvider, close) } diff --git a/apps/android/app/src/main/java/chat/simplex/app/views/chat/item/CIInvalidJSONView.kt b/apps/multiplatform/android/src/main/java/chat/simplex/app/views/chat/item/CIInvalidJSONView.kt similarity index 90% rename from apps/android/app/src/main/java/chat/simplex/app/views/chat/item/CIInvalidJSONView.kt rename to apps/multiplatform/android/src/main/java/chat/simplex/app/views/chat/item/CIInvalidJSONView.kt index f22fcf46a2..34ea631c72 100644 --- a/apps/android/app/src/main/java/chat/simplex/app/views/chat/item/CIInvalidJSONView.kt +++ b/apps/multiplatform/android/src/main/java/chat/simplex/app/views/chat/item/CIInvalidJSONView.kt @@ -1,6 +1,5 @@ package chat.simplex.app.views.chat.item -import SectionSpacer import SectionView import androidx.compose.foundation.* import androidx.compose.foundation.layout.* @@ -8,7 +7,6 @@ import androidx.compose.material.* import androidx.compose.runtime.Composable import androidx.compose.ui.Modifier import androidx.compose.ui.graphics.Color -import androidx.compose.ui.platform.LocalContext import androidx.compose.ui.res.painterResource import androidx.compose.ui.res.stringResource import androidx.compose.ui.text.font.FontStyle @@ -33,9 +31,8 @@ fun InvalidJSONView(json: String) { Column { Spacer(Modifier.height(DEFAULT_PADDING)) SectionView { - val context = LocalContext.current SettingsActionItem(painterResource(R.drawable.ic_share), generalGetString(R.string.share_verb), click = { - shareText(context, json) + shareText(json) }) } Column(Modifier.padding(DEFAULT_PADDING).fillMaxWidth().verticalScroll(rememberScrollState())) { diff --git a/apps/android/app/src/main/java/chat/simplex/app/views/chat/item/CIMetaView.kt b/apps/multiplatform/android/src/main/java/chat/simplex/app/views/chat/item/CIMetaView.kt similarity index 100% rename from apps/android/app/src/main/java/chat/simplex/app/views/chat/item/CIMetaView.kt rename to apps/multiplatform/android/src/main/java/chat/simplex/app/views/chat/item/CIMetaView.kt diff --git a/apps/android/app/src/main/java/chat/simplex/app/views/chat/item/CIRcvDecryptionError.kt b/apps/multiplatform/android/src/main/java/chat/simplex/app/views/chat/item/CIRcvDecryptionError.kt similarity index 100% rename from apps/android/app/src/main/java/chat/simplex/app/views/chat/item/CIRcvDecryptionError.kt rename to apps/multiplatform/android/src/main/java/chat/simplex/app/views/chat/item/CIRcvDecryptionError.kt diff --git a/apps/android/app/src/main/java/chat/simplex/app/views/chat/item/CIVideoView.kt b/apps/multiplatform/android/src/main/java/chat/simplex/app/views/chat/item/CIVideoView.kt similarity index 98% rename from apps/android/app/src/main/java/chat/simplex/app/views/chat/item/CIVideoView.kt rename to apps/multiplatform/android/src/main/java/chat/simplex/app/views/chat/item/CIVideoView.kt index 63d554b9bb..cfbfb714b0 100644 --- a/apps/android/app/src/main/java/chat/simplex/app/views/chat/item/CIVideoView.kt +++ b/apps/multiplatform/android/src/main/java/chat/simplex/app/views/chat/item/CIVideoView.kt @@ -46,7 +46,7 @@ fun CIVideoView( contentAlignment = Alignment.TopEnd ) { val context = LocalContext.current - val filePath = remember(file) { getLoadedFilePath(SimplexApp.context, file) } + val filePath = remember(file) { getLoadedFilePath(file) } val preview = remember(image) { base64ToBitmap(image) } if (file != null && filePath != null) { val uri = remember(filePath) { FileProvider.getUriForFile(context, "${BuildConfig.APPLICATION_ID}.provider", File(filePath)) } @@ -100,7 +100,7 @@ fun CIVideoView( @Composable private fun VideoView(uri: Uri, file: CIFile, defaultPreview: Bitmap, defaultDuration: Long, showMenu: MutableState, onClick: () -> Unit) { val context = LocalContext.current - val player = remember(uri) { VideoPlayer.getOrCreate(uri, false, defaultPreview, defaultDuration, true, context) } + val player = remember(uri) { VideoPlayer.getOrCreate(uri, false, defaultPreview, defaultDuration, true) } val videoPlaying = remember(uri.path) { player.videoPlaying } val progress = remember(uri.path) { player.progress } val duration = remember(uri.path) { player.duration } diff --git a/apps/android/app/src/main/java/chat/simplex/app/views/chat/item/CIVoiceView.kt b/apps/multiplatform/android/src/main/java/chat/simplex/app/views/chat/item/CIVoiceView.kt similarity index 99% rename from apps/android/app/src/main/java/chat/simplex/app/views/chat/item/CIVoiceView.kt rename to apps/multiplatform/android/src/main/java/chat/simplex/app/views/chat/item/CIVoiceView.kt index 00f96c60f7..840ba7b03f 100644 --- a/apps/android/app/src/main/java/chat/simplex/app/views/chat/item/CIVoiceView.kt +++ b/apps/multiplatform/android/src/main/java/chat/simplex/app/views/chat/item/CIVoiceView.kt @@ -44,7 +44,7 @@ fun CIVoiceView( ) { if (file != null) { val context = LocalContext.current - val filePath = remember(file.filePath, file.fileStatus) { getLoadedFilePath(context, file) } + val filePath = remember(file.filePath, file.fileStatus) { getLoadedFilePath(file) } var brokenAudio by rememberSaveable(file.filePath) { mutableStateOf(false) } val audioPlaying = rememberSaveable(file.filePath) { mutableStateOf(false) } val progress = rememberSaveable(file.filePath) { mutableStateOf(0) } diff --git a/apps/android/app/src/main/java/chat/simplex/app/views/chat/item/ChatItemView.kt b/apps/multiplatform/android/src/main/java/chat/simplex/app/views/chat/item/ChatItemView.kt similarity index 97% rename from apps/android/app/src/main/java/chat/simplex/app/views/chat/item/ChatItemView.kt rename to apps/multiplatform/android/src/main/java/chat/simplex/app/views/chat/item/ChatItemView.kt index cd9dad7086..582d50b489 100644 --- a/apps/android/app/src/main/java/chat/simplex/app/views/chat/item/ChatItemView.kt +++ b/apps/multiplatform/android/src/main/java/chat/simplex/app/views/chat/item/ChatItemView.kt @@ -3,7 +3,6 @@ package chat.simplex.app.views.chat.item import android.Manifest import android.os.Build import androidx.compose.foundation.* -import androidx.compose.foundation.gestures.scrollable import androidx.compose.foundation.layout.* import androidx.compose.foundation.shape.RoundedCornerShape import androidx.compose.material.* @@ -21,13 +20,11 @@ import androidx.compose.ui.text.font.FontWeight import androidx.compose.ui.tooling.preview.Preview import androidx.compose.ui.unit.dp import androidx.compose.ui.unit.sp -import chat.simplex.app.* import chat.simplex.app.R import chat.simplex.app.model.* import chat.simplex.app.ui.theme.* import chat.simplex.app.views.chat.* import chat.simplex.app.views.helpers.* -import chat.simplex.app.views.usersettings.IncognitoView import com.google.accompanist.permissions.rememberPermissionState import kotlinx.datetime.Clock @@ -59,7 +56,7 @@ fun ChatItemView( val showMenu = remember { mutableStateOf(false) } val revealed = remember { mutableStateOf(false) } val fullDeleteAllowed = remember(cInfo) { cInfo.featureEnabled(ChatFeature.FullDelete) } - val saveFileLauncher = rememberSaveFileLauncher(cxt = context, ciFile = cItem.file) + val saveFileLauncher = rememberSaveFileLauncher(ciFile = cItem.file) val onLinkLongClick = { _: String -> showMenu.value = true } val live = composeState.value.liveMessage != null @@ -176,26 +173,26 @@ fun ChatItemView( }) } ItemAction(stringResource(R.string.share_verb), painterResource(R.drawable.ic_share), onClick = { - val filePath = getLoadedFilePath(SimplexApp.context, cItem.file) + val filePath = getLoadedFilePath(cItem.file) when { - filePath != null -> shareFile(context, cItem.text, filePath) - else -> shareText(context, cItem.content.text) + filePath != null -> shareFile(cItem.text, filePath) + else -> shareText(cItem.content.text) } showMenu.value = false }) ItemAction(stringResource(R.string.copy_verb), painterResource(R.drawable.ic_content_copy), onClick = { - copyText(context, cItem.content.text) + copyText(cItem.content.text) showMenu.value = false }) if (cItem.content.msgContent is MsgContent.MCImage || cItem.content.msgContent is MsgContent.MCVideo || cItem.content.msgContent is MsgContent.MCFile || cItem.content.msgContent is MsgContent.MCVoice) { - val filePath = getLoadedFilePath(context, cItem.file) + val filePath = getLoadedFilePath(cItem.file) if (filePath != null) { val writePermissionState = rememberPermissionState(permission = Manifest.permission.WRITE_EXTERNAL_STORAGE) ItemAction(stringResource(R.string.save_verb), painterResource(R.drawable.ic_download), onClick = { when (cItem.content.msgContent) { is MsgContent.MCImage -> { if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.R || writePermissionState.hasPermission) { - saveImage(context, cItem.file) + saveImage(cItem.file) } else { writePermissionState.launchPermissionRequest() } diff --git a/apps/android/app/src/main/java/chat/simplex/app/views/chat/item/DeletedItemView.kt b/apps/multiplatform/android/src/main/java/chat/simplex/app/views/chat/item/DeletedItemView.kt similarity index 100% rename from apps/android/app/src/main/java/chat/simplex/app/views/chat/item/DeletedItemView.kt rename to apps/multiplatform/android/src/main/java/chat/simplex/app/views/chat/item/DeletedItemView.kt diff --git a/apps/android/app/src/main/java/chat/simplex/app/views/chat/item/EmojiItemView.kt b/apps/multiplatform/android/src/main/java/chat/simplex/app/views/chat/item/EmojiItemView.kt similarity index 100% rename from apps/android/app/src/main/java/chat/simplex/app/views/chat/item/EmojiItemView.kt rename to apps/multiplatform/android/src/main/java/chat/simplex/app/views/chat/item/EmojiItemView.kt diff --git a/apps/android/app/src/main/java/chat/simplex/app/views/chat/item/FramedItemView.kt b/apps/multiplatform/android/src/main/java/chat/simplex/app/views/chat/item/FramedItemView.kt similarity index 99% rename from apps/android/app/src/main/java/chat/simplex/app/views/chat/item/FramedItemView.kt rename to apps/multiplatform/android/src/main/java/chat/simplex/app/views/chat/item/FramedItemView.kt index 61d8080e06..d81b7943bc 100644 --- a/apps/android/app/src/main/java/chat/simplex/app/views/chat/item/FramedItemView.kt +++ b/apps/multiplatform/android/src/main/java/chat/simplex/app/views/chat/item/FramedItemView.kt @@ -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 = measureable.fastMap { + val placeables: List = measureable.map { if (it.layoutId == priorityLayoutId) imagePlaceable!! else diff --git a/apps/android/app/src/main/java/chat/simplex/app/views/chat/item/ImageFullScreenView.kt b/apps/multiplatform/android/src/main/java/chat/simplex/app/views/chat/item/ImageFullScreenView.kt similarity index 99% rename from apps/android/app/src/main/java/chat/simplex/app/views/chat/item/ImageFullScreenView.kt rename to apps/multiplatform/android/src/main/java/chat/simplex/app/views/chat/item/ImageFullScreenView.kt index d916e4cb94..a0279d0220 100644 --- a/apps/android/app/src/main/java/chat/simplex/app/views/chat/item/ImageFullScreenView.kt +++ b/apps/multiplatform/android/src/main/java/chat/simplex/app/views/chat/item/ImageFullScreenView.kt @@ -178,7 +178,7 @@ fun ImageFullScreenView(imageProvider: () -> ImageGalleryProvider, close: () -> @Composable private fun VideoView(modifier: Modifier, uri: Uri, defaultPreview: Bitmap, currentPage: Boolean) { val context = LocalContext.current - val player = remember(uri) { VideoPlayer.getOrCreate(uri, true, defaultPreview, 0L, true, context) } + val player = remember(uri) { VideoPlayer.getOrCreate(uri, true, defaultPreview, 0L, true) } val isCurrentPage = rememberUpdatedState(currentPage) val play = { player.play(true) diff --git a/apps/android/app/src/main/java/chat/simplex/app/views/chat/item/IntegrityErrorItemView.kt b/apps/multiplatform/android/src/main/java/chat/simplex/app/views/chat/item/IntegrityErrorItemView.kt similarity index 100% rename from apps/android/app/src/main/java/chat/simplex/app/views/chat/item/IntegrityErrorItemView.kt rename to apps/multiplatform/android/src/main/java/chat/simplex/app/views/chat/item/IntegrityErrorItemView.kt diff --git a/apps/android/app/src/main/java/chat/simplex/app/views/chat/item/MarkedDeletedItemView.kt b/apps/multiplatform/android/src/main/java/chat/simplex/app/views/chat/item/MarkedDeletedItemView.kt similarity index 100% rename from apps/android/app/src/main/java/chat/simplex/app/views/chat/item/MarkedDeletedItemView.kt rename to apps/multiplatform/android/src/main/java/chat/simplex/app/views/chat/item/MarkedDeletedItemView.kt diff --git a/apps/android/app/src/main/java/chat/simplex/app/views/chat/item/TextItemView.kt b/apps/multiplatform/android/src/main/java/chat/simplex/app/views/chat/item/TextItemView.kt similarity index 100% rename from apps/android/app/src/main/java/chat/simplex/app/views/chat/item/TextItemView.kt rename to apps/multiplatform/android/src/main/java/chat/simplex/app/views/chat/item/TextItemView.kt diff --git a/apps/android/app/src/main/java/chat/simplex/app/views/chatlist/ChatHelpView.kt b/apps/multiplatform/android/src/main/java/chat/simplex/app/views/chatlist/ChatHelpView.kt similarity index 100% rename from apps/android/app/src/main/java/chat/simplex/app/views/chatlist/ChatHelpView.kt rename to apps/multiplatform/android/src/main/java/chat/simplex/app/views/chatlist/ChatHelpView.kt diff --git a/apps/android/app/src/main/java/chat/simplex/app/views/chatlist/ChatListNavLinkView.kt b/apps/multiplatform/android/src/main/java/chat/simplex/app/views/chatlist/ChatListNavLinkView.kt similarity index 100% rename from apps/android/app/src/main/java/chat/simplex/app/views/chatlist/ChatListNavLinkView.kt rename to apps/multiplatform/android/src/main/java/chat/simplex/app/views/chatlist/ChatListNavLinkView.kt diff --git a/apps/android/app/src/main/java/chat/simplex/app/views/chatlist/ChatListView.kt b/apps/multiplatform/android/src/main/java/chat/simplex/app/views/chatlist/ChatListView.kt similarity index 100% rename from apps/android/app/src/main/java/chat/simplex/app/views/chatlist/ChatListView.kt rename to apps/multiplatform/android/src/main/java/chat/simplex/app/views/chatlist/ChatListView.kt diff --git a/apps/android/app/src/main/java/chat/simplex/app/views/chatlist/ChatPreviewView.kt b/apps/multiplatform/android/src/main/java/chat/simplex/app/views/chatlist/ChatPreviewView.kt similarity index 100% rename from apps/android/app/src/main/java/chat/simplex/app/views/chatlist/ChatPreviewView.kt rename to apps/multiplatform/android/src/main/java/chat/simplex/app/views/chatlist/ChatPreviewView.kt diff --git a/apps/android/app/src/main/java/chat/simplex/app/views/chatlist/ContactConnectionView.kt b/apps/multiplatform/android/src/main/java/chat/simplex/app/views/chatlist/ContactConnectionView.kt similarity index 100% rename from apps/android/app/src/main/java/chat/simplex/app/views/chatlist/ContactConnectionView.kt rename to apps/multiplatform/android/src/main/java/chat/simplex/app/views/chatlist/ContactConnectionView.kt diff --git a/apps/android/app/src/main/java/chat/simplex/app/views/chatlist/ContactRequestView.kt b/apps/multiplatform/android/src/main/java/chat/simplex/app/views/chatlist/ContactRequestView.kt similarity index 100% rename from apps/android/app/src/main/java/chat/simplex/app/views/chatlist/ContactRequestView.kt rename to apps/multiplatform/android/src/main/java/chat/simplex/app/views/chatlist/ContactRequestView.kt diff --git a/apps/android/app/src/main/java/chat/simplex/app/views/chatlist/ShareListNavLinkView.kt b/apps/multiplatform/android/src/main/java/chat/simplex/app/views/chatlist/ShareListNavLinkView.kt similarity index 100% rename from apps/android/app/src/main/java/chat/simplex/app/views/chatlist/ShareListNavLinkView.kt rename to apps/multiplatform/android/src/main/java/chat/simplex/app/views/chatlist/ShareListNavLinkView.kt diff --git a/apps/android/app/src/main/java/chat/simplex/app/views/chatlist/ShareListView.kt b/apps/multiplatform/android/src/main/java/chat/simplex/app/views/chatlist/ShareListView.kt similarity index 100% rename from apps/android/app/src/main/java/chat/simplex/app/views/chatlist/ShareListView.kt rename to apps/multiplatform/android/src/main/java/chat/simplex/app/views/chatlist/ShareListView.kt diff --git a/apps/android/app/src/main/java/chat/simplex/app/views/chatlist/UserPicker.kt b/apps/multiplatform/android/src/main/java/chat/simplex/app/views/chatlist/UserPicker.kt similarity index 100% rename from apps/android/app/src/main/java/chat/simplex/app/views/chatlist/UserPicker.kt rename to apps/multiplatform/android/src/main/java/chat/simplex/app/views/chatlist/UserPicker.kt diff --git a/apps/android/app/src/main/java/chat/simplex/app/views/database/ChatArchiveView.kt b/apps/multiplatform/android/src/main/java/chat/simplex/app/views/database/ChatArchiveView.kt similarity index 92% rename from apps/android/app/src/main/java/chat/simplex/app/views/database/ChatArchiveView.kt rename to apps/multiplatform/android/src/main/java/chat/simplex/app/views/database/ChatArchiveView.kt index 6df30f9170..6bafc207c1 100644 --- a/apps/android/app/src/main/java/chat/simplex/app/views/database/ChatArchiveView.kt +++ b/apps/multiplatform/android/src/main/java/chat/simplex/app/views/database/ChatArchiveView.kt @@ -14,15 +14,14 @@ import androidx.activity.result.contract.ActivityResultContracts import androidx.compose.foundation.layout.* import androidx.compose.material.* import androidx.compose.runtime.* -import androidx.compose.ui.Alignment import androidx.compose.ui.Modifier import androidx.compose.ui.graphics.Color import androidx.compose.ui.platform.LocalContext import androidx.compose.ui.res.painterResource import androidx.compose.ui.res.stringResource import androidx.compose.ui.tooling.preview.Preview +import chat.simplex.app.* import chat.simplex.app.R -import chat.simplex.app.TAG import chat.simplex.app.model.ChatModel import chat.simplex.app.ui.theme.SimpleXTheme import chat.simplex.app.views.helpers.* @@ -36,8 +35,8 @@ import java.util.* @Composable fun ChatArchiveView(m: ChatModel, title: String, archiveName: String, archiveTime: Instant) { val context = LocalContext.current - val archivePath = "${getFilesDirectory(context)}/$archiveName" - val saveArchiveLauncher = rememberSaveArchiveLauncher(cxt = context, archivePath) + val archivePath = "${getFilesDirectory()}/$archiveName" + val saveArchiveLauncher = rememberSaveArchiveLauncher(archivePath) ChatArchiveLayout( title, archiveTime, @@ -82,10 +81,11 @@ fun ChatArchiveLayout( } @Composable -private fun rememberSaveArchiveLauncher(cxt: Context, chatArchivePath: String): ManagedActivityResultLauncher = +private fun rememberSaveArchiveLauncher(chatArchivePath: String): ManagedActivityResultLauncher = rememberLauncherForActivityResult( contract = ActivityResultContracts.CreateDocument(), onResult = { destination -> + val cxt = SimplexApp.context try { destination?.let { val contentResolver = cxt.contentResolver diff --git a/apps/android/app/src/main/java/chat/simplex/app/views/database/DatabaseEncryptionView.kt b/apps/multiplatform/android/src/main/java/chat/simplex/app/views/database/DatabaseEncryptionView.kt similarity index 99% rename from apps/android/app/src/main/java/chat/simplex/app/views/database/DatabaseEncryptionView.kt rename to apps/multiplatform/android/src/main/java/chat/simplex/app/views/database/DatabaseEncryptionView.kt index 8923962bcd..642d855ac1 100644 --- a/apps/android/app/src/main/java/chat/simplex/app/views/database/DatabaseEncryptionView.kt +++ b/apps/multiplatform/android/src/main/java/chat/simplex/app/views/database/DatabaseEncryptionView.kt @@ -497,7 +497,7 @@ fun PreviewDatabaseEncryptionLayout() { SimpleXTheme { DatabaseEncryptionLayout( useKeychain = remember { mutableStateOf(true) }, - prefs = AppPreferences(SimplexApp.context), + prefs = AppPreferences(), chatDbEncrypted = true, currentKey = remember { mutableStateOf("") }, newKey = remember { mutableStateOf("") }, diff --git a/apps/android/app/src/main/java/chat/simplex/app/views/database/DatabaseErrorView.kt b/apps/multiplatform/android/src/main/java/chat/simplex/app/views/database/DatabaseErrorView.kt similarity index 98% rename from apps/android/app/src/main/java/chat/simplex/app/views/database/DatabaseErrorView.kt rename to apps/multiplatform/android/src/main/java/chat/simplex/app/views/database/DatabaseErrorView.kt index c1aa1fe5a6..a8192e3d89 100644 --- a/apps/android/app/src/main/java/chat/simplex/app/views/database/DatabaseErrorView.kt +++ b/apps/multiplatform/android/src/main/java/chat/simplex/app/views/database/DatabaseErrorView.kt @@ -42,7 +42,7 @@ fun DatabaseErrorView( var storedDBKey by remember { mutableStateOf(DatabaseUtils.ksDatabasePassword.get()) } var useKeychain by remember { mutableStateOf(appPreferences.storeDBPassphrase.get()) } val context = LocalContext.current - val restoreDbFromBackup = remember { mutableStateOf(shouldShowRestoreDbButton(appPreferences, context)) } + val restoreDbFromBackup = remember { mutableStateOf(shouldShowRestoreDbButton(appPreferences)) } fun callRunChat(confirmMigrations: MigrationConfirmation? = null) { val useKey = if (useKeychain) null else dbKey.value @@ -164,7 +164,7 @@ fun DatabaseErrorView( title = generalGetString(R.string.restore_database_alert_title), text = generalGetString(R.string.restore_database_alert_desc), confirmText = generalGetString(R.string.restore_database_alert_confirm), - onConfirm = { restoreDb(restoreDbFromBackup, appPreferences, context) }, + onConfirm = { restoreDb(restoreDbFromBackup, appPreferences) }, destructive = true, ) } @@ -226,7 +226,8 @@ private fun runChat( } } -private fun shouldShowRestoreDbButton(prefs: AppPreferences, context: Context): Boolean { +private fun shouldShowRestoreDbButton(prefs: AppPreferences): Boolean { + val context = SimplexApp.context val startedAt = prefs.encryptionStartedAt.get() ?: return false /** Just in case there is any small difference between reported Java's [Clock.System.now] and Linux's time on a file */ val safeDiffInTime = 10_000L @@ -238,7 +239,8 @@ private fun shouldShowRestoreDbButton(prefs: AppPreferences, context: Context): startedAt.toEpochMilliseconds() - safeDiffInTime <= filesAgent.lastModified() } -private fun restoreDb(restoreDbFromBackup: MutableState, prefs: AppPreferences, context: Context) { +private fun restoreDb(restoreDbFromBackup: MutableState, prefs: AppPreferences) { + val context = SimplexApp.context val filesChatBase = context.dataDir.absolutePath + File.separator + "files_chat.db" val filesAgentBase = context.dataDir.absolutePath + File.separator + "files_agent.db" try { @@ -299,7 +301,7 @@ fun PreviewChatInfoLayout() { SimpleXTheme { DatabaseErrorView( remember { mutableStateOf(DBMigrationResult.ErrorNotADatabase("simplex_v1_chat.db")) }, - AppPreferences(SimplexApp.context) + AppPreferences() ) } } diff --git a/apps/android/app/src/main/java/chat/simplex/app/views/database/DatabaseView.kt b/apps/multiplatform/android/src/main/java/chat/simplex/app/views/database/DatabaseView.kt similarity index 90% rename from apps/android/app/src/main/java/chat/simplex/app/views/database/DatabaseView.kt rename to apps/multiplatform/android/src/main/java/chat/simplex/app/views/database/DatabaseView.kt index 99293f76ef..41b19cb60c 100644 --- a/apps/android/app/src/main/java/chat/simplex/app/views/database/DatabaseView.kt +++ b/apps/multiplatform/android/src/main/java/chat/simplex/app/views/database/DatabaseView.kt @@ -57,11 +57,11 @@ fun DatabaseView( val chatArchiveTime = remember { mutableStateOf(prefs.chatArchiveTime.get()) } val chatLastStart = remember { mutableStateOf(prefs.chatLastStart.get()) } val chatArchiveFile = remember { mutableStateOf(null) } - val saveArchiveLauncher = rememberSaveArchiveLauncher(cxt = context, chatArchiveFile) - val appFilesCountAndSize = remember { mutableStateOf(directoryFileCountAndSize(getAppFilesDirectory(context))) } + val saveArchiveLauncher = rememberSaveArchiveLauncher(chatArchiveFile) + val appFilesCountAndSize = remember { mutableStateOf(directoryFileCountAndSize(getAppFilesDirectory())) } val importArchiveLauncher = rememberGetContentLauncher { uri: Uri? -> if (uri != null) { - importArchiveAlert(m, context, uri, appFilesCountAndSize, progressIndicator) + importArchiveAlert(m, uri, appFilesCountAndSize, progressIndicator) } } LaunchedEffect(m.chatRunning) { @@ -88,17 +88,17 @@ fun DatabaseView( m.currentUser.value, m.users, startChat = { startChat(m, runChat, chatLastStart, m.chatDbChanged) }, - stopChatAlert = { stopChatAlert(m, runChat, context) }, - exportArchive = { exportArchive(context, m, progressIndicator, chatArchiveName, chatArchiveTime, chatArchiveFile, saveArchiveLauncher) }, + stopChatAlert = { stopChatAlert(m, runChat) }, + exportArchive = { exportArchive(m, progressIndicator, chatArchiveName, chatArchiveTime, chatArchiveFile, saveArchiveLauncher) }, deleteChatAlert = { deleteChatAlert(m, progressIndicator) }, - deleteAppFilesAndMedia = { deleteFilesAndMediaAlert(context, appFilesCountAndSize) }, + deleteAppFilesAndMedia = { deleteFilesAndMediaAlert(appFilesCountAndSize) }, onChatItemTTLSelected = { val oldValue = chatItemTTL.value chatItemTTL.value = it if (it < oldValue) { - setChatItemTTLAlert(m, chatItemTTL, progressIndicator, appFilesCountAndSize, context) + setChatItemTTLAlert(m, chatItemTTL, progressIndicator, appFilesCountAndSize) } else if (it != oldValue) { - setCiTTL(m, chatItemTTL, progressIndicator, appFilesCountAndSize, context) + setCiTTL(m, chatItemTTL, progressIndicator, appFilesCountAndSize) } }, showSettingsModal @@ -283,13 +283,12 @@ private fun setChatItemTTLAlert( m: ChatModel, selectedChatItemTTL: MutableState, progressIndicator: MutableState, appFilesCountAndSize: MutableState>, - context: Context ) { AlertManager.shared.showAlertDialog( title = generalGetString(R.string.enable_automatic_deletion_question), text = generalGetString(R.string.enable_automatic_deletion_message), confirmText = generalGetString(R.string.delete_messages), - onConfirm = { setCiTTL(m, selectedChatItemTTL, progressIndicator, appFilesCountAndSize, context) }, + onConfirm = { setCiTTL(m, selectedChatItemTTL, progressIndicator, appFilesCountAndSize) }, onDismiss = { selectedChatItemTTL.value = m.chatItemTTL.value }, destructive = true, ) @@ -387,12 +386,12 @@ private fun startChat(m: ChatModel, runChat: MutableState, chatLastSta } } -private fun stopChatAlert(m: ChatModel, runChat: MutableState, context: Context) { +private fun stopChatAlert(m: ChatModel, runChat: MutableState) { AlertManager.shared.showAlertDialog( title = generalGetString(R.string.stop_chat_question), text = generalGetString(R.string.stop_chat_to_export_import_or_delete_chat_database), confirmText = generalGetString(R.string.stop_chat_confirmation), - onConfirm = { authStopChat(m, runChat, context) }, + onConfirm = { authStopChat(m, runChat) }, onDismiss = { runChat.value = true } ) } @@ -404,16 +403,15 @@ private fun exportProhibitedAlert() { ) } -private fun authStopChat(m: ChatModel, runChat: MutableState, context: Context) { +private fun authStopChat(m: ChatModel, runChat: MutableState) { if (m.controller.appPrefs.performLA.get()) { authenticate( generalGetString(R.string.auth_stop_chat), generalGetString(R.string.auth_log_in_using_credential), - activity = context as FragmentActivity, completed = { laResult -> when (laResult) { LAResult.Success, is LAResult.Unavailable -> { - stopChat(m, runChat, context) + stopChat(m, runChat) } is LAResult.Error -> { runChat.value = true @@ -425,11 +423,11 @@ private fun authStopChat(m: ChatModel, runChat: MutableState, context: } ) } else { - stopChat(m, runChat, context) + stopChat(m, runChat) } } -private fun stopChat(m: ChatModel, runChat: MutableState, context: Context) { +private fun stopChat(m: ChatModel, runChat: MutableState) { withApi { try { runChat.value = false @@ -455,7 +453,6 @@ suspend fun deleteChatAsync(m: ChatModel) { } private fun exportArchive( - context: Context, m: ChatModel, progressIndicator: MutableState, chatArchiveName: MutableState, @@ -466,7 +463,7 @@ private fun exportArchive( progressIndicator.value = true withApi { try { - val archiveFile = exportChatArchive(m, context, chatArchiveName, chatArchiveTime, chatArchiveFile) + val archiveFile = exportChatArchive(m, chatArchiveName, chatArchiveTime, chatArchiveFile) chatArchiveFile.value = archiveFile saveArchiveLauncher.launch(archiveFile.substringAfterLast("/")) progressIndicator.value = false @@ -479,7 +476,6 @@ private fun exportArchive( private suspend fun exportChatArchive( m: ChatModel, - context: Context, chatArchiveName: MutableState, chatArchiveTime: MutableState, chatArchiveFile: MutableState @@ -487,10 +483,10 @@ private suspend fun exportChatArchive( val archiveTime = Clock.System.now() val ts = SimpleDateFormat("yyyy-MM-dd'T'HHmmss", Locale.US).format(Date.from(archiveTime.toJavaInstant())) val archiveName = "simplex-chat.$ts.zip" - val archivePath = "${getFilesDirectory(context)}/$archiveName" - val config = ArchiveConfig(archivePath, parentTempDirectory = context.cacheDir.toString()) + val archivePath = "${getFilesDirectory()}/$archiveName" + val config = ArchiveConfig(archivePath, parentTempDirectory = SimplexApp.context.cacheDir.toString()) m.controller.apiExportArchive(config) - deleteOldArchive(m, context) + deleteOldArchive(m) m.controller.appPrefs.chatArchiveName.set(archiveName) chatArchiveName.value = archiveName m.controller.appPrefs.chatArchiveTime.set(archiveTime) @@ -499,10 +495,10 @@ private suspend fun exportChatArchive( return archivePath } -private fun deleteOldArchive(m: ChatModel, context: Context) { +private fun deleteOldArchive(m: ChatModel) { val chatArchiveName = m.controller.appPrefs.chatArchiveName.get() if (chatArchiveName != null) { - val file = File("${getFilesDirectory(context)}/$chatArchiveName") + val file = File("${getFilesDirectory()}/$chatArchiveName") val fileDeleted = file.delete() if (fileDeleted) { m.controller.appPrefs.chatArchiveName.set(null) @@ -514,15 +510,16 @@ private fun deleteOldArchive(m: ChatModel, context: Context) { } @Composable -private fun rememberSaveArchiveLauncher(cxt: Context, chatArchiveFile: MutableState): ManagedActivityResultLauncher = +private fun rememberSaveArchiveLauncher(chatArchiveFile: MutableState): ManagedActivityResultLauncher = rememberLauncherForActivityResult( contract = ActivityResultContracts.CreateDocument(), onResult = { destination -> + val cxt = SimplexApp.context try { destination?.let { val filePath = chatArchiveFile.value if (filePath != null) { - val contentResolver = cxt.contentResolver + val contentResolver = SimplexApp.context.contentResolver contentResolver.openOutputStream(destination)?.let { stream -> val outputStream = BufferedOutputStream(stream) File(filePath).inputStream().use { it.copyTo(outputStream) } @@ -544,7 +541,6 @@ private fun rememberSaveArchiveLauncher(cxt: Context, chatArchiveFile: MutableSt private fun importArchiveAlert( m: ChatModel, - context: Context, importedArchiveUri: Uri, appFilesCountAndSize: MutableState>, progressIndicator: MutableState @@ -553,29 +549,28 @@ private fun importArchiveAlert( title = generalGetString(R.string.import_database_question), text = generalGetString(R.string.your_current_chat_database_will_be_deleted_and_replaced_with_the_imported_one), confirmText = generalGetString(R.string.import_database_confirmation), - onConfirm = { importArchive(m, context, importedArchiveUri, appFilesCountAndSize, progressIndicator) }, + onConfirm = { importArchive(m, importedArchiveUri, appFilesCountAndSize, progressIndicator) }, destructive = true, ) } private fun importArchive( m: ChatModel, - context: Context, importedArchiveUri: Uri, appFilesCountAndSize: MutableState>, progressIndicator: MutableState ) { progressIndicator.value = true - val archivePath = saveArchiveFromUri(context, importedArchiveUri) + val archivePath = saveArchiveFromUri(importedArchiveUri) if (archivePath != null) { withApi { try { m.controller.apiDeleteStorage() try { - val config = ArchiveConfig(archivePath, parentTempDirectory = context.cacheDir.toString()) + val config = ArchiveConfig(archivePath, parentTempDirectory = SimplexApp.context.cacheDir.toString()) val archiveErrors = m.controller.apiImportArchive(config) DatabaseUtils.ksDatabasePassword.remove() - appFilesCountAndSize.value = directoryFileCountAndSize(getAppFilesDirectory(context)) + appFilesCountAndSize.value = directoryFileCountAndSize(getAppFilesDirectory()) if (archiveErrors.isEmpty()) { operationEnded(m, progressIndicator) { AlertManager.shared.showAlertMsg(generalGetString(R.string.chat_database_imported), text = generalGetString(R.string.restart_the_app_to_use_imported_chat_database)) @@ -601,12 +596,12 @@ private fun importArchive( } } -private fun saveArchiveFromUri(context: Context, importedArchiveUri: Uri): String? { +private fun saveArchiveFromUri(importedArchiveUri: Uri): String? { return try { - val inputStream = context.contentResolver.openInputStream(importedArchiveUri) - val archiveName = getFileName(context, importedArchiveUri) + val inputStream = SimplexApp.context.contentResolver.openInputStream(importedArchiveUri) + val archiveName = getFileName(importedArchiveUri) if (inputStream != null && archiveName != null) { - val archivePath = "${context.cacheDir}/$archiveName" + val archivePath = "${SimplexApp.context.cacheDir}/$archiveName" val destFile = File(archivePath) IOUtils.copy(inputStream, FileOutputStream(destFile)) archivePath @@ -651,7 +646,6 @@ private fun setCiTTL( chatItemTTL: MutableState, progressIndicator: MutableState, appFilesCountAndSize: MutableState>, - context: Context ) { Log.d(TAG, "DatabaseView setChatItemTTL ${chatItemTTL.value.seconds ?: -1}") progressIndicator.value = true @@ -660,11 +654,11 @@ private fun setCiTTL( m.controller.setChatItemTTL(chatItemTTL.value) // Update model on success m.chatItemTTL.value = chatItemTTL.value - afterSetCiTTL(m, progressIndicator, appFilesCountAndSize, context) + afterSetCiTTL(m, progressIndicator, appFilesCountAndSize) } catch (e: Exception) { // Rollback to model's value chatItemTTL.value = m.chatItemTTL.value - afterSetCiTTL(m, progressIndicator, appFilesCountAndSize, context) + afterSetCiTTL(m, progressIndicator, appFilesCountAndSize) AlertManager.shared.showAlertMsg(generalGetString(R.string.error_changing_message_deletion), e.stackTraceToString()) } } @@ -673,11 +667,10 @@ private fun setCiTTL( private fun afterSetCiTTL( m: ChatModel, progressIndicator: MutableState, - appFilesCountAndSize: MutableState>, - context: Context + appFilesCountAndSize: MutableState> ) { progressIndicator.value = false - appFilesCountAndSize.value = directoryFileCountAndSize(getAppFilesDirectory(context)) + appFilesCountAndSize.value = directoryFileCountAndSize(getAppFilesDirectory()) withApi { try { val chats = m.controller.apiGetChats() @@ -688,19 +681,19 @@ private fun afterSetCiTTL( } } -private fun deleteFilesAndMediaAlert(context: Context, appFilesCountAndSize: MutableState>) { +private fun deleteFilesAndMediaAlert(appFilesCountAndSize: MutableState>) { AlertManager.shared.showAlertDialog( title = generalGetString(R.string.delete_files_and_media_question), text = generalGetString(R.string.delete_files_and_media_desc), confirmText = generalGetString(R.string.delete_verb), - onConfirm = { deleteFiles(appFilesCountAndSize, context) }, + onConfirm = { deleteFiles(appFilesCountAndSize) }, destructive = true ) } -private fun deleteFiles(appFilesCountAndSize: MutableState>, context: Context) { - deleteAppFiles(context) - appFilesCountAndSize.value = directoryFileCountAndSize(getAppFilesDirectory(context)) +private fun deleteFiles(appFilesCountAndSize: MutableState>) { + deleteAppFiles() + appFilesCountAndSize.value = directoryFileCountAndSize(getAppFilesDirectory()) } private fun operationEnded(m: ChatModel, progressIndicator: MutableState, alert: () -> Unit) { diff --git a/apps/android/app/src/main/java/chat/simplex/app/views/helpers/AlertManager.kt b/apps/multiplatform/android/src/main/java/chat/simplex/app/views/helpers/AlertManager.kt similarity index 100% rename from apps/android/app/src/main/java/chat/simplex/app/views/helpers/AlertManager.kt rename to apps/multiplatform/android/src/main/java/chat/simplex/app/views/helpers/AlertManager.kt diff --git a/apps/android/app/src/main/java/chat/simplex/app/views/helpers/AnimationUtils.kt b/apps/multiplatform/android/src/main/java/chat/simplex/app/views/helpers/AnimationUtils.kt similarity index 100% rename from apps/android/app/src/main/java/chat/simplex/app/views/helpers/AnimationUtils.kt rename to apps/multiplatform/android/src/main/java/chat/simplex/app/views/helpers/AnimationUtils.kt diff --git a/apps/android/app/src/main/java/chat/simplex/app/views/helpers/ChatInfoImage.kt b/apps/multiplatform/android/src/main/java/chat/simplex/app/views/helpers/ChatInfoImage.kt similarity index 100% rename from apps/android/app/src/main/java/chat/simplex/app/views/helpers/ChatInfoImage.kt rename to apps/multiplatform/android/src/main/java/chat/simplex/app/views/helpers/ChatInfoImage.kt diff --git a/apps/android/app/src/main/java/chat/simplex/app/views/helpers/ChooseAttachmentView.kt b/apps/multiplatform/android/src/main/java/chat/simplex/app/views/helpers/ChooseAttachmentView.kt similarity index 100% rename from apps/android/app/src/main/java/chat/simplex/app/views/helpers/ChooseAttachmentView.kt rename to apps/multiplatform/android/src/main/java/chat/simplex/app/views/helpers/ChooseAttachmentView.kt diff --git a/apps/android/app/src/main/java/chat/simplex/app/views/helpers/CloseSheetBar.kt b/apps/multiplatform/android/src/main/java/chat/simplex/app/views/helpers/CloseSheetBar.kt similarity index 100% rename from apps/android/app/src/main/java/chat/simplex/app/views/helpers/CloseSheetBar.kt rename to apps/multiplatform/android/src/main/java/chat/simplex/app/views/helpers/CloseSheetBar.kt diff --git a/apps/android/app/src/main/java/chat/simplex/app/views/helpers/CustomIcons.kt b/apps/multiplatform/android/src/main/java/chat/simplex/app/views/helpers/CustomIcons.kt similarity index 100% rename from apps/android/app/src/main/java/chat/simplex/app/views/helpers/CustomIcons.kt rename to apps/multiplatform/android/src/main/java/chat/simplex/app/views/helpers/CustomIcons.kt diff --git a/apps/android/app/src/main/java/chat/simplex/app/views/helpers/CustomTimePicker.kt b/apps/multiplatform/android/src/main/java/chat/simplex/app/views/helpers/CustomTimePicker.kt similarity index 100% rename from apps/android/app/src/main/java/chat/simplex/app/views/helpers/CustomTimePicker.kt rename to apps/multiplatform/android/src/main/java/chat/simplex/app/views/helpers/CustomTimePicker.kt diff --git a/apps/android/app/src/main/java/chat/simplex/app/views/helpers/DataClasses.kt b/apps/multiplatform/android/src/main/java/chat/simplex/app/views/helpers/DataClasses.kt similarity index 100% rename from apps/android/app/src/main/java/chat/simplex/app/views/helpers/DataClasses.kt rename to apps/multiplatform/android/src/main/java/chat/simplex/app/views/helpers/DataClasses.kt diff --git a/apps/android/app/src/main/java/chat/simplex/app/views/helpers/DatabaseUtils.kt b/apps/multiplatform/android/src/main/java/chat/simplex/app/views/helpers/DatabaseUtils.kt similarity index 97% rename from apps/android/app/src/main/java/chat/simplex/app/views/helpers/DatabaseUtils.kt rename to apps/multiplatform/android/src/main/java/chat/simplex/app/views/helpers/DatabaseUtils.kt index 68829da559..f200d4ab69 100644 --- a/apps/android/app/src/main/java/chat/simplex/app/views/helpers/DatabaseUtils.kt +++ b/apps/multiplatform/android/src/main/java/chat/simplex/app/views/helpers/DatabaseUtils.kt @@ -2,8 +2,7 @@ package chat.simplex.app.views.helpers import android.util.Log import chat.simplex.app.* -import chat.simplex.app.model.AppPreferences -import chat.simplex.app.model.SharedPreference +import chat.simplex.app.model.* import chat.simplex.app.views.usersettings.Cryptor import kotlinx.serialization.* import java.io.File @@ -13,7 +12,7 @@ object DatabaseUtils { private val cryptor = Cryptor() private val appPreferences: AppPreferences by lazy { - AppPreferences(SimplexApp.context) + ChatController.appPrefs } private const val DATABASE_PASSWORD_ALIAS: String = "databasePassword" diff --git a/apps/android/app/src/main/java/chat/simplex/app/views/helpers/DefaultBasicTextField.kt b/apps/multiplatform/android/src/main/java/chat/simplex/app/views/helpers/DefaultBasicTextField.kt similarity index 100% rename from apps/android/app/src/main/java/chat/simplex/app/views/helpers/DefaultBasicTextField.kt rename to apps/multiplatform/android/src/main/java/chat/simplex/app/views/helpers/DefaultBasicTextField.kt diff --git a/apps/android/app/src/main/java/chat/simplex/app/views/helpers/DefaultDropdownMenu.kt b/apps/multiplatform/android/src/main/java/chat/simplex/app/views/helpers/DefaultDropdownMenu.kt similarity index 100% rename from apps/android/app/src/main/java/chat/simplex/app/views/helpers/DefaultDropdownMenu.kt rename to apps/multiplatform/android/src/main/java/chat/simplex/app/views/helpers/DefaultDropdownMenu.kt diff --git a/apps/android/app/src/main/java/chat/simplex/app/views/helpers/DefaultSwitch.kt b/apps/multiplatform/android/src/main/java/chat/simplex/app/views/helpers/DefaultSwitch.kt similarity index 100% rename from apps/android/app/src/main/java/chat/simplex/app/views/helpers/DefaultSwitch.kt rename to apps/multiplatform/android/src/main/java/chat/simplex/app/views/helpers/DefaultSwitch.kt diff --git a/apps/android/app/src/main/java/chat/simplex/app/views/helpers/DefaultTopAppBar.kt b/apps/multiplatform/android/src/main/java/chat/simplex/app/views/helpers/DefaultTopAppBar.kt similarity index 100% rename from apps/android/app/src/main/java/chat/simplex/app/views/helpers/DefaultTopAppBar.kt rename to apps/multiplatform/android/src/main/java/chat/simplex/app/views/helpers/DefaultTopAppBar.kt diff --git a/apps/android/app/src/main/java/chat/simplex/app/views/helpers/Enums.kt b/apps/multiplatform/android/src/main/java/chat/simplex/app/views/helpers/Enums.kt similarity index 100% rename from apps/android/app/src/main/java/chat/simplex/app/views/helpers/Enums.kt rename to apps/multiplatform/android/src/main/java/chat/simplex/app/views/helpers/Enums.kt diff --git a/apps/android/app/src/main/java/chat/simplex/app/views/helpers/ExposedDropDownSettingRow.kt b/apps/multiplatform/android/src/main/java/chat/simplex/app/views/helpers/ExposedDropDownSettingRow.kt similarity index 100% rename from apps/android/app/src/main/java/chat/simplex/app/views/helpers/ExposedDropDownSettingRow.kt rename to apps/multiplatform/android/src/main/java/chat/simplex/app/views/helpers/ExposedDropDownSettingRow.kt diff --git a/apps/android/app/src/main/java/chat/simplex/app/views/helpers/GestureDetector.kt b/apps/multiplatform/android/src/main/java/chat/simplex/app/views/helpers/GestureDetector.kt similarity index 94% rename from apps/android/app/src/main/java/chat/simplex/app/views/helpers/GestureDetector.kt rename to apps/multiplatform/android/src/main/java/chat/simplex/app/views/helpers/GestureDetector.kt index 463d0eacbd..daf323dc0d 100644 --- a/apps/android/app/src/main/java/chat/simplex/app/views/helpers/GestureDetector.kt +++ b/apps/multiplatform/android/src/main/java/chat/simplex/app/views/helpers/GestureDetector.kt @@ -28,9 +28,6 @@ import androidx.compose.ui.geometry.Offset import androidx.compose.ui.input.pointer.* import androidx.compose.ui.platform.LocalViewConfiguration import androidx.compose.ui.unit.Density -import androidx.compose.ui.util.fastAll -import androidx.compose.ui.util.fastAny -import androidx.compose.ui.util.fastForEach import chat.simplex.app.TAG import kotlinx.coroutines.* import kotlinx.coroutines.sync.Mutex @@ -98,8 +95,8 @@ suspend fun PointerInputScope.detectGesture( private suspend fun AwaitPointerEventScope.consumeUntilUp() { do { val event = awaitPointerEvent() - event.changes.fastForEach { it.consumeAllChanges() } - } while (event.changes.fastAny { it.pressed }) + event.changes.forEach { it.consumeAllChanges() } + } while (event.changes.any { it.pressed }) } suspend fun AwaitPointerEventScope.awaitFirstDown( @@ -115,7 +112,7 @@ internal suspend fun AwaitPointerEventScope.awaitFirstDownOnPass( do { event = awaitPointerEvent(pass) } while ( - !event.changes.fastAll { + !event.changes.all { if (requireUnconsumed) it.changedToDown() else it.changedToDownIgnoreConsumed() } ) @@ -125,18 +122,18 @@ internal suspend fun AwaitPointerEventScope.awaitFirstDownOnPass( suspend fun AwaitPointerEventScope.waitForUpOrCancellation(): PointerInputChange? { while (true) { val event = awaitPointerEvent(PointerEventPass.Main) - if (event.changes.fastAll { it.changedToUp() }) { + if (event.changes.all { it.changedToUp() }) { return event.changes[0] } - if (event.changes.fastAny { + if (event.changes.any { it.consumed.downChange || it.isOutOfBounds(size, extendedTouchPadding) } ) { return null } val consumeCheck = awaitPointerEvent(PointerEventPass.Final) - if (consumeCheck.changes.fastAny { it.positionChangeConsumed() }) { + if (consumeCheck.changes.any { it.positionChangeConsumed() }) { return null } } @@ -254,7 +251,7 @@ suspend fun PointerInputScope.detectTransformGestures( awaitFirstDown(requireUnconsumed = false) do { val event = awaitPointerEvent() - val canceled = event.changes.fastAny { it.isConsumed } + val canceled = event.changes.any { it.isConsumed } if (!canceled) { val zoomChange = event.calculateZoom() val rotationChange = event.calculateRotation() @@ -288,14 +285,14 @@ suspend fun PointerInputScope.detectTransformGestures( ) { onGesture(centroid, panChange, zoomChange, effectiveRotation) } - event.changes.fastForEach { + event.changes.forEach { if (it.positionChanged() && zoom != 1f && allowIntercept()) { it.consume() } } } } - } while (!canceled && event.changes.fastAny { it.pressed }) + } while (!canceled && event.changes.any { it.pressed }) } } } diff --git a/apps/android/app/src/main/java/chat/simplex/app/views/helpers/GetImageView.kt b/apps/multiplatform/android/src/main/java/chat/simplex/app/views/helpers/GetImageView.kt similarity index 99% rename from apps/android/app/src/main/java/chat/simplex/app/views/helpers/GetImageView.kt rename to apps/multiplatform/android/src/main/java/chat/simplex/app/views/helpers/GetImageView.kt index 9ad3180ae5..0ea74b4b5f 100644 --- a/apps/android/app/src/main/java/chat/simplex/app/views/helpers/GetImageView.kt +++ b/apps/multiplatform/android/src/main/java/chat/simplex/app/views/helpers/GetImageView.kt @@ -111,7 +111,7 @@ fun base64ToBitmap(base64ImageString: String): Bitmap { class CustomTakePicturePreview(var uri: Uri?, var tmpFile: File?): ActivityResultContract() { @CallSuper override fun createIntent(context: Context, input: Void?): Intent { - tmpFile = File.createTempFile("image", ".bmp", File(getAppFilesDirectory(SimplexApp.context))) + tmpFile = File.createTempFile("image", ".bmp", File(getAppFilesDirectory())) // Since the class should return Uri, the file should be deleted somewhere else. And in order to be sure, delegate this to system tmpFile?.deleteOnExit() uri = FileProvider.getUriForFile(context, "${BuildConfig.APPLICATION_ID}.provider", tmpFile!!) diff --git a/apps/android/app/src/main/java/chat/simplex/app/views/helpers/LinkPreviews.kt b/apps/multiplatform/android/src/main/java/chat/simplex/app/views/helpers/LinkPreviews.kt similarity index 100% rename from apps/android/app/src/main/java/chat/simplex/app/views/helpers/LinkPreviews.kt rename to apps/multiplatform/android/src/main/java/chat/simplex/app/views/helpers/LinkPreviews.kt diff --git a/apps/android/app/src/main/java/chat/simplex/app/views/helpers/LocalAuthentication.kt b/apps/multiplatform/android/src/main/java/chat/simplex/app/views/helpers/LocalAuthentication.kt similarity index 98% rename from apps/android/app/src/main/java/chat/simplex/app/views/helpers/LocalAuthentication.kt rename to apps/multiplatform/android/src/main/java/chat/simplex/app/views/helpers/LocalAuthentication.kt index 74c8cdedd0..ed406c32fd 100644 --- a/apps/android/app/src/main/java/chat/simplex/app/views/helpers/LocalAuthentication.kt +++ b/apps/multiplatform/android/src/main/java/chat/simplex/app/views/helpers/LocalAuthentication.kt @@ -40,10 +40,10 @@ fun authenticate( promptTitle: String, promptSubtitle: String, selfDestruct: Boolean = false, - activity: FragmentActivity, usingLAMode: LAMode = SimplexApp.context.chatModel.controller.appPrefs.laMode.get(), completed: (LAResult) -> Unit ) { + val activity = SimplexApp.context.mainActivity.get() ?: return completed(LAResult.Error("")) when (usingLAMode) { LAMode.SYSTEM -> when { SDK_INT in 28..29 -> diff --git a/apps/android/app/src/main/java/chat/simplex/app/views/helpers/MessagesFetcherWorker.kt b/apps/multiplatform/android/src/main/java/chat/simplex/app/views/helpers/MessagesFetcherWorker.kt similarity index 100% rename from apps/android/app/src/main/java/chat/simplex/app/views/helpers/MessagesFetcherWorker.kt rename to apps/multiplatform/android/src/main/java/chat/simplex/app/views/helpers/MessagesFetcherWorker.kt diff --git a/apps/android/app/src/main/java/chat/simplex/app/views/helpers/ModalView.kt b/apps/multiplatform/android/src/main/java/chat/simplex/app/views/helpers/ModalView.kt similarity index 100% rename from apps/android/app/src/main/java/chat/simplex/app/views/helpers/ModalView.kt rename to apps/multiplatform/android/src/main/java/chat/simplex/app/views/helpers/ModalView.kt diff --git a/apps/android/app/src/main/java/chat/simplex/app/views/helpers/Modifiers.kt b/apps/multiplatform/android/src/main/java/chat/simplex/app/views/helpers/Modifiers.kt similarity index 100% rename from apps/android/app/src/main/java/chat/simplex/app/views/helpers/Modifiers.kt rename to apps/multiplatform/android/src/main/java/chat/simplex/app/views/helpers/Modifiers.kt diff --git a/apps/android/app/src/main/java/chat/simplex/app/views/helpers/RecAndPlay.kt b/apps/multiplatform/android/src/main/java/chat/simplex/app/views/helpers/RecAndPlay.kt similarity index 98% rename from apps/android/app/src/main/java/chat/simplex/app/views/helpers/RecAndPlay.kt rename to apps/multiplatform/android/src/main/java/chat/simplex/app/views/helpers/RecAndPlay.kt index 63da5f5c24..e902f3dc37 100644 --- a/apps/android/app/src/main/java/chat/simplex/app/views/helpers/RecAndPlay.kt +++ b/apps/multiplatform/android/src/main/java/chat/simplex/app/views/helpers/RecAndPlay.kt @@ -51,7 +51,7 @@ class RecorderNative(): Recorder { rec.setAudioEncodingBitRate(32000) rec.setMaxDuration(MAX_VOICE_MILLIS_FOR_SENDING) val tmpDir = SimplexApp.context.getDir("temp", Application.MODE_PRIVATE) - val fileToSave = File.createTempFile(generateNewFileName(SimplexApp.context, "voice", "${extension}_"), ".tmp", tmpDir) + val fileToSave = File.createTempFile(generateNewFileName("voice", "${extension}_"), ".tmp", tmpDir) fileToSave.deleteOnExit() val path = fileToSave.absolutePath filePath = path diff --git a/apps/android/app/src/main/java/chat/simplex/app/views/helpers/SearchTextField.kt b/apps/multiplatform/android/src/main/java/chat/simplex/app/views/helpers/SearchTextField.kt similarity index 100% rename from apps/android/app/src/main/java/chat/simplex/app/views/helpers/SearchTextField.kt rename to apps/multiplatform/android/src/main/java/chat/simplex/app/views/helpers/SearchTextField.kt diff --git a/apps/android/app/src/main/java/chat/simplex/app/views/helpers/Section.kt b/apps/multiplatform/android/src/main/java/chat/simplex/app/views/helpers/Section.kt similarity index 100% rename from apps/android/app/src/main/java/chat/simplex/app/views/helpers/Section.kt rename to apps/multiplatform/android/src/main/java/chat/simplex/app/views/helpers/Section.kt diff --git a/apps/android/app/src/main/java/chat/simplex/app/views/helpers/Share.kt b/apps/multiplatform/android/src/main/java/chat/simplex/app/views/helpers/Share.kt similarity index 78% rename from apps/android/app/src/main/java/chat/simplex/app/views/helpers/Share.kt rename to apps/multiplatform/android/src/main/java/chat/simplex/app/views/helpers/Share.kt index 015f05f0e6..0affca2e34 100644 --- a/apps/android/app/src/main/java/chat/simplex/app/views/helpers/Share.kt +++ b/apps/multiplatform/android/src/main/java/chat/simplex/app/views/helpers/Share.kt @@ -18,17 +18,19 @@ import chat.simplex.app.model.CIFile import java.io.BufferedOutputStream import java.io.File -fun shareText(cxt: Context, text: String) { +fun shareText(text: String) { val sendIntent: Intent = Intent().apply { action = Intent.ACTION_SEND putExtra(Intent.EXTRA_TEXT, text) type = "text/plain" } val shareIntent = Intent.createChooser(sendIntent, null) - cxt.startActivity(shareIntent) + // This flag is needed when you start a new activity from non-Activity context + shareIntent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK) + SimplexApp.context.startActivity(shareIntent) } -fun shareFile(cxt: Context, text: String, filePath: String) { +fun shareFile(text: String, filePath: String) { val uri = FileProvider.getUriForFile(SimplexApp.context, "${BuildConfig.APPLICATION_ID}.provider", File(filePath)) val ext = filePath.substringAfterLast(".") val mimeType = MimeTypeMap.getSingleton().getMimeTypeFromExtension(ext) ?: return @@ -41,32 +43,37 @@ fun shareFile(cxt: Context, text: String, filePath: String) { type = mimeType } val shareIntent = Intent.createChooser(sendIntent, null) - cxt.startActivity(shareIntent) + // This flag is needed when you start a new activity from non-Activity context + shareIntent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK) + SimplexApp.context.startActivity(shareIntent) } -fun copyText(cxt: Context, text: String) { - val clipboard = ContextCompat.getSystemService(cxt, ClipboardManager::class.java) +fun copyText(text: String) { + val clipboard = ContextCompat.getSystemService(SimplexApp.context, ClipboardManager::class.java) clipboard?.setPrimaryClip(ClipData.newPlainText("text", text)) } -fun sendEmail(context: Context, subject: String, body: CharSequence) { +fun sendEmail(subject: String, body: CharSequence) { val emailIntent = Intent(Intent.ACTION_SENDTO, Uri.parse("mailto:")) emailIntent.putExtra(Intent.EXTRA_SUBJECT, subject) emailIntent.putExtra(Intent.EXTRA_TEXT, body) + // This flag is needed when you start a new activity from non-Activity context + emailIntent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK) try { - context.startActivity(emailIntent) + SimplexApp.context.startActivity(emailIntent) } catch (e: ActivityNotFoundException) { Log.e(TAG, "No activity was found for handling email intent") } } @Composable -fun rememberSaveFileLauncher(cxt: Context, ciFile: CIFile?): ManagedActivityResultLauncher = +fun rememberSaveFileLauncher(ciFile: CIFile?): ManagedActivityResultLauncher = rememberLauncherForActivityResult( contract = ActivityResultContracts.CreateDocument(), onResult = { destination -> destination?.let { - val filePath = getLoadedFilePath(cxt, ciFile) + val cxt = SimplexApp.context + val filePath = getLoadedFilePath(ciFile) if (filePath != null) { val contentResolver = cxt.contentResolver contentResolver.openOutputStream(destination)?.let { stream -> @@ -95,8 +102,9 @@ fun imageMimeType(fileName: String): String { } /** Before calling, make sure the user allows to write to external storage [Manifest.permission.WRITE_EXTERNAL_STORAGE] */ -fun saveImage(cxt: Context, ciFile: CIFile?) { - val filePath = getLoadedFilePath(cxt, ciFile) +fun saveImage(ciFile: CIFile?) { + val cxt = SimplexApp.context + val filePath = getLoadedFilePath(ciFile) val fileName = ciFile?.fileName if (filePath != null && fileName != null) { val values = ContentValues() diff --git a/apps/android/app/src/main/java/chat/simplex/app/views/helpers/SimpleButton.kt b/apps/multiplatform/android/src/main/java/chat/simplex/app/views/helpers/SimpleButton.kt similarity index 100% rename from apps/android/app/src/main/java/chat/simplex/app/views/helpers/SimpleButton.kt rename to apps/multiplatform/android/src/main/java/chat/simplex/app/views/helpers/SimpleButton.kt diff --git a/apps/android/app/src/main/java/chat/simplex/app/views/helpers/TextEditor.kt b/apps/multiplatform/android/src/main/java/chat/simplex/app/views/helpers/TextEditor.kt similarity index 100% rename from apps/android/app/src/main/java/chat/simplex/app/views/helpers/TextEditor.kt rename to apps/multiplatform/android/src/main/java/chat/simplex/app/views/helpers/TextEditor.kt diff --git a/apps/android/app/src/main/java/chat/simplex/app/views/helpers/Util.kt b/apps/multiplatform/android/src/main/java/chat/simplex/app/views/helpers/Util.kt similarity index 87% rename from apps/android/app/src/main/java/chat/simplex/app/views/helpers/Util.kt rename to apps/multiplatform/android/src/main/java/chat/simplex/app/views/helpers/Util.kt index 1f6d3e7f0b..dea06315a7 100644 --- a/apps/android/app/src/main/java/chat/simplex/app/views/helpers/Util.kt +++ b/apps/multiplatform/android/src/main/java/chat/simplex/app/views/helpers/Util.kt @@ -247,30 +247,30 @@ const val MAX_FILE_SIZE_SMP: Long = 8000000 const val MAX_FILE_SIZE_XFTP: Long = 1_073_741_824 // 1GB -fun getFilesDirectory(context: Context): String { - return context.filesDir.toString() +fun getFilesDirectory(): String { + return SimplexApp.context.filesDir.toString() } -fun getTempFilesDirectory(context: Context): String { - return "${getFilesDirectory(context)}/temp_files" +fun getTempFilesDirectory(): String { + return "${getFilesDirectory()}/temp_files" } -fun getAppFilesDirectory(context: Context): String { - return "${getFilesDirectory(context)}/app_files" +fun getAppFilesDirectory(): String { + return "${getFilesDirectory()}/app_files" } -fun getAppFilePath(context: Context, fileName: String): String { - return "${getAppFilesDirectory(context)}/$fileName" +fun getAppFilePath(fileName: String): String { + return "${getAppFilesDirectory()}/$fileName" } fun getAppFileUri(fileName: String): Uri { - return Uri.parse("${getAppFilesDirectory(SimplexApp.context)}/$fileName") + return Uri.parse("${getAppFilesDirectory()}/$fileName") } -fun getLoadedFilePath(context: Context, file: CIFile?): String? { +fun getLoadedFilePath(file: CIFile?): String? { return if (file?.filePath != null && file.loaded) { - val filePath = getAppFilePath(context, file.filePath) + val filePath = getAppFilePath(file.filePath) if (File(filePath).exists()) filePath else null } else { null @@ -278,12 +278,12 @@ fun getLoadedFilePath(context: Context, file: CIFile?): String? { } // https://developer.android.com/training/data-storage/shared/documents-files#bitmap -fun getLoadedImage(context: Context, file: CIFile?): Bitmap? { - val filePath = getLoadedFilePath(context, file) +fun getLoadedImage(file: CIFile?): Bitmap? { + val filePath = getLoadedFilePath(file) return if (filePath != null) { try { - val uri = FileProvider.getUriForFile(context, "${BuildConfig.APPLICATION_ID}.provider", File(filePath)) - val parcelFileDescriptor = context.contentResolver.openFileDescriptor(uri, "r") + val uri = FileProvider.getUriForFile(SimplexApp.context, "${BuildConfig.APPLICATION_ID}.provider", File(filePath)) + val parcelFileDescriptor = SimplexApp.context.contentResolver.openFileDescriptor(uri, "r") val fileDescriptor = parcelFileDescriptor?.fileDescriptor val image = decodeSampledBitmapFromFileDescriptor(fileDescriptor, 1000, 1000) parcelFileDescriptor?.close() @@ -329,24 +329,24 @@ private fun calculateInSampleSize(options: BitmapFactory.Options, reqWidth: Int, return inSampleSize } -fun getFileName(context: Context, uri: Uri): String? { - return context.contentResolver.query(uri, null, null, null, null)?.use { cursor -> +fun getFileName(uri: Uri): String? { + return SimplexApp.context.contentResolver.query(uri, null, null, null, null)?.use { cursor -> val nameIndex = cursor.getColumnIndex(OpenableColumns.DISPLAY_NAME) cursor.moveToFirst() cursor.getString(nameIndex) } } -fun getAppFilePath(context: Context, uri: Uri): String? { - return context.contentResolver.query(uri, null, null, null, null)?.use { cursor -> +fun getAppFilePath(uri: Uri): String? { + return SimplexApp.context.contentResolver.query(uri, null, null, null, null)?.use { cursor -> val nameIndex = cursor.getColumnIndex(OpenableColumns.DISPLAY_NAME) cursor.moveToFirst() - getAppFilePath(context, cursor.getString(nameIndex)) + getAppFilePath(cursor.getString(nameIndex)) } } -fun getFileSize(context: Context, uri: Uri): Long? { - return context.contentResolver.query(uri, null, null, null, null)?.use { cursor -> +fun getFileSize(uri: Uri): Long? { + return SimplexApp.context.contentResolver.query(uri, null, null, null, null)?.use { cursor -> val sizeIndex = cursor.getColumnIndex(OpenableColumns.SIZE) cursor.moveToFirst() cursor.getLong(sizeIndex) @@ -369,7 +369,7 @@ fun getBitmapFromUri(uri: Uri, withAlertOnException: Boolean = true): Bitmap? { null } } else { - BitmapFactory.decodeFile(getAppFilePath(SimplexApp.context, uri)) + BitmapFactory.decodeFile(getAppFilePath(uri)) } } @@ -389,7 +389,7 @@ fun getDrawableFromUri(uri: Uri, withAlertOnException: Boolean = true): Drawable null } } else { - Drawable.createFromPath(getAppFilePath(SimplexApp.context, uri)) + Drawable.createFromPath(getAppFilePath(uri)) } } @@ -409,17 +409,17 @@ fun getThemeFromUri(uri: Uri, withAlertOnException: Boolean = true): ThemeOverri return null } -fun saveImage(context: Context, uri: Uri): String? { +fun saveImage(uri: Uri): String? { val bitmap = getBitmapFromUri(uri) ?: return null - return saveImage(context, bitmap) + return saveImage(bitmap) } -fun saveImage(context: Context, image: Bitmap): String? { +fun saveImage(image: Bitmap): String? { return try { val ext = if (image.hasAlpha()) "png" else "jpg" val dataResized = resizeImageToDataSize(image, ext == "png", maxDataSize = MAX_IMAGE_SIZE) - val fileToSave = generateNewFileName(context, "IMG", ext) - val file = File(getAppFilePath(context, fileToSave)) + val fileToSave = generateNewFileName("IMG", ext) + val file = File(getAppFilePath(fileToSave)) val output = FileOutputStream(file) dataResized.writeTo(output) output.flush() @@ -431,9 +431,9 @@ fun saveImage(context: Context, image: Bitmap): String? { } } -fun saveAnimImage(context: Context, uri: Uri): String? { +fun saveAnimImage(uri: Uri): String? { return try { - val filename = getFileName(context, uri)?.lowercase() + val filename = getFileName(uri)?.lowercase() var ext = when { // remove everything but extension filename?.contains(".") == true -> filename.replaceBeforeLast('.', "").replace(".", "") @@ -441,10 +441,10 @@ fun saveAnimImage(context: Context, uri: Uri): String? { } // Just in case the image has a strange extension if (ext.length < 3 || ext.length > 4) ext = "gif" - val fileToSave = generateNewFileName(context, "IMG", ext) - val file = File(getAppFilePath(context, fileToSave)) + val fileToSave = generateNewFileName("IMG", ext) + val file = File(getAppFilePath(fileToSave)) val output = FileOutputStream(file) - context.contentResolver.openInputStream(uri)!!.use { input -> + SimplexApp.context.contentResolver.openInputStream(uri)!!.use { input -> output.use { output -> input.copyTo(output) } @@ -460,7 +460,7 @@ fun saveTempImageUncompressed(image: Bitmap, asPng: Boolean): File? { return try { val ext = if (asPng) "png" else "jpg" val tmpDir = SimplexApp.context.getDir("temp", Application.MODE_PRIVATE) - return File(tmpDir.absolutePath + File.separator + generateNewFileName(SimplexApp.context, "IMG", ext)).apply { + return File(tmpDir.absolutePath + File.separator + generateNewFileName("IMG", ext)).apply { outputStream().use { out -> image.compress(if (asPng) Bitmap.CompressFormat.PNG else Bitmap.CompressFormat.JPEG, 85, out) out.flush() @@ -474,13 +474,13 @@ fun saveTempImageUncompressed(image: Bitmap, asPng: Boolean): File? { } } -fun saveFileFromUri(context: Context, uri: Uri): String? { +fun saveFileFromUri(uri: Uri): String? { return try { - val inputStream = context.contentResolver.openInputStream(uri) - val fileToSave = getFileName(context, uri) + val inputStream = SimplexApp.context.contentResolver.openInputStream(uri) + val fileToSave = getFileName(uri) if (inputStream != null && fileToSave != null) { - val destFileName = uniqueCombine(context, fileToSave) - val destFile = File(getAppFilePath(context, destFileName)) + val destFileName = uniqueCombine(fileToSave) + val destFile = File(getAppFilePath(destFileName)) IOUtils.copy(inputStream, FileOutputStream(destFile)) destFileName } else { @@ -493,21 +493,21 @@ fun saveFileFromUri(context: Context, uri: Uri): String? { } } -fun generateNewFileName(context: Context, prefix: String, ext: String): String { +fun generateNewFileName(prefix: String, ext: String): String { val sdf = SimpleDateFormat("yyyyMMdd_HHmmss", Locale.US) sdf.timeZone = TimeZone.getTimeZone("GMT") val timestamp = sdf.format(Date()) - return uniqueCombine(context, "${prefix}_$timestamp.$ext") + return uniqueCombine("${prefix}_$timestamp.$ext") } -fun uniqueCombine(context: Context, fileName: String): String { +fun uniqueCombine(fileName: String): String { val orig = File(fileName) val name = orig.nameWithoutExtension val ext = orig.extension fun tryCombine(n: Int): String { val suffix = if (n == 0) "" else "_$n" val f = "$name$suffix.$ext" - return if (File(getAppFilePath(context, f)).exists()) tryCombine(n + 1) else f + return if (File(getAppFilePath(f)).exists()) tryCombine(n + 1) else f } return tryCombine(0) } @@ -530,8 +530,8 @@ fun formatBytes(bytes: Long): String { } } -fun removeFile(context: Context, fileName: String): Boolean { - val file = File(getAppFilePath(context, fileName)) +fun removeFile(fileName: String): Boolean { + val file = File(getAppFilePath(fileName)) val fileDeleted = file.delete() if (!fileDeleted) { Log.e(chat.simplex.app.TAG, "Util.kt removeFile error") @@ -539,11 +539,11 @@ fun removeFile(context: Context, fileName: String): Boolean { return fileDeleted } -fun deleteAppFiles(context: Context) { - val dir = File(getAppFilesDirectory(context)) +fun deleteAppFiles() { + val dir = File(getAppFilesDirectory()) try { dir.list()?.forEach { - removeFile(context, it) + removeFile(it) } } catch (e: java.lang.Exception) { Log.e(TAG, "Util deleteAppFiles error: ${e.stackTraceToString()}") diff --git a/apps/android/app/src/main/java/chat/simplex/app/views/helpers/VideoPlayer.kt b/apps/multiplatform/android/src/main/java/chat/simplex/app/views/helpers/VideoPlayer.kt similarity index 97% rename from apps/android/app/src/main/java/chat/simplex/app/views/helpers/VideoPlayer.kt rename to apps/multiplatform/android/src/main/java/chat/simplex/app/views/helpers/VideoPlayer.kt index dc0744a752..25f5d6aacf 100644 --- a/apps/android/app/src/main/java/chat/simplex/app/views/helpers/VideoPlayer.kt +++ b/apps/multiplatform/android/src/main/java/chat/simplex/app/views/helpers/VideoPlayer.kt @@ -24,7 +24,6 @@ class VideoPlayer private constructor( private val defaultPreview: Bitmap, defaultDuration: Long, soundEnabled: Boolean, - context: Context ) { companion object { private val players: MutableMap, VideoPlayer> = mutableMapOf() @@ -36,9 +35,8 @@ class VideoPlayer private constructor( defaultPreview: Bitmap, defaultDuration: Long, soundEnabled: Boolean, - context: Context ): VideoPlayer = - players.getOrPut(uri to gallery) { VideoPlayer(uri, gallery, defaultPreview, defaultDuration, soundEnabled, context) } + players.getOrPut(uri to gallery) { VideoPlayer(uri, gallery, defaultPreview, defaultDuration, soundEnabled) } fun enableSound(enable: Boolean, fileName: String?, gallery: Boolean): Boolean = player(fileName, gallery)?.enableSound(enable) == true @@ -76,8 +74,8 @@ class VideoPlayer private constructor( setPreviewAndDuration() } - val player = ExoPlayer.Builder(context, - DefaultRenderersFactory(context)) + val player = ExoPlayer.Builder(SimplexApp.context, + DefaultRenderersFactory(SimplexApp.context)) /*.setLoadControl(DefaultLoadControl.Builder() .setPrioritizeTimeOverSizeThresholds(false) // Could probably save some megabytes in memory in case it will be needed .createDefaultLoadControl())*/ @@ -108,7 +106,7 @@ class VideoPlayer private constructor( } private fun start(seek: Long? = null, onProgressUpdate: (position: Long?, state: TrackState) -> Unit): Boolean { - val filepath = getAppFilePath(SimplexApp.context, uri) + val filepath = getAppFilePath(uri) if (filepath == null || !File(filepath).exists()) { Log.e(TAG, "No such file: $uri") brokenVideo.value = true diff --git a/apps/android/app/src/main/java/chat/simplex/app/views/localauth/LocalAuthView.kt b/apps/multiplatform/android/src/main/java/chat/simplex/app/views/localauth/LocalAuthView.kt similarity index 100% rename from apps/android/app/src/main/java/chat/simplex/app/views/localauth/LocalAuthView.kt rename to apps/multiplatform/android/src/main/java/chat/simplex/app/views/localauth/LocalAuthView.kt diff --git a/apps/android/app/src/main/java/chat/simplex/app/views/localauth/PasscodeView.kt b/apps/multiplatform/android/src/main/java/chat/simplex/app/views/localauth/PasscodeView.kt similarity index 100% rename from apps/android/app/src/main/java/chat/simplex/app/views/localauth/PasscodeView.kt rename to apps/multiplatform/android/src/main/java/chat/simplex/app/views/localauth/PasscodeView.kt diff --git a/apps/android/app/src/main/java/chat/simplex/app/views/localauth/PasswordEntry.kt b/apps/multiplatform/android/src/main/java/chat/simplex/app/views/localauth/PasswordEntry.kt similarity index 100% rename from apps/android/app/src/main/java/chat/simplex/app/views/localauth/PasswordEntry.kt rename to apps/multiplatform/android/src/main/java/chat/simplex/app/views/localauth/PasswordEntry.kt diff --git a/apps/android/app/src/main/java/chat/simplex/app/views/localauth/SetAppPasscodeView.kt b/apps/multiplatform/android/src/main/java/chat/simplex/app/views/localauth/SetAppPasscodeView.kt similarity index 100% rename from apps/android/app/src/main/java/chat/simplex/app/views/localauth/SetAppPasscodeView.kt rename to apps/multiplatform/android/src/main/java/chat/simplex/app/views/localauth/SetAppPasscodeView.kt diff --git a/apps/android/app/src/main/java/chat/simplex/app/views/newchat/AddContactLearnMore.kt b/apps/multiplatform/android/src/main/java/chat/simplex/app/views/newchat/AddContactLearnMore.kt similarity index 100% rename from apps/android/app/src/main/java/chat/simplex/app/views/newchat/AddContactLearnMore.kt rename to apps/multiplatform/android/src/main/java/chat/simplex/app/views/newchat/AddContactLearnMore.kt diff --git a/apps/android/app/src/main/java/chat/simplex/app/views/newchat/AddContactView.kt b/apps/multiplatform/android/src/main/java/chat/simplex/app/views/newchat/AddContactView.kt similarity index 97% rename from apps/android/app/src/main/java/chat/simplex/app/views/newchat/AddContactView.kt rename to apps/multiplatform/android/src/main/java/chat/simplex/app/views/newchat/AddContactView.kt index 99eab4584d..cc5117bb9a 100644 --- a/apps/android/app/src/main/java/chat/simplex/app/views/newchat/AddContactView.kt +++ b/apps/multiplatform/android/src/main/java/chat/simplex/app/views/newchat/AddContactView.kt @@ -12,7 +12,6 @@ import androidx.compose.ui.res.painterResource import androidx.compose.runtime.* import androidx.compose.ui.Alignment import androidx.compose.ui.Modifier -import androidx.compose.ui.platform.LocalContext import androidx.compose.ui.res.stringResource import androidx.compose.ui.text.style.TextAlign import androidx.compose.ui.tooling.preview.Preview @@ -24,11 +23,10 @@ import chat.simplex.app.views.usersettings.SettingsActionItem @Composable fun AddContactView(connReqInvitation: String, connIncognito: Boolean) { - val cxt = LocalContext.current AddContactLayout( connReq = connReqInvitation, connIncognito = connIncognito, - share = { shareText(cxt, connReqInvitation) }, + share = { shareText(connReqInvitation) }, learnMore = { ModalManager.shared.showModal { Column( diff --git a/apps/android/app/src/main/java/chat/simplex/app/views/newchat/AddGroupView.kt b/apps/multiplatform/android/src/main/java/chat/simplex/app/views/newchat/AddGroupView.kt similarity index 100% rename from apps/android/app/src/main/java/chat/simplex/app/views/newchat/AddGroupView.kt rename to apps/multiplatform/android/src/main/java/chat/simplex/app/views/newchat/AddGroupView.kt diff --git a/apps/android/app/src/main/java/chat/simplex/app/views/newchat/ConnectViaLinkView.kt b/apps/multiplatform/android/src/main/java/chat/simplex/app/views/newchat/ConnectViaLinkView.kt similarity index 100% rename from apps/android/app/src/main/java/chat/simplex/app/views/newchat/ConnectViaLinkView.kt rename to apps/multiplatform/android/src/main/java/chat/simplex/app/views/newchat/ConnectViaLinkView.kt diff --git a/apps/android/app/src/main/java/chat/simplex/app/views/newchat/ContactConnectionInfoView.kt b/apps/multiplatform/android/src/main/java/chat/simplex/app/views/newchat/ContactConnectionInfoView.kt similarity index 96% rename from apps/android/app/src/main/java/chat/simplex/app/views/newchat/ContactConnectionInfoView.kt rename to apps/multiplatform/android/src/main/java/chat/simplex/app/views/newchat/ContactConnectionInfoView.kt index fa7ec6f01f..50ad56a5eb 100644 --- a/apps/android/app/src/main/java/chat/simplex/app/views/newchat/ContactConnectionInfoView.kt +++ b/apps/multiplatform/android/src/main/java/chat/simplex/app/views/newchat/ContactConnectionInfoView.kt @@ -11,7 +11,6 @@ import androidx.compose.material.* import androidx.compose.runtime.* import androidx.compose.ui.Modifier import androidx.compose.ui.graphics.Color -import androidx.compose.ui.platform.LocalContext import androidx.compose.ui.res.painterResource import androidx.compose.ui.res.stringResource import androidx.compose.ui.tooling.preview.Preview @@ -45,7 +44,6 @@ fun ContactConnectionInfoView( } } } - val context = LocalContext.current ContactConnectionInfoLayout( connReq = connReqInvitation, contactConnection, @@ -53,7 +51,7 @@ fun ContactConnectionInfoView( focusAlias, deleteConnection = { deleteContactConnectionAlert(contactConnection, chatModel, close) }, onLocalAliasChanged = { setContactAlias(contactConnection, it, chatModel) }, - share = { if (connReqInvitation != null) shareText(context, connReqInvitation) }, + share = { if (connReqInvitation != null) shareText(connReqInvitation) }, learnMore = { ModalManager.shared.showModal { Column( diff --git a/apps/android/app/src/main/java/chat/simplex/app/views/newchat/CreateLinkView.kt b/apps/multiplatform/android/src/main/java/chat/simplex/app/views/newchat/CreateLinkView.kt similarity index 100% rename from apps/android/app/src/main/java/chat/simplex/app/views/newchat/CreateLinkView.kt rename to apps/multiplatform/android/src/main/java/chat/simplex/app/views/newchat/CreateLinkView.kt diff --git a/apps/android/app/src/main/java/chat/simplex/app/views/newchat/NewChatSheet.kt b/apps/multiplatform/android/src/main/java/chat/simplex/app/views/newchat/NewChatSheet.kt similarity index 100% rename from apps/android/app/src/main/java/chat/simplex/app/views/newchat/NewChatSheet.kt rename to apps/multiplatform/android/src/main/java/chat/simplex/app/views/newchat/NewChatSheet.kt diff --git a/apps/android/app/src/main/java/chat/simplex/app/views/newchat/PasteToConnect.kt b/apps/multiplatform/android/src/main/java/chat/simplex/app/views/newchat/PasteToConnect.kt similarity index 100% rename from apps/android/app/src/main/java/chat/simplex/app/views/newchat/PasteToConnect.kt rename to apps/multiplatform/android/src/main/java/chat/simplex/app/views/newchat/PasteToConnect.kt diff --git a/apps/android/app/src/main/java/chat/simplex/app/views/newchat/QRCode.kt b/apps/multiplatform/android/src/main/java/chat/simplex/app/views/newchat/QRCode.kt similarity index 97% rename from apps/android/app/src/main/java/chat/simplex/app/views/newchat/QRCode.kt rename to apps/multiplatform/android/src/main/java/chat/simplex/app/views/newchat/QRCode.kt index 1f190b4216..62c1f4f72e 100644 --- a/apps/android/app/src/main/java/chat/simplex/app/views/newchat/QRCode.kt +++ b/apps/multiplatform/android/src/main/java/chat/simplex/app/views/newchat/QRCode.kt @@ -27,7 +27,6 @@ fun QRCode( tintColor: Color = Color(0xff062d56), withLogo: Boolean = true ) { - val context = LocalContext.current val scope = rememberCoroutineScope() BoxWithConstraints { @@ -47,7 +46,7 @@ fun QRCode( .let { if (withLogo) it.addLogo() else it } val file = saveTempImageUncompressed(image, false) if (file != null) { - shareFile(context, "", file.absolutePath) + shareFile("", file.absolutePath) } } } diff --git a/apps/android/app/src/main/java/chat/simplex/app/views/newchat/QRCodeScanner.kt b/apps/multiplatform/android/src/main/java/chat/simplex/app/views/newchat/QRCodeScanner.kt similarity index 100% rename from apps/android/app/src/main/java/chat/simplex/app/views/newchat/QRCodeScanner.kt rename to apps/multiplatform/android/src/main/java/chat/simplex/app/views/newchat/QRCodeScanner.kt diff --git a/apps/android/app/src/main/java/chat/simplex/app/views/newchat/ScanToConnectView.kt b/apps/multiplatform/android/src/main/java/chat/simplex/app/views/newchat/ScanToConnectView.kt similarity index 100% rename from apps/android/app/src/main/java/chat/simplex/app/views/newchat/ScanToConnectView.kt rename to apps/multiplatform/android/src/main/java/chat/simplex/app/views/newchat/ScanToConnectView.kt diff --git a/apps/android/app/src/main/java/chat/simplex/app/views/onboarding/CreateSimpleXAddress.kt b/apps/multiplatform/android/src/main/java/chat/simplex/app/views/onboarding/CreateSimpleXAddress.kt similarity index 96% rename from apps/android/app/src/main/java/chat/simplex/app/views/onboarding/CreateSimpleXAddress.kt rename to apps/multiplatform/android/src/main/java/chat/simplex/app/views/onboarding/CreateSimpleXAddress.kt index 60dcb04838..f536039542 100644 --- a/apps/android/app/src/main/java/chat/simplex/app/views/onboarding/CreateSimpleXAddress.kt +++ b/apps/multiplatform/android/src/main/java/chat/simplex/app/views/onboarding/CreateSimpleXAddress.kt @@ -9,7 +9,6 @@ import androidx.compose.material.* import androidx.compose.runtime.* import androidx.compose.ui.Alignment import androidx.compose.ui.Modifier -import androidx.compose.ui.platform.LocalContext import androidx.compose.ui.res.painterResource import androidx.compose.ui.res.stringResource import androidx.compose.ui.text.style.TextAlign @@ -24,16 +23,14 @@ import chat.simplex.app.views.newchat.QRCode @Composable fun CreateSimpleXAddress(m: ChatModel) { - val context = LocalContext.current var progressIndicator by remember { mutableStateOf(false) } val userAddress = remember { m.userAddress } CreateSimpleXAddressLayout( userAddress.value, - share = { address: String -> shareText(context, address) }, + share = { address: String -> shareText(address) }, sendEmail = { address -> sendEmail( - context, generalGetString(R.string.email_invite_subject), generalGetString(R.string.email_invite_body).format(address.connReqContact) ) diff --git a/apps/android/app/src/main/java/chat/simplex/app/views/onboarding/HowItWorks.kt b/apps/multiplatform/android/src/main/java/chat/simplex/app/views/onboarding/HowItWorks.kt similarity index 100% rename from apps/android/app/src/main/java/chat/simplex/app/views/onboarding/HowItWorks.kt rename to apps/multiplatform/android/src/main/java/chat/simplex/app/views/onboarding/HowItWorks.kt diff --git a/apps/android/app/src/main/java/chat/simplex/app/views/onboarding/OnboardingView.kt b/apps/multiplatform/android/src/main/java/chat/simplex/app/views/onboarding/OnboardingView.kt similarity index 100% rename from apps/android/app/src/main/java/chat/simplex/app/views/onboarding/OnboardingView.kt rename to apps/multiplatform/android/src/main/java/chat/simplex/app/views/onboarding/OnboardingView.kt diff --git a/apps/android/app/src/main/java/chat/simplex/app/views/onboarding/SetNotificationsMode.kt b/apps/multiplatform/android/src/main/java/chat/simplex/app/views/onboarding/SetNotificationsMode.kt similarity index 80% rename from apps/android/app/src/main/java/chat/simplex/app/views/onboarding/SetNotificationsMode.kt rename to apps/multiplatform/android/src/main/java/chat/simplex/app/views/onboarding/SetNotificationsMode.kt index 8f08fe4cf4..ebb934ec75 100644 --- a/apps/android/app/src/main/java/chat/simplex/app/views/onboarding/SetNotificationsMode.kt +++ b/apps/multiplatform/android/src/main/java/chat/simplex/app/views/onboarding/SetNotificationsMode.kt @@ -1,5 +1,7 @@ package chat.simplex.app.views.onboarding +import android.Manifest +import android.os.Build import androidx.annotation.StringRes import androidx.compose.foundation.* import androidx.compose.foundation.layout.* @@ -15,11 +17,14 @@ import androidx.compose.ui.text.style.TextAlign import androidx.compose.ui.unit.dp import androidx.compose.ui.unit.sp import chat.simplex.app.R +import chat.simplex.app.SimplexApp import chat.simplex.app.model.ChatModel +import chat.simplex.app.model.NtfManager import chat.simplex.app.ui.theme.* import chat.simplex.app.views.helpers.* import chat.simplex.app.views.usersettings.NotificationsMode import chat.simplex.app.views.usersettings.changeNotificationsMode +import com.google.accompanist.permissions.rememberPermissionState @Composable fun SetNotificationsMode(m: ChatModel) { @@ -47,8 +52,24 @@ fun SetNotificationsMode(m: ChatModel) { } Spacer(Modifier.fillMaxHeight().weight(1f)) } - LaunchedEffect(Unit) { - m.controller.ntfManager.createNtfChannelsMaybeShowAlert() + SetNotificationsModeAdditions() +} + +@Composable +fun SetNotificationsModeAdditions() { + if (Build.VERSION.SDK_INT >= 33) { + val notificationsPermissionState = rememberPermissionState(Manifest.permission.POST_NOTIFICATIONS) + LaunchedEffect(notificationsPermissionState.hasPermission) { + if (notificationsPermissionState.hasPermission) { + SimplexApp.context.chatModel.controller.ntfManager.createNtfChannelsMaybeShowAlert() + } else { + notificationsPermissionState.launchPermissionRequest() + } + } + } else { + LaunchedEffect(Unit) { + SimplexApp.context.chatModel.controller.ntfManager.createNtfChannelsMaybeShowAlert() + } } } diff --git a/apps/android/app/src/main/java/chat/simplex/app/views/onboarding/SimpleXInfo.kt b/apps/multiplatform/android/src/main/java/chat/simplex/app/views/onboarding/SimpleXInfo.kt similarity index 100% rename from apps/android/app/src/main/java/chat/simplex/app/views/onboarding/SimpleXInfo.kt rename to apps/multiplatform/android/src/main/java/chat/simplex/app/views/onboarding/SimpleXInfo.kt diff --git a/apps/android/app/src/main/java/chat/simplex/app/views/onboarding/WhatsNewView.kt b/apps/multiplatform/android/src/main/java/chat/simplex/app/views/onboarding/WhatsNewView.kt similarity index 100% rename from apps/android/app/src/main/java/chat/simplex/app/views/onboarding/WhatsNewView.kt rename to apps/multiplatform/android/src/main/java/chat/simplex/app/views/onboarding/WhatsNewView.kt diff --git a/apps/android/app/src/main/java/chat/simplex/app/views/usersettings/AdvancedNetworkSettings.kt b/apps/multiplatform/android/src/main/java/chat/simplex/app/views/usersettings/AdvancedNetworkSettings.kt similarity index 100% rename from apps/android/app/src/main/java/chat/simplex/app/views/usersettings/AdvancedNetworkSettings.kt rename to apps/multiplatform/android/src/main/java/chat/simplex/app/views/usersettings/AdvancedNetworkSettings.kt diff --git a/apps/android/app/src/main/java/chat/simplex/app/views/usersettings/Appearance.kt b/apps/multiplatform/android/src/main/java/chat/simplex/app/views/usersettings/Appearance.kt similarity index 98% rename from apps/android/app/src/main/java/chat/simplex/app/views/usersettings/Appearance.kt rename to apps/multiplatform/android/src/main/java/chat/simplex/app/views/usersettings/Appearance.kt index 8530394170..5233192e2d 100644 --- a/apps/android/app/src/main/java/chat/simplex/app/views/usersettings/Appearance.kt +++ b/apps/multiplatform/android/src/main/java/chat/simplex/app/views/usersettings/Appearance.kt @@ -236,9 +236,8 @@ fun CustomizeThemeView(editColor: (ThemeColor, Color) -> Unit) { } SectionSpacer() SectionView { - val context = LocalContext.current val theme = remember { mutableStateOf(null as String?) } - val exportThemeLauncher = rememberSaveThemeLauncher(context, theme) + val exportThemeLauncher = rememberSaveThemeLauncher(theme) SectionItemView({ val overrides = ThemeManager.currentThemeOverridesForExport(isInDarkTheme) theme.value = yaml.encodeToString(overrides) @@ -375,10 +374,11 @@ private fun DarkThemeSelector(state: State, onSelected: (String) -> Uni //} @Composable -private fun rememberSaveThemeLauncher(cxt: Context, theme: MutableState): ManagedActivityResultLauncher = +private fun rememberSaveThemeLauncher(theme: MutableState): ManagedActivityResultLauncher = rememberLauncherForActivityResult( contract = ActivityResultContracts.CreateDocument(), onResult = { destination -> + val cxt = SimplexApp.context try { destination?.let { val theme = theme.value diff --git a/apps/android/app/src/main/java/chat/simplex/app/views/usersettings/CallSettings.kt b/apps/multiplatform/android/src/main/java/chat/simplex/app/views/usersettings/CallSettings.kt similarity index 100% rename from apps/android/app/src/main/java/chat/simplex/app/views/usersettings/CallSettings.kt rename to apps/multiplatform/android/src/main/java/chat/simplex/app/views/usersettings/CallSettings.kt diff --git a/apps/android/app/src/main/java/chat/simplex/app/views/usersettings/Cryptor.kt b/apps/multiplatform/android/src/main/java/chat/simplex/app/views/usersettings/Cryptor.kt similarity index 100% rename from apps/android/app/src/main/java/chat/simplex/app/views/usersettings/Cryptor.kt rename to apps/multiplatform/android/src/main/java/chat/simplex/app/views/usersettings/Cryptor.kt diff --git a/apps/android/app/src/main/java/chat/simplex/app/views/usersettings/DeveloperView.kt b/apps/multiplatform/android/src/main/java/chat/simplex/app/views/usersettings/DeveloperView.kt similarity index 100% rename from apps/android/app/src/main/java/chat/simplex/app/views/usersettings/DeveloperView.kt rename to apps/multiplatform/android/src/main/java/chat/simplex/app/views/usersettings/DeveloperView.kt diff --git a/apps/android/app/src/main/java/chat/simplex/app/views/usersettings/HelpView.kt b/apps/multiplatform/android/src/main/java/chat/simplex/app/views/usersettings/HelpView.kt similarity index 100% rename from apps/android/app/src/main/java/chat/simplex/app/views/usersettings/HelpView.kt rename to apps/multiplatform/android/src/main/java/chat/simplex/app/views/usersettings/HelpView.kt diff --git a/apps/android/app/src/main/java/chat/simplex/app/views/usersettings/HiddenProfileView.kt b/apps/multiplatform/android/src/main/java/chat/simplex/app/views/usersettings/HiddenProfileView.kt similarity index 100% rename from apps/android/app/src/main/java/chat/simplex/app/views/usersettings/HiddenProfileView.kt rename to apps/multiplatform/android/src/main/java/chat/simplex/app/views/usersettings/HiddenProfileView.kt diff --git a/apps/android/app/src/main/java/chat/simplex/app/views/usersettings/IncognitoView.kt b/apps/multiplatform/android/src/main/java/chat/simplex/app/views/usersettings/IncognitoView.kt similarity index 100% rename from apps/android/app/src/main/java/chat/simplex/app/views/usersettings/IncognitoView.kt rename to apps/multiplatform/android/src/main/java/chat/simplex/app/views/usersettings/IncognitoView.kt diff --git a/apps/android/app/src/main/java/chat/simplex/app/views/usersettings/MarkdownHelpView.kt b/apps/multiplatform/android/src/main/java/chat/simplex/app/views/usersettings/MarkdownHelpView.kt similarity index 100% rename from apps/android/app/src/main/java/chat/simplex/app/views/usersettings/MarkdownHelpView.kt rename to apps/multiplatform/android/src/main/java/chat/simplex/app/views/usersettings/MarkdownHelpView.kt diff --git a/apps/android/app/src/main/java/chat/simplex/app/views/usersettings/NetworkAndServers.kt b/apps/multiplatform/android/src/main/java/chat/simplex/app/views/usersettings/NetworkAndServers.kt similarity index 100% rename from apps/android/app/src/main/java/chat/simplex/app/views/usersettings/NetworkAndServers.kt rename to apps/multiplatform/android/src/main/java/chat/simplex/app/views/usersettings/NetworkAndServers.kt diff --git a/apps/android/app/src/main/java/chat/simplex/app/views/usersettings/NotificationsSettingsView.kt b/apps/multiplatform/android/src/main/java/chat/simplex/app/views/usersettings/NotificationsSettingsView.kt similarity index 99% rename from apps/android/app/src/main/java/chat/simplex/app/views/usersettings/NotificationsSettingsView.kt rename to apps/multiplatform/android/src/main/java/chat/simplex/app/views/usersettings/NotificationsSettingsView.kt index e80283ecba..8bd917e7a5 100644 --- a/apps/android/app/src/main/java/chat/simplex/app/views/usersettings/NotificationsSettingsView.kt +++ b/apps/multiplatform/android/src/main/java/chat/simplex/app/views/usersettings/NotificationsSettingsView.kt @@ -188,7 +188,7 @@ fun notificationPreviewModes(): List> { fun changeNotificationsMode(mode: NotificationsMode, chatModel: ChatModel) { chatModel.controller.appPrefs.notificationsMode.set(mode.name) - if (mode.requiresIgnoringBattery && !chatModel.controller.isIgnoringBatteryOptimizations(chatModel.controller.appContext)) { + if (mode.requiresIgnoringBattery && !chatModel.controller.isIgnoringBatteryOptimizations()) { chatModel.controller.appPrefs.backgroundServiceNoticeShown.set(false) } chatModel.notificationsMode.value = mode diff --git a/apps/android/app/src/main/java/chat/simplex/app/views/usersettings/Preferences.kt b/apps/multiplatform/android/src/main/java/chat/simplex/app/views/usersettings/Preferences.kt similarity index 100% rename from apps/android/app/src/main/java/chat/simplex/app/views/usersettings/Preferences.kt rename to apps/multiplatform/android/src/main/java/chat/simplex/app/views/usersettings/Preferences.kt diff --git a/apps/android/app/src/main/java/chat/simplex/app/views/usersettings/PrivacySettings.kt b/apps/multiplatform/android/src/main/java/chat/simplex/app/views/usersettings/PrivacySettings.kt similarity index 97% rename from apps/android/app/src/main/java/chat/simplex/app/views/usersettings/PrivacySettings.kt rename to apps/multiplatform/android/src/main/java/chat/simplex/app/views/usersettings/PrivacySettings.kt index c73bd6e7ce..b961ba58a2 100644 --- a/apps/android/app/src/main/java/chat/simplex/app/views/usersettings/PrivacySettings.kt +++ b/apps/multiplatform/android/src/main/java/chat/simplex/app/views/usersettings/PrivacySettings.kt @@ -143,7 +143,7 @@ fun SimplexLockView( } else { generalGetString(R.string.chat_lock) }, - generalGetString(R.string.change_lock_mode), activity = activity + generalGetString(R.string.change_lock_mode) ) { laResult -> when (laResult) { is LAResult.Error -> { @@ -153,7 +153,7 @@ fun SimplexLockView( LAResult.Success -> { when (toLAMode) { LAMode.SYSTEM -> { - authenticate(generalGetString(R.string.auth_enable_simplex_lock), promptSubtitle = "", activity = activity, usingLAMode = toLAMode) { laResult -> + authenticate(generalGetString(R.string.auth_enable_simplex_lock), promptSubtitle = "", usingLAMode = toLAMode) { laResult -> when (laResult) { LAResult.Success -> { currentLAMode.set(toLAMode) @@ -189,7 +189,7 @@ fun SimplexLockView( } fun toggleSelfDestruct(selfDestruct: SharedPreference) { - authenticate(generalGetString(R.string.la_current_app_passcode), generalGetString(R.string.change_self_destruct_mode), activity = activity) { laResult -> + authenticate(generalGetString(R.string.la_current_app_passcode), generalGetString(R.string.change_self_destruct_mode)) { laResult -> when (laResult) { is LAResult.Error -> laFailedAlert() is LAResult.Failed -> { /* Can be called multiple times on every failure */ } @@ -208,7 +208,7 @@ fun SimplexLockView( } fun changeLAPassword() { - authenticate(generalGetString(R.string.la_current_app_passcode), generalGetString(R.string.la_change_app_passcode), activity = activity) { laResult -> + authenticate(generalGetString(R.string.la_current_app_passcode), generalGetString(R.string.la_change_app_passcode)) { laResult -> when (laResult) { LAResult.Success -> { ModalManager.shared.showCustomModal { close -> @@ -231,7 +231,7 @@ fun SimplexLockView( } fun changeSelfDestructPassword() { - authenticate(generalGetString(R.string.la_current_app_passcode), generalGetString(R.string.change_self_destruct_passcode), activity = activity) { laResult -> + authenticate(generalGetString(R.string.la_current_app_passcode), generalGetString(R.string.change_self_destruct_passcode)) { laResult -> when (laResult) { LAResult.Success -> { ModalManager.shared.showCustomModal { close -> diff --git a/apps/android/app/src/main/java/chat/simplex/app/views/usersettings/ProtocolServerView.kt b/apps/multiplatform/android/src/main/java/chat/simplex/app/views/usersettings/ProtocolServerView.kt similarity index 100% rename from apps/android/app/src/main/java/chat/simplex/app/views/usersettings/ProtocolServerView.kt rename to apps/multiplatform/android/src/main/java/chat/simplex/app/views/usersettings/ProtocolServerView.kt diff --git a/apps/android/app/src/main/java/chat/simplex/app/views/usersettings/ProtocolServersView.kt b/apps/multiplatform/android/src/main/java/chat/simplex/app/views/usersettings/ProtocolServersView.kt similarity index 100% rename from apps/android/app/src/main/java/chat/simplex/app/views/usersettings/ProtocolServersView.kt rename to apps/multiplatform/android/src/main/java/chat/simplex/app/views/usersettings/ProtocolServersView.kt diff --git a/apps/android/app/src/main/java/chat/simplex/app/views/usersettings/RTCServers.kt b/apps/multiplatform/android/src/main/java/chat/simplex/app/views/usersettings/RTCServers.kt similarity index 100% rename from apps/android/app/src/main/java/chat/simplex/app/views/usersettings/RTCServers.kt rename to apps/multiplatform/android/src/main/java/chat/simplex/app/views/usersettings/RTCServers.kt diff --git a/apps/android/app/src/main/java/chat/simplex/app/views/usersettings/ScanProtocolServer.kt b/apps/multiplatform/android/src/main/java/chat/simplex/app/views/usersettings/ScanProtocolServer.kt similarity index 100% rename from apps/android/app/src/main/java/chat/simplex/app/views/usersettings/ScanProtocolServer.kt rename to apps/multiplatform/android/src/main/java/chat/simplex/app/views/usersettings/ScanProtocolServer.kt diff --git a/apps/android/app/src/main/java/chat/simplex/app/views/usersettings/SettingsView.kt b/apps/multiplatform/android/src/main/java/chat/simplex/app/views/usersettings/SettingsView.kt similarity index 98% rename from apps/android/app/src/main/java/chat/simplex/app/views/usersettings/SettingsView.kt rename to apps/multiplatform/android/src/main/java/chat/simplex/app/views/usersettings/SettingsView.kt index 682c44a12a..2aae79fa8a 100644 --- a/apps/android/app/src/main/java/chat/simplex/app/views/usersettings/SettingsView.kt +++ b/apps/multiplatform/android/src/main/java/chat/simplex/app/views/usersettings/SettingsView.kt @@ -6,7 +6,6 @@ import SectionItemView import SectionItemViewWithIcon import SectionView import TextIconSpaced -import android.content.* import android.content.res.Configuration import androidx.compose.foundation.* import androidx.compose.foundation.layout.* @@ -45,7 +44,6 @@ fun SettingsView(chatModel: ChatModel, setPerformLA: (Boolean, FragmentActivity) if (user != null) { val requireAuth = remember { chatModel.controller.appPrefs.performLA.state } - val context = LocalContext.current SettingsLayout( profile = user.profile, stopped, @@ -92,7 +90,7 @@ fun SettingsView(chatModel: ChatModel, setPerformLA: (Boolean, FragmentActivity) LaunchedEffect(Unit) { if (autoShow) { autoShow = false - runAuth(title, desc, context, onFinishAuth) + runAuth(title, desc, onFinishAuth) } } Box( @@ -103,7 +101,7 @@ fun SettingsView(chatModel: ChatModel, setPerformLA: (Boolean, FragmentActivity) stringResource(R.string.auth_unlock), icon = painterResource(R.drawable.ic_lock), click = { - runAuth(title, desc, context, onFinishAuth) + runAuth(title, desc, onFinishAuth) } ) } @@ -481,11 +479,10 @@ fun PreferenceToggleWithIcon( } } -private fun runAuth(title: String, desc: String, context: Context, onFinish: (success: Boolean) -> Unit) { +private fun runAuth(title: String, desc: String, onFinish: (success: Boolean) -> Unit) { authenticate( title, desc, - activity = context as FragmentActivity, completed = { laResult -> onFinish(laResult == LAResult.Success || laResult is LAResult.Unavailable) } diff --git a/apps/android/app/src/main/java/chat/simplex/app/views/usersettings/UserAddressLearnMore.kt b/apps/multiplatform/android/src/main/java/chat/simplex/app/views/usersettings/UserAddressLearnMore.kt similarity index 100% rename from apps/android/app/src/main/java/chat/simplex/app/views/usersettings/UserAddressLearnMore.kt rename to apps/multiplatform/android/src/main/java/chat/simplex/app/views/usersettings/UserAddressLearnMore.kt diff --git a/apps/android/app/src/main/java/chat/simplex/app/views/usersettings/UserAddressView.kt b/apps/multiplatform/android/src/main/java/chat/simplex/app/views/usersettings/UserAddressView.kt similarity index 98% rename from apps/android/app/src/main/java/chat/simplex/app/views/usersettings/UserAddressView.kt rename to apps/multiplatform/android/src/main/java/chat/simplex/app/views/usersettings/UserAddressView.kt index c9ce92f10b..8f4b467dab 100644 --- a/apps/android/app/src/main/java/chat/simplex/app/views/usersettings/UserAddressView.kt +++ b/apps/multiplatform/android/src/main/java/chat/simplex/app/views/usersettings/UserAddressView.kt @@ -16,7 +16,6 @@ import androidx.compose.runtime.* import androidx.compose.runtime.saveable.rememberSaveable import androidx.compose.ui.Alignment import androidx.compose.ui.Modifier -import androidx.compose.ui.platform.LocalContext import androidx.compose.ui.res.painterResource import androidx.compose.ui.res.stringResource import androidx.compose.ui.tooling.preview.Preview @@ -36,7 +35,6 @@ fun UserAddressView( shareViaProfile: Boolean = false, close: () -> Unit ) { - val context = LocalContext.current val shareViaProfile = remember { mutableStateOf(shareViaProfile) } var progressIndicator by remember { mutableStateOf(false) } val onCloseHandler: MutableState<(close: () -> Unit) -> Unit> = remember { mutableStateOf({ _ -> }) } @@ -94,10 +92,9 @@ fun UserAddressView( } } }, - share = { userAddress: String -> shareText(context, userAddress) }, + share = { userAddress: String -> shareText(userAddress) }, sendEmail = { userAddress -> sendEmail( - context, generalGetString(R.string.email_invite_subject), generalGetString(R.string.email_invite_body).format(userAddress.connReqContact) ) diff --git a/apps/android/app/src/main/java/chat/simplex/app/views/usersettings/UserProfileView.kt b/apps/multiplatform/android/src/main/java/chat/simplex/app/views/usersettings/UserProfileView.kt similarity index 100% rename from apps/android/app/src/main/java/chat/simplex/app/views/usersettings/UserProfileView.kt rename to apps/multiplatform/android/src/main/java/chat/simplex/app/views/usersettings/UserProfileView.kt diff --git a/apps/android/app/src/main/java/chat/simplex/app/views/usersettings/UserProfilesView.kt b/apps/multiplatform/android/src/main/java/chat/simplex/app/views/usersettings/UserProfilesView.kt similarity index 100% rename from apps/android/app/src/main/java/chat/simplex/app/views/usersettings/UserProfilesView.kt rename to apps/multiplatform/android/src/main/java/chat/simplex/app/views/usersettings/UserProfilesView.kt diff --git a/apps/android/app/src/main/java/chat/simplex/app/views/usersettings/VersionInfoView.kt b/apps/multiplatform/android/src/main/java/chat/simplex/app/views/usersettings/VersionInfoView.kt similarity index 100% rename from apps/android/app/src/main/java/chat/simplex/app/views/usersettings/VersionInfoView.kt rename to apps/multiplatform/android/src/main/java/chat/simplex/app/views/usersettings/VersionInfoView.kt diff --git a/apps/android/app/src/main/res/drawable-hdpi/decentralized.png b/apps/multiplatform/android/src/main/res/drawable-hdpi/decentralized.png similarity index 100% rename from apps/android/app/src/main/res/drawable-hdpi/decentralized.png rename to apps/multiplatform/android/src/main/res/drawable-hdpi/decentralized.png diff --git a/apps/android/app/src/main/res/drawable-hdpi/decentralized_light.png b/apps/multiplatform/android/src/main/res/drawable-hdpi/decentralized_light.png similarity index 100% rename from apps/android/app/src/main/res/drawable-hdpi/decentralized_light.png rename to apps/multiplatform/android/src/main/res/drawable-hdpi/decentralized_light.png diff --git a/apps/android/app/src/main/res/drawable-hdpi/icon.png b/apps/multiplatform/android/src/main/res/drawable-hdpi/icon.png similarity index 100% rename from apps/android/app/src/main/res/drawable-hdpi/icon.png rename to apps/multiplatform/android/src/main/res/drawable-hdpi/icon.png diff --git a/apps/android/app/src/main/res/drawable-hdpi/ntf_icon.png b/apps/multiplatform/android/src/main/res/drawable-hdpi/ntf_icon.png similarity index 100% rename from apps/android/app/src/main/res/drawable-hdpi/ntf_icon.png rename to apps/multiplatform/android/src/main/res/drawable-hdpi/ntf_icon.png diff --git a/apps/android/app/src/main/res/drawable-hdpi/ntf_service_icon.png b/apps/multiplatform/android/src/main/res/drawable-hdpi/ntf_service_icon.png similarity index 100% rename from apps/android/app/src/main/res/drawable-hdpi/ntf_service_icon.png rename to apps/multiplatform/android/src/main/res/drawable-hdpi/ntf_service_icon.png diff --git a/apps/android/app/src/main/res/drawable-hdpi/privacy.png b/apps/multiplatform/android/src/main/res/drawable-hdpi/privacy.png similarity index 100% rename from apps/android/app/src/main/res/drawable-hdpi/privacy.png rename to apps/multiplatform/android/src/main/res/drawable-hdpi/privacy.png diff --git a/apps/android/app/src/main/res/drawable-hdpi/shield.png b/apps/multiplatform/android/src/main/res/drawable-hdpi/shield.png similarity index 100% rename from apps/android/app/src/main/res/drawable-hdpi/shield.png rename to apps/multiplatform/android/src/main/res/drawable-hdpi/shield.png diff --git a/apps/android/app/src/main/res/drawable-mdpi/decentralized.png b/apps/multiplatform/android/src/main/res/drawable-mdpi/decentralized.png similarity index 100% rename from apps/android/app/src/main/res/drawable-mdpi/decentralized.png rename to apps/multiplatform/android/src/main/res/drawable-mdpi/decentralized.png diff --git a/apps/android/app/src/main/res/drawable-mdpi/decentralized_light.png b/apps/multiplatform/android/src/main/res/drawable-mdpi/decentralized_light.png similarity index 100% rename from apps/android/app/src/main/res/drawable-mdpi/decentralized_light.png rename to apps/multiplatform/android/src/main/res/drawable-mdpi/decentralized_light.png diff --git a/apps/android/app/src/main/res/drawable-mdpi/ntf_icon.png b/apps/multiplatform/android/src/main/res/drawable-mdpi/ntf_icon.png similarity index 100% rename from apps/android/app/src/main/res/drawable-mdpi/ntf_icon.png rename to apps/multiplatform/android/src/main/res/drawable-mdpi/ntf_icon.png diff --git a/apps/android/app/src/main/res/drawable-mdpi/ntf_service_icon.png b/apps/multiplatform/android/src/main/res/drawable-mdpi/ntf_service_icon.png similarity index 100% rename from apps/android/app/src/main/res/drawable-mdpi/ntf_service_icon.png rename to apps/multiplatform/android/src/main/res/drawable-mdpi/ntf_service_icon.png diff --git a/apps/android/app/src/main/res/drawable-mdpi/privacy.png b/apps/multiplatform/android/src/main/res/drawable-mdpi/privacy.png similarity index 100% rename from apps/android/app/src/main/res/drawable-mdpi/privacy.png rename to apps/multiplatform/android/src/main/res/drawable-mdpi/privacy.png diff --git a/apps/android/app/src/main/res/drawable-mdpi/shield.png b/apps/multiplatform/android/src/main/res/drawable-mdpi/shield.png similarity index 100% rename from apps/android/app/src/main/res/drawable-mdpi/shield.png rename to apps/multiplatform/android/src/main/res/drawable-mdpi/shield.png diff --git a/apps/android/app/src/main/res/drawable-v24/ic_launcher_foreground.xml b/apps/multiplatform/android/src/main/res/drawable-v24/ic_launcher_foreground.xml similarity index 100% rename from apps/android/app/src/main/res/drawable-v24/ic_launcher_foreground.xml rename to apps/multiplatform/android/src/main/res/drawable-v24/ic_launcher_foreground.xml diff --git a/apps/android/app/src/main/res/drawable-xhdpi/decentralized.png b/apps/multiplatform/android/src/main/res/drawable-xhdpi/decentralized.png similarity index 100% rename from apps/android/app/src/main/res/drawable-xhdpi/decentralized.png rename to apps/multiplatform/android/src/main/res/drawable-xhdpi/decentralized.png diff --git a/apps/android/app/src/main/res/drawable-xhdpi/decentralized_light.png b/apps/multiplatform/android/src/main/res/drawable-xhdpi/decentralized_light.png similarity index 100% rename from apps/android/app/src/main/res/drawable-xhdpi/decentralized_light.png rename to apps/multiplatform/android/src/main/res/drawable-xhdpi/decentralized_light.png diff --git a/apps/android/app/src/main/res/drawable-xhdpi/ntf_icon.png b/apps/multiplatform/android/src/main/res/drawable-xhdpi/ntf_icon.png similarity index 100% rename from apps/android/app/src/main/res/drawable-xhdpi/ntf_icon.png rename to apps/multiplatform/android/src/main/res/drawable-xhdpi/ntf_icon.png diff --git a/apps/android/app/src/main/res/drawable-xhdpi/ntf_service_icon.png b/apps/multiplatform/android/src/main/res/drawable-xhdpi/ntf_service_icon.png similarity index 100% rename from apps/android/app/src/main/res/drawable-xhdpi/ntf_service_icon.png rename to apps/multiplatform/android/src/main/res/drawable-xhdpi/ntf_service_icon.png diff --git a/apps/android/app/src/main/res/drawable-xhdpi/privacy.png b/apps/multiplatform/android/src/main/res/drawable-xhdpi/privacy.png similarity index 100% rename from apps/android/app/src/main/res/drawable-xhdpi/privacy.png rename to apps/multiplatform/android/src/main/res/drawable-xhdpi/privacy.png diff --git a/apps/android/app/src/main/res/drawable-xhdpi/shield.png b/apps/multiplatform/android/src/main/res/drawable-xhdpi/shield.png similarity index 100% rename from apps/android/app/src/main/res/drawable-xhdpi/shield.png rename to apps/multiplatform/android/src/main/res/drawable-xhdpi/shield.png diff --git a/apps/android/app/src/main/res/drawable-xxhdpi/decentralized.png b/apps/multiplatform/android/src/main/res/drawable-xxhdpi/decentralized.png similarity index 100% rename from apps/android/app/src/main/res/drawable-xxhdpi/decentralized.png rename to apps/multiplatform/android/src/main/res/drawable-xxhdpi/decentralized.png diff --git a/apps/android/app/src/main/res/drawable-xxhdpi/decentralized_light.png b/apps/multiplatform/android/src/main/res/drawable-xxhdpi/decentralized_light.png similarity index 100% rename from apps/android/app/src/main/res/drawable-xxhdpi/decentralized_light.png rename to apps/multiplatform/android/src/main/res/drawable-xxhdpi/decentralized_light.png diff --git a/apps/android/app/src/main/res/drawable-xxhdpi/ntf_icon.png b/apps/multiplatform/android/src/main/res/drawable-xxhdpi/ntf_icon.png similarity index 100% rename from apps/android/app/src/main/res/drawable-xxhdpi/ntf_icon.png rename to apps/multiplatform/android/src/main/res/drawable-xxhdpi/ntf_icon.png diff --git a/apps/android/app/src/main/res/drawable-xxhdpi/ntf_service_icon.png b/apps/multiplatform/android/src/main/res/drawable-xxhdpi/ntf_service_icon.png similarity index 100% rename from apps/android/app/src/main/res/drawable-xxhdpi/ntf_service_icon.png rename to apps/multiplatform/android/src/main/res/drawable-xxhdpi/ntf_service_icon.png diff --git a/apps/android/app/src/main/res/drawable-xxhdpi/privacy.png b/apps/multiplatform/android/src/main/res/drawable-xxhdpi/privacy.png similarity index 100% rename from apps/android/app/src/main/res/drawable-xxhdpi/privacy.png rename to apps/multiplatform/android/src/main/res/drawable-xxhdpi/privacy.png diff --git a/apps/android/app/src/main/res/drawable-xxhdpi/shield.png b/apps/multiplatform/android/src/main/res/drawable-xxhdpi/shield.png similarity index 100% rename from apps/android/app/src/main/res/drawable-xxhdpi/shield.png rename to apps/multiplatform/android/src/main/res/drawable-xxhdpi/shield.png diff --git a/apps/android/app/src/main/res/drawable-xxxhdpi/decentralized.png b/apps/multiplatform/android/src/main/res/drawable-xxxhdpi/decentralized.png similarity index 100% rename from apps/android/app/src/main/res/drawable-xxxhdpi/decentralized.png rename to apps/multiplatform/android/src/main/res/drawable-xxxhdpi/decentralized.png diff --git a/apps/android/app/src/main/res/drawable-xxxhdpi/decentralized_light.png b/apps/multiplatform/android/src/main/res/drawable-xxxhdpi/decentralized_light.png similarity index 100% rename from apps/android/app/src/main/res/drawable-xxxhdpi/decentralized_light.png rename to apps/multiplatform/android/src/main/res/drawable-xxxhdpi/decentralized_light.png diff --git a/apps/android/app/src/main/res/drawable-xxxhdpi/ntf_icon.png b/apps/multiplatform/android/src/main/res/drawable-xxxhdpi/ntf_icon.png similarity index 100% rename from apps/android/app/src/main/res/drawable-xxxhdpi/ntf_icon.png rename to apps/multiplatform/android/src/main/res/drawable-xxxhdpi/ntf_icon.png diff --git a/apps/android/app/src/main/res/drawable-xxxhdpi/ntf_service_icon.png b/apps/multiplatform/android/src/main/res/drawable-xxxhdpi/ntf_service_icon.png similarity index 100% rename from apps/android/app/src/main/res/drawable-xxxhdpi/ntf_service_icon.png rename to apps/multiplatform/android/src/main/res/drawable-xxxhdpi/ntf_service_icon.png diff --git a/apps/android/app/src/main/res/drawable-xxxhdpi/privacy.png b/apps/multiplatform/android/src/main/res/drawable-xxxhdpi/privacy.png similarity index 100% rename from apps/android/app/src/main/res/drawable-xxxhdpi/privacy.png rename to apps/multiplatform/android/src/main/res/drawable-xxxhdpi/privacy.png diff --git a/apps/android/app/src/main/res/drawable-xxxhdpi/shield.png b/apps/multiplatform/android/src/main/res/drawable-xxxhdpi/shield.png similarity index 100% rename from apps/android/app/src/main/res/drawable-xxxhdpi/shield.png rename to apps/multiplatform/android/src/main/res/drawable-xxxhdpi/shield.png diff --git a/apps/android/app/src/main/res/drawable/edit_text_cursor.xml b/apps/multiplatform/android/src/main/res/drawable/edit_text_cursor.xml similarity index 100% rename from apps/android/app/src/main/res/drawable/edit_text_cursor.xml rename to apps/multiplatform/android/src/main/res/drawable/edit_text_cursor.xml diff --git a/apps/android/app/src/main/res/drawable/ic_account_circle_filled.xml b/apps/multiplatform/android/src/main/res/drawable/ic_account_circle_filled.xml similarity index 100% rename from apps/android/app/src/main/res/drawable/ic_account_circle_filled.xml rename to apps/multiplatform/android/src/main/res/drawable/ic_account_circle_filled.xml diff --git a/apps/android/app/src/main/res/drawable/ic_add.xml b/apps/multiplatform/android/src/main/res/drawable/ic_add.xml similarity index 100% rename from apps/android/app/src/main/res/drawable/ic_add.xml rename to apps/multiplatform/android/src/main/res/drawable/ic_add.xml diff --git a/apps/android/app/src/main/res/drawable/ic_add_link.xml b/apps/multiplatform/android/src/main/res/drawable/ic_add_link.xml similarity index 100% rename from apps/android/app/src/main/res/drawable/ic_add_link.xml rename to apps/multiplatform/android/src/main/res/drawable/ic_add_link.xml diff --git a/apps/android/app/src/main/res/drawable/ic_add_photo.xml b/apps/multiplatform/android/src/main/res/drawable/ic_add_photo.xml similarity index 100% rename from apps/android/app/src/main/res/drawable/ic_add_photo.xml rename to apps/multiplatform/android/src/main/res/drawable/ic_add_photo.xml diff --git a/apps/android/app/src/main/res/drawable/ic_add_reaction.xml b/apps/multiplatform/android/src/main/res/drawable/ic_add_reaction.xml similarity index 100% rename from apps/android/app/src/main/res/drawable/ic_add_reaction.xml rename to apps/multiplatform/android/src/main/res/drawable/ic_add_reaction.xml diff --git a/apps/android/app/src/main/res/drawable/ic_add_reaction_filled.xml b/apps/multiplatform/android/src/main/res/drawable/ic_add_reaction_filled.xml similarity index 100% rename from apps/android/app/src/main/res/drawable/ic_add_reaction_filled.xml rename to apps/multiplatform/android/src/main/res/drawable/ic_add_reaction_filled.xml diff --git a/apps/android/app/src/main/res/drawable/ic_all_inclusive.xml b/apps/multiplatform/android/src/main/res/drawable/ic_all_inclusive.xml similarity index 100% rename from apps/android/app/src/main/res/drawable/ic_all_inclusive.xml rename to apps/multiplatform/android/src/main/res/drawable/ic_all_inclusive.xml diff --git a/apps/android/app/src/main/res/drawable/ic_arrow_back_ios_new.xml b/apps/multiplatform/android/src/main/res/drawable/ic_arrow_back_ios_new.xml similarity index 100% rename from apps/android/app/src/main/res/drawable/ic_arrow_back_ios_new.xml rename to apps/multiplatform/android/src/main/res/drawable/ic_arrow_back_ios_new.xml diff --git a/apps/android/app/src/main/res/drawable/ic_arrow_downward.xml b/apps/multiplatform/android/src/main/res/drawable/ic_arrow_downward.xml similarity index 100% rename from apps/android/app/src/main/res/drawable/ic_arrow_downward.xml rename to apps/multiplatform/android/src/main/res/drawable/ic_arrow_downward.xml diff --git a/apps/android/app/src/main/res/drawable/ic_arrow_forward_ios.xml b/apps/multiplatform/android/src/main/res/drawable/ic_arrow_forward_ios.xml similarity index 100% rename from apps/android/app/src/main/res/drawable/ic_arrow_forward_ios.xml rename to apps/multiplatform/android/src/main/res/drawable/ic_arrow_forward_ios.xml diff --git a/apps/android/app/src/main/res/drawable/ic_arrow_upward.xml b/apps/multiplatform/android/src/main/res/drawable/ic_arrow_upward.xml similarity index 100% rename from apps/android/app/src/main/res/drawable/ic_arrow_upward.xml rename to apps/multiplatform/android/src/main/res/drawable/ic_arrow_upward.xml diff --git a/apps/android/app/src/main/res/drawable/ic_article.xml b/apps/multiplatform/android/src/main/res/drawable/ic_article.xml similarity index 100% rename from apps/android/app/src/main/res/drawable/ic_article.xml rename to apps/multiplatform/android/src/main/res/drawable/ic_article.xml diff --git a/apps/android/app/src/main/res/drawable/ic_attach_file_filled_500.xml b/apps/multiplatform/android/src/main/res/drawable/ic_attach_file_filled_500.xml similarity index 100% rename from apps/android/app/src/main/res/drawable/ic_attach_file_filled_500.xml rename to apps/multiplatform/android/src/main/res/drawable/ic_attach_file_filled_500.xml diff --git a/apps/android/app/src/main/res/drawable/ic_backspace.xml b/apps/multiplatform/android/src/main/res/drawable/ic_backspace.xml similarity index 100% rename from apps/android/app/src/main/res/drawable/ic_backspace.xml rename to apps/multiplatform/android/src/main/res/drawable/ic_backspace.xml diff --git a/apps/android/app/src/main/res/drawable/ic_backup.xml b/apps/multiplatform/android/src/main/res/drawable/ic_backup.xml similarity index 100% rename from apps/android/app/src/main/res/drawable/ic_backup.xml rename to apps/multiplatform/android/src/main/res/drawable/ic_backup.xml diff --git a/apps/android/app/src/main/res/drawable/ic_battery_2_bar.xml b/apps/multiplatform/android/src/main/res/drawable/ic_battery_2_bar.xml similarity index 100% rename from apps/android/app/src/main/res/drawable/ic_battery_2_bar.xml rename to apps/multiplatform/android/src/main/res/drawable/ic_battery_2_bar.xml diff --git a/apps/android/app/src/main/res/drawable/ic_battery_3_bar.xml b/apps/multiplatform/android/src/main/res/drawable/ic_battery_3_bar.xml similarity index 100% rename from apps/android/app/src/main/res/drawable/ic_battery_3_bar.xml rename to apps/multiplatform/android/src/main/res/drawable/ic_battery_3_bar.xml diff --git a/apps/android/app/src/main/res/drawable/ic_bolt.xml b/apps/multiplatform/android/src/main/res/drawable/ic_bolt.xml similarity index 100% rename from apps/android/app/src/main/res/drawable/ic_bolt.xml rename to apps/multiplatform/android/src/main/res/drawable/ic_bolt.xml diff --git a/apps/android/app/src/main/res/drawable/ic_bolt_filled.xml b/apps/multiplatform/android/src/main/res/drawable/ic_bolt_filled.xml similarity index 100% rename from apps/android/app/src/main/res/drawable/ic_bolt_filled.xml rename to apps/multiplatform/android/src/main/res/drawable/ic_bolt_filled.xml diff --git a/apps/android/app/src/main/res/drawable/ic_cable.xml b/apps/multiplatform/android/src/main/res/drawable/ic_cable.xml similarity index 100% rename from apps/android/app/src/main/res/drawable/ic_cable.xml rename to apps/multiplatform/android/src/main/res/drawable/ic_cable.xml diff --git a/apps/android/app/src/main/res/drawable/ic_call.xml b/apps/multiplatform/android/src/main/res/drawable/ic_call.xml similarity index 100% rename from apps/android/app/src/main/res/drawable/ic_call.xml rename to apps/multiplatform/android/src/main/res/drawable/ic_call.xml diff --git a/apps/android/app/src/main/res/drawable/ic_call_500.xml b/apps/multiplatform/android/src/main/res/drawable/ic_call_500.xml similarity index 100% rename from apps/android/app/src/main/res/drawable/ic_call_500.xml rename to apps/multiplatform/android/src/main/res/drawable/ic_call_500.xml diff --git a/apps/android/app/src/main/res/drawable/ic_call_end.xml b/apps/multiplatform/android/src/main/res/drawable/ic_call_end.xml similarity index 100% rename from apps/android/app/src/main/res/drawable/ic_call_end.xml rename to apps/multiplatform/android/src/main/res/drawable/ic_call_end.xml diff --git a/apps/android/app/src/main/res/drawable/ic_call_end_filled.xml b/apps/multiplatform/android/src/main/res/drawable/ic_call_end_filled.xml similarity index 100% rename from apps/android/app/src/main/res/drawable/ic_call_end_filled.xml rename to apps/multiplatform/android/src/main/res/drawable/ic_call_end_filled.xml diff --git a/apps/android/app/src/main/res/drawable/ic_call_filled.xml b/apps/multiplatform/android/src/main/res/drawable/ic_call_filled.xml similarity index 100% rename from apps/android/app/src/main/res/drawable/ic_call_filled.xml rename to apps/multiplatform/android/src/main/res/drawable/ic_call_filled.xml diff --git a/apps/android/app/src/main/res/drawable/ic_camera_enhance.xml b/apps/multiplatform/android/src/main/res/drawable/ic_camera_enhance.xml similarity index 100% rename from apps/android/app/src/main/res/drawable/ic_camera_enhance.xml rename to apps/multiplatform/android/src/main/res/drawable/ic_camera_enhance.xml diff --git a/apps/android/app/src/main/res/drawable/ic_cancel_filled.xml b/apps/multiplatform/android/src/main/res/drawable/ic_cancel_filled.xml similarity index 100% rename from apps/android/app/src/main/res/drawable/ic_cancel_filled.xml rename to apps/multiplatform/android/src/main/res/drawable/ic_cancel_filled.xml diff --git a/apps/android/app/src/main/res/drawable/ic_chat.xml b/apps/multiplatform/android/src/main/res/drawable/ic_chat.xml similarity index 100% rename from apps/android/app/src/main/res/drawable/ic_chat.xml rename to apps/multiplatform/android/src/main/res/drawable/ic_chat.xml diff --git a/apps/android/app/src/main/res/drawable/ic_check.xml b/apps/multiplatform/android/src/main/res/drawable/ic_check.xml similarity index 100% rename from apps/android/app/src/main/res/drawable/ic_check.xml rename to apps/multiplatform/android/src/main/res/drawable/ic_check.xml diff --git a/apps/android/app/src/main/res/drawable/ic_check_circle_filled.xml b/apps/multiplatform/android/src/main/res/drawable/ic_check_circle_filled.xml similarity index 100% rename from apps/android/app/src/main/res/drawable/ic_check_circle_filled.xml rename to apps/multiplatform/android/src/main/res/drawable/ic_check_circle_filled.xml diff --git a/apps/android/app/src/main/res/drawable/ic_check_fill.xml b/apps/multiplatform/android/src/main/res/drawable/ic_check_fill.xml similarity index 100% rename from apps/android/app/src/main/res/drawable/ic_check_fill.xml rename to apps/multiplatform/android/src/main/res/drawable/ic_check_fill.xml diff --git a/apps/android/app/src/main/res/drawable/ic_check_filled.xml b/apps/multiplatform/android/src/main/res/drawable/ic_check_filled.xml similarity index 100% rename from apps/android/app/src/main/res/drawable/ic_check_filled.xml rename to apps/multiplatform/android/src/main/res/drawable/ic_check_filled.xml diff --git a/apps/android/app/src/main/res/drawable/ic_chevron_right.xml b/apps/multiplatform/android/src/main/res/drawable/ic_chevron_right.xml similarity index 100% rename from apps/android/app/src/main/res/drawable/ic_chevron_right.xml rename to apps/multiplatform/android/src/main/res/drawable/ic_chevron_right.xml diff --git a/apps/android/app/src/main/res/drawable/ic_circle.xml b/apps/multiplatform/android/src/main/res/drawable/ic_circle.xml similarity index 100% rename from apps/android/app/src/main/res/drawable/ic_circle.xml rename to apps/multiplatform/android/src/main/res/drawable/ic_circle.xml diff --git a/apps/android/app/src/main/res/drawable/ic_circle_filled.xml b/apps/multiplatform/android/src/main/res/drawable/ic_circle_filled.xml similarity index 100% rename from apps/android/app/src/main/res/drawable/ic_circle_filled.xml rename to apps/multiplatform/android/src/main/res/drawable/ic_circle_filled.xml diff --git a/apps/android/app/src/main/res/drawable/ic_close.xml b/apps/multiplatform/android/src/main/res/drawable/ic_close.xml similarity index 100% rename from apps/android/app/src/main/res/drawable/ic_close.xml rename to apps/multiplatform/android/src/main/res/drawable/ic_close.xml diff --git a/apps/android/app/src/main/res/drawable/ic_code.xml b/apps/multiplatform/android/src/main/res/drawable/ic_code.xml similarity index 100% rename from apps/android/app/src/main/res/drawable/ic_code.xml rename to apps/multiplatform/android/src/main/res/drawable/ic_code.xml diff --git a/apps/android/app/src/main/res/drawable/ic_content_copy.xml b/apps/multiplatform/android/src/main/res/drawable/ic_content_copy.xml similarity index 100% rename from apps/android/app/src/main/res/drawable/ic_content_copy.xml rename to apps/multiplatform/android/src/main/res/drawable/ic_content_copy.xml diff --git a/apps/android/app/src/main/res/drawable/ic_content_paste.xml b/apps/multiplatform/android/src/main/res/drawable/ic_content_paste.xml similarity index 100% rename from apps/android/app/src/main/res/drawable/ic_content_paste.xml rename to apps/multiplatform/android/src/main/res/drawable/ic_content_paste.xml diff --git a/apps/android/app/src/main/res/drawable/ic_database.xml b/apps/multiplatform/android/src/main/res/drawable/ic_database.xml similarity index 100% rename from apps/android/app/src/main/res/drawable/ic_database.xml rename to apps/multiplatform/android/src/main/res/drawable/ic_database.xml diff --git a/apps/android/app/src/main/res/drawable/ic_delete.xml b/apps/multiplatform/android/src/main/res/drawable/ic_delete.xml similarity index 100% rename from apps/android/app/src/main/res/drawable/ic_delete.xml rename to apps/multiplatform/android/src/main/res/drawable/ic_delete.xml diff --git a/apps/android/app/src/main/res/drawable/ic_delete_forever.xml b/apps/multiplatform/android/src/main/res/drawable/ic_delete_forever.xml similarity index 100% rename from apps/android/app/src/main/res/drawable/ic_delete_forever.xml rename to apps/multiplatform/android/src/main/res/drawable/ic_delete_forever.xml diff --git a/apps/android/app/src/main/res/drawable/ic_delete_forever_filled.xml b/apps/multiplatform/android/src/main/res/drawable/ic_delete_forever_filled.xml similarity index 100% rename from apps/android/app/src/main/res/drawable/ic_delete_forever_filled.xml rename to apps/multiplatform/android/src/main/res/drawable/ic_delete_forever_filled.xml diff --git a/apps/android/app/src/main/res/drawable/ic_dns.xml b/apps/multiplatform/android/src/main/res/drawable/ic_dns.xml similarity index 100% rename from apps/android/app/src/main/res/drawable/ic_dns.xml rename to apps/multiplatform/android/src/main/res/drawable/ic_dns.xml diff --git a/apps/android/app/src/main/res/drawable/ic_do_not_disturb_on.xml b/apps/multiplatform/android/src/main/res/drawable/ic_do_not_disturb_on.xml similarity index 100% rename from apps/android/app/src/main/res/drawable/ic_do_not_disturb_on.xml rename to apps/multiplatform/android/src/main/res/drawable/ic_do_not_disturb_on.xml diff --git a/apps/android/app/src/main/res/drawable/ic_done_filled.xml b/apps/multiplatform/android/src/main/res/drawable/ic_done_filled.xml similarity index 100% rename from apps/android/app/src/main/res/drawable/ic_done_filled.xml rename to apps/multiplatform/android/src/main/res/drawable/ic_done_filled.xml diff --git a/apps/android/app/src/main/res/drawable/ic_download.xml b/apps/multiplatform/android/src/main/res/drawable/ic_download.xml similarity index 100% rename from apps/android/app/src/main/res/drawable/ic_download.xml rename to apps/multiplatform/android/src/main/res/drawable/ic_download.xml diff --git a/apps/android/app/src/main/res/drawable/ic_draft.xml b/apps/multiplatform/android/src/main/res/drawable/ic_draft.xml similarity index 100% rename from apps/android/app/src/main/res/drawable/ic_draft.xml rename to apps/multiplatform/android/src/main/res/drawable/ic_draft.xml diff --git a/apps/android/app/src/main/res/drawable/ic_draft_filled.xml b/apps/multiplatform/android/src/main/res/drawable/ic_draft_filled.xml similarity index 100% rename from apps/android/app/src/main/res/drawable/ic_draft_filled.xml rename to apps/multiplatform/android/src/main/res/drawable/ic_draft_filled.xml diff --git a/apps/android/app/src/main/res/drawable/ic_drive_folder_upload.xml b/apps/multiplatform/android/src/main/res/drawable/ic_drive_folder_upload.xml similarity index 100% rename from apps/android/app/src/main/res/drawable/ic_drive_folder_upload.xml rename to apps/multiplatform/android/src/main/res/drawable/ic_drive_folder_upload.xml diff --git a/apps/android/app/src/main/res/drawable/ic_edit.xml b/apps/multiplatform/android/src/main/res/drawable/ic_edit.xml similarity index 100% rename from apps/android/app/src/main/res/drawable/ic_edit.xml rename to apps/multiplatform/android/src/main/res/drawable/ic_edit.xml diff --git a/apps/android/app/src/main/res/drawable/ic_edit_filled.xml b/apps/multiplatform/android/src/main/res/drawable/ic_edit_filled.xml similarity index 100% rename from apps/android/app/src/main/res/drawable/ic_edit_filled.xml rename to apps/multiplatform/android/src/main/res/drawable/ic_edit_filled.xml diff --git a/apps/android/app/src/main/res/drawable/ic_edit_note.xml b/apps/multiplatform/android/src/main/res/drawable/ic_edit_note.xml similarity index 100% rename from apps/android/app/src/main/res/drawable/ic_edit_note.xml rename to apps/multiplatform/android/src/main/res/drawable/ic_edit_note.xml diff --git a/apps/android/app/src/main/res/drawable/ic_electrical_services.xml b/apps/multiplatform/android/src/main/res/drawable/ic_electrical_services.xml similarity index 100% rename from apps/android/app/src/main/res/drawable/ic_electrical_services.xml rename to apps/multiplatform/android/src/main/res/drawable/ic_electrical_services.xml diff --git a/apps/android/app/src/main/res/drawable/ic_error.xml b/apps/multiplatform/android/src/main/res/drawable/ic_error.xml similarity index 100% rename from apps/android/app/src/main/res/drawable/ic_error.xml rename to apps/multiplatform/android/src/main/res/drawable/ic_error.xml diff --git a/apps/android/app/src/main/res/drawable/ic_error_filled.xml b/apps/multiplatform/android/src/main/res/drawable/ic_error_filled.xml similarity index 100% rename from apps/android/app/src/main/res/drawable/ic_error_filled.xml rename to apps/multiplatform/android/src/main/res/drawable/ic_error_filled.xml diff --git a/apps/android/app/src/main/res/drawable/ic_expand_less.xml b/apps/multiplatform/android/src/main/res/drawable/ic_expand_less.xml similarity index 100% rename from apps/android/app/src/main/res/drawable/ic_expand_less.xml rename to apps/multiplatform/android/src/main/res/drawable/ic_expand_less.xml diff --git a/apps/android/app/src/main/res/drawable/ic_expand_more.xml b/apps/multiplatform/android/src/main/res/drawable/ic_expand_more.xml similarity index 100% rename from apps/android/app/src/main/res/drawable/ic_expand_more.xml rename to apps/multiplatform/android/src/main/res/drawable/ic_expand_more.xml diff --git a/apps/android/app/src/main/res/drawable/ic_filter_list.xml b/apps/multiplatform/android/src/main/res/drawable/ic_filter_list.xml similarity index 100% rename from apps/android/app/src/main/res/drawable/ic_filter_list.xml rename to apps/multiplatform/android/src/main/res/drawable/ic_filter_list.xml diff --git a/apps/android/app/src/main/res/drawable/ic_flag.xml b/apps/multiplatform/android/src/main/res/drawable/ic_flag.xml similarity index 100% rename from apps/android/app/src/main/res/drawable/ic_flag.xml rename to apps/multiplatform/android/src/main/res/drawable/ic_flag.xml diff --git a/apps/android/app/src/main/res/drawable/ic_flip_camera_android_filled.xml b/apps/multiplatform/android/src/main/res/drawable/ic_flip_camera_android_filled.xml similarity index 100% rename from apps/android/app/src/main/res/drawable/ic_flip_camera_android_filled.xml rename to apps/multiplatform/android/src/main/res/drawable/ic_flip_camera_android_filled.xml diff --git a/apps/android/app/src/main/res/drawable/ic_github.xml b/apps/multiplatform/android/src/main/res/drawable/ic_github.xml similarity index 100% rename from apps/android/app/src/main/res/drawable/ic_github.xml rename to apps/multiplatform/android/src/main/res/drawable/ic_github.xml diff --git a/apps/android/app/src/main/res/drawable/ic_group.xml b/apps/multiplatform/android/src/main/res/drawable/ic_group.xml similarity index 100% rename from apps/android/app/src/main/res/drawable/ic_group.xml rename to apps/multiplatform/android/src/main/res/drawable/ic_group.xml diff --git a/apps/android/app/src/main/res/drawable/ic_help.xml b/apps/multiplatform/android/src/main/res/drawable/ic_help.xml similarity index 100% rename from apps/android/app/src/main/res/drawable/ic_help.xml rename to apps/multiplatform/android/src/main/res/drawable/ic_help.xml diff --git a/apps/android/app/src/main/res/drawable/ic_image.xml b/apps/multiplatform/android/src/main/res/drawable/ic_image.xml similarity index 100% rename from apps/android/app/src/main/res/drawable/ic_image.xml rename to apps/multiplatform/android/src/main/res/drawable/ic_image.xml diff --git a/apps/android/app/src/main/res/drawable/ic_info.xml b/apps/multiplatform/android/src/main/res/drawable/ic_info.xml similarity index 100% rename from apps/android/app/src/main/res/drawable/ic_info.xml rename to apps/multiplatform/android/src/main/res/drawable/ic_info.xml diff --git a/apps/android/app/src/main/res/drawable/ic_inventory_2.xml b/apps/multiplatform/android/src/main/res/drawable/ic_inventory_2.xml similarity index 100% rename from apps/android/app/src/main/res/drawable/ic_inventory_2.xml rename to apps/multiplatform/android/src/main/res/drawable/ic_inventory_2.xml diff --git a/apps/android/app/src/main/res/drawable/ic_ios_share.xml b/apps/multiplatform/android/src/main/res/drawable/ic_ios_share.xml similarity index 100% rename from apps/android/app/src/main/res/drawable/ic_ios_share.xml rename to apps/multiplatform/android/src/main/res/drawable/ic_ios_share.xml diff --git a/apps/android/app/src/main/res/drawable/ic_keyboard.xml b/apps/multiplatform/android/src/main/res/drawable/ic_keyboard.xml similarity index 100% rename from apps/android/app/src/main/res/drawable/ic_keyboard.xml rename to apps/multiplatform/android/src/main/res/drawable/ic_keyboard.xml diff --git a/apps/android/app/src/main/res/drawable/ic_keyboard_arrow_down.xml b/apps/multiplatform/android/src/main/res/drawable/ic_keyboard_arrow_down.xml similarity index 100% rename from apps/android/app/src/main/res/drawable/ic_keyboard_arrow_down.xml rename to apps/multiplatform/android/src/main/res/drawable/ic_keyboard_arrow_down.xml diff --git a/apps/android/app/src/main/res/drawable/ic_keyboard_voice.xml b/apps/multiplatform/android/src/main/res/drawable/ic_keyboard_voice.xml similarity index 100% rename from apps/android/app/src/main/res/drawable/ic_keyboard_voice.xml rename to apps/multiplatform/android/src/main/res/drawable/ic_keyboard_voice.xml diff --git a/apps/android/app/src/main/res/drawable/ic_keyboard_voice_filled.xml b/apps/multiplatform/android/src/main/res/drawable/ic_keyboard_voice_filled.xml similarity index 100% rename from apps/android/app/src/main/res/drawable/ic_keyboard_voice_filled.xml rename to apps/multiplatform/android/src/main/res/drawable/ic_keyboard_voice_filled.xml diff --git a/apps/android/app/src/main/res/drawable/ic_launcher_background.xml b/apps/multiplatform/android/src/main/res/drawable/ic_launcher_background.xml similarity index 100% rename from apps/android/app/src/main/res/drawable/ic_launcher_background.xml rename to apps/multiplatform/android/src/main/res/drawable/ic_launcher_background.xml diff --git a/apps/android/app/src/main/res/drawable/ic_light_mode.xml b/apps/multiplatform/android/src/main/res/drawable/ic_light_mode.xml similarity index 100% rename from apps/android/app/src/main/res/drawable/ic_light_mode.xml rename to apps/multiplatform/android/src/main/res/drawable/ic_light_mode.xml diff --git a/apps/android/app/src/main/res/drawable/ic_link.xml b/apps/multiplatform/android/src/main/res/drawable/ic_link.xml similarity index 100% rename from apps/android/app/src/main/res/drawable/ic_link.xml rename to apps/multiplatform/android/src/main/res/drawable/ic_link.xml diff --git a/apps/android/app/src/main/res/drawable/ic_lock.xml b/apps/multiplatform/android/src/main/res/drawable/ic_lock.xml similarity index 100% rename from apps/android/app/src/main/res/drawable/ic_lock.xml rename to apps/multiplatform/android/src/main/res/drawable/ic_lock.xml diff --git a/apps/android/app/src/main/res/drawable/ic_lock_filled.xml b/apps/multiplatform/android/src/main/res/drawable/ic_lock_filled.xml similarity index 100% rename from apps/android/app/src/main/res/drawable/ic_lock_filled.xml rename to apps/multiplatform/android/src/main/res/drawable/ic_lock_filled.xml diff --git a/apps/android/app/src/main/res/drawable/ic_lock_open.xml b/apps/multiplatform/android/src/main/res/drawable/ic_lock_open.xml similarity index 100% rename from apps/android/app/src/main/res/drawable/ic_lock_open.xml rename to apps/multiplatform/android/src/main/res/drawable/ic_lock_open.xml diff --git a/apps/android/app/src/main/res/drawable/ic_login.xml b/apps/multiplatform/android/src/main/res/drawable/ic_login.xml similarity index 100% rename from apps/android/app/src/main/res/drawable/ic_login.xml rename to apps/multiplatform/android/src/main/res/drawable/ic_login.xml diff --git a/apps/android/app/src/main/res/drawable/ic_logout.xml b/apps/multiplatform/android/src/main/res/drawable/ic_logout.xml similarity index 100% rename from apps/android/app/src/main/res/drawable/ic_logout.xml rename to apps/multiplatform/android/src/main/res/drawable/ic_logout.xml diff --git a/apps/android/app/src/main/res/drawable/ic_mail.xml b/apps/multiplatform/android/src/main/res/drawable/ic_mail.xml similarity index 100% rename from apps/android/app/src/main/res/drawable/ic_mail.xml rename to apps/multiplatform/android/src/main/res/drawable/ic_mail.xml diff --git a/apps/android/app/src/main/res/drawable/ic_manage_accounts.xml b/apps/multiplatform/android/src/main/res/drawable/ic_manage_accounts.xml similarity index 100% rename from apps/android/app/src/main/res/drawable/ic_manage_accounts.xml rename to apps/multiplatform/android/src/main/res/drawable/ic_manage_accounts.xml diff --git a/apps/android/app/src/main/res/drawable/ic_maps_ugc.xml b/apps/multiplatform/android/src/main/res/drawable/ic_maps_ugc.xml similarity index 100% rename from apps/android/app/src/main/res/drawable/ic_maps_ugc.xml rename to apps/multiplatform/android/src/main/res/drawable/ic_maps_ugc.xml diff --git a/apps/android/app/src/main/res/drawable/ic_mark_chat_unread.xml b/apps/multiplatform/android/src/main/res/drawable/ic_mark_chat_unread.xml similarity index 100% rename from apps/android/app/src/main/res/drawable/ic_mark_chat_unread.xml rename to apps/multiplatform/android/src/main/res/drawable/ic_mark_chat_unread.xml diff --git a/apps/android/app/src/main/res/drawable/ic_menu.xml b/apps/multiplatform/android/src/main/res/drawable/ic_menu.xml similarity index 100% rename from apps/android/app/src/main/res/drawable/ic_menu.xml rename to apps/multiplatform/android/src/main/res/drawable/ic_menu.xml diff --git a/apps/android/app/src/main/res/drawable/ic_mic.xml b/apps/multiplatform/android/src/main/res/drawable/ic_mic.xml similarity index 100% rename from apps/android/app/src/main/res/drawable/ic_mic.xml rename to apps/multiplatform/android/src/main/res/drawable/ic_mic.xml diff --git a/apps/android/app/src/main/res/drawable/ic_mic_filled.xml b/apps/multiplatform/android/src/main/res/drawable/ic_mic_filled.xml similarity index 100% rename from apps/android/app/src/main/res/drawable/ic_mic_filled.xml rename to apps/multiplatform/android/src/main/res/drawable/ic_mic_filled.xml diff --git a/apps/android/app/src/main/res/drawable/ic_mic_off.xml b/apps/multiplatform/android/src/main/res/drawable/ic_mic_off.xml similarity index 100% rename from apps/android/app/src/main/res/drawable/ic_mic_off.xml rename to apps/multiplatform/android/src/main/res/drawable/ic_mic_off.xml diff --git a/apps/android/app/src/main/res/drawable/ic_more_horiz.xml b/apps/multiplatform/android/src/main/res/drawable/ic_more_horiz.xml similarity index 100% rename from apps/android/app/src/main/res/drawable/ic_more_horiz.xml rename to apps/multiplatform/android/src/main/res/drawable/ic_more_horiz.xml diff --git a/apps/android/app/src/main/res/drawable/ic_note_add.xml b/apps/multiplatform/android/src/main/res/drawable/ic_note_add.xml similarity index 100% rename from apps/android/app/src/main/res/drawable/ic_note_add.xml rename to apps/multiplatform/android/src/main/res/drawable/ic_note_add.xml diff --git a/apps/android/app/src/main/res/drawable/ic_notifications.xml b/apps/multiplatform/android/src/main/res/drawable/ic_notifications.xml similarity index 100% rename from apps/android/app/src/main/res/drawable/ic_notifications.xml rename to apps/multiplatform/android/src/main/res/drawable/ic_notifications.xml diff --git a/apps/android/app/src/main/res/drawable/ic_notifications_off.xml b/apps/multiplatform/android/src/main/res/drawable/ic_notifications_off.xml similarity index 100% rename from apps/android/app/src/main/res/drawable/ic_notifications_off.xml rename to apps/multiplatform/android/src/main/res/drawable/ic_notifications_off.xml diff --git a/apps/android/app/src/main/res/drawable/ic_notifications_off_filled.xml b/apps/multiplatform/android/src/main/res/drawable/ic_notifications_off_filled.xml similarity index 100% rename from apps/android/app/src/main/res/drawable/ic_notifications_off_filled.xml rename to apps/multiplatform/android/src/main/res/drawable/ic_notifications_off_filled.xml diff --git a/apps/android/app/src/main/res/drawable/ic_open_in_new.xml b/apps/multiplatform/android/src/main/res/drawable/ic_open_in_new.xml similarity index 100% rename from apps/android/app/src/main/res/drawable/ic_open_in_new.xml rename to apps/multiplatform/android/src/main/res/drawable/ic_open_in_new.xml diff --git a/apps/android/app/src/main/res/drawable/ic_outline_terminal.xml b/apps/multiplatform/android/src/main/res/drawable/ic_outline_terminal.xml similarity index 100% rename from apps/android/app/src/main/res/drawable/ic_outline_terminal.xml rename to apps/multiplatform/android/src/main/res/drawable/ic_outline_terminal.xml diff --git a/apps/android/app/src/main/res/drawable/ic_pause_filled.xml b/apps/multiplatform/android/src/main/res/drawable/ic_pause_filled.xml similarity index 100% rename from apps/android/app/src/main/res/drawable/ic_pause_filled.xml rename to apps/multiplatform/android/src/main/res/drawable/ic_pause_filled.xml diff --git a/apps/android/app/src/main/res/drawable/ic_pending.xml b/apps/multiplatform/android/src/main/res/drawable/ic_pending.xml similarity index 100% rename from apps/android/app/src/main/res/drawable/ic_pending.xml rename to apps/multiplatform/android/src/main/res/drawable/ic_pending.xml diff --git a/apps/android/app/src/main/res/drawable/ic_pending_filled.xml b/apps/multiplatform/android/src/main/res/drawable/ic_pending_filled.xml similarity index 100% rename from apps/android/app/src/main/res/drawable/ic_pending_filled.xml rename to apps/multiplatform/android/src/main/res/drawable/ic_pending_filled.xml diff --git a/apps/android/app/src/main/res/drawable/ic_person.xml b/apps/multiplatform/android/src/main/res/drawable/ic_person.xml similarity index 100% rename from apps/android/app/src/main/res/drawable/ic_person.xml rename to apps/multiplatform/android/src/main/res/drawable/ic_person.xml diff --git a/apps/android/app/src/main/res/drawable/ic_person_add.xml b/apps/multiplatform/android/src/main/res/drawable/ic_person_add.xml similarity index 100% rename from apps/android/app/src/main/res/drawable/ic_person_add.xml rename to apps/multiplatform/android/src/main/res/drawable/ic_person_add.xml diff --git a/apps/android/app/src/main/res/drawable/ic_person_add_500.xml b/apps/multiplatform/android/src/main/res/drawable/ic_person_add_500.xml similarity index 100% rename from apps/android/app/src/main/res/drawable/ic_person_add_500.xml rename to apps/multiplatform/android/src/main/res/drawable/ic_person_add_500.xml diff --git a/apps/android/app/src/main/res/drawable/ic_phone_in_talk_filled.xml b/apps/multiplatform/android/src/main/res/drawable/ic_phone_in_talk_filled.xml similarity index 100% rename from apps/android/app/src/main/res/drawable/ic_phone_in_talk_filled.xml rename to apps/multiplatform/android/src/main/res/drawable/ic_phone_in_talk_filled.xml diff --git a/apps/android/app/src/main/res/drawable/ic_photo_camera.xml b/apps/multiplatform/android/src/main/res/drawable/ic_photo_camera.xml similarity index 100% rename from apps/android/app/src/main/res/drawable/ic_photo_camera.xml rename to apps/multiplatform/android/src/main/res/drawable/ic_photo_camera.xml diff --git a/apps/android/app/src/main/res/drawable/ic_play_arrow_filled.xml b/apps/multiplatform/android/src/main/res/drawable/ic_play_arrow_filled.xml similarity index 100% rename from apps/android/app/src/main/res/drawable/ic_play_arrow_filled.xml rename to apps/multiplatform/android/src/main/res/drawable/ic_play_arrow_filled.xml diff --git a/apps/android/app/src/main/res/drawable/ic_power_settings_new.xml b/apps/multiplatform/android/src/main/res/drawable/ic_power_settings_new.xml similarity index 100% rename from apps/android/app/src/main/res/drawable/ic_power_settings_new.xml rename to apps/multiplatform/android/src/main/res/drawable/ic_power_settings_new.xml diff --git a/apps/android/app/src/main/res/drawable/ic_priority_high.xml b/apps/multiplatform/android/src/main/res/drawable/ic_priority_high.xml similarity index 100% rename from apps/android/app/src/main/res/drawable/ic_priority_high.xml rename to apps/multiplatform/android/src/main/res/drawable/ic_priority_high.xml diff --git a/apps/android/app/src/main/res/drawable/ic_qr_code.xml b/apps/multiplatform/android/src/main/res/drawable/ic_qr_code.xml similarity index 100% rename from apps/android/app/src/main/res/drawable/ic_qr_code.xml rename to apps/multiplatform/android/src/main/res/drawable/ic_qr_code.xml diff --git a/apps/android/app/src/main/res/drawable/ic_repeat_one.xml b/apps/multiplatform/android/src/main/res/drawable/ic_repeat_one.xml similarity index 100% rename from apps/android/app/src/main/res/drawable/ic_repeat_one.xml rename to apps/multiplatform/android/src/main/res/drawable/ic_repeat_one.xml diff --git a/apps/android/app/src/main/res/drawable/ic_replay.xml b/apps/multiplatform/android/src/main/res/drawable/ic_replay.xml similarity index 100% rename from apps/android/app/src/main/res/drawable/ic_replay.xml rename to apps/multiplatform/android/src/main/res/drawable/ic_replay.xml diff --git a/apps/android/app/src/main/res/drawable/ic_reply.xml b/apps/multiplatform/android/src/main/res/drawable/ic_reply.xml similarity index 100% rename from apps/android/app/src/main/res/drawable/ic_reply.xml rename to apps/multiplatform/android/src/main/res/drawable/ic_reply.xml diff --git a/apps/android/app/src/main/res/drawable/ic_report_filled.xml b/apps/multiplatform/android/src/main/res/drawable/ic_report_filled.xml similarity index 100% rename from apps/android/app/src/main/res/drawable/ic_report_filled.xml rename to apps/multiplatform/android/src/main/res/drawable/ic_report_filled.xml diff --git a/apps/android/app/src/main/res/drawable/ic_restart_alt.xml b/apps/multiplatform/android/src/main/res/drawable/ic_restart_alt.xml similarity index 100% rename from apps/android/app/src/main/res/drawable/ic_restart_alt.xml rename to apps/multiplatform/android/src/main/res/drawable/ic_restart_alt.xml diff --git a/apps/android/app/src/main/res/drawable/ic_ring_volume.xml b/apps/multiplatform/android/src/main/res/drawable/ic_ring_volume.xml similarity index 100% rename from apps/android/app/src/main/res/drawable/ic_ring_volume.xml rename to apps/multiplatform/android/src/main/res/drawable/ic_ring_volume.xml diff --git a/apps/android/app/src/main/res/drawable/ic_safety_divider.xml b/apps/multiplatform/android/src/main/res/drawable/ic_safety_divider.xml similarity index 100% rename from apps/android/app/src/main/res/drawable/ic_safety_divider.xml rename to apps/multiplatform/android/src/main/res/drawable/ic_safety_divider.xml diff --git a/apps/android/app/src/main/res/drawable/ic_search.xml b/apps/multiplatform/android/src/main/res/drawable/ic_search.xml similarity index 100% rename from apps/android/app/src/main/res/drawable/ic_search.xml rename to apps/multiplatform/android/src/main/res/drawable/ic_search.xml diff --git a/apps/android/app/src/main/res/drawable/ic_search_500.xml b/apps/multiplatform/android/src/main/res/drawable/ic_search_500.xml similarity index 100% rename from apps/android/app/src/main/res/drawable/ic_search_500.xml rename to apps/multiplatform/android/src/main/res/drawable/ic_search_500.xml diff --git a/apps/android/app/src/main/res/drawable/ic_security.xml b/apps/multiplatform/android/src/main/res/drawable/ic_security.xml similarity index 100% rename from apps/android/app/src/main/res/drawable/ic_security.xml rename to apps/multiplatform/android/src/main/res/drawable/ic_security.xml diff --git a/apps/android/app/src/main/res/drawable/ic_settings.xml b/apps/multiplatform/android/src/main/res/drawable/ic_settings.xml similarity index 100% rename from apps/android/app/src/main/res/drawable/ic_settings.xml rename to apps/multiplatform/android/src/main/res/drawable/ic_settings.xml diff --git a/apps/android/app/src/main/res/drawable/ic_settings_backup_restore.xml b/apps/multiplatform/android/src/main/res/drawable/ic_settings_backup_restore.xml similarity index 100% rename from apps/android/app/src/main/res/drawable/ic_settings_backup_restore.xml rename to apps/multiplatform/android/src/main/res/drawable/ic_settings_backup_restore.xml diff --git a/apps/android/app/src/main/res/drawable/ic_settings_ethernet.xml b/apps/multiplatform/android/src/main/res/drawable/ic_settings_ethernet.xml similarity index 100% rename from apps/android/app/src/main/res/drawable/ic_settings_ethernet.xml rename to apps/multiplatform/android/src/main/res/drawable/ic_settings_ethernet.xml diff --git a/apps/android/app/src/main/res/drawable/ic_settings_phone.xml b/apps/multiplatform/android/src/main/res/drawable/ic_settings_phone.xml similarity index 100% rename from apps/android/app/src/main/res/drawable/ic_settings_phone.xml rename to apps/multiplatform/android/src/main/res/drawable/ic_settings_phone.xml diff --git a/apps/android/app/src/main/res/drawable/ic_share.xml b/apps/multiplatform/android/src/main/res/drawable/ic_share.xml similarity index 100% rename from apps/android/app/src/main/res/drawable/ic_share.xml rename to apps/multiplatform/android/src/main/res/drawable/ic_share.xml diff --git a/apps/android/app/src/main/res/drawable/ic_share_filled.xml b/apps/multiplatform/android/src/main/res/drawable/ic_share_filled.xml similarity index 100% rename from apps/android/app/src/main/res/drawable/ic_share_filled.xml rename to apps/multiplatform/android/src/main/res/drawable/ic_share_filled.xml diff --git a/apps/android/app/src/main/res/drawable/ic_shield.xml b/apps/multiplatform/android/src/main/res/drawable/ic_shield.xml similarity index 100% rename from apps/android/app/src/main/res/drawable/ic_shield.xml rename to apps/multiplatform/android/src/main/res/drawable/ic_shield.xml diff --git a/apps/android/app/src/main/res/drawable/ic_smart_display.xml b/apps/multiplatform/android/src/main/res/drawable/ic_smart_display.xml similarity index 100% rename from apps/android/app/src/main/res/drawable/ic_smart_display.xml rename to apps/multiplatform/android/src/main/res/drawable/ic_smart_display.xml diff --git a/apps/android/app/src/main/res/drawable/ic_star.xml b/apps/multiplatform/android/src/main/res/drawable/ic_star.xml similarity index 100% rename from apps/android/app/src/main/res/drawable/ic_star.xml rename to apps/multiplatform/android/src/main/res/drawable/ic_star.xml diff --git a/apps/android/app/src/main/res/drawable/ic_star_filled.xml b/apps/multiplatform/android/src/main/res/drawable/ic_star_filled.xml similarity index 100% rename from apps/android/app/src/main/res/drawable/ic_star_filled.xml rename to apps/multiplatform/android/src/main/res/drawable/ic_star_filled.xml diff --git a/apps/android/app/src/main/res/drawable/ic_star_off.xml b/apps/multiplatform/android/src/main/res/drawable/ic_star_off.xml similarity index 100% rename from apps/android/app/src/main/res/drawable/ic_star_off.xml rename to apps/multiplatform/android/src/main/res/drawable/ic_star_off.xml diff --git a/apps/android/app/src/main/res/drawable/ic_stop_filled.xml b/apps/multiplatform/android/src/main/res/drawable/ic_stop_filled.xml similarity index 100% rename from apps/android/app/src/main/res/drawable/ic_stop_filled.xml rename to apps/multiplatform/android/src/main/res/drawable/ic_stop_filled.xml diff --git a/apps/android/app/src/main/res/drawable/ic_supervised_user_circle_filled.xml b/apps/multiplatform/android/src/main/res/drawable/ic_supervised_user_circle_filled.xml similarity index 100% rename from apps/android/app/src/main/res/drawable/ic_supervised_user_circle_filled.xml rename to apps/multiplatform/android/src/main/res/drawable/ic_supervised_user_circle_filled.xml diff --git a/apps/android/app/src/main/res/drawable/ic_swap_horizontal_circle.xml b/apps/multiplatform/android/src/main/res/drawable/ic_swap_horizontal_circle.xml similarity index 100% rename from apps/android/app/src/main/res/drawable/ic_swap_horizontal_circle.xml rename to apps/multiplatform/android/src/main/res/drawable/ic_swap_horizontal_circle.xml diff --git a/apps/android/app/src/main/res/drawable/ic_swap_horizontal_circle_filled.xml b/apps/multiplatform/android/src/main/res/drawable/ic_swap_horizontal_circle_filled.xml similarity index 100% rename from apps/android/app/src/main/res/drawable/ic_swap_horizontal_circle_filled.xml rename to apps/multiplatform/android/src/main/res/drawable/ic_swap_horizontal_circle_filled.xml diff --git a/apps/android/app/src/main/res/drawable/ic_tag.xml b/apps/multiplatform/android/src/main/res/drawable/ic_tag.xml similarity index 100% rename from apps/android/app/src/main/res/drawable/ic_tag.xml rename to apps/multiplatform/android/src/main/res/drawable/ic_tag.xml diff --git a/apps/android/app/src/main/res/drawable/ic_task.xml b/apps/multiplatform/android/src/main/res/drawable/ic_task.xml similarity index 100% rename from apps/android/app/src/main/res/drawable/ic_task.xml rename to apps/multiplatform/android/src/main/res/drawable/ic_task.xml diff --git a/apps/android/app/src/main/res/drawable/ic_theater_comedy.xml b/apps/multiplatform/android/src/main/res/drawable/ic_theater_comedy.xml similarity index 100% rename from apps/android/app/src/main/res/drawable/ic_theater_comedy.xml rename to apps/multiplatform/android/src/main/res/drawable/ic_theater_comedy.xml diff --git a/apps/android/app/src/main/res/drawable/ic_theater_comedy_filled.xml b/apps/multiplatform/android/src/main/res/drawable/ic_theater_comedy_filled.xml similarity index 100% rename from apps/android/app/src/main/res/drawable/ic_theater_comedy_filled.xml rename to apps/multiplatform/android/src/main/res/drawable/ic_theater_comedy_filled.xml diff --git a/apps/android/app/src/main/res/drawable/ic_timer.xml b/apps/multiplatform/android/src/main/res/drawable/ic_timer.xml similarity index 100% rename from apps/android/app/src/main/res/drawable/ic_timer.xml rename to apps/multiplatform/android/src/main/res/drawable/ic_timer.xml diff --git a/apps/android/app/src/main/res/drawable/ic_timer_filled.xml b/apps/multiplatform/android/src/main/res/drawable/ic_timer_filled.xml similarity index 100% rename from apps/android/app/src/main/res/drawable/ic_timer_filled.xml rename to apps/multiplatform/android/src/main/res/drawable/ic_timer_filled.xml diff --git a/apps/android/app/src/main/res/drawable/ic_toggle_on.xml b/apps/multiplatform/android/src/main/res/drawable/ic_toggle_on.xml similarity index 100% rename from apps/android/app/src/main/res/drawable/ic_toggle_on.xml rename to apps/multiplatform/android/src/main/res/drawable/ic_toggle_on.xml diff --git a/apps/android/app/src/main/res/drawable/ic_translate.xml b/apps/multiplatform/android/src/main/res/drawable/ic_translate.xml similarity index 100% rename from apps/android/app/src/main/res/drawable/ic_translate.xml rename to apps/multiplatform/android/src/main/res/drawable/ic_translate.xml diff --git a/apps/android/app/src/main/res/drawable/ic_travel_explore.xml b/apps/multiplatform/android/src/main/res/drawable/ic_travel_explore.xml similarity index 100% rename from apps/android/app/src/main/res/drawable/ic_travel_explore.xml rename to apps/multiplatform/android/src/main/res/drawable/ic_travel_explore.xml diff --git a/apps/android/app/src/main/res/drawable/ic_upload_file.xml b/apps/multiplatform/android/src/main/res/drawable/ic_upload_file.xml similarity index 100% rename from apps/android/app/src/main/res/drawable/ic_upload_file.xml rename to apps/multiplatform/android/src/main/res/drawable/ic_upload_file.xml diff --git a/apps/android/app/src/main/res/drawable/ic_verified_user.xml b/apps/multiplatform/android/src/main/res/drawable/ic_verified_user.xml similarity index 100% rename from apps/android/app/src/main/res/drawable/ic_verified_user.xml rename to apps/multiplatform/android/src/main/res/drawable/ic_verified_user.xml diff --git a/apps/android/app/src/main/res/drawable/ic_videocam.xml b/apps/multiplatform/android/src/main/res/drawable/ic_videocam.xml similarity index 100% rename from apps/android/app/src/main/res/drawable/ic_videocam.xml rename to apps/multiplatform/android/src/main/res/drawable/ic_videocam.xml diff --git a/apps/android/app/src/main/res/drawable/ic_videocam_filled.xml b/apps/multiplatform/android/src/main/res/drawable/ic_videocam_filled.xml similarity index 100% rename from apps/android/app/src/main/res/drawable/ic_videocam_filled.xml rename to apps/multiplatform/android/src/main/res/drawable/ic_videocam_filled.xml diff --git a/apps/android/app/src/main/res/drawable/ic_videocam_off.xml b/apps/multiplatform/android/src/main/res/drawable/ic_videocam_off.xml similarity index 100% rename from apps/android/app/src/main/res/drawable/ic_videocam_off.xml rename to apps/multiplatform/android/src/main/res/drawable/ic_videocam_off.xml diff --git a/apps/android/app/src/main/res/drawable/ic_visibility.xml b/apps/multiplatform/android/src/main/res/drawable/ic_visibility.xml similarity index 100% rename from apps/android/app/src/main/res/drawable/ic_visibility.xml rename to apps/multiplatform/android/src/main/res/drawable/ic_visibility.xml diff --git a/apps/android/app/src/main/res/drawable/ic_visibility_filled.xml b/apps/multiplatform/android/src/main/res/drawable/ic_visibility_filled.xml similarity index 100% rename from apps/android/app/src/main/res/drawable/ic_visibility_filled.xml rename to apps/multiplatform/android/src/main/res/drawable/ic_visibility_filled.xml diff --git a/apps/android/app/src/main/res/drawable/ic_visibility_off.xml b/apps/multiplatform/android/src/main/res/drawable/ic_visibility_off.xml similarity index 100% rename from apps/android/app/src/main/res/drawable/ic_visibility_off.xml rename to apps/multiplatform/android/src/main/res/drawable/ic_visibility_off.xml diff --git a/apps/android/app/src/main/res/drawable/ic_visibility_off_filled.xml b/apps/multiplatform/android/src/main/res/drawable/ic_visibility_off_filled.xml similarity index 100% rename from apps/android/app/src/main/res/drawable/ic_visibility_off_filled.xml rename to apps/multiplatform/android/src/main/res/drawable/ic_visibility_off_filled.xml diff --git a/apps/android/app/src/main/res/drawable/ic_volume_down.xml b/apps/multiplatform/android/src/main/res/drawable/ic_volume_down.xml similarity index 100% rename from apps/android/app/src/main/res/drawable/ic_volume_down.xml rename to apps/multiplatform/android/src/main/res/drawable/ic_volume_down.xml diff --git a/apps/android/app/src/main/res/drawable/ic_volume_up.xml b/apps/multiplatform/android/src/main/res/drawable/ic_volume_up.xml similarity index 100% rename from apps/android/app/src/main/res/drawable/ic_volume_up.xml rename to apps/multiplatform/android/src/main/res/drawable/ic_volume_up.xml diff --git a/apps/android/app/src/main/res/drawable/ic_vpn_key_filled.xml b/apps/multiplatform/android/src/main/res/drawable/ic_vpn_key_filled.xml similarity index 100% rename from apps/android/app/src/main/res/drawable/ic_vpn_key_filled.xml rename to apps/multiplatform/android/src/main/res/drawable/ic_vpn_key_filled.xml diff --git a/apps/android/app/src/main/res/drawable/ic_vpn_key_off_filled.xml b/apps/multiplatform/android/src/main/res/drawable/ic_vpn_key_off_filled.xml similarity index 100% rename from apps/android/app/src/main/res/drawable/ic_vpn_key_off_filled.xml rename to apps/multiplatform/android/src/main/res/drawable/ic_vpn_key_off_filled.xml diff --git a/apps/android/app/src/main/res/drawable/ic_warning_filled.xml b/apps/multiplatform/android/src/main/res/drawable/ic_warning_filled.xml similarity index 100% rename from apps/android/app/src/main/res/drawable/ic_warning_filled.xml rename to apps/multiplatform/android/src/main/res/drawable/ic_warning_filled.xml diff --git a/apps/android/app/src/main/res/drawable/ic_wifi_tethering.xml b/apps/multiplatform/android/src/main/res/drawable/ic_wifi_tethering.xml similarity index 100% rename from apps/android/app/src/main/res/drawable/ic_wifi_tethering.xml rename to apps/multiplatform/android/src/main/res/drawable/ic_wifi_tethering.xml diff --git a/apps/android/app/src/main/res/drawable/logo.png b/apps/multiplatform/android/src/main/res/drawable/logo.png similarity index 100% rename from apps/android/app/src/main/res/drawable/logo.png rename to apps/multiplatform/android/src/main/res/drawable/logo.png diff --git a/apps/android/app/src/main/res/drawable/logo_light.png b/apps/multiplatform/android/src/main/res/drawable/logo_light.png similarity index 100% rename from apps/android/app/src/main/res/drawable/logo_light.png rename to apps/multiplatform/android/src/main/res/drawable/logo_light.png diff --git a/apps/android/app/src/main/res/drawable/send_msg_view_background.xml b/apps/multiplatform/android/src/main/res/drawable/send_msg_view_background.xml similarity index 100% rename from apps/android/app/src/main/res/drawable/send_msg_view_background.xml rename to apps/multiplatform/android/src/main/res/drawable/send_msg_view_background.xml diff --git a/apps/android/app/src/main/res/font/inter_bold.ttf b/apps/multiplatform/android/src/main/res/font/inter_bold.ttf similarity index 100% rename from apps/android/app/src/main/res/font/inter_bold.ttf rename to apps/multiplatform/android/src/main/res/font/inter_bold.ttf diff --git a/apps/android/app/src/main/res/font/inter_italic.ttf b/apps/multiplatform/android/src/main/res/font/inter_italic.ttf similarity index 100% rename from apps/android/app/src/main/res/font/inter_italic.ttf rename to apps/multiplatform/android/src/main/res/font/inter_italic.ttf diff --git a/apps/android/app/src/main/res/font/inter_light.ttf b/apps/multiplatform/android/src/main/res/font/inter_light.ttf similarity index 100% rename from apps/android/app/src/main/res/font/inter_light.ttf rename to apps/multiplatform/android/src/main/res/font/inter_light.ttf diff --git a/apps/android/app/src/main/res/font/inter_medium.ttf b/apps/multiplatform/android/src/main/res/font/inter_medium.ttf similarity index 100% rename from apps/android/app/src/main/res/font/inter_medium.ttf rename to apps/multiplatform/android/src/main/res/font/inter_medium.ttf diff --git a/apps/android/app/src/main/res/font/inter_regular.ttf b/apps/multiplatform/android/src/main/res/font/inter_regular.ttf similarity index 100% rename from apps/android/app/src/main/res/font/inter_regular.ttf rename to apps/multiplatform/android/src/main/res/font/inter_regular.ttf diff --git a/apps/android/app/src/main/res/font/inter_semi_bold.ttf b/apps/multiplatform/android/src/main/res/font/inter_semi_bold.ttf similarity index 100% rename from apps/android/app/src/main/res/font/inter_semi_bold.ttf rename to apps/multiplatform/android/src/main/res/font/inter_semi_bold.ttf diff --git a/apps/android/app/src/main/res/mipmap-anydpi-v26/icon.xml b/apps/multiplatform/android/src/main/res/mipmap-anydpi-v26/icon.xml similarity index 100% rename from apps/android/app/src/main/res/mipmap-anydpi-v26/icon.xml rename to apps/multiplatform/android/src/main/res/mipmap-anydpi-v26/icon.xml diff --git a/apps/android/app/src/main/res/mipmap-anydpi-v26/icon_dark_blue.xml b/apps/multiplatform/android/src/main/res/mipmap-anydpi-v26/icon_dark_blue.xml similarity index 100% rename from apps/android/app/src/main/res/mipmap-anydpi-v26/icon_dark_blue.xml rename to apps/multiplatform/android/src/main/res/mipmap-anydpi-v26/icon_dark_blue.xml diff --git a/apps/android/app/src/main/res/mipmap-anydpi-v26/icon_dark_blue_round.xml b/apps/multiplatform/android/src/main/res/mipmap-anydpi-v26/icon_dark_blue_round.xml similarity index 100% rename from apps/android/app/src/main/res/mipmap-anydpi-v26/icon_dark_blue_round.xml rename to apps/multiplatform/android/src/main/res/mipmap-anydpi-v26/icon_dark_blue_round.xml diff --git a/apps/android/app/src/main/res/mipmap-anydpi-v26/icon_round.xml b/apps/multiplatform/android/src/main/res/mipmap-anydpi-v26/icon_round.xml similarity index 100% rename from apps/android/app/src/main/res/mipmap-anydpi-v26/icon_round.xml rename to apps/multiplatform/android/src/main/res/mipmap-anydpi-v26/icon_round.xml diff --git a/apps/android/app/src/main/res/mipmap-anydpi-v26/icon_round_dark_blue.xml b/apps/multiplatform/android/src/main/res/mipmap-anydpi-v26/icon_round_dark_blue.xml similarity index 100% rename from apps/android/app/src/main/res/mipmap-anydpi-v26/icon_round_dark_blue.xml rename to apps/multiplatform/android/src/main/res/mipmap-anydpi-v26/icon_round_dark_blue.xml diff --git a/apps/android/app/src/main/res/mipmap-hdpi/icon.png b/apps/multiplatform/android/src/main/res/mipmap-hdpi/icon.png similarity index 100% rename from apps/android/app/src/main/res/mipmap-hdpi/icon.png rename to apps/multiplatform/android/src/main/res/mipmap-hdpi/icon.png diff --git a/apps/android/app/src/main/res/mipmap-hdpi/icon_dark_blue.png b/apps/multiplatform/android/src/main/res/mipmap-hdpi/icon_dark_blue.png similarity index 100% rename from apps/android/app/src/main/res/mipmap-hdpi/icon_dark_blue.png rename to apps/multiplatform/android/src/main/res/mipmap-hdpi/icon_dark_blue.png diff --git a/apps/android/app/src/main/res/mipmap-hdpi/icon_dark_blue_foreground.png b/apps/multiplatform/android/src/main/res/mipmap-hdpi/icon_dark_blue_foreground.png similarity index 100% rename from apps/android/app/src/main/res/mipmap-hdpi/icon_dark_blue_foreground.png rename to apps/multiplatform/android/src/main/res/mipmap-hdpi/icon_dark_blue_foreground.png diff --git a/apps/android/app/src/main/res/mipmap-hdpi/icon_dark_blue_round.png b/apps/multiplatform/android/src/main/res/mipmap-hdpi/icon_dark_blue_round.png similarity index 100% rename from apps/android/app/src/main/res/mipmap-hdpi/icon_dark_blue_round.png rename to apps/multiplatform/android/src/main/res/mipmap-hdpi/icon_dark_blue_round.png diff --git a/apps/android/app/src/main/res/mipmap-hdpi/icon_foreground.png b/apps/multiplatform/android/src/main/res/mipmap-hdpi/icon_foreground.png similarity index 100% rename from apps/android/app/src/main/res/mipmap-hdpi/icon_foreground.png rename to apps/multiplatform/android/src/main/res/mipmap-hdpi/icon_foreground.png diff --git a/apps/android/app/src/main/res/mipmap-hdpi/icon_round.png b/apps/multiplatform/android/src/main/res/mipmap-hdpi/icon_round.png similarity index 100% rename from apps/android/app/src/main/res/mipmap-hdpi/icon_round.png rename to apps/multiplatform/android/src/main/res/mipmap-hdpi/icon_round.png diff --git a/apps/android/app/src/main/res/mipmap-mdpi/icon.png b/apps/multiplatform/android/src/main/res/mipmap-mdpi/icon.png similarity index 100% rename from apps/android/app/src/main/res/mipmap-mdpi/icon.png rename to apps/multiplatform/android/src/main/res/mipmap-mdpi/icon.png diff --git a/apps/android/app/src/main/res/mipmap-mdpi/icon_dark_blue.png b/apps/multiplatform/android/src/main/res/mipmap-mdpi/icon_dark_blue.png similarity index 100% rename from apps/android/app/src/main/res/mipmap-mdpi/icon_dark_blue.png rename to apps/multiplatform/android/src/main/res/mipmap-mdpi/icon_dark_blue.png diff --git a/apps/android/app/src/main/res/mipmap-mdpi/icon_dark_blue_foreground.png b/apps/multiplatform/android/src/main/res/mipmap-mdpi/icon_dark_blue_foreground.png similarity index 100% rename from apps/android/app/src/main/res/mipmap-mdpi/icon_dark_blue_foreground.png rename to apps/multiplatform/android/src/main/res/mipmap-mdpi/icon_dark_blue_foreground.png diff --git a/apps/android/app/src/main/res/mipmap-mdpi/icon_dark_blue_round.png b/apps/multiplatform/android/src/main/res/mipmap-mdpi/icon_dark_blue_round.png similarity index 100% rename from apps/android/app/src/main/res/mipmap-mdpi/icon_dark_blue_round.png rename to apps/multiplatform/android/src/main/res/mipmap-mdpi/icon_dark_blue_round.png diff --git a/apps/android/app/src/main/res/mipmap-mdpi/icon_foreground.png b/apps/multiplatform/android/src/main/res/mipmap-mdpi/icon_foreground.png similarity index 100% rename from apps/android/app/src/main/res/mipmap-mdpi/icon_foreground.png rename to apps/multiplatform/android/src/main/res/mipmap-mdpi/icon_foreground.png diff --git a/apps/android/app/src/main/res/mipmap-mdpi/icon_round.png b/apps/multiplatform/android/src/main/res/mipmap-mdpi/icon_round.png similarity index 100% rename from apps/android/app/src/main/res/mipmap-mdpi/icon_round.png rename to apps/multiplatform/android/src/main/res/mipmap-mdpi/icon_round.png diff --git a/apps/android/app/src/main/res/mipmap-xhdpi/icon.png b/apps/multiplatform/android/src/main/res/mipmap-xhdpi/icon.png similarity index 100% rename from apps/android/app/src/main/res/mipmap-xhdpi/icon.png rename to apps/multiplatform/android/src/main/res/mipmap-xhdpi/icon.png diff --git a/apps/android/app/src/main/res/mipmap-xhdpi/icon_dark_blue.png b/apps/multiplatform/android/src/main/res/mipmap-xhdpi/icon_dark_blue.png similarity index 100% rename from apps/android/app/src/main/res/mipmap-xhdpi/icon_dark_blue.png rename to apps/multiplatform/android/src/main/res/mipmap-xhdpi/icon_dark_blue.png diff --git a/apps/android/app/src/main/res/mipmap-xhdpi/icon_dark_blue_foreground.png b/apps/multiplatform/android/src/main/res/mipmap-xhdpi/icon_dark_blue_foreground.png similarity index 100% rename from apps/android/app/src/main/res/mipmap-xhdpi/icon_dark_blue_foreground.png rename to apps/multiplatform/android/src/main/res/mipmap-xhdpi/icon_dark_blue_foreground.png diff --git a/apps/android/app/src/main/res/mipmap-xhdpi/icon_dark_blue_round.png b/apps/multiplatform/android/src/main/res/mipmap-xhdpi/icon_dark_blue_round.png similarity index 100% rename from apps/android/app/src/main/res/mipmap-xhdpi/icon_dark_blue_round.png rename to apps/multiplatform/android/src/main/res/mipmap-xhdpi/icon_dark_blue_round.png diff --git a/apps/android/app/src/main/res/mipmap-xhdpi/icon_foreground.png b/apps/multiplatform/android/src/main/res/mipmap-xhdpi/icon_foreground.png similarity index 100% rename from apps/android/app/src/main/res/mipmap-xhdpi/icon_foreground.png rename to apps/multiplatform/android/src/main/res/mipmap-xhdpi/icon_foreground.png diff --git a/apps/android/app/src/main/res/mipmap-xhdpi/icon_round.png b/apps/multiplatform/android/src/main/res/mipmap-xhdpi/icon_round.png similarity index 100% rename from apps/android/app/src/main/res/mipmap-xhdpi/icon_round.png rename to apps/multiplatform/android/src/main/res/mipmap-xhdpi/icon_round.png diff --git a/apps/android/app/src/main/res/mipmap-xxhdpi/icon.png b/apps/multiplatform/android/src/main/res/mipmap-xxhdpi/icon.png similarity index 100% rename from apps/android/app/src/main/res/mipmap-xxhdpi/icon.png rename to apps/multiplatform/android/src/main/res/mipmap-xxhdpi/icon.png diff --git a/apps/android/app/src/main/res/mipmap-xxhdpi/icon_dark_blue.png b/apps/multiplatform/android/src/main/res/mipmap-xxhdpi/icon_dark_blue.png similarity index 100% rename from apps/android/app/src/main/res/mipmap-xxhdpi/icon_dark_blue.png rename to apps/multiplatform/android/src/main/res/mipmap-xxhdpi/icon_dark_blue.png diff --git a/apps/android/app/src/main/res/mipmap-xxhdpi/icon_dark_blue_foreground.png b/apps/multiplatform/android/src/main/res/mipmap-xxhdpi/icon_dark_blue_foreground.png similarity index 100% rename from apps/android/app/src/main/res/mipmap-xxhdpi/icon_dark_blue_foreground.png rename to apps/multiplatform/android/src/main/res/mipmap-xxhdpi/icon_dark_blue_foreground.png diff --git a/apps/android/app/src/main/res/mipmap-xxhdpi/icon_dark_blue_round.png b/apps/multiplatform/android/src/main/res/mipmap-xxhdpi/icon_dark_blue_round.png similarity index 100% rename from apps/android/app/src/main/res/mipmap-xxhdpi/icon_dark_blue_round.png rename to apps/multiplatform/android/src/main/res/mipmap-xxhdpi/icon_dark_blue_round.png diff --git a/apps/android/app/src/main/res/mipmap-xxhdpi/icon_foreground.png b/apps/multiplatform/android/src/main/res/mipmap-xxhdpi/icon_foreground.png similarity index 100% rename from apps/android/app/src/main/res/mipmap-xxhdpi/icon_foreground.png rename to apps/multiplatform/android/src/main/res/mipmap-xxhdpi/icon_foreground.png diff --git a/apps/android/app/src/main/res/mipmap-xxhdpi/icon_round.png b/apps/multiplatform/android/src/main/res/mipmap-xxhdpi/icon_round.png similarity index 100% rename from apps/android/app/src/main/res/mipmap-xxhdpi/icon_round.png rename to apps/multiplatform/android/src/main/res/mipmap-xxhdpi/icon_round.png diff --git a/apps/android/app/src/main/res/mipmap-xxxhdpi/icon.png b/apps/multiplatform/android/src/main/res/mipmap-xxxhdpi/icon.png similarity index 100% rename from apps/android/app/src/main/res/mipmap-xxxhdpi/icon.png rename to apps/multiplatform/android/src/main/res/mipmap-xxxhdpi/icon.png diff --git a/apps/android/app/src/main/res/mipmap-xxxhdpi/icon_dark_blue.png b/apps/multiplatform/android/src/main/res/mipmap-xxxhdpi/icon_dark_blue.png similarity index 100% rename from apps/android/app/src/main/res/mipmap-xxxhdpi/icon_dark_blue.png rename to apps/multiplatform/android/src/main/res/mipmap-xxxhdpi/icon_dark_blue.png diff --git a/apps/android/app/src/main/res/mipmap-xxxhdpi/icon_dark_blue_foreground.png b/apps/multiplatform/android/src/main/res/mipmap-xxxhdpi/icon_dark_blue_foreground.png similarity index 100% rename from apps/android/app/src/main/res/mipmap-xxxhdpi/icon_dark_blue_foreground.png rename to apps/multiplatform/android/src/main/res/mipmap-xxxhdpi/icon_dark_blue_foreground.png diff --git a/apps/android/app/src/main/res/mipmap-xxxhdpi/icon_dark_blue_round.png b/apps/multiplatform/android/src/main/res/mipmap-xxxhdpi/icon_dark_blue_round.png similarity index 100% rename from apps/android/app/src/main/res/mipmap-xxxhdpi/icon_dark_blue_round.png rename to apps/multiplatform/android/src/main/res/mipmap-xxxhdpi/icon_dark_blue_round.png diff --git a/apps/android/app/src/main/res/mipmap-xxxhdpi/icon_foreground.png b/apps/multiplatform/android/src/main/res/mipmap-xxxhdpi/icon_foreground.png similarity index 100% rename from apps/android/app/src/main/res/mipmap-xxxhdpi/icon_foreground.png rename to apps/multiplatform/android/src/main/res/mipmap-xxxhdpi/icon_foreground.png diff --git a/apps/android/app/src/main/res/mipmap-xxxhdpi/icon_round.png b/apps/multiplatform/android/src/main/res/mipmap-xxxhdpi/icon_round.png similarity index 100% rename from apps/android/app/src/main/res/mipmap-xxxhdpi/icon_round.png rename to apps/multiplatform/android/src/main/res/mipmap-xxxhdpi/icon_round.png diff --git a/apps/android/app/src/main/res/raw/ring_once.mp3 b/apps/multiplatform/android/src/main/res/raw/ring_once.mp3 similarity index 100% rename from apps/android/app/src/main/res/raw/ring_once.mp3 rename to apps/multiplatform/android/src/main/res/raw/ring_once.mp3 diff --git a/apps/android/app/src/main/res/values-ar/strings.xml b/apps/multiplatform/android/src/main/res/values-ar/strings.xml similarity index 100% rename from apps/android/app/src/main/res/values-ar/strings.xml rename to apps/multiplatform/android/src/main/res/values-ar/strings.xml diff --git a/apps/android/app/src/main/res/values-cs/strings.xml b/apps/multiplatform/android/src/main/res/values-cs/strings.xml similarity index 100% rename from apps/android/app/src/main/res/values-cs/strings.xml rename to apps/multiplatform/android/src/main/res/values-cs/strings.xml diff --git a/apps/android/app/src/main/res/values-de/strings.xml b/apps/multiplatform/android/src/main/res/values-de/strings.xml similarity index 100% rename from apps/android/app/src/main/res/values-de/strings.xml rename to apps/multiplatform/android/src/main/res/values-de/strings.xml diff --git a/apps/android/app/src/main/res/values-el/strings.xml b/apps/multiplatform/android/src/main/res/values-el/strings.xml similarity index 98% rename from apps/android/app/src/main/res/values-el/strings.xml rename to apps/multiplatform/android/src/main/res/values-el/strings.xml index 6f31189d09..6926e0c891 100644 --- a/apps/android/app/src/main/res/values-el/strings.xml +++ b/apps/multiplatform/android/src/main/res/values-el/strings.xml @@ -42,7 +42,6 @@ Αποδοχή αιτήματος σύνδεσης; αποδεκτή κλήση Πρόσβαση στους διακομιστές μέσω SOCKS proxy στην πόρτα 9050; Ο διακομιστής μεσολάβησης (proxy server) πρέπει να είναι ενεργός πριν ενεργοποιηθεί αυτή η ρύθμιση. - Αποδοχή αιτημάτων Προσθήκη διακομιστή… Προχωρημένες ρυθμίσεις δικτύου Προσθήκη διακομιστών μέσω σάρωσης QR κωδικών. diff --git a/apps/android/app/src/main/res/values-es/strings.xml b/apps/multiplatform/android/src/main/res/values-es/strings.xml similarity index 100% rename from apps/android/app/src/main/res/values-es/strings.xml rename to apps/multiplatform/android/src/main/res/values-es/strings.xml diff --git a/apps/android/app/src/main/res/values-fi/strings.xml b/apps/multiplatform/android/src/main/res/values-fi/strings.xml similarity index 99% rename from apps/android/app/src/main/res/values-fi/strings.xml rename to apps/multiplatform/android/src/main/res/values-fi/strings.xml index c4c7dc1aab..83c37137a0 100644 --- a/apps/android/app/src/main/res/values-fi/strings.xml +++ b/apps/multiplatform/android/src/main/res/values-fi/strings.xml @@ -54,7 +54,6 @@ Tietokannan salauksen tunnuslause päivitetään ja tallennetaan Keystoreen. Poista keskusteluprofiili käyttäjälle poistettu - KEHITÄ LAITE %dh Yhteysvirhe diff --git a/apps/android/app/src/main/res/values-fr/strings.xml b/apps/multiplatform/android/src/main/res/values-fr/strings.xml similarity index 100% rename from apps/android/app/src/main/res/values-fr/strings.xml rename to apps/multiplatform/android/src/main/res/values-fr/strings.xml diff --git a/apps/android/app/src/main/res/values-hi/strings.xml b/apps/multiplatform/android/src/main/res/values-hi/strings.xml similarity index 100% rename from apps/android/app/src/main/res/values-hi/strings.xml rename to apps/multiplatform/android/src/main/res/values-hi/strings.xml diff --git a/apps/android/app/src/main/res/values-hr/strings.xml b/apps/multiplatform/android/src/main/res/values-hr/strings.xml similarity index 100% rename from apps/android/app/src/main/res/values-hr/strings.xml rename to apps/multiplatform/android/src/main/res/values-hr/strings.xml diff --git a/apps/android/app/src/main/res/values-it/strings.xml b/apps/multiplatform/android/src/main/res/values-it/strings.xml similarity index 100% rename from apps/android/app/src/main/res/values-it/strings.xml rename to apps/multiplatform/android/src/main/res/values-it/strings.xml diff --git a/apps/android/app/src/main/res/values-iw/strings.xml b/apps/multiplatform/android/src/main/res/values-iw/strings.xml similarity index 99% rename from apps/android/app/src/main/res/values-iw/strings.xml rename to apps/multiplatform/android/src/main/res/values-iw/strings.xml index cdda3579ba..ca8df0dfab 100644 --- a/apps/android/app/src/main/res/values-iw/strings.xml +++ b/apps/multiplatform/android/src/main/res/values-iw/strings.xml @@ -4,7 +4,6 @@ אשר לאשר בקשת חיבור\? אודות SimpleX Chat - אשר בקשות אודות SimpleX ענה שבוע @@ -25,7 +24,6 @@ מראה גרסת האפליקציה: v%s גרסת אפליקציה: %s - הודעת פתיחה כל אנשי הקשר יישארו מחוברים. תמיד להשתמש בממסר ענה לשיחה @@ -83,7 +81,6 @@ אימות אימות נכשל חזרה - אוטומטית מודגש גיבוב הודעה שגוי מזהה הודעה שגוי @@ -228,7 +225,6 @@ תרומה גרסת ליבה: v%s צור כתובת - בקשות ליצירת קשר צור צור פרופיל יצירת הפרופיל שלך diff --git a/apps/android/app/src/main/res/values-ja/strings.xml b/apps/multiplatform/android/src/main/res/values-ja/strings.xml similarity index 100% rename from apps/android/app/src/main/res/values-ja/strings.xml rename to apps/multiplatform/android/src/main/res/values-ja/strings.xml diff --git a/apps/android/app/src/main/res/values-ko/strings.xml b/apps/multiplatform/android/src/main/res/values-ko/strings.xml similarity index 100% rename from apps/android/app/src/main/res/values-ko/strings.xml rename to apps/multiplatform/android/src/main/res/values-ko/strings.xml diff --git a/apps/android/app/src/main/res/values-lt/strings.xml b/apps/multiplatform/android/src/main/res/values-lt/strings.xml similarity index 100% rename from apps/android/app/src/main/res/values-lt/strings.xml rename to apps/multiplatform/android/src/main/res/values-lt/strings.xml diff --git a/apps/android/app/src/main/res/values-ml/strings.xml b/apps/multiplatform/android/src/main/res/values-ml/strings.xml similarity index 100% rename from apps/android/app/src/main/res/values-ml/strings.xml rename to apps/multiplatform/android/src/main/res/values-ml/strings.xml diff --git a/apps/android/app/src/main/res/values-night/themes.xml b/apps/multiplatform/android/src/main/res/values-night/themes.xml similarity index 100% rename from apps/android/app/src/main/res/values-night/themes.xml rename to apps/multiplatform/android/src/main/res/values-night/themes.xml diff --git a/apps/android/app/src/main/res/values-nl/strings.xml b/apps/multiplatform/android/src/main/res/values-nl/strings.xml similarity index 100% rename from apps/android/app/src/main/res/values-nl/strings.xml rename to apps/multiplatform/android/src/main/res/values-nl/strings.xml diff --git a/apps/android/app/src/main/res/values-pl/strings.xml b/apps/multiplatform/android/src/main/res/values-pl/strings.xml similarity index 100% rename from apps/android/app/src/main/res/values-pl/strings.xml rename to apps/multiplatform/android/src/main/res/values-pl/strings.xml diff --git a/apps/android/app/src/main/res/values-pt-rBR/strings.xml b/apps/multiplatform/android/src/main/res/values-pt-rBR/strings.xml similarity index 100% rename from apps/android/app/src/main/res/values-pt-rBR/strings.xml rename to apps/multiplatform/android/src/main/res/values-pt-rBR/strings.xml diff --git a/apps/android/app/src/main/res/values-pt/strings.xml b/apps/multiplatform/android/src/main/res/values-pt/strings.xml similarity index 99% rename from apps/android/app/src/main/res/values-pt/strings.xml rename to apps/multiplatform/android/src/main/res/values-pt/strings.xml index 4e19cdcd79..6c8bef2c55 100644 --- a/apps/android/app/src/main/res/values-pt/strings.xml +++ b/apps/multiplatform/android/src/main/res/values-pt/strings.xml @@ -7,7 +7,6 @@ Consola de conversa Teste ao servidor falhou! Usar proxy SOCKS - Usar proxy SOCKS (porto 9050) Usar proxy SOCKS\? Porto porto %d @@ -15,7 +14,6 @@ Versão da aplicação Todos os seus contatos permanecerão conectados. Eliminar endereço - Automaticamente a + b chamada perdida Utilizar sempre o servidor de relay @@ -129,7 +127,6 @@ Aceitar Aceitar pedido de ligação\? Aceitar modo anónimo - Aceitar pedidos Adicionar servidores pré-definidos Aceder aos servidores via proxy SOCKS no porto %d\? O proxy tem de iniciar antes de ativar esta opção. Adicionar a outro dispositivo @@ -357,7 +354,6 @@ Conectado conectando… Tempo limite de conexão - Pedidos de contato Preferências de contato Contato escondido: O contato ainda não está conectado! @@ -736,7 +732,6 @@ Erro de desencriptação O código de acesso é substituído por um código auto-destrutivo. ID da base de dados: %d - DESENVOLVER Nomes, avatares e isolamento de transporte diferentes. Secundário adicional Realce adicional diff --git a/apps/android/app/src/main/res/values-ru/strings.xml b/apps/multiplatform/android/src/main/res/values-ru/strings.xml similarity index 100% rename from apps/android/app/src/main/res/values-ru/strings.xml rename to apps/multiplatform/android/src/main/res/values-ru/strings.xml diff --git a/apps/android/app/src/main/res/values-th/strings.xml b/apps/multiplatform/android/src/main/res/values-th/strings.xml similarity index 99% rename from apps/android/app/src/main/res/values-th/strings.xml rename to apps/multiplatform/android/src/main/res/values-th/strings.xml index d604bcd42b..f19d43a145 100644 --- a/apps/android/app/src/main/res/values-th/strings.xml +++ b/apps/multiplatform/android/src/main/res/values-th/strings.xml @@ -401,7 +401,6 @@ เปิดใช้งานรหัสผ่านแบบทําลายตัวเอง เปิดใช้งานการทำลายตัวเอง ความช่วยเหลือ - พัฒนา เครื่องมือสำหรับนักพัฒนา อุปกรณ์ คุณลักษณะสำหรับการทดลอง diff --git a/apps/android/app/src/main/res/values-uk/strings.xml b/apps/multiplatform/android/src/main/res/values-uk/strings.xml similarity index 100% rename from apps/android/app/src/main/res/values-uk/strings.xml rename to apps/multiplatform/android/src/main/res/values-uk/strings.xml diff --git a/apps/android/app/src/main/res/values-zh-rCN/strings.xml b/apps/multiplatform/android/src/main/res/values-zh-rCN/strings.xml similarity index 100% rename from apps/android/app/src/main/res/values-zh-rCN/strings.xml rename to apps/multiplatform/android/src/main/res/values-zh-rCN/strings.xml diff --git a/apps/android/app/src/main/res/values-zh-rTW/strings.xml b/apps/multiplatform/android/src/main/res/values-zh-rTW/strings.xml similarity index 100% rename from apps/android/app/src/main/res/values-zh-rTW/strings.xml rename to apps/multiplatform/android/src/main/res/values-zh-rTW/strings.xml diff --git a/apps/android/app/src/main/res/values/colors.xml b/apps/multiplatform/android/src/main/res/values/colors.xml similarity index 100% rename from apps/android/app/src/main/res/values/colors.xml rename to apps/multiplatform/android/src/main/res/values/colors.xml diff --git a/apps/android/app/src/main/res/values/icon_background.xml b/apps/multiplatform/android/src/main/res/values/icon_background.xml similarity index 100% rename from apps/android/app/src/main/res/values/icon_background.xml rename to apps/multiplatform/android/src/main/res/values/icon_background.xml diff --git a/apps/android/app/src/main/res/values/icon_dark_blue_background.xml b/apps/multiplatform/android/src/main/res/values/icon_dark_blue_background.xml similarity index 100% rename from apps/android/app/src/main/res/values/icon_dark_blue_background.xml rename to apps/multiplatform/android/src/main/res/values/icon_dark_blue_background.xml diff --git a/apps/android/app/src/main/res/values/strings.xml b/apps/multiplatform/android/src/main/res/values/strings.xml similarity index 100% rename from apps/android/app/src/main/res/values/strings.xml rename to apps/multiplatform/android/src/main/res/values/strings.xml diff --git a/apps/android/app/src/main/res/values/themes.xml b/apps/multiplatform/android/src/main/res/values/themes.xml similarity index 100% rename from apps/android/app/src/main/res/values/themes.xml rename to apps/multiplatform/android/src/main/res/values/themes.xml diff --git a/apps/android/app/src/main/res/xml/file_paths.xml b/apps/multiplatform/android/src/main/res/xml/file_paths.xml similarity index 100% rename from apps/android/app/src/main/res/xml/file_paths.xml rename to apps/multiplatform/android/src/main/res/xml/file_paths.xml diff --git a/apps/android/app/src/main/res/xml/locales_config.xml b/apps/multiplatform/android/src/main/res/xml/locales_config.xml similarity index 100% rename from apps/android/app/src/main/res/xml/locales_config.xml rename to apps/multiplatform/android/src/main/res/xml/locales_config.xml diff --git a/apps/android/app/src/test/java/chat/simplex/app/ExampleUnitTest.kt b/apps/multiplatform/android/src/test/java/chat/simplex/app/ExampleUnitTest.kt similarity index 100% rename from apps/android/app/src/test/java/chat/simplex/app/ExampleUnitTest.kt rename to apps/multiplatform/android/src/test/java/chat/simplex/app/ExampleUnitTest.kt diff --git a/apps/multiplatform/build.gradle.kts b/apps/multiplatform/build.gradle.kts new file mode 100644 index 0000000000..94cecd1725 --- /dev/null +++ b/apps/multiplatform/build.gradle.kts @@ -0,0 +1,73 @@ +import org.gradle.initialization.Environment.Properties +import java.io.File +import java.io.FileInputStream + +buildscript { + val prop = java.util.Properties().apply { + try { + load(java.io.FileInputStream(File(rootProject.rootDir, "local.properties"))) + } catch (e: Exception) { + // No file was created + } + } + extra.set("compose.version", prop["compose.version"] ?: extra["compose.version"]) + extra.set("kotlin.version", prop["kotlin.version"] ?: extra["kotlin.version"]) + extra.set("gradle.plugin.version", prop["gradle.plugin.version"] ?: extra["gradle.plugin.version"]) + extra.set("abi_filter", prop["abi_filter"] ?: "arm64-v8a") + // Name that will be shown for debug build. By default, it is from strings + extra.set("app.name", prop["app.name"] ?: "@string/app_name") + // Whether the app is debuggable or not. Specify `false` if yo`u want good performance in debug builds + extra.set("enable_debuggable", prop["debuggable"] != "false") + // Ending part of package name. + // Provide, for example, `application_id.suffix=.debug` in local.properties + // to allow debug & release versions to coexist + extra.set("application_id.suffix", prop["application_id.suffix"] ?: "") + // Compression level for debug AND release apk. 0 = disable compression. Max is 9 + extra.set("compression.level", (prop["compression.level"] as String?)?.toIntOrNull() ?: 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:${rootProject.extra["gradle.plugin.version"]}") + classpath(kotlin("gradle-plugin", version = rootProject.extra["kotlin.version"] as String)) + classpath("org.jetbrains.kotlin:kotlin-serialization:1.3.2") + classpath("dev.icerock.moko:resources-generator:0.22.3") + + // 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" +}*/ + +group = "chat.simplex" +version = extra["android.version_name"] as String + +allprojects { + repositories { + google() + mavenCentral() + maven("https://maven.pkg.jetbrains.space/public/p/compose/dev") + maven("https://jitpack.io") + } +} + +plugins { + kotlin("multiplatform") apply false + kotlin("android") apply false + id("com.android.application") apply false + id("com.android.library") apply false + id("org.jetbrains.compose") apply false + id("org.jetbrains.kotlin.plugin.serialization") apply false +} + +tasks.register("clean", Delete::class) { + delete(rootProject.buildDir) +} diff --git a/apps/multiplatform/common/build.gradle.kts b/apps/multiplatform/common/build.gradle.kts new file mode 100644 index 0000000000..c3474f451d --- /dev/null +++ b/apps/multiplatform/common/build.gradle.kts @@ -0,0 +1,140 @@ +plugins { + kotlin("multiplatform") + id("org.jetbrains.compose") + id("com.android.library") + id("org.jetbrains.kotlin.plugin.serialization") + id("dev.icerock.mobile.multiplatform-resources") + id("com.github.gmazzo.buildconfig") version "4.0.4" +} + +group = "chat.simplex" +version = extra["android.version_name"] as String + +kotlin { + android() + jvm("desktop") { + jvmToolchain(11) + } + sourceSets { + all { + languageSettings { + optIn("kotlinx.coroutines.DelicateCoroutinesApi") + optIn("androidx.compose.foundation.ExperimentalFoundationApi") + optIn("androidx.compose.ui.text.ExperimentalTextApi") + optIn("androidx.compose.material.ExperimentalMaterialApi") + optIn("com.arkivanov.decompose.ExperimentalDecomposeApi") + optIn("kotlinx.serialization.InternalSerializationApi") + optIn("kotlinx.serialization.ExperimentalSerializationApi") + optIn("androidx.compose.ui.ExperimentalComposeUiApi") + optIn("com.google.accompanist.permissions.ExperimentalPermissionsApi") + } + } + + val commonMain by getting { + kotlin.srcDir("./build/generated/moko/commonMain/src/") + dependencies { + api(compose.runtime) + api(compose.foundation) + api(compose.material) + api("org.jetbrains.kotlinx:kotlinx-serialization-json:1.3.2") + api("org.jetbrains.kotlinx:kotlinx-datetime:0.3.2") + api("com.russhwolf:multiplatform-settings:1.0.0") + api("com.charleskorn.kaml:kaml:0.43.0") + api("dev.icerock.moko:resources-compose:0.22.3") + api("org.jetbrains.compose.ui:ui-text:${rootProject.extra["compose.version"] as String}") + implementation("org.jetbrains.compose.components:components-animatedimage:${rootProject.extra["compose.version"] as String}") + //Barcode + api("org.boofcv:boofcv-core:0.40.1") + implementation("com.godaddy.android.colorpicker:compose-color-picker-jvm:0.7.0") + // Link Previews + implementation("org.jsoup:jsoup:1.13.1") + } + } + val commonTest by getting { + dependencies { + implementation(kotlin("test")) + } + } + // LALAL CHANGE TO IMPLEMENTATION + val androidMain by getting { + dependencies { + api("androidx.appcompat:appcompat:1.5.1") + api("androidx.core:core-ktx:1.9.0") + api("androidx.activity:activity-compose:1.5.0") + val work_version = "2.7.1" + api("androidx.work:work-runtime-ktx:$work_version") + api("androidx.work:work-multiprocess:$work_version") + api("com.google.accompanist:accompanist-insets:0.23.0") + api("dev.icerock.moko:resources:0.22.3") + + // Video support + api("com.google.android.exoplayer:exoplayer:2.17.1") + + // Biometric authentication + api("androidx.biometric:biometric:1.2.0-alpha04") + + //Barcode + api("org.boofcv:boofcv-android:0.40.1") + + //Camera Permission + api("com.google.accompanist:accompanist-permissions:0.23.0") + + api("androidx.webkit:webkit:1.4.0") + + // GIFs support + api("io.coil-kt:coil-compose:2.1.0") + api("io.coil-kt:coil-gif:2.1.0") + + api("com.jakewharton:process-phoenix:2.1.2") + + val camerax_version = "1.1.0-beta01" + api("androidx.camera:camera-core:${camerax_version}") + api("androidx.camera:camera-camera2:${camerax_version}") + api("androidx.camera:camera-lifecycle:${camerax_version}") + api("androidx.camera:camera-view:${camerax_version}") + + // LALAL REPLACE IT WITH SOURCE + // Wheel picker + api("com.github.zj565061763:compose-wheel-picker:1.0.0-alpha10") + + // LALAL REMOVE + api("org.jsoup:jsoup:1.13.1") + api("com.godaddy.android.colorpicker:compose-color-picker-jvm:0.7.0") + api("androidx.compose.ui:ui-tooling-preview:${extra["compose.version"]}") + } + } + val desktopMain by getting { + dependencies { + implementation("dev.icerock.moko:resources:0.22.3") + implementation("org.jetbrains.kotlinx:kotlinx-coroutines-swing:1.7.1") + } + } + val desktopTest by getting + } +} + +android { + compileSdkVersion(33) + sourceSets["main"].manifest.srcFile("src/androidMain/AndroidManifest.xml") + defaultConfig { + minSdkVersion(26) + targetSdkVersion(33) + } + compileOptions { + sourceCompatibility = JavaVersion.VERSION_1_8 + targetCompatibility = JavaVersion.VERSION_1_8 + } +} + +multiplatformResources { + multiplatformResourcesPackage = "com.icerockdev.library" + // multiplatformResourcesClassName = "MR" +} + +buildConfig { + forClass("BuildConfigCommon") { + buildConfigField("String", "ANDROID_VERSION_NAME", "\"${extra["android.version_name"]}\"") + buildConfigField("int", "ANDROID_VERSION_CODE", "${extra["android.version_code"]}") + buildConfigField("String", "DESKTOP_VERSION_NAME", "\"${extra["desktop.version_name"]}\"") + } +} diff --git a/apps/multiplatform/common/src/androidMain/AndroidManifest.xml b/apps/multiplatform/common/src/androidMain/AndroidManifest.xml new file mode 100644 index 0000000000..74520465ae --- /dev/null +++ b/apps/multiplatform/common/src/androidMain/AndroidManifest.xml @@ -0,0 +1,4 @@ + + + + diff --git a/apps/android/app/src/main/cpp/CMakeLists.txt b/apps/multiplatform/common/src/commonMain/cpp/android/CMakeLists.txt similarity index 99% rename from apps/android/app/src/main/cpp/CMakeLists.txt rename to apps/multiplatform/common/src/commonMain/cpp/android/CMakeLists.txt index de688975c6..44cb31d424 100644 --- a/apps/android/app/src/main/cpp/CMakeLists.txt +++ b/apps/multiplatform/common/src/commonMain/cpp/android/CMakeLists.txt @@ -64,4 +64,4 @@ target_link_libraries( # Specifies the target library. # Links the target library to the log library # included in the NDK. - ${log-lib}) \ No newline at end of file + ${log-lib}) diff --git a/apps/android/app/src/main/cpp/simplex-api.c b/apps/multiplatform/common/src/commonMain/cpp/android/simplex-api.c similarity index 100% rename from apps/android/app/src/main/cpp/simplex-api.c rename to apps/multiplatform/common/src/commonMain/cpp/android/simplex-api.c diff --git a/apps/multiplatform/common/src/commonMain/cpp/desktop/CMakeLists.txt b/apps/multiplatform/common/src/commonMain/cpp/desktop/CMakeLists.txt new file mode 100644 index 0000000000..2f8f119d75 --- /dev/null +++ b/apps/multiplatform/common/src/commonMain/cpp/desktop/CMakeLists.txt @@ -0,0 +1,92 @@ +# Manually downloaded headers because `find_package(JNI REQUIRED)` doesn't work while cross-compiling: + +# https://raw.githubusercontent.com/openjdk/jdk/master/src/java.base/share/native/include/jni.h +include_directories(${CMAKE_SOURCE_DIR}/include) +if(UNIX) + # https://raw.githubusercontent.com/openjdk/jdk/master/src/java.base/unix/native/include/jni_md.h + include_directories(${CMAKE_SOURCE_DIR}/include/unix) +elseif(WIN32) + # https://raw.githubusercontent.com/openjdk/jdk/master/src/java.base/windows/native/include/jni_md.h + include_directories(${CMAKE_SOURCE_DIR}/include/windows) +endif() + +# Sets the minimum version of CMake required to build the native library. +cmake_minimum_required(VERSION 3.10.2) + +# Declares and names the project. +project("app") + +if(UNIX AND NOT APPLE) + set(OS_LIB_PATH "linux") + set(OS_LIB_EXT "so") +elseif(WIN32) + set(OS_LIB_PATH "windows") + set(OS_LIB_EXT "dll") +else() + set(OS_LIB_PATH "mac") + set(OS_LIB_EXT "dylib") + set(CMAKE_MACOSX_RPATH 1) + set(CMAKE_BUILD_RPATH "@loader_path") +endif() + +if(${CMAKE_SYSTEM_PROCESSOR} MATCHES "amd64") + set(OS_LIB_ARCH "x86_64") +elseif(${CMAKE_SYSTEM_PROCESSOR} MATCHES "arm64") + set(OS_LIB_ARCH "aarch64") +else() + set(OS_LIB_ARCH "${CMAKE_SYSTEM_PROCESSOR}") +endif() + +# Makes ld search libs in the same dir as libapp-lib, not in system dirs +#set(CMAKE_BUILD_RPATH "$ORIGIN") + +# Creates and names a library, sets it as either STATIC +# or SHARED, and provides the relative paths to its source code. +# You can define multiple libraries, and CMake builds them for you. +# Gradle automatically packages shared libraries with your APK. + +add_library( # Sets the name of the library. + app-lib + + # Sets the library as a shared library. + SHARED + + # Provides a relative path to your source file(s). + simplex-api.c) + +add_library( simplex SHARED IMPORTED ) +# Lib has different name because of version, find it +FILE(GLOB SIMPLEXLIB ${CMAKE_SOURCE_DIR}/libs/${OS_LIB_PATH}-${OS_LIB_ARCH}/libHSsimplex-chat-*.${OS_LIB_EXT}) +set_target_properties( simplex PROPERTIES IMPORTED_LOCATION ${SIMPLEXLIB}) + + +# Specifies libraries CMake should link to your target library. You +# can link multiple libraries, such as libraries you define in this +# build script, prebuilt third-party libraries, or system libraries. +if(NOT APPLE) + target_link_libraries(app-lib simplex) +else() + # Without direct linking it can't find hs_init in linking step + add_library( rts SHARED IMPORTED ) + FILE(GLOB RTSLIB ${CMAKE_SOURCE_DIR}/libs/${OS_LIB_PATH}-${OS_LIB_ARCH}/deps/libHSrts_thr-*.${OS_LIB_EXT}) + set_target_properties( rts PROPERTIES IMPORTED_LOCATION ${RTSLIB}) + + target_link_libraries(app-lib rts simplex) +endif() + + + +# Trying to copy resulting files into needed directory, but none of these work for some reason. This could allow to +# remove gradle's copy hooks +#add_custom_target(lalal) +#MACRO(POST_BUILD_COPY src_files dest_path) +# ADD_CUSTOM_COMMAND(TARGET lalal POST_BUILD +# COMMAND ${CMAKE_COMMAND} -E copy ${src_files} ${dest_path}) +#ENDMACRO(POST_BUILD_COPY src_files dest_path) + +#file(COPY "${CMAKE_SOURCE_DIR}/libs/${OS_LIB_PATH}-${OS_LIB_ARCH}/libsimplex.${OS_LIB_EXT}" DESTINATION "../../resources/libs/${OS_LIB_PATH}-${OS_LIB_ARCH}/libsimplex.${OS_LIB_EXT}") +#POST_BUILD_COPY("${CMAKE_BINARY_DIR}/libapp-lib.${OS_LIB_EXT}" "../resources/libs/${OS_LIB_PATH}-${OS_LIB_ARCH}/") + +#add_custom_command(TARGET lalal POST_BUILD +# COMMAND ${CMAKE_COMMAND} -E copy ${CMAKE_BINARY_DIR}/libapp-lib.${OS_LIB_EXT} "../../resources/libs/${OS_LIB_PATH}-${OS_LIB_ARCH}/libapp-lib.${OS_LIB_EXT}" +#) diff --git a/apps/multiplatform/common/src/commonMain/cpp/desktop/include/jni.h b/apps/multiplatform/common/src/commonMain/cpp/desktop/include/jni.h new file mode 100644 index 0000000000..c85da1bc67 --- /dev/null +++ b/apps/multiplatform/common/src/commonMain/cpp/desktop/include/jni.h @@ -0,0 +1,2001 @@ +/* + * Copyright (c) 1996, 2023, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +/* + * We used part of Netscape's Java Runtime Interface (JRI) as the starting + * point of our design and implementation. + */ + +/****************************************************************************** + * Java Runtime Interface + * Copyright (c) 1996 Netscape Communications Corporation. All rights reserved. + *****************************************************************************/ + +#ifndef _JAVASOFT_JNI_H_ +#define _JAVASOFT_JNI_H_ + +#include +#include + +/* jni_md.h contains the machine-dependent typedefs for jbyte, jint + and jlong */ + +#include "jni_md.h" + +#ifdef __cplusplus +extern "C" { +#endif + +/* + * JNI Types + */ + +#ifndef JNI_TYPES_ALREADY_DEFINED_IN_JNI_MD_H + +typedef unsigned char jboolean; +typedef unsigned short jchar; +typedef short jshort; +typedef float jfloat; +typedef double jdouble; + +typedef jint jsize; + +#ifdef __cplusplus + +class _jobject {}; +class _jclass : public _jobject {}; +class _jthrowable : public _jobject {}; +class _jstring : public _jobject {}; +class _jarray : public _jobject {}; +class _jbooleanArray : public _jarray {}; +class _jbyteArray : public _jarray {}; +class _jcharArray : public _jarray {}; +class _jshortArray : public _jarray {}; +class _jintArray : public _jarray {}; +class _jlongArray : public _jarray {}; +class _jfloatArray : public _jarray {}; +class _jdoubleArray : public _jarray {}; +class _jobjectArray : public _jarray {}; + +typedef _jobject *jobject; +typedef _jclass *jclass; +typedef _jthrowable *jthrowable; +typedef _jstring *jstring; +typedef _jarray *jarray; +typedef _jbooleanArray *jbooleanArray; +typedef _jbyteArray *jbyteArray; +typedef _jcharArray *jcharArray; +typedef _jshortArray *jshortArray; +typedef _jintArray *jintArray; +typedef _jlongArray *jlongArray; +typedef _jfloatArray *jfloatArray; +typedef _jdoubleArray *jdoubleArray; +typedef _jobjectArray *jobjectArray; + +#else + +struct _jobject; + +typedef struct _jobject *jobject; +typedef jobject jclass; +typedef jobject jthrowable; +typedef jobject jstring; +typedef jobject jarray; +typedef jarray jbooleanArray; +typedef jarray jbyteArray; +typedef jarray jcharArray; +typedef jarray jshortArray; +typedef jarray jintArray; +typedef jarray jlongArray; +typedef jarray jfloatArray; +typedef jarray jdoubleArray; +typedef jarray jobjectArray; + +#endif + +typedef jobject jweak; + +typedef union jvalue { + jboolean z; + jbyte b; + jchar c; + jshort s; + jint i; + jlong j; + jfloat f; + jdouble d; + jobject l; +} jvalue; + +struct _jfieldID; +typedef struct _jfieldID *jfieldID; + +struct _jmethodID; +typedef struct _jmethodID *jmethodID; + +/* Return values from jobjectRefType */ +typedef enum _jobjectType { + JNIInvalidRefType = 0, + JNILocalRefType = 1, + JNIGlobalRefType = 2, + JNIWeakGlobalRefType = 3 +} jobjectRefType; + + +#endif /* JNI_TYPES_ALREADY_DEFINED_IN_JNI_MD_H */ + +/* + * jboolean constants + */ + +#define JNI_FALSE 0 +#define JNI_TRUE 1 + +/* + * possible return values for JNI functions. + */ + +#define JNI_OK 0 /* success */ +#define JNI_ERR (-1) /* unknown error */ +#define JNI_EDETACHED (-2) /* thread detached from the VM */ +#define JNI_EVERSION (-3) /* JNI version error */ +#define JNI_ENOMEM (-4) /* not enough memory */ +#define JNI_EEXIST (-5) /* VM already created */ +#define JNI_EINVAL (-6) /* invalid arguments */ + +/* + * used in ReleaseScalarArrayElements + */ + +#define JNI_COMMIT 1 +#define JNI_ABORT 2 + +/* + * used in RegisterNatives to describe native method name, signature, + * and function pointer. + */ + +typedef struct { + char *name; + char *signature; + void *fnPtr; +} JNINativeMethod; + +/* + * JNI Native Method Interface. + */ + +struct JNINativeInterface_; + +struct JNIEnv_; + +#ifdef __cplusplus +typedef JNIEnv_ JNIEnv; +#else +typedef const struct JNINativeInterface_ *JNIEnv; +#endif + +/* + * JNI Invocation Interface. + */ + +struct JNIInvokeInterface_; + +struct JavaVM_; + +#ifdef __cplusplus +typedef JavaVM_ JavaVM; +#else +typedef const struct JNIInvokeInterface_ *JavaVM; +#endif + +struct JNINativeInterface_ { + void *reserved0; + void *reserved1; + void *reserved2; + + void *reserved3; + jint (JNICALL *GetVersion)(JNIEnv *env); + + jclass (JNICALL *DefineClass) + (JNIEnv *env, const char *name, jobject loader, const jbyte *buf, + jsize len); + jclass (JNICALL *FindClass) + (JNIEnv *env, const char *name); + + jmethodID (JNICALL *FromReflectedMethod) + (JNIEnv *env, jobject method); + jfieldID (JNICALL *FromReflectedField) + (JNIEnv *env, jobject field); + + jobject (JNICALL *ToReflectedMethod) + (JNIEnv *env, jclass cls, jmethodID methodID, jboolean isStatic); + + jclass (JNICALL *GetSuperclass) + (JNIEnv *env, jclass sub); + jboolean (JNICALL *IsAssignableFrom) + (JNIEnv *env, jclass sub, jclass sup); + + jobject (JNICALL *ToReflectedField) + (JNIEnv *env, jclass cls, jfieldID fieldID, jboolean isStatic); + + jint (JNICALL *Throw) + (JNIEnv *env, jthrowable obj); + jint (JNICALL *ThrowNew) + (JNIEnv *env, jclass clazz, const char *msg); + jthrowable (JNICALL *ExceptionOccurred) + (JNIEnv *env); + void (JNICALL *ExceptionDescribe) + (JNIEnv *env); + void (JNICALL *ExceptionClear) + (JNIEnv *env); + void (JNICALL *FatalError) + (JNIEnv *env, const char *msg); + + jint (JNICALL *PushLocalFrame) + (JNIEnv *env, jint capacity); + jobject (JNICALL *PopLocalFrame) + (JNIEnv *env, jobject result); + + jobject (JNICALL *NewGlobalRef) + (JNIEnv *env, jobject lobj); + void (JNICALL *DeleteGlobalRef) + (JNIEnv *env, jobject gref); + void (JNICALL *DeleteLocalRef) + (JNIEnv *env, jobject obj); + jboolean (JNICALL *IsSameObject) + (JNIEnv *env, jobject obj1, jobject obj2); + jobject (JNICALL *NewLocalRef) + (JNIEnv *env, jobject ref); + jint (JNICALL *EnsureLocalCapacity) + (JNIEnv *env, jint capacity); + + jobject (JNICALL *AllocObject) + (JNIEnv *env, jclass clazz); + jobject (JNICALL *NewObject) + (JNIEnv *env, jclass clazz, jmethodID methodID, ...); + jobject (JNICALL *NewObjectV) + (JNIEnv *env, jclass clazz, jmethodID methodID, va_list args); + jobject (JNICALL *NewObjectA) + (JNIEnv *env, jclass clazz, jmethodID methodID, const jvalue *args); + + jclass (JNICALL *GetObjectClass) + (JNIEnv *env, jobject obj); + jboolean (JNICALL *IsInstanceOf) + (JNIEnv *env, jobject obj, jclass clazz); + + jmethodID (JNICALL *GetMethodID) + (JNIEnv *env, jclass clazz, const char *name, const char *sig); + + jobject (JNICALL *CallObjectMethod) + (JNIEnv *env, jobject obj, jmethodID methodID, ...); + jobject (JNICALL *CallObjectMethodV) + (JNIEnv *env, jobject obj, jmethodID methodID, va_list args); + jobject (JNICALL *CallObjectMethodA) + (JNIEnv *env, jobject obj, jmethodID methodID, const jvalue * args); + + jboolean (JNICALL *CallBooleanMethod) + (JNIEnv *env, jobject obj, jmethodID methodID, ...); + jboolean (JNICALL *CallBooleanMethodV) + (JNIEnv *env, jobject obj, jmethodID methodID, va_list args); + jboolean (JNICALL *CallBooleanMethodA) + (JNIEnv *env, jobject obj, jmethodID methodID, const jvalue * args); + + jbyte (JNICALL *CallByteMethod) + (JNIEnv *env, jobject obj, jmethodID methodID, ...); + jbyte (JNICALL *CallByteMethodV) + (JNIEnv *env, jobject obj, jmethodID methodID, va_list args); + jbyte (JNICALL *CallByteMethodA) + (JNIEnv *env, jobject obj, jmethodID methodID, const jvalue *args); + + jchar (JNICALL *CallCharMethod) + (JNIEnv *env, jobject obj, jmethodID methodID, ...); + jchar (JNICALL *CallCharMethodV) + (JNIEnv *env, jobject obj, jmethodID methodID, va_list args); + jchar (JNICALL *CallCharMethodA) + (JNIEnv *env, jobject obj, jmethodID methodID, const jvalue *args); + + jshort (JNICALL *CallShortMethod) + (JNIEnv *env, jobject obj, jmethodID methodID, ...); + jshort (JNICALL *CallShortMethodV) + (JNIEnv *env, jobject obj, jmethodID methodID, va_list args); + jshort (JNICALL *CallShortMethodA) + (JNIEnv *env, jobject obj, jmethodID methodID, const jvalue *args); + + jint (JNICALL *CallIntMethod) + (JNIEnv *env, jobject obj, jmethodID methodID, ...); + jint (JNICALL *CallIntMethodV) + (JNIEnv *env, jobject obj, jmethodID methodID, va_list args); + jint (JNICALL *CallIntMethodA) + (JNIEnv *env, jobject obj, jmethodID methodID, const jvalue *args); + + jlong (JNICALL *CallLongMethod) + (JNIEnv *env, jobject obj, jmethodID methodID, ...); + jlong (JNICALL *CallLongMethodV) + (JNIEnv *env, jobject obj, jmethodID methodID, va_list args); + jlong (JNICALL *CallLongMethodA) + (JNIEnv *env, jobject obj, jmethodID methodID, const jvalue *args); + + jfloat (JNICALL *CallFloatMethod) + (JNIEnv *env, jobject obj, jmethodID methodID, ...); + jfloat (JNICALL *CallFloatMethodV) + (JNIEnv *env, jobject obj, jmethodID methodID, va_list args); + jfloat (JNICALL *CallFloatMethodA) + (JNIEnv *env, jobject obj, jmethodID methodID, const jvalue *args); + + jdouble (JNICALL *CallDoubleMethod) + (JNIEnv *env, jobject obj, jmethodID methodID, ...); + jdouble (JNICALL *CallDoubleMethodV) + (JNIEnv *env, jobject obj, jmethodID methodID, va_list args); + jdouble (JNICALL *CallDoubleMethodA) + (JNIEnv *env, jobject obj, jmethodID methodID, const jvalue *args); + + void (JNICALL *CallVoidMethod) + (JNIEnv *env, jobject obj, jmethodID methodID, ...); + void (JNICALL *CallVoidMethodV) + (JNIEnv *env, jobject obj, jmethodID methodID, va_list args); + void (JNICALL *CallVoidMethodA) + (JNIEnv *env, jobject obj, jmethodID methodID, const jvalue * args); + + jobject (JNICALL *CallNonvirtualObjectMethod) + (JNIEnv *env, jobject obj, jclass clazz, jmethodID methodID, ...); + jobject (JNICALL *CallNonvirtualObjectMethodV) + (JNIEnv *env, jobject obj, jclass clazz, jmethodID methodID, + va_list args); + jobject (JNICALL *CallNonvirtualObjectMethodA) + (JNIEnv *env, jobject obj, jclass clazz, jmethodID methodID, + const jvalue * args); + + jboolean (JNICALL *CallNonvirtualBooleanMethod) + (JNIEnv *env, jobject obj, jclass clazz, jmethodID methodID, ...); + jboolean (JNICALL *CallNonvirtualBooleanMethodV) + (JNIEnv *env, jobject obj, jclass clazz, jmethodID methodID, + va_list args); + jboolean (JNICALL *CallNonvirtualBooleanMethodA) + (JNIEnv *env, jobject obj, jclass clazz, jmethodID methodID, + const jvalue * args); + + jbyte (JNICALL *CallNonvirtualByteMethod) + (JNIEnv *env, jobject obj, jclass clazz, jmethodID methodID, ...); + jbyte (JNICALL *CallNonvirtualByteMethodV) + (JNIEnv *env, jobject obj, jclass clazz, jmethodID methodID, + va_list args); + jbyte (JNICALL *CallNonvirtualByteMethodA) + (JNIEnv *env, jobject obj, jclass clazz, jmethodID methodID, + const jvalue *args); + + jchar (JNICALL *CallNonvirtualCharMethod) + (JNIEnv *env, jobject obj, jclass clazz, jmethodID methodID, ...); + jchar (JNICALL *CallNonvirtualCharMethodV) + (JNIEnv *env, jobject obj, jclass clazz, jmethodID methodID, + va_list args); + jchar (JNICALL *CallNonvirtualCharMethodA) + (JNIEnv *env, jobject obj, jclass clazz, jmethodID methodID, + const jvalue *args); + + jshort (JNICALL *CallNonvirtualShortMethod) + (JNIEnv *env, jobject obj, jclass clazz, jmethodID methodID, ...); + jshort (JNICALL *CallNonvirtualShortMethodV) + (JNIEnv *env, jobject obj, jclass clazz, jmethodID methodID, + va_list args); + jshort (JNICALL *CallNonvirtualShortMethodA) + (JNIEnv *env, jobject obj, jclass clazz, jmethodID methodID, + const jvalue *args); + + jint (JNICALL *CallNonvirtualIntMethod) + (JNIEnv *env, jobject obj, jclass clazz, jmethodID methodID, ...); + jint (JNICALL *CallNonvirtualIntMethodV) + (JNIEnv *env, jobject obj, jclass clazz, jmethodID methodID, + va_list args); + jint (JNICALL *CallNonvirtualIntMethodA) + (JNIEnv *env, jobject obj, jclass clazz, jmethodID methodID, + const jvalue *args); + + jlong (JNICALL *CallNonvirtualLongMethod) + (JNIEnv *env, jobject obj, jclass clazz, jmethodID methodID, ...); + jlong (JNICALL *CallNonvirtualLongMethodV) + (JNIEnv *env, jobject obj, jclass clazz, jmethodID methodID, + va_list args); + jlong (JNICALL *CallNonvirtualLongMethodA) + (JNIEnv *env, jobject obj, jclass clazz, jmethodID methodID, + const jvalue *args); + + jfloat (JNICALL *CallNonvirtualFloatMethod) + (JNIEnv *env, jobject obj, jclass clazz, jmethodID methodID, ...); + jfloat (JNICALL *CallNonvirtualFloatMethodV) + (JNIEnv *env, jobject obj, jclass clazz, jmethodID methodID, + va_list args); + jfloat (JNICALL *CallNonvirtualFloatMethodA) + (JNIEnv *env, jobject obj, jclass clazz, jmethodID methodID, + const jvalue *args); + + jdouble (JNICALL *CallNonvirtualDoubleMethod) + (JNIEnv *env, jobject obj, jclass clazz, jmethodID methodID, ...); + jdouble (JNICALL *CallNonvirtualDoubleMethodV) + (JNIEnv *env, jobject obj, jclass clazz, jmethodID methodID, + va_list args); + jdouble (JNICALL *CallNonvirtualDoubleMethodA) + (JNIEnv *env, jobject obj, jclass clazz, jmethodID methodID, + const jvalue *args); + + void (JNICALL *CallNonvirtualVoidMethod) + (JNIEnv *env, jobject obj, jclass clazz, jmethodID methodID, ...); + void (JNICALL *CallNonvirtualVoidMethodV) + (JNIEnv *env, jobject obj, jclass clazz, jmethodID methodID, + va_list args); + void (JNICALL *CallNonvirtualVoidMethodA) + (JNIEnv *env, jobject obj, jclass clazz, jmethodID methodID, + const jvalue * args); + + jfieldID (JNICALL *GetFieldID) + (JNIEnv *env, jclass clazz, const char *name, const char *sig); + + jobject (JNICALL *GetObjectField) + (JNIEnv *env, jobject obj, jfieldID fieldID); + jboolean (JNICALL *GetBooleanField) + (JNIEnv *env, jobject obj, jfieldID fieldID); + jbyte (JNICALL *GetByteField) + (JNIEnv *env, jobject obj, jfieldID fieldID); + jchar (JNICALL *GetCharField) + (JNIEnv *env, jobject obj, jfieldID fieldID); + jshort (JNICALL *GetShortField) + (JNIEnv *env, jobject obj, jfieldID fieldID); + jint (JNICALL *GetIntField) + (JNIEnv *env, jobject obj, jfieldID fieldID); + jlong (JNICALL *GetLongField) + (JNIEnv *env, jobject obj, jfieldID fieldID); + jfloat (JNICALL *GetFloatField) + (JNIEnv *env, jobject obj, jfieldID fieldID); + jdouble (JNICALL *GetDoubleField) + (JNIEnv *env, jobject obj, jfieldID fieldID); + + void (JNICALL *SetObjectField) + (JNIEnv *env, jobject obj, jfieldID fieldID, jobject val); + void (JNICALL *SetBooleanField) + (JNIEnv *env, jobject obj, jfieldID fieldID, jboolean val); + void (JNICALL *SetByteField) + (JNIEnv *env, jobject obj, jfieldID fieldID, jbyte val); + void (JNICALL *SetCharField) + (JNIEnv *env, jobject obj, jfieldID fieldID, jchar val); + void (JNICALL *SetShortField) + (JNIEnv *env, jobject obj, jfieldID fieldID, jshort val); + void (JNICALL *SetIntField) + (JNIEnv *env, jobject obj, jfieldID fieldID, jint val); + void (JNICALL *SetLongField) + (JNIEnv *env, jobject obj, jfieldID fieldID, jlong val); + void (JNICALL *SetFloatField) + (JNIEnv *env, jobject obj, jfieldID fieldID, jfloat val); + void (JNICALL *SetDoubleField) + (JNIEnv *env, jobject obj, jfieldID fieldID, jdouble val); + + jmethodID (JNICALL *GetStaticMethodID) + (JNIEnv *env, jclass clazz, const char *name, const char *sig); + + jobject (JNICALL *CallStaticObjectMethod) + (JNIEnv *env, jclass clazz, jmethodID methodID, ...); + jobject (JNICALL *CallStaticObjectMethodV) + (JNIEnv *env, jclass clazz, jmethodID methodID, va_list args); + jobject (JNICALL *CallStaticObjectMethodA) + (JNIEnv *env, jclass clazz, jmethodID methodID, const jvalue *args); + + jboolean (JNICALL *CallStaticBooleanMethod) + (JNIEnv *env, jclass clazz, jmethodID methodID, ...); + jboolean (JNICALL *CallStaticBooleanMethodV) + (JNIEnv *env, jclass clazz, jmethodID methodID, va_list args); + jboolean (JNICALL *CallStaticBooleanMethodA) + (JNIEnv *env, jclass clazz, jmethodID methodID, const jvalue *args); + + jbyte (JNICALL *CallStaticByteMethod) + (JNIEnv *env, jclass clazz, jmethodID methodID, ...); + jbyte (JNICALL *CallStaticByteMethodV) + (JNIEnv *env, jclass clazz, jmethodID methodID, va_list args); + jbyte (JNICALL *CallStaticByteMethodA) + (JNIEnv *env, jclass clazz, jmethodID methodID, const jvalue *args); + + jchar (JNICALL *CallStaticCharMethod) + (JNIEnv *env, jclass clazz, jmethodID methodID, ...); + jchar (JNICALL *CallStaticCharMethodV) + (JNIEnv *env, jclass clazz, jmethodID methodID, va_list args); + jchar (JNICALL *CallStaticCharMethodA) + (JNIEnv *env, jclass clazz, jmethodID methodID, const jvalue *args); + + jshort (JNICALL *CallStaticShortMethod) + (JNIEnv *env, jclass clazz, jmethodID methodID, ...); + jshort (JNICALL *CallStaticShortMethodV) + (JNIEnv *env, jclass clazz, jmethodID methodID, va_list args); + jshort (JNICALL *CallStaticShortMethodA) + (JNIEnv *env, jclass clazz, jmethodID methodID, const jvalue *args); + + jint (JNICALL *CallStaticIntMethod) + (JNIEnv *env, jclass clazz, jmethodID methodID, ...); + jint (JNICALL *CallStaticIntMethodV) + (JNIEnv *env, jclass clazz, jmethodID methodID, va_list args); + jint (JNICALL *CallStaticIntMethodA) + (JNIEnv *env, jclass clazz, jmethodID methodID, const jvalue *args); + + jlong (JNICALL *CallStaticLongMethod) + (JNIEnv *env, jclass clazz, jmethodID methodID, ...); + jlong (JNICALL *CallStaticLongMethodV) + (JNIEnv *env, jclass clazz, jmethodID methodID, va_list args); + jlong (JNICALL *CallStaticLongMethodA) + (JNIEnv *env, jclass clazz, jmethodID methodID, const jvalue *args); + + jfloat (JNICALL *CallStaticFloatMethod) + (JNIEnv *env, jclass clazz, jmethodID methodID, ...); + jfloat (JNICALL *CallStaticFloatMethodV) + (JNIEnv *env, jclass clazz, jmethodID methodID, va_list args); + jfloat (JNICALL *CallStaticFloatMethodA) + (JNIEnv *env, jclass clazz, jmethodID methodID, const jvalue *args); + + jdouble (JNICALL *CallStaticDoubleMethod) + (JNIEnv *env, jclass clazz, jmethodID methodID, ...); + jdouble (JNICALL *CallStaticDoubleMethodV) + (JNIEnv *env, jclass clazz, jmethodID methodID, va_list args); + jdouble (JNICALL *CallStaticDoubleMethodA) + (JNIEnv *env, jclass clazz, jmethodID methodID, const jvalue *args); + + void (JNICALL *CallStaticVoidMethod) + (JNIEnv *env, jclass cls, jmethodID methodID, ...); + void (JNICALL *CallStaticVoidMethodV) + (JNIEnv *env, jclass cls, jmethodID methodID, va_list args); + void (JNICALL *CallStaticVoidMethodA) + (JNIEnv *env, jclass cls, jmethodID methodID, const jvalue * args); + + jfieldID (JNICALL *GetStaticFieldID) + (JNIEnv *env, jclass clazz, const char *name, const char *sig); + jobject (JNICALL *GetStaticObjectField) + (JNIEnv *env, jclass clazz, jfieldID fieldID); + jboolean (JNICALL *GetStaticBooleanField) + (JNIEnv *env, jclass clazz, jfieldID fieldID); + jbyte (JNICALL *GetStaticByteField) + (JNIEnv *env, jclass clazz, jfieldID fieldID); + jchar (JNICALL *GetStaticCharField) + (JNIEnv *env, jclass clazz, jfieldID fieldID); + jshort (JNICALL *GetStaticShortField) + (JNIEnv *env, jclass clazz, jfieldID fieldID); + jint (JNICALL *GetStaticIntField) + (JNIEnv *env, jclass clazz, jfieldID fieldID); + jlong (JNICALL *GetStaticLongField) + (JNIEnv *env, jclass clazz, jfieldID fieldID); + jfloat (JNICALL *GetStaticFloatField) + (JNIEnv *env, jclass clazz, jfieldID fieldID); + jdouble (JNICALL *GetStaticDoubleField) + (JNIEnv *env, jclass clazz, jfieldID fieldID); + + void (JNICALL *SetStaticObjectField) + (JNIEnv *env, jclass clazz, jfieldID fieldID, jobject value); + void (JNICALL *SetStaticBooleanField) + (JNIEnv *env, jclass clazz, jfieldID fieldID, jboolean value); + void (JNICALL *SetStaticByteField) + (JNIEnv *env, jclass clazz, jfieldID fieldID, jbyte value); + void (JNICALL *SetStaticCharField) + (JNIEnv *env, jclass clazz, jfieldID fieldID, jchar value); + void (JNICALL *SetStaticShortField) + (JNIEnv *env, jclass clazz, jfieldID fieldID, jshort value); + void (JNICALL *SetStaticIntField) + (JNIEnv *env, jclass clazz, jfieldID fieldID, jint value); + void (JNICALL *SetStaticLongField) + (JNIEnv *env, jclass clazz, jfieldID fieldID, jlong value); + void (JNICALL *SetStaticFloatField) + (JNIEnv *env, jclass clazz, jfieldID fieldID, jfloat value); + void (JNICALL *SetStaticDoubleField) + (JNIEnv *env, jclass clazz, jfieldID fieldID, jdouble value); + + jstring (JNICALL *NewString) + (JNIEnv *env, const jchar *unicode, jsize len); + jsize (JNICALL *GetStringLength) + (JNIEnv *env, jstring str); + const jchar *(JNICALL *GetStringChars) + (JNIEnv *env, jstring str, jboolean *isCopy); + void (JNICALL *ReleaseStringChars) + (JNIEnv *env, jstring str, const jchar *chars); + + jstring (JNICALL *NewStringUTF) + (JNIEnv *env, const char *utf); + jsize (JNICALL *GetStringUTFLength) + (JNIEnv *env, jstring str); + const char* (JNICALL *GetStringUTFChars) + (JNIEnv *env, jstring str, jboolean *isCopy); + void (JNICALL *ReleaseStringUTFChars) + (JNIEnv *env, jstring str, const char* chars); + + + jsize (JNICALL *GetArrayLength) + (JNIEnv *env, jarray array); + + jobjectArray (JNICALL *NewObjectArray) + (JNIEnv *env, jsize len, jclass clazz, jobject init); + jobject (JNICALL *GetObjectArrayElement) + (JNIEnv *env, jobjectArray array, jsize index); + void (JNICALL *SetObjectArrayElement) + (JNIEnv *env, jobjectArray array, jsize index, jobject val); + + jbooleanArray (JNICALL *NewBooleanArray) + (JNIEnv *env, jsize len); + jbyteArray (JNICALL *NewByteArray) + (JNIEnv *env, jsize len); + jcharArray (JNICALL *NewCharArray) + (JNIEnv *env, jsize len); + jshortArray (JNICALL *NewShortArray) + (JNIEnv *env, jsize len); + jintArray (JNICALL *NewIntArray) + (JNIEnv *env, jsize len); + jlongArray (JNICALL *NewLongArray) + (JNIEnv *env, jsize len); + jfloatArray (JNICALL *NewFloatArray) + (JNIEnv *env, jsize len); + jdoubleArray (JNICALL *NewDoubleArray) + (JNIEnv *env, jsize len); + + jboolean * (JNICALL *GetBooleanArrayElements) + (JNIEnv *env, jbooleanArray array, jboolean *isCopy); + jbyte * (JNICALL *GetByteArrayElements) + (JNIEnv *env, jbyteArray array, jboolean *isCopy); + jchar * (JNICALL *GetCharArrayElements) + (JNIEnv *env, jcharArray array, jboolean *isCopy); + jshort * (JNICALL *GetShortArrayElements) + (JNIEnv *env, jshortArray array, jboolean *isCopy); + jint * (JNICALL *GetIntArrayElements) + (JNIEnv *env, jintArray array, jboolean *isCopy); + jlong * (JNICALL *GetLongArrayElements) + (JNIEnv *env, jlongArray array, jboolean *isCopy); + jfloat * (JNICALL *GetFloatArrayElements) + (JNIEnv *env, jfloatArray array, jboolean *isCopy); + jdouble * (JNICALL *GetDoubleArrayElements) + (JNIEnv *env, jdoubleArray array, jboolean *isCopy); + + void (JNICALL *ReleaseBooleanArrayElements) + (JNIEnv *env, jbooleanArray array, jboolean *elems, jint mode); + void (JNICALL *ReleaseByteArrayElements) + (JNIEnv *env, jbyteArray array, jbyte *elems, jint mode); + void (JNICALL *ReleaseCharArrayElements) + (JNIEnv *env, jcharArray array, jchar *elems, jint mode); + void (JNICALL *ReleaseShortArrayElements) + (JNIEnv *env, jshortArray array, jshort *elems, jint mode); + void (JNICALL *ReleaseIntArrayElements) + (JNIEnv *env, jintArray array, jint *elems, jint mode); + void (JNICALL *ReleaseLongArrayElements) + (JNIEnv *env, jlongArray array, jlong *elems, jint mode); + void (JNICALL *ReleaseFloatArrayElements) + (JNIEnv *env, jfloatArray array, jfloat *elems, jint mode); + void (JNICALL *ReleaseDoubleArrayElements) + (JNIEnv *env, jdoubleArray array, jdouble *elems, jint mode); + + void (JNICALL *GetBooleanArrayRegion) + (JNIEnv *env, jbooleanArray array, jsize start, jsize l, jboolean *buf); + void (JNICALL *GetByteArrayRegion) + (JNIEnv *env, jbyteArray array, jsize start, jsize len, jbyte *buf); + void (JNICALL *GetCharArrayRegion) + (JNIEnv *env, jcharArray array, jsize start, jsize len, jchar *buf); + void (JNICALL *GetShortArrayRegion) + (JNIEnv *env, jshortArray array, jsize start, jsize len, jshort *buf); + void (JNICALL *GetIntArrayRegion) + (JNIEnv *env, jintArray array, jsize start, jsize len, jint *buf); + void (JNICALL *GetLongArrayRegion) + (JNIEnv *env, jlongArray array, jsize start, jsize len, jlong *buf); + void (JNICALL *GetFloatArrayRegion) + (JNIEnv *env, jfloatArray array, jsize start, jsize len, jfloat *buf); + void (JNICALL *GetDoubleArrayRegion) + (JNIEnv *env, jdoubleArray array, jsize start, jsize len, jdouble *buf); + + void (JNICALL *SetBooleanArrayRegion) + (JNIEnv *env, jbooleanArray array, jsize start, jsize l, const jboolean *buf); + void (JNICALL *SetByteArrayRegion) + (JNIEnv *env, jbyteArray array, jsize start, jsize len, const jbyte *buf); + void (JNICALL *SetCharArrayRegion) + (JNIEnv *env, jcharArray array, jsize start, jsize len, const jchar *buf); + void (JNICALL *SetShortArrayRegion) + (JNIEnv *env, jshortArray array, jsize start, jsize len, const jshort *buf); + void (JNICALL *SetIntArrayRegion) + (JNIEnv *env, jintArray array, jsize start, jsize len, const jint *buf); + void (JNICALL *SetLongArrayRegion) + (JNIEnv *env, jlongArray array, jsize start, jsize len, const jlong *buf); + void (JNICALL *SetFloatArrayRegion) + (JNIEnv *env, jfloatArray array, jsize start, jsize len, const jfloat *buf); + void (JNICALL *SetDoubleArrayRegion) + (JNIEnv *env, jdoubleArray array, jsize start, jsize len, const jdouble *buf); + + jint (JNICALL *RegisterNatives) + (JNIEnv *env, jclass clazz, const JNINativeMethod *methods, + jint nMethods); + jint (JNICALL *UnregisterNatives) + (JNIEnv *env, jclass clazz); + + jint (JNICALL *MonitorEnter) + (JNIEnv *env, jobject obj); + jint (JNICALL *MonitorExit) + (JNIEnv *env, jobject obj); + + jint (JNICALL *GetJavaVM) + (JNIEnv *env, JavaVM **vm); + + void (JNICALL *GetStringRegion) + (JNIEnv *env, jstring str, jsize start, jsize len, jchar *buf); + void (JNICALL *GetStringUTFRegion) + (JNIEnv *env, jstring str, jsize start, jsize len, char *buf); + + void * (JNICALL *GetPrimitiveArrayCritical) + (JNIEnv *env, jarray array, jboolean *isCopy); + void (JNICALL *ReleasePrimitiveArrayCritical) + (JNIEnv *env, jarray array, void *carray, jint mode); + + const jchar * (JNICALL *GetStringCritical) + (JNIEnv *env, jstring string, jboolean *isCopy); + void (JNICALL *ReleaseStringCritical) + (JNIEnv *env, jstring string, const jchar *cstring); + + jweak (JNICALL *NewWeakGlobalRef) + (JNIEnv *env, jobject obj); + void (JNICALL *DeleteWeakGlobalRef) + (JNIEnv *env, jweak ref); + + jboolean (JNICALL *ExceptionCheck) + (JNIEnv *env); + + jobject (JNICALL *NewDirectByteBuffer) + (JNIEnv* env, void* address, jlong capacity); + void* (JNICALL *GetDirectBufferAddress) + (JNIEnv* env, jobject buf); + jlong (JNICALL *GetDirectBufferCapacity) + (JNIEnv* env, jobject buf); + + /* New JNI 1.6 Features */ + + jobjectRefType (JNICALL *GetObjectRefType) + (JNIEnv* env, jobject obj); + + /* Module Features */ + + jobject (JNICALL *GetModule) + (JNIEnv* env, jclass clazz); + + /* Virtual threads */ + + jboolean (JNICALL *IsVirtualThread) + (JNIEnv* env, jobject obj); +}; + +/* + * We use inlined functions for C++ so that programmers can write: + * + * env->FindClass("java/lang/String") + * + * in C++ rather than: + * + * (*env)->FindClass(env, "java/lang/String") + * + * in C. + */ + +struct JNIEnv_ { + const struct JNINativeInterface_ *functions; +#ifdef __cplusplus + + jint GetVersion() { + return functions->GetVersion(this); + } + jclass DefineClass(const char *name, jobject loader, const jbyte *buf, + jsize len) { + return functions->DefineClass(this, name, loader, buf, len); + } + jclass FindClass(const char *name) { + return functions->FindClass(this, name); + } + jmethodID FromReflectedMethod(jobject method) { + return functions->FromReflectedMethod(this,method); + } + jfieldID FromReflectedField(jobject field) { + return functions->FromReflectedField(this,field); + } + + jobject ToReflectedMethod(jclass cls, jmethodID methodID, jboolean isStatic) { + return functions->ToReflectedMethod(this, cls, methodID, isStatic); + } + + jclass GetSuperclass(jclass sub) { + return functions->GetSuperclass(this, sub); + } + jboolean IsAssignableFrom(jclass sub, jclass sup) { + return functions->IsAssignableFrom(this, sub, sup); + } + + jobject ToReflectedField(jclass cls, jfieldID fieldID, jboolean isStatic) { + return functions->ToReflectedField(this,cls,fieldID,isStatic); + } + + jint Throw(jthrowable obj) { + return functions->Throw(this, obj); + } + jint ThrowNew(jclass clazz, const char *msg) { + return functions->ThrowNew(this, clazz, msg); + } + jthrowable ExceptionOccurred() { + return functions->ExceptionOccurred(this); + } + void ExceptionDescribe() { + functions->ExceptionDescribe(this); + } + void ExceptionClear() { + functions->ExceptionClear(this); + } + void FatalError(const char *msg) { + functions->FatalError(this, msg); + } + + jint PushLocalFrame(jint capacity) { + return functions->PushLocalFrame(this,capacity); + } + jobject PopLocalFrame(jobject result) { + return functions->PopLocalFrame(this,result); + } + + jobject NewGlobalRef(jobject lobj) { + return functions->NewGlobalRef(this,lobj); + } + void DeleteGlobalRef(jobject gref) { + functions->DeleteGlobalRef(this,gref); + } + void DeleteLocalRef(jobject obj) { + functions->DeleteLocalRef(this, obj); + } + + jboolean IsSameObject(jobject obj1, jobject obj2) { + return functions->IsSameObject(this,obj1,obj2); + } + + jobject NewLocalRef(jobject ref) { + return functions->NewLocalRef(this,ref); + } + jint EnsureLocalCapacity(jint capacity) { + return functions->EnsureLocalCapacity(this,capacity); + } + + jobject AllocObject(jclass clazz) { + return functions->AllocObject(this,clazz); + } + jobject NewObject(jclass clazz, jmethodID methodID, ...) { + va_list args; + jobject result; + va_start(args, methodID); + result = functions->NewObjectV(this,clazz,methodID,args); + va_end(args); + return result; + } + jobject NewObjectV(jclass clazz, jmethodID methodID, + va_list args) { + return functions->NewObjectV(this,clazz,methodID,args); + } + jobject NewObjectA(jclass clazz, jmethodID methodID, + const jvalue *args) { + return functions->NewObjectA(this,clazz,methodID,args); + } + + jclass GetObjectClass(jobject obj) { + return functions->GetObjectClass(this,obj); + } + jboolean IsInstanceOf(jobject obj, jclass clazz) { + return functions->IsInstanceOf(this,obj,clazz); + } + + jmethodID GetMethodID(jclass clazz, const char *name, + const char *sig) { + return functions->GetMethodID(this,clazz,name,sig); + } + + jobject CallObjectMethod(jobject obj, jmethodID methodID, ...) { + va_list args; + jobject result; + va_start(args,methodID); + result = functions->CallObjectMethodV(this,obj,methodID,args); + va_end(args); + return result; + } + jobject CallObjectMethodV(jobject obj, jmethodID methodID, + va_list args) { + return functions->CallObjectMethodV(this,obj,methodID,args); + } + jobject CallObjectMethodA(jobject obj, jmethodID methodID, + const jvalue * args) { + return functions->CallObjectMethodA(this,obj,methodID,args); + } + + jboolean CallBooleanMethod(jobject obj, + jmethodID methodID, ...) { + va_list args; + jboolean result; + va_start(args,methodID); + result = functions->CallBooleanMethodV(this,obj,methodID,args); + va_end(args); + return result; + } + jboolean CallBooleanMethodV(jobject obj, jmethodID methodID, + va_list args) { + return functions->CallBooleanMethodV(this,obj,methodID,args); + } + jboolean CallBooleanMethodA(jobject obj, jmethodID methodID, + const jvalue * args) { + return functions->CallBooleanMethodA(this,obj,methodID, args); + } + + jbyte CallByteMethod(jobject obj, jmethodID methodID, ...) { + va_list args; + jbyte result; + va_start(args,methodID); + result = functions->CallByteMethodV(this,obj,methodID,args); + va_end(args); + return result; + } + jbyte CallByteMethodV(jobject obj, jmethodID methodID, + va_list args) { + return functions->CallByteMethodV(this,obj,methodID,args); + } + jbyte CallByteMethodA(jobject obj, jmethodID methodID, + const jvalue * args) { + return functions->CallByteMethodA(this,obj,methodID,args); + } + + jchar CallCharMethod(jobject obj, jmethodID methodID, ...) { + va_list args; + jchar result; + va_start(args,methodID); + result = functions->CallCharMethodV(this,obj,methodID,args); + va_end(args); + return result; + } + jchar CallCharMethodV(jobject obj, jmethodID methodID, + va_list args) { + return functions->CallCharMethodV(this,obj,methodID,args); + } + jchar CallCharMethodA(jobject obj, jmethodID methodID, + const jvalue * args) { + return functions->CallCharMethodA(this,obj,methodID,args); + } + + jshort CallShortMethod(jobject obj, jmethodID methodID, ...) { + va_list args; + jshort result; + va_start(args,methodID); + result = functions->CallShortMethodV(this,obj,methodID,args); + va_end(args); + return result; + } + jshort CallShortMethodV(jobject obj, jmethodID methodID, + va_list args) { + return functions->CallShortMethodV(this,obj,methodID,args); + } + jshort CallShortMethodA(jobject obj, jmethodID methodID, + const jvalue * args) { + return functions->CallShortMethodA(this,obj,methodID,args); + } + + jint CallIntMethod(jobject obj, jmethodID methodID, ...) { + va_list args; + jint result; + va_start(args,methodID); + result = functions->CallIntMethodV(this,obj,methodID,args); + va_end(args); + return result; + } + jint CallIntMethodV(jobject obj, jmethodID methodID, + va_list args) { + return functions->CallIntMethodV(this,obj,methodID,args); + } + jint CallIntMethodA(jobject obj, jmethodID methodID, + const jvalue * args) { + return functions->CallIntMethodA(this,obj,methodID,args); + } + + jlong CallLongMethod(jobject obj, jmethodID methodID, ...) { + va_list args; + jlong result; + va_start(args,methodID); + result = functions->CallLongMethodV(this,obj,methodID,args); + va_end(args); + return result; + } + jlong CallLongMethodV(jobject obj, jmethodID methodID, + va_list args) { + return functions->CallLongMethodV(this,obj,methodID,args); + } + jlong CallLongMethodA(jobject obj, jmethodID methodID, + const jvalue * args) { + return functions->CallLongMethodA(this,obj,methodID,args); + } + + jfloat CallFloatMethod(jobject obj, jmethodID methodID, ...) { + va_list args; + jfloat result; + va_start(args,methodID); + result = functions->CallFloatMethodV(this,obj,methodID,args); + va_end(args); + return result; + } + jfloat CallFloatMethodV(jobject obj, jmethodID methodID, + va_list args) { + return functions->CallFloatMethodV(this,obj,methodID,args); + } + jfloat CallFloatMethodA(jobject obj, jmethodID methodID, + const jvalue * args) { + return functions->CallFloatMethodA(this,obj,methodID,args); + } + + jdouble CallDoubleMethod(jobject obj, jmethodID methodID, ...) { + va_list args; + jdouble result; + va_start(args,methodID); + result = functions->CallDoubleMethodV(this,obj,methodID,args); + va_end(args); + return result; + } + jdouble CallDoubleMethodV(jobject obj, jmethodID methodID, + va_list args) { + return functions->CallDoubleMethodV(this,obj,methodID,args); + } + jdouble CallDoubleMethodA(jobject obj, jmethodID methodID, + const jvalue * args) { + return functions->CallDoubleMethodA(this,obj,methodID,args); + } + + void CallVoidMethod(jobject obj, jmethodID methodID, ...) { + va_list args; + va_start(args,methodID); + functions->CallVoidMethodV(this,obj,methodID,args); + va_end(args); + } + void CallVoidMethodV(jobject obj, jmethodID methodID, + va_list args) { + functions->CallVoidMethodV(this,obj,methodID,args); + } + void CallVoidMethodA(jobject obj, jmethodID methodID, + const jvalue * args) { + functions->CallVoidMethodA(this,obj,methodID,args); + } + + jobject CallNonvirtualObjectMethod(jobject obj, jclass clazz, + jmethodID methodID, ...) { + va_list args; + jobject result; + va_start(args,methodID); + result = functions->CallNonvirtualObjectMethodV(this,obj,clazz, + methodID,args); + va_end(args); + return result; + } + jobject CallNonvirtualObjectMethodV(jobject obj, jclass clazz, + jmethodID methodID, va_list args) { + return functions->CallNonvirtualObjectMethodV(this,obj,clazz, + methodID,args); + } + jobject CallNonvirtualObjectMethodA(jobject obj, jclass clazz, + jmethodID methodID, const jvalue * args) { + return functions->CallNonvirtualObjectMethodA(this,obj,clazz, + methodID,args); + } + + jboolean CallNonvirtualBooleanMethod(jobject obj, jclass clazz, + jmethodID methodID, ...) { + va_list args; + jboolean result; + va_start(args,methodID); + result = functions->CallNonvirtualBooleanMethodV(this,obj,clazz, + methodID,args); + va_end(args); + return result; + } + jboolean CallNonvirtualBooleanMethodV(jobject obj, jclass clazz, + jmethodID methodID, va_list args) { + return functions->CallNonvirtualBooleanMethodV(this,obj,clazz, + methodID,args); + } + jboolean CallNonvirtualBooleanMethodA(jobject obj, jclass clazz, + jmethodID methodID, const jvalue * args) { + return functions->CallNonvirtualBooleanMethodA(this,obj,clazz, + methodID, args); + } + + jbyte CallNonvirtualByteMethod(jobject obj, jclass clazz, + jmethodID methodID, ...) { + va_list args; + jbyte result; + va_start(args,methodID); + result = functions->CallNonvirtualByteMethodV(this,obj,clazz, + methodID,args); + va_end(args); + return result; + } + jbyte CallNonvirtualByteMethodV(jobject obj, jclass clazz, + jmethodID methodID, va_list args) { + return functions->CallNonvirtualByteMethodV(this,obj,clazz, + methodID,args); + } + jbyte CallNonvirtualByteMethodA(jobject obj, jclass clazz, + jmethodID methodID, const jvalue * args) { + return functions->CallNonvirtualByteMethodA(this,obj,clazz, + methodID,args); + } + + jchar CallNonvirtualCharMethod(jobject obj, jclass clazz, + jmethodID methodID, ...) { + va_list args; + jchar result; + va_start(args,methodID); + result = functions->CallNonvirtualCharMethodV(this,obj,clazz, + methodID,args); + va_end(args); + return result; + } + jchar CallNonvirtualCharMethodV(jobject obj, jclass clazz, + jmethodID methodID, va_list args) { + return functions->CallNonvirtualCharMethodV(this,obj,clazz, + methodID,args); + } + jchar CallNonvirtualCharMethodA(jobject obj, jclass clazz, + jmethodID methodID, const jvalue * args) { + return functions->CallNonvirtualCharMethodA(this,obj,clazz, + methodID,args); + } + + jshort CallNonvirtualShortMethod(jobject obj, jclass clazz, + jmethodID methodID, ...) { + va_list args; + jshort result; + va_start(args,methodID); + result = functions->CallNonvirtualShortMethodV(this,obj,clazz, + methodID,args); + va_end(args); + return result; + } + jshort CallNonvirtualShortMethodV(jobject obj, jclass clazz, + jmethodID methodID, va_list args) { + return functions->CallNonvirtualShortMethodV(this,obj,clazz, + methodID,args); + } + jshort CallNonvirtualShortMethodA(jobject obj, jclass clazz, + jmethodID methodID, const jvalue * args) { + return functions->CallNonvirtualShortMethodA(this,obj,clazz, + methodID,args); + } + + jint CallNonvirtualIntMethod(jobject obj, jclass clazz, + jmethodID methodID, ...) { + va_list args; + jint result; + va_start(args,methodID); + result = functions->CallNonvirtualIntMethodV(this,obj,clazz, + methodID,args); + va_end(args); + return result; + } + jint CallNonvirtualIntMethodV(jobject obj, jclass clazz, + jmethodID methodID, va_list args) { + return functions->CallNonvirtualIntMethodV(this,obj,clazz, + methodID,args); + } + jint CallNonvirtualIntMethodA(jobject obj, jclass clazz, + jmethodID methodID, const jvalue * args) { + return functions->CallNonvirtualIntMethodA(this,obj,clazz, + methodID,args); + } + + jlong CallNonvirtualLongMethod(jobject obj, jclass clazz, + jmethodID methodID, ...) { + va_list args; + jlong result; + va_start(args,methodID); + result = functions->CallNonvirtualLongMethodV(this,obj,clazz, + methodID,args); + va_end(args); + return result; + } + jlong CallNonvirtualLongMethodV(jobject obj, jclass clazz, + jmethodID methodID, va_list args) { + return functions->CallNonvirtualLongMethodV(this,obj,clazz, + methodID,args); + } + jlong CallNonvirtualLongMethodA(jobject obj, jclass clazz, + jmethodID methodID, const jvalue * args) { + return functions->CallNonvirtualLongMethodA(this,obj,clazz, + methodID,args); + } + + jfloat CallNonvirtualFloatMethod(jobject obj, jclass clazz, + jmethodID methodID, ...) { + va_list args; + jfloat result; + va_start(args,methodID); + result = functions->CallNonvirtualFloatMethodV(this,obj,clazz, + methodID,args); + va_end(args); + return result; + } + jfloat CallNonvirtualFloatMethodV(jobject obj, jclass clazz, + jmethodID methodID, + va_list args) { + return functions->CallNonvirtualFloatMethodV(this,obj,clazz, + methodID,args); + } + jfloat CallNonvirtualFloatMethodA(jobject obj, jclass clazz, + jmethodID methodID, + const jvalue * args) { + return functions->CallNonvirtualFloatMethodA(this,obj,clazz, + methodID,args); + } + + jdouble CallNonvirtualDoubleMethod(jobject obj, jclass clazz, + jmethodID methodID, ...) { + va_list args; + jdouble result; + va_start(args,methodID); + result = functions->CallNonvirtualDoubleMethodV(this,obj,clazz, + methodID,args); + va_end(args); + return result; + } + jdouble CallNonvirtualDoubleMethodV(jobject obj, jclass clazz, + jmethodID methodID, + va_list args) { + return functions->CallNonvirtualDoubleMethodV(this,obj,clazz, + methodID,args); + } + jdouble CallNonvirtualDoubleMethodA(jobject obj, jclass clazz, + jmethodID methodID, + const jvalue * args) { + return functions->CallNonvirtualDoubleMethodA(this,obj,clazz, + methodID,args); + } + + void CallNonvirtualVoidMethod(jobject obj, jclass clazz, + jmethodID methodID, ...) { + va_list args; + va_start(args,methodID); + functions->CallNonvirtualVoidMethodV(this,obj,clazz,methodID,args); + va_end(args); + } + void CallNonvirtualVoidMethodV(jobject obj, jclass clazz, + jmethodID methodID, + va_list args) { + functions->CallNonvirtualVoidMethodV(this,obj,clazz,methodID,args); + } + void CallNonvirtualVoidMethodA(jobject obj, jclass clazz, + jmethodID methodID, + const jvalue * args) { + functions->CallNonvirtualVoidMethodA(this,obj,clazz,methodID,args); + } + + jfieldID GetFieldID(jclass clazz, const char *name, + const char *sig) { + return functions->GetFieldID(this,clazz,name,sig); + } + + jobject GetObjectField(jobject obj, jfieldID fieldID) { + return functions->GetObjectField(this,obj,fieldID); + } + jboolean GetBooleanField(jobject obj, jfieldID fieldID) { + return functions->GetBooleanField(this,obj,fieldID); + } + jbyte GetByteField(jobject obj, jfieldID fieldID) { + return functions->GetByteField(this,obj,fieldID); + } + jchar GetCharField(jobject obj, jfieldID fieldID) { + return functions->GetCharField(this,obj,fieldID); + } + jshort GetShortField(jobject obj, jfieldID fieldID) { + return functions->GetShortField(this,obj,fieldID); + } + jint GetIntField(jobject obj, jfieldID fieldID) { + return functions->GetIntField(this,obj,fieldID); + } + jlong GetLongField(jobject obj, jfieldID fieldID) { + return functions->GetLongField(this,obj,fieldID); + } + jfloat GetFloatField(jobject obj, jfieldID fieldID) { + return functions->GetFloatField(this,obj,fieldID); + } + jdouble GetDoubleField(jobject obj, jfieldID fieldID) { + return functions->GetDoubleField(this,obj,fieldID); + } + + void SetObjectField(jobject obj, jfieldID fieldID, jobject val) { + functions->SetObjectField(this,obj,fieldID,val); + } + void SetBooleanField(jobject obj, jfieldID fieldID, + jboolean val) { + functions->SetBooleanField(this,obj,fieldID,val); + } + void SetByteField(jobject obj, jfieldID fieldID, + jbyte val) { + functions->SetByteField(this,obj,fieldID,val); + } + void SetCharField(jobject obj, jfieldID fieldID, + jchar val) { + functions->SetCharField(this,obj,fieldID,val); + } + void SetShortField(jobject obj, jfieldID fieldID, + jshort val) { + functions->SetShortField(this,obj,fieldID,val); + } + void SetIntField(jobject obj, jfieldID fieldID, + jint val) { + functions->SetIntField(this,obj,fieldID,val); + } + void SetLongField(jobject obj, jfieldID fieldID, + jlong val) { + functions->SetLongField(this,obj,fieldID,val); + } + void SetFloatField(jobject obj, jfieldID fieldID, + jfloat val) { + functions->SetFloatField(this,obj,fieldID,val); + } + void SetDoubleField(jobject obj, jfieldID fieldID, + jdouble val) { + functions->SetDoubleField(this,obj,fieldID,val); + } + + jmethodID GetStaticMethodID(jclass clazz, const char *name, + const char *sig) { + return functions->GetStaticMethodID(this,clazz,name,sig); + } + + jobject CallStaticObjectMethod(jclass clazz, jmethodID methodID, + ...) { + va_list args; + jobject result; + va_start(args,methodID); + result = functions->CallStaticObjectMethodV(this,clazz,methodID,args); + va_end(args); + return result; + } + jobject CallStaticObjectMethodV(jclass clazz, jmethodID methodID, + va_list args) { + return functions->CallStaticObjectMethodV(this,clazz,methodID,args); + } + jobject CallStaticObjectMethodA(jclass clazz, jmethodID methodID, + const jvalue *args) { + return functions->CallStaticObjectMethodA(this,clazz,methodID,args); + } + + jboolean CallStaticBooleanMethod(jclass clazz, + jmethodID methodID, ...) { + va_list args; + jboolean result; + va_start(args,methodID); + result = functions->CallStaticBooleanMethodV(this,clazz,methodID,args); + va_end(args); + return result; + } + jboolean CallStaticBooleanMethodV(jclass clazz, + jmethodID methodID, va_list args) { + return functions->CallStaticBooleanMethodV(this,clazz,methodID,args); + } + jboolean CallStaticBooleanMethodA(jclass clazz, + jmethodID methodID, const jvalue *args) { + return functions->CallStaticBooleanMethodA(this,clazz,methodID,args); + } + + jbyte CallStaticByteMethod(jclass clazz, + jmethodID methodID, ...) { + va_list args; + jbyte result; + va_start(args,methodID); + result = functions->CallStaticByteMethodV(this,clazz,methodID,args); + va_end(args); + return result; + } + jbyte CallStaticByteMethodV(jclass clazz, + jmethodID methodID, va_list args) { + return functions->CallStaticByteMethodV(this,clazz,methodID,args); + } + jbyte CallStaticByteMethodA(jclass clazz, + jmethodID methodID, const jvalue *args) { + return functions->CallStaticByteMethodA(this,clazz,methodID,args); + } + + jchar CallStaticCharMethod(jclass clazz, + jmethodID methodID, ...) { + va_list args; + jchar result; + va_start(args,methodID); + result = functions->CallStaticCharMethodV(this,clazz,methodID,args); + va_end(args); + return result; + } + jchar CallStaticCharMethodV(jclass clazz, + jmethodID methodID, va_list args) { + return functions->CallStaticCharMethodV(this,clazz,methodID,args); + } + jchar CallStaticCharMethodA(jclass clazz, + jmethodID methodID, const jvalue *args) { + return functions->CallStaticCharMethodA(this,clazz,methodID,args); + } + + jshort CallStaticShortMethod(jclass clazz, + jmethodID methodID, ...) { + va_list args; + jshort result; + va_start(args,methodID); + result = functions->CallStaticShortMethodV(this,clazz,methodID,args); + va_end(args); + return result; + } + jshort CallStaticShortMethodV(jclass clazz, + jmethodID methodID, va_list args) { + return functions->CallStaticShortMethodV(this,clazz,methodID,args); + } + jshort CallStaticShortMethodA(jclass clazz, + jmethodID methodID, const jvalue *args) { + return functions->CallStaticShortMethodA(this,clazz,methodID,args); + } + + jint CallStaticIntMethod(jclass clazz, + jmethodID methodID, ...) { + va_list args; + jint result; + va_start(args,methodID); + result = functions->CallStaticIntMethodV(this,clazz,methodID,args); + va_end(args); + return result; + } + jint CallStaticIntMethodV(jclass clazz, + jmethodID methodID, va_list args) { + return functions->CallStaticIntMethodV(this,clazz,methodID,args); + } + jint CallStaticIntMethodA(jclass clazz, + jmethodID methodID, const jvalue *args) { + return functions->CallStaticIntMethodA(this,clazz,methodID,args); + } + + jlong CallStaticLongMethod(jclass clazz, + jmethodID methodID, ...) { + va_list args; + jlong result; + va_start(args,methodID); + result = functions->CallStaticLongMethodV(this,clazz,methodID,args); + va_end(args); + return result; + } + jlong CallStaticLongMethodV(jclass clazz, + jmethodID methodID, va_list args) { + return functions->CallStaticLongMethodV(this,clazz,methodID,args); + } + jlong CallStaticLongMethodA(jclass clazz, + jmethodID methodID, const jvalue *args) { + return functions->CallStaticLongMethodA(this,clazz,methodID,args); + } + + jfloat CallStaticFloatMethod(jclass clazz, + jmethodID methodID, ...) { + va_list args; + jfloat result; + va_start(args,methodID); + result = functions->CallStaticFloatMethodV(this,clazz,methodID,args); + va_end(args); + return result; + } + jfloat CallStaticFloatMethodV(jclass clazz, + jmethodID methodID, va_list args) { + return functions->CallStaticFloatMethodV(this,clazz,methodID,args); + } + jfloat CallStaticFloatMethodA(jclass clazz, + jmethodID methodID, const jvalue *args) { + return functions->CallStaticFloatMethodA(this,clazz,methodID,args); + } + + jdouble CallStaticDoubleMethod(jclass clazz, + jmethodID methodID, ...) { + va_list args; + jdouble result; + va_start(args,methodID); + result = functions->CallStaticDoubleMethodV(this,clazz,methodID,args); + va_end(args); + return result; + } + jdouble CallStaticDoubleMethodV(jclass clazz, + jmethodID methodID, va_list args) { + return functions->CallStaticDoubleMethodV(this,clazz,methodID,args); + } + jdouble CallStaticDoubleMethodA(jclass clazz, + jmethodID methodID, const jvalue *args) { + return functions->CallStaticDoubleMethodA(this,clazz,methodID,args); + } + + void CallStaticVoidMethod(jclass cls, jmethodID methodID, ...) { + va_list args; + va_start(args,methodID); + functions->CallStaticVoidMethodV(this,cls,methodID,args); + va_end(args); + } + void CallStaticVoidMethodV(jclass cls, jmethodID methodID, + va_list args) { + functions->CallStaticVoidMethodV(this,cls,methodID,args); + } + void CallStaticVoidMethodA(jclass cls, jmethodID methodID, + const jvalue * args) { + functions->CallStaticVoidMethodA(this,cls,methodID,args); + } + + jfieldID GetStaticFieldID(jclass clazz, const char *name, + const char *sig) { + return functions->GetStaticFieldID(this,clazz,name,sig); + } + jobject GetStaticObjectField(jclass clazz, jfieldID fieldID) { + return functions->GetStaticObjectField(this,clazz,fieldID); + } + jboolean GetStaticBooleanField(jclass clazz, jfieldID fieldID) { + return functions->GetStaticBooleanField(this,clazz,fieldID); + } + jbyte GetStaticByteField(jclass clazz, jfieldID fieldID) { + return functions->GetStaticByteField(this,clazz,fieldID); + } + jchar GetStaticCharField(jclass clazz, jfieldID fieldID) { + return functions->GetStaticCharField(this,clazz,fieldID); + } + jshort GetStaticShortField(jclass clazz, jfieldID fieldID) { + return functions->GetStaticShortField(this,clazz,fieldID); + } + jint GetStaticIntField(jclass clazz, jfieldID fieldID) { + return functions->GetStaticIntField(this,clazz,fieldID); + } + jlong GetStaticLongField(jclass clazz, jfieldID fieldID) { + return functions->GetStaticLongField(this,clazz,fieldID); + } + jfloat GetStaticFloatField(jclass clazz, jfieldID fieldID) { + return functions->GetStaticFloatField(this,clazz,fieldID); + } + jdouble GetStaticDoubleField(jclass clazz, jfieldID fieldID) { + return functions->GetStaticDoubleField(this,clazz,fieldID); + } + + void SetStaticObjectField(jclass clazz, jfieldID fieldID, + jobject value) { + functions->SetStaticObjectField(this,clazz,fieldID,value); + } + void SetStaticBooleanField(jclass clazz, jfieldID fieldID, + jboolean value) { + functions->SetStaticBooleanField(this,clazz,fieldID,value); + } + void SetStaticByteField(jclass clazz, jfieldID fieldID, + jbyte value) { + functions->SetStaticByteField(this,clazz,fieldID,value); + } + void SetStaticCharField(jclass clazz, jfieldID fieldID, + jchar value) { + functions->SetStaticCharField(this,clazz,fieldID,value); + } + void SetStaticShortField(jclass clazz, jfieldID fieldID, + jshort value) { + functions->SetStaticShortField(this,clazz,fieldID,value); + } + void SetStaticIntField(jclass clazz, jfieldID fieldID, + jint value) { + functions->SetStaticIntField(this,clazz,fieldID,value); + } + void SetStaticLongField(jclass clazz, jfieldID fieldID, + jlong value) { + functions->SetStaticLongField(this,clazz,fieldID,value); + } + void SetStaticFloatField(jclass clazz, jfieldID fieldID, + jfloat value) { + functions->SetStaticFloatField(this,clazz,fieldID,value); + } + void SetStaticDoubleField(jclass clazz, jfieldID fieldID, + jdouble value) { + functions->SetStaticDoubleField(this,clazz,fieldID,value); + } + + jstring NewString(const jchar *unicode, jsize len) { + return functions->NewString(this,unicode,len); + } + jsize GetStringLength(jstring str) { + return functions->GetStringLength(this,str); + } + const jchar *GetStringChars(jstring str, jboolean *isCopy) { + return functions->GetStringChars(this,str,isCopy); + } + void ReleaseStringChars(jstring str, const jchar *chars) { + functions->ReleaseStringChars(this,str,chars); + } + + jstring NewStringUTF(const char *utf) { + return functions->NewStringUTF(this,utf); + } + jsize GetStringUTFLength(jstring str) { + return functions->GetStringUTFLength(this,str); + } + const char* GetStringUTFChars(jstring str, jboolean *isCopy) { + return functions->GetStringUTFChars(this,str,isCopy); + } + void ReleaseStringUTFChars(jstring str, const char* chars) { + functions->ReleaseStringUTFChars(this,str,chars); + } + + jsize GetArrayLength(jarray array) { + return functions->GetArrayLength(this,array); + } + + jobjectArray NewObjectArray(jsize len, jclass clazz, + jobject init) { + return functions->NewObjectArray(this,len,clazz,init); + } + jobject GetObjectArrayElement(jobjectArray array, jsize index) { + return functions->GetObjectArrayElement(this,array,index); + } + void SetObjectArrayElement(jobjectArray array, jsize index, + jobject val) { + functions->SetObjectArrayElement(this,array,index,val); + } + + jbooleanArray NewBooleanArray(jsize len) { + return functions->NewBooleanArray(this,len); + } + jbyteArray NewByteArray(jsize len) { + return functions->NewByteArray(this,len); + } + jcharArray NewCharArray(jsize len) { + return functions->NewCharArray(this,len); + } + jshortArray NewShortArray(jsize len) { + return functions->NewShortArray(this,len); + } + jintArray NewIntArray(jsize len) { + return functions->NewIntArray(this,len); + } + jlongArray NewLongArray(jsize len) { + return functions->NewLongArray(this,len); + } + jfloatArray NewFloatArray(jsize len) { + return functions->NewFloatArray(this,len); + } + jdoubleArray NewDoubleArray(jsize len) { + return functions->NewDoubleArray(this,len); + } + + jboolean * GetBooleanArrayElements(jbooleanArray array, jboolean *isCopy) { + return functions->GetBooleanArrayElements(this,array,isCopy); + } + jbyte * GetByteArrayElements(jbyteArray array, jboolean *isCopy) { + return functions->GetByteArrayElements(this,array,isCopy); + } + jchar * GetCharArrayElements(jcharArray array, jboolean *isCopy) { + return functions->GetCharArrayElements(this,array,isCopy); + } + jshort * GetShortArrayElements(jshortArray array, jboolean *isCopy) { + return functions->GetShortArrayElements(this,array,isCopy); + } + jint * GetIntArrayElements(jintArray array, jboolean *isCopy) { + return functions->GetIntArrayElements(this,array,isCopy); + } + jlong * GetLongArrayElements(jlongArray array, jboolean *isCopy) { + return functions->GetLongArrayElements(this,array,isCopy); + } + jfloat * GetFloatArrayElements(jfloatArray array, jboolean *isCopy) { + return functions->GetFloatArrayElements(this,array,isCopy); + } + jdouble * GetDoubleArrayElements(jdoubleArray array, jboolean *isCopy) { + return functions->GetDoubleArrayElements(this,array,isCopy); + } + + void ReleaseBooleanArrayElements(jbooleanArray array, + jboolean *elems, + jint mode) { + functions->ReleaseBooleanArrayElements(this,array,elems,mode); + } + void ReleaseByteArrayElements(jbyteArray array, + jbyte *elems, + jint mode) { + functions->ReleaseByteArrayElements(this,array,elems,mode); + } + void ReleaseCharArrayElements(jcharArray array, + jchar *elems, + jint mode) { + functions->ReleaseCharArrayElements(this,array,elems,mode); + } + void ReleaseShortArrayElements(jshortArray array, + jshort *elems, + jint mode) { + functions->ReleaseShortArrayElements(this,array,elems,mode); + } + void ReleaseIntArrayElements(jintArray array, + jint *elems, + jint mode) { + functions->ReleaseIntArrayElements(this,array,elems,mode); + } + void ReleaseLongArrayElements(jlongArray array, + jlong *elems, + jint mode) { + functions->ReleaseLongArrayElements(this,array,elems,mode); + } + void ReleaseFloatArrayElements(jfloatArray array, + jfloat *elems, + jint mode) { + functions->ReleaseFloatArrayElements(this,array,elems,mode); + } + void ReleaseDoubleArrayElements(jdoubleArray array, + jdouble *elems, + jint mode) { + functions->ReleaseDoubleArrayElements(this,array,elems,mode); + } + + void GetBooleanArrayRegion(jbooleanArray array, + jsize start, jsize len, jboolean *buf) { + functions->GetBooleanArrayRegion(this,array,start,len,buf); + } + void GetByteArrayRegion(jbyteArray array, + jsize start, jsize len, jbyte *buf) { + functions->GetByteArrayRegion(this,array,start,len,buf); + } + void GetCharArrayRegion(jcharArray array, + jsize start, jsize len, jchar *buf) { + functions->GetCharArrayRegion(this,array,start,len,buf); + } + void GetShortArrayRegion(jshortArray array, + jsize start, jsize len, jshort *buf) { + functions->GetShortArrayRegion(this,array,start,len,buf); + } + void GetIntArrayRegion(jintArray array, + jsize start, jsize len, jint *buf) { + functions->GetIntArrayRegion(this,array,start,len,buf); + } + void GetLongArrayRegion(jlongArray array, + jsize start, jsize len, jlong *buf) { + functions->GetLongArrayRegion(this,array,start,len,buf); + } + void GetFloatArrayRegion(jfloatArray array, + jsize start, jsize len, jfloat *buf) { + functions->GetFloatArrayRegion(this,array,start,len,buf); + } + void GetDoubleArrayRegion(jdoubleArray array, + jsize start, jsize len, jdouble *buf) { + functions->GetDoubleArrayRegion(this,array,start,len,buf); + } + + void SetBooleanArrayRegion(jbooleanArray array, jsize start, jsize len, + const jboolean *buf) { + functions->SetBooleanArrayRegion(this,array,start,len,buf); + } + void SetByteArrayRegion(jbyteArray array, jsize start, jsize len, + const jbyte *buf) { + functions->SetByteArrayRegion(this,array,start,len,buf); + } + void SetCharArrayRegion(jcharArray array, jsize start, jsize len, + const jchar *buf) { + functions->SetCharArrayRegion(this,array,start,len,buf); + } + void SetShortArrayRegion(jshortArray array, jsize start, jsize len, + const jshort *buf) { + functions->SetShortArrayRegion(this,array,start,len,buf); + } + void SetIntArrayRegion(jintArray array, jsize start, jsize len, + const jint *buf) { + functions->SetIntArrayRegion(this,array,start,len,buf); + } + void SetLongArrayRegion(jlongArray array, jsize start, jsize len, + const jlong *buf) { + functions->SetLongArrayRegion(this,array,start,len,buf); + } + void SetFloatArrayRegion(jfloatArray array, jsize start, jsize len, + const jfloat *buf) { + functions->SetFloatArrayRegion(this,array,start,len,buf); + } + void SetDoubleArrayRegion(jdoubleArray array, jsize start, jsize len, + const jdouble *buf) { + functions->SetDoubleArrayRegion(this,array,start,len,buf); + } + + jint RegisterNatives(jclass clazz, const JNINativeMethod *methods, + jint nMethods) { + return functions->RegisterNatives(this,clazz,methods,nMethods); + } + jint UnregisterNatives(jclass clazz) { + return functions->UnregisterNatives(this,clazz); + } + + jint MonitorEnter(jobject obj) { + return functions->MonitorEnter(this,obj); + } + jint MonitorExit(jobject obj) { + return functions->MonitorExit(this,obj); + } + + jint GetJavaVM(JavaVM **vm) { + return functions->GetJavaVM(this,vm); + } + + void GetStringRegion(jstring str, jsize start, jsize len, jchar *buf) { + functions->GetStringRegion(this,str,start,len,buf); + } + void GetStringUTFRegion(jstring str, jsize start, jsize len, char *buf) { + functions->GetStringUTFRegion(this,str,start,len,buf); + } + + void * GetPrimitiveArrayCritical(jarray array, jboolean *isCopy) { + return functions->GetPrimitiveArrayCritical(this,array,isCopy); + } + void ReleasePrimitiveArrayCritical(jarray array, void *carray, jint mode) { + functions->ReleasePrimitiveArrayCritical(this,array,carray,mode); + } + + const jchar * GetStringCritical(jstring string, jboolean *isCopy) { + return functions->GetStringCritical(this,string,isCopy); + } + void ReleaseStringCritical(jstring string, const jchar *cstring) { + functions->ReleaseStringCritical(this,string,cstring); + } + + jweak NewWeakGlobalRef(jobject obj) { + return functions->NewWeakGlobalRef(this,obj); + } + void DeleteWeakGlobalRef(jweak ref) { + functions->DeleteWeakGlobalRef(this,ref); + } + + jboolean ExceptionCheck() { + return functions->ExceptionCheck(this); + } + + jobject NewDirectByteBuffer(void* address, jlong capacity) { + return functions->NewDirectByteBuffer(this, address, capacity); + } + void* GetDirectBufferAddress(jobject buf) { + return functions->GetDirectBufferAddress(this, buf); + } + jlong GetDirectBufferCapacity(jobject buf) { + return functions->GetDirectBufferCapacity(this, buf); + } + jobjectRefType GetObjectRefType(jobject obj) { + return functions->GetObjectRefType(this, obj); + } + + /* Module Features */ + + jobject GetModule(jclass clazz) { + return functions->GetModule(this, clazz); + } + + /* Virtual threads */ + + jboolean IsVirtualThread(jobject obj) { + return functions->IsVirtualThread(this, obj); + } + +#endif /* __cplusplus */ +}; + +/* + * optionString may be any option accepted by the JVM, or one of the + * following: + * + * -D= Set a system property. + * -verbose[:class|gc|jni] Enable verbose output, comma-separated. E.g. + * "-verbose:class" or "-verbose:gc,class" + * Standard names include: gc, class, and jni. + * All nonstandard (VM-specific) names must begin + * with "X". + * vfprintf extraInfo is a pointer to the vfprintf hook. + * exit extraInfo is a pointer to the exit hook. + * abort extraInfo is a pointer to the abort hook. + */ +typedef struct JavaVMOption { + char *optionString; + void *extraInfo; +} JavaVMOption; + +typedef struct JavaVMInitArgs { + jint version; + + jint nOptions; + JavaVMOption *options; + jboolean ignoreUnrecognized; +} JavaVMInitArgs; + +typedef struct JavaVMAttachArgs { + jint version; + + char *name; + jobject group; +} JavaVMAttachArgs; + +/* These will be VM-specific. */ + +#define JDK1_2 +#define JDK1_4 + +/* End VM-specific. */ + +struct JNIInvokeInterface_ { + void *reserved0; + void *reserved1; + void *reserved2; + + jint (JNICALL *DestroyJavaVM)(JavaVM *vm); + + jint (JNICALL *AttachCurrentThread)(JavaVM *vm, void **penv, void *args); + + jint (JNICALL *DetachCurrentThread)(JavaVM *vm); + + jint (JNICALL *GetEnv)(JavaVM *vm, void **penv, jint version); + + jint (JNICALL *AttachCurrentThreadAsDaemon)(JavaVM *vm, void **penv, void *args); +}; + +struct JavaVM_ { + const struct JNIInvokeInterface_ *functions; +#ifdef __cplusplus + + jint DestroyJavaVM() { + return functions->DestroyJavaVM(this); + } + jint AttachCurrentThread(void **penv, void *args) { + return functions->AttachCurrentThread(this, penv, args); + } + jint DetachCurrentThread() { + return functions->DetachCurrentThread(this); + } + + jint GetEnv(void **penv, jint version) { + return functions->GetEnv(this, penv, version); + } + jint AttachCurrentThreadAsDaemon(void **penv, void *args) { + return functions->AttachCurrentThreadAsDaemon(this, penv, args); + } +#endif +}; + +#ifdef _JNI_IMPLEMENTATION_ +#define _JNI_IMPORT_OR_EXPORT_ JNIEXPORT +#else +#define _JNI_IMPORT_OR_EXPORT_ JNIIMPORT +#endif +_JNI_IMPORT_OR_EXPORT_ jint JNICALL +JNI_GetDefaultJavaVMInitArgs(void *args); + +_JNI_IMPORT_OR_EXPORT_ jint JNICALL +JNI_CreateJavaVM(JavaVM **pvm, void **penv, void *args); + +_JNI_IMPORT_OR_EXPORT_ jint JNICALL +JNI_GetCreatedJavaVMs(JavaVM **, jsize, jsize *); + +/* Defined by native libraries. */ +JNIEXPORT jint JNICALL +JNI_OnLoad(JavaVM *vm, void *reserved); + +JNIEXPORT void JNICALL +JNI_OnUnload(JavaVM *vm, void *reserved); + +#define JNI_VERSION_1_1 0x00010001 +#define JNI_VERSION_1_2 0x00010002 +#define JNI_VERSION_1_4 0x00010004 +#define JNI_VERSION_1_6 0x00010006 +#define JNI_VERSION_1_8 0x00010008 +#define JNI_VERSION_9 0x00090000 +#define JNI_VERSION_10 0x000a0000 +#define JNI_VERSION_19 0x00130000 +#define JNI_VERSION_20 0x00140000 +#define JNI_VERSION_21 0x00150000 + +#ifdef __cplusplus +} /* extern "C" */ +#endif /* __cplusplus */ + +#endif /* !_JAVASOFT_JNI_H_ */ diff --git a/apps/multiplatform/common/src/commonMain/cpp/desktop/include/unix/jni_md.h b/apps/multiplatform/common/src/commonMain/cpp/desktop/include/unix/jni_md.h new file mode 100644 index 0000000000..6e583da714 --- /dev/null +++ b/apps/multiplatform/common/src/commonMain/cpp/desktop/include/unix/jni_md.h @@ -0,0 +1,66 @@ +/* + * Copyright (c) 1996, 2020, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +#ifndef _JAVASOFT_JNI_MD_H_ +#define _JAVASOFT_JNI_MD_H_ + +#ifndef __has_attribute + #define __has_attribute(x) 0 +#endif + +#ifndef JNIEXPORT + #if (defined(__GNUC__) && ((__GNUC__ > 4) || (__GNUC__ == 4) && (__GNUC_MINOR__ > 2))) || __has_attribute(visibility) + #ifdef ARM + #define JNIEXPORT __attribute__((externally_visible,visibility("default"))) + #else + #define JNIEXPORT __attribute__((visibility("default"))) + #endif + #else + #define JNIEXPORT + #endif +#endif + +#if (defined(__GNUC__) && ((__GNUC__ > 4) || (__GNUC__ == 4) && (__GNUC_MINOR__ > 2))) || __has_attribute(visibility) + #ifdef ARM + #define JNIIMPORT __attribute__((externally_visible,visibility("default"))) + #else + #define JNIIMPORT __attribute__((visibility("default"))) + #endif +#else + #define JNIIMPORT +#endif + +#define JNICALL + +typedef int jint; +#ifdef _LP64 +typedef long jlong; +#else +typedef long long jlong; +#endif + +typedef signed char jbyte; + +#endif /* !_JAVASOFT_JNI_MD_H_ */ diff --git a/apps/multiplatform/common/src/commonMain/cpp/desktop/include/windows/jni_md.h b/apps/multiplatform/common/src/commonMain/cpp/desktop/include/windows/jni_md.h new file mode 100644 index 0000000000..b8c144fb04 --- /dev/null +++ b/apps/multiplatform/common/src/commonMain/cpp/desktop/include/windows/jni_md.h @@ -0,0 +1,40 @@ +/* + * Copyright (c) 1996, 2020, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +#ifndef _JAVASOFT_JNI_MD_H_ +#define _JAVASOFT_JNI_MD_H_ + +#ifndef JNIEXPORT + #define JNIEXPORT __declspec(dllexport) +#endif +#define JNIIMPORT __declspec(dllimport) +#define JNICALL __stdcall + +// 'long' is always 32 bit on windows so this matches what jdk expects +typedef long jint; +typedef __int64 jlong; +typedef signed char jbyte; + +#endif /* !_JAVASOFT_JNI_MD_H_ */ diff --git a/apps/multiplatform/common/src/commonMain/cpp/desktop/simplex-api.c b/apps/multiplatform/common/src/commonMain/cpp/desktop/simplex-api.c new file mode 100644 index 0000000000..e772adffa2 --- /dev/null +++ b/apps/multiplatform/common/src/commonMain/cpp/desktop/simplex-api.c @@ -0,0 +1,98 @@ +#include + +// from the RTS +void hs_init(int * argc, char **argv[]); + +//extern void __svfscanf(void){}; +//extern void __vfwscanf(void){}; +//extern void __memset_chk_fail(void){}; +//extern void __strcpy_chk_generic(void){}; +//extern void __strcat_chk_generic(void){}; +//extern void __libc_globals(void){}; +//extern void __rel_iplt_start(void){}; + +// Android 9 only, not 13 +//extern void reallocarray(void){}; + +JNIEXPORT void JNICALL +Java_chat_simplex_common_platform_BackendKt_initHS(JNIEnv *env, jclass clazz) { + hs_init(NULL, NULL); +} + +// from simplex-chat +typedef long* chat_ctrl; + +extern char *chat_migrate_init(const char *path, const char *key, const char *confirm, chat_ctrl *ctrl); +extern char *chat_send_cmd(chat_ctrl ctrl, const char *cmd); +extern char *chat_recv_msg(chat_ctrl ctrl); // deprecated +extern char *chat_recv_msg_wait(chat_ctrl ctrl, const int wait); +extern char *chat_parse_markdown(const char *str); +extern char *chat_parse_server(const char *str); +extern char *chat_password_hash(const char *pwd, const char *salt); + +JNIEXPORT jobjectArray JNICALL +Java_chat_simplex_common_platform_BackendKt_chatMigrateInit(JNIEnv *env, jclass clazz, jstring dbPath, jstring dbKey, jstring confirm) { + const char *_dbPath = (*env)->GetStringUTFChars(env, dbPath, JNI_FALSE); + const char *_dbKey = (*env)->GetStringUTFChars(env, dbKey, JNI_FALSE); + const char *_confirm = (*env)->GetStringUTFChars(env, confirm, JNI_FALSE); + jlong _ctrl = (jlong) 0; + jstring res = (*env)->NewStringUTF(env, chat_migrate_init(_dbPath, _dbKey, _confirm, &_ctrl)); + (*env)->ReleaseStringUTFChars(env, dbPath, _dbPath); + (*env)->ReleaseStringUTFChars(env, dbKey, _dbKey); + (*env)->ReleaseStringUTFChars(env, dbKey, _confirm); + + // Creating array of Object's (boxed values can be passed, eg. Long instead of long) + jobjectArray ret = (jobjectArray)(*env)->NewObjectArray(env, 2, (*env)->FindClass(env, "java/lang/Object"), NULL); + // Java's String + (*env)->SetObjectArrayElement(env, ret, 0, res); + // Java's Long + (*env)->SetObjectArrayElement(env, ret, 1, + (*env)->NewObject(env, (*env)->FindClass(env, "java/lang/Long"), + (*env)->GetMethodID(env, (*env)->FindClass(env, "java/lang/Long"), "", "(J)V"), + _ctrl)); + return ret; +} + +JNIEXPORT jstring JNICALL +Java_chat_simplex_common_platform_BackendKt_chatSendCmd(JNIEnv *env, jclass clazz, jlong controller, jstring msg) { + const char *_msg = (*env)->GetStringUTFChars(env, msg, JNI_FALSE); + jstring res = (*env)->NewStringUTF(env, chat_send_cmd((void*)controller, _msg)); + (*env)->ReleaseStringUTFChars(env, msg, _msg); + return res; +} + +JNIEXPORT jstring JNICALL +Java_chat_simplex_common_platform_BackendKt_chatRecvMsg(JNIEnv *env, jclass clazz, jlong controller) { + return (*env)->NewStringUTF(env, chat_recv_msg((void*)controller)); +} + +JNIEXPORT jstring JNICALL +Java_chat_simplex_common_platform_BackendKt_chatRecvMsgWait(JNIEnv *env, jclass clazz, jlong controller, jint wait) { + return (*env)->NewStringUTF(env, chat_recv_msg_wait((void*)controller, wait)); +} + +JNIEXPORT jstring JNICALL +Java_chat_simplex_common_platform_BackendKt_chatParseMarkdown(JNIEnv *env, jclass clazz, jstring str) { + const char *_str = (*env)->GetStringUTFChars(env, str, JNI_FALSE); + jstring res = (*env)->NewStringUTF(env, chat_parse_markdown(_str)); + (*env)->ReleaseStringUTFChars(env, str, _str); + return res; +} + +JNIEXPORT jstring JNICALL +Java_chat_simplex_common_platform_BackendKt_chatParseServer(JNIEnv *env, jclass clazz, jstring str) { + const char *_str = (*env)->GetStringUTFChars(env, str, JNI_FALSE); + jstring res = (*env)->NewStringUTF(env, chat_parse_server(_str)); + (*env)->ReleaseStringUTFChars(env, str, _str); + return res; +} + +JNIEXPORT jstring JNICALL +Java_chat_simplex_common_platform_BackendKt_chatPasswordHash(JNIEnv *env, jclass clazz, jstring pwd, jstring salt) { + const char *_pwd = (*env)->GetStringUTFChars(env, pwd, JNI_FALSE); + const char *_salt = (*env)->GetStringUTFChars(env, salt, JNI_FALSE); + jstring res = (*env)->NewStringUTF(env, chat_password_hash(_pwd, _salt)); + (*env)->ReleaseStringUTFChars(env, pwd, _pwd); + (*env)->ReleaseStringUTFChars(env, salt, _salt); + return res; +} diff --git a/apps/multiplatform/common/src/commonMain/cpp/desktop/toolchains/aarch64-linux-gnu-gcc.cmake b/apps/multiplatform/common/src/commonMain/cpp/desktop/toolchains/aarch64-linux-gnu-gcc.cmake new file mode 100644 index 0000000000..4dd58aabf0 --- /dev/null +++ b/apps/multiplatform/common/src/commonMain/cpp/desktop/toolchains/aarch64-linux-gnu-gcc.cmake @@ -0,0 +1,20 @@ +set(CMAKE_SYSTEM_NAME Linux) +set(CMAKE_SYSTEM_PROCESSOR aarch64) + +set(CMAKE_C_COMPILER "aarch64-linux-gnu-gcc") +set(CMAKE_CXX_COMPILER "aarch64-linux-gnu-g++") +set(CMAKE_AR "aarch64-linux-gnu-ar" CACHE FILEPATH Archiver) +set(CMAKE_RANLIB "aarch64-linux-gnu-ranlib" CACHE FILEPATH Indexer) + +set(CMAKE_FIND_ROOT_PATH /usr/aarch64-linux-gnu) + +set(CMAKE_FIND_ROOT_PATH_MODE_PROGRAM NEVER) +set(CMAKE_FIND_ROOT_PATH_MODE_LIBRARY ONLY) +set(CMAKE_FIND_ROOT_PATH_MODE_INCLUDE ONLY) + +set(CMAKE_C_FLAGS "-march=armv8-a") +set(CMAKE_CXX_FLAGS "-march=armv8-a") + +# cache flags +set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS}" CACHE STRING "c flags") +set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS}" CACHE STRING "c++ flags") diff --git a/apps/multiplatform/common/src/commonMain/cpp/desktop/toolchains/aarch64-mac-apple-darwin-gcc.cmake b/apps/multiplatform/common/src/commonMain/cpp/desktop/toolchains/aarch64-mac-apple-darwin-gcc.cmake new file mode 100644 index 0000000000..f69e17306f --- /dev/null +++ b/apps/multiplatform/common/src/commonMain/cpp/desktop/toolchains/aarch64-mac-apple-darwin-gcc.cmake @@ -0,0 +1,17 @@ +set(CMAKE_SYSTEM_NAME Darwin) +set(CMAKE_SYSTEM_PROCESSOR aarch64) + +set(CMAKE_C_COMPILER "gcc-13") +set(CMAKE_CXX_COMPILER "g++-13") +set(CMAKE_AR "gcc-ar-13" CACHE FILEPATH Archiver) +set(CMAKE_RANLIB "gcc-ranlib-13" CACHE FILEPATH Indexer) + +set(CMAKE_FIND_ROOT_PATH /usr/) + +set(CMAKE_FIND_ROOT_PATH_MODE_PROGRAM NEVER) +set(CMAKE_FIND_ROOT_PATH_MODE_LIBRARY ONLY) +set(CMAKE_FIND_ROOT_PATH_MODE_INCLUDE ONLY) + +# cache flags +set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS}" CACHE STRING "c flags") +set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS}" CACHE STRING "c++ flags") diff --git a/apps/multiplatform/common/src/commonMain/cpp/desktop/toolchains/x86_64-linux-gnu-gcc.cmake b/apps/multiplatform/common/src/commonMain/cpp/desktop/toolchains/x86_64-linux-gnu-gcc.cmake new file mode 100644 index 0000000000..e5dba46894 --- /dev/null +++ b/apps/multiplatform/common/src/commonMain/cpp/desktop/toolchains/x86_64-linux-gnu-gcc.cmake @@ -0,0 +1,17 @@ +set(CMAKE_SYSTEM_NAME Linux) +set(CMAKE_SYSTEM_PROCESSOR amd64) + +set(CMAKE_C_COMPILER "x86_64-linux-gnu-gcc") +set(CMAKE_CXX_COMPILER "x86_64-linux-gnu-g++") +set(CMAKE_AR "x86_64-linux-gnu-gcc-ar" CACHE FILEPATH Archiver) +set(CMAKE_RANLIB "x86_64-linux-gnu-gcc-ranlib" CACHE FILEPATH Indexer) + +set(CMAKE_FIND_ROOT_PATH /usr/x86_64-linux-gnu) + +set(CMAKE_FIND_ROOT_PATH_MODE_PROGRAM NEVER) +set(CMAKE_FIND_ROOT_PATH_MODE_LIBRARY ONLY) +set(CMAKE_FIND_ROOT_PATH_MODE_INCLUDE ONLY) + +# cache flags +set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS}" CACHE STRING "c flags") +set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS}" CACHE STRING "c++ flags") diff --git a/apps/multiplatform/common/src/commonMain/cpp/desktop/toolchains/x86_64-mac-apple-darwin-gcc.cmake b/apps/multiplatform/common/src/commonMain/cpp/desktop/toolchains/x86_64-mac-apple-darwin-gcc.cmake new file mode 100644 index 0000000000..a28b89d8af --- /dev/null +++ b/apps/multiplatform/common/src/commonMain/cpp/desktop/toolchains/x86_64-mac-apple-darwin-gcc.cmake @@ -0,0 +1,17 @@ +set(CMAKE_SYSTEM_NAME Darwin) +set(CMAKE_SYSTEM_PROCESSOR amd64) + +set(CMAKE_C_COMPILER "gcc-13") +set(CMAKE_CXX_COMPILER "g++-13") +set(CMAKE_AR "gcc-ar-13" CACHE FILEPATH Archiver) +set(CMAKE_RANLIB "gcc-ranlib-13" CACHE FILEPATH Indexer) + +set(CMAKE_FIND_ROOT_PATH /usr/) + +set(CMAKE_FIND_ROOT_PATH_MODE_PROGRAM NEVER) +set(CMAKE_FIND_ROOT_PATH_MODE_LIBRARY ONLY) +set(CMAKE_FIND_ROOT_PATH_MODE_INCLUDE ONLY) + +# cache flags +set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS}" CACHE STRING "c flags") +set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS}" CACHE STRING "c++ flags") diff --git a/apps/multiplatform/common/src/commonMain/cpp/desktop/toolchains/x86_64-windows-mingw32-gcc.cmake b/apps/multiplatform/common/src/commonMain/cpp/desktop/toolchains/x86_64-windows-mingw32-gcc.cmake new file mode 100644 index 0000000000..e34e871b18 --- /dev/null +++ b/apps/multiplatform/common/src/commonMain/cpp/desktop/toolchains/x86_64-windows-mingw32-gcc.cmake @@ -0,0 +1,17 @@ +set(CMAKE_SYSTEM_NAME Windows) +set(CMAKE_SYSTEM_PROCESSOR amd64) + +set(CMAKE_C_COMPILER "x86_64-w64-mingw32-gcc") +set(CMAKE_CXX_COMPILER "x86_64-w64-mingw32-g++") +set(CMAKE_AR "x86_64-w64-mingw32-ar" CACHE FILEPATH Archiver) +set(CMAKE_RANLIB "x86_64-w64-mingw32-ranlib" CACHE FILEPATH Indexer) + +set(CMAKE_FIND_ROOT_PATH /usr/x86_64-w64-mingw32) + +set(CMAKE_FIND_ROOT_PATH_MODE_PROGRAM NEVER) +set(CMAKE_FIND_ROOT_PATH_MODE_LIBRARY ONLY) +set(CMAKE_FIND_ROOT_PATH_MODE_INCLUDE ONLY) + +# cache flags +set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS}" CACHE STRING "c flags") +set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS}" CACHE STRING "c++ flags") diff --git a/apps/multiplatform/desktop/build.gradle.kts b/apps/multiplatform/desktop/build.gradle.kts new file mode 100644 index 0000000000..95a4908dbd --- /dev/null +++ b/apps/multiplatform/desktop/build.gradle.kts @@ -0,0 +1,169 @@ +import org.jetbrains.compose.desktop.application.dsl.TargetFormat + +plugins { + kotlin("multiplatform") + id("org.jetbrains.compose") + id("io.github.tomtzook.gradle-cmake") version "1.2.2" +} + +group = "chat.simplex" +version = extra["desktop.version_name"] as String + + +kotlin { + jvm { + jvmToolchain(11) + withJava() + } + sourceSets { + val jvmMain by getting { + dependencies { + implementation(project(":common")) + implementation(compose.desktop.currentOs) + } + } + val jvmTest by getting + } +} + +// https://github.com/JetBrains/compose-multiplatform/tree/master/tutorials/Native_distributions_and_local_execution +compose { + desktop { + application { + mainClass = "MainKt" + nativeDistributions { + modules("jdk.zipfs") + //includeAllModules = true + outputBaseDir.set(project.file("../release")) + targetFormats( + TargetFormat.Deb, TargetFormat.Dmg, TargetFormat.Msi, TargetFormat.Exe + //, TargetFormat.AppImage // Gradle doesn't sync on Mac with it + ) + linux { + iconFile.set(project.file("../common/src/commonMain/resources/distribute/simplex.png")) + appCategory = "Messenger" + } + windows { + // LALAL + iconFile.set(project.file("../common/src/commonMain/resources/distribute/simplex.ico")) + console = true + perUserInstall = true + dirChooser = true + } + macOS { + // LALAL + //iconFile.set(project.file("../common/src/commonMain/resources/distribute/simplex.icns")) + appCategory = "public.app-category.social-networking" + bundleID = "chat.simplex.app" + } + packageName = "simplex" + // Packaging requires to have version like MAJOR.MINOR.PATCH + var adjustedVersion = rootProject.extra["desktop.version_name"] as String + adjustedVersion = adjustedVersion.replace(Regex("[^0-9.]"), "") + if (adjustedVersion.split(".").size != 3) { + adjustedVersion += ".0" + } + version = adjustedVersion + } + } + } +} + +val cppPath = "../common/src/commonMain/cpp" +cmake { + // Run this command to make build for all targets: + // ./gradlew common:cmakeBuild -PcrossCompile + if (project.hasProperty("crossCompile")) { + machines.customMachines.register("linux-amd64") { + toolchainFile.set(project.file("$cppPath/toolchains/x86_64-linux-gnu-gcc.cmake")) + } + /*machines.customMachines.register("linux-aarch64") { + toolchainFile.set(project.file("$cppPath/toolchains/aarch64-linux-gnu-gcc.cmake")) + }*/ + machines.customMachines.register("win-amd64") { + toolchainFile.set(project.file("$cppPath/toolchains/x86_64-windows-mingw32-gcc.cmake")) + } + if (machines.host.name == "mac-amd64") { + machines.customMachines.register("mac-amd64") { + toolchainFile.set(project.file("$cppPath/toolchains/x86_64-mac-apple-darwin-gcc.cmake")) + } + } + if (machines.host.name == "mac-aarch64") { + machines.customMachines.register("mac-aarch64") { + toolchainFile.set(project.file("$cppPath/toolchains/aarch64-mac-apple-darwin-gcc.cmake")) + } + } + } + val compileMachineTargets = arrayListOf(machines.host) + compileMachineTargets.addAll(machines.customMachines) + targets { + val main by creating { + cmakeLists.set(file("$cppPath/desktop/CMakeLists.txt")) + targetMachines.addAll(compileMachineTargets.toSet()) + } + } +} + +tasks.named("clean") { + dependsOn("cmakeClean") +} +tasks.named("compileJava") { + dependsOn("cmakeBuildAndCopy") +} +afterEvaluate { + tasks.create("cmakeBuildAndCopy") { + dependsOn("cmakeBuild") + doLast { + copy { + from("${project(":desktop").buildDir}/cmake/main/linux-amd64", "$cppPath/desktop/libs/linux-x86_64", "$cppPath/desktop/libs/linux-x86_64/deps") + into("../common/src/commonMain/resources/libs/linux-x86_64") + include("*.so") + eachFile { + path = name + } + includeEmptyDirs = false + duplicatesStrategy = DuplicatesStrategy.INCLUDE + } + copy { + from("${project(":desktop").buildDir}/cmake/main/linux-aarch64", "$cppPath/desktop/libs/linux-aarch64", "$cppPath/desktop/libs/linux-aarch64/deps") + into("../common/src/commonMain/resources/libs/linux-aarch64") + include("*.so") + eachFile { + path = name + } + includeEmptyDirs = false + duplicatesStrategy = DuplicatesStrategy.INCLUDE + } + copy { + from("${project(":desktop").buildDir}/cmake/main/win-amd64", "$cppPath/desktop/libs/windows-x86_64", "$cppPath/desktop/libs/windows-x86_64/deps") + into("../common/src/commonMain/resources/libs/windows-x86_64") + include("*.dll") + eachFile { + path = name + } + includeEmptyDirs = false + duplicatesStrategy = DuplicatesStrategy.INCLUDE + } + copy { + from("${project(":desktop").buildDir}/cmake/main/mac-x86_64", "$cppPath/desktop/libs/mac-x86_64", "$cppPath/desktop/libs/mac-x86_64/deps") + into("../common/src/commonMain/resources/libs/mac-x86_64") + include("*.dylib") + eachFile { + path = name + } + includeEmptyDirs = false + duplicatesStrategy = DuplicatesStrategy.INCLUDE + } + copy { + from("${project(":desktop").buildDir}/cmake/main/mac-aarch64", "$cppPath/desktop/libs/mac-aarch64", "$cppPath/desktop/libs/mac-aarch64/deps") + into("../common/src/commonMain/resources/libs/mac-aarch64") + include("*.dylib") + eachFile { + path = name + } + includeEmptyDirs = false + duplicatesStrategy = DuplicatesStrategy.INCLUDE + } + } + } +} diff --git a/apps/android/gradle.properties b/apps/multiplatform/gradle.properties similarity index 87% rename from apps/android/gradle.properties rename to apps/multiplatform/gradle.properties index cbbffa0869..ac73c373a4 100644 --- a/apps/android/gradle.properties +++ b/apps/multiplatform/gradle.properties @@ -23,3 +23,13 @@ kotlin.code.style=official android.nonTransitiveRClass=true # Automatically convert third-party libraries to use AndroidX android.enableJetifier=true +kotlin.mpp.androidSourceSetLayoutVersion=2 + +android.version_name=5.2-beta.0 +android.version_code=129 + +desktop.version_name=1.0 + +kotlin.version=1.8.20 +gradle.plugin.version=7.4.2 +compose.version=1.4.0 diff --git a/apps/android/gradle/wrapper/gradle-wrapper.jar b/apps/multiplatform/gradle/wrapper/gradle-wrapper.jar similarity index 100% rename from apps/android/gradle/wrapper/gradle-wrapper.jar rename to apps/multiplatform/gradle/wrapper/gradle-wrapper.jar diff --git a/apps/android/gradle/wrapper/gradle-wrapper.properties b/apps/multiplatform/gradle/wrapper/gradle-wrapper.properties similarity index 93% rename from apps/android/gradle/wrapper/gradle-wrapper.properties rename to apps/multiplatform/gradle/wrapper/gradle-wrapper.properties index 88ac00ff11..00c09d815c 100644 --- a/apps/android/gradle/wrapper/gradle-wrapper.properties +++ b/apps/multiplatform/gradle/wrapper/gradle-wrapper.properties @@ -1,6 +1,6 @@ #Mon Feb 14 14:23:51 GMT 2022 distributionBase=GRADLE_USER_HOME -distributionUrl=https\://services.gradle.org/distributions/gradle-7.3.3-bin.zip +distributionUrl=https\://services.gradle.org/distributions/gradle-7.5.1-bin.zip distributionPath=wrapper/dists zipStorePath=wrapper/dists zipStoreBase=GRADLE_USER_HOME diff --git a/apps/android/gradlew b/apps/multiplatform/gradlew similarity index 100% rename from apps/android/gradlew rename to apps/multiplatform/gradlew diff --git a/apps/android/gradlew.bat b/apps/multiplatform/gradlew.bat similarity index 100% rename from apps/android/gradlew.bat rename to apps/multiplatform/gradlew.bat diff --git a/apps/multiplatform/settings.gradle.kts b/apps/multiplatform/settings.gradle.kts new file mode 100644 index 0000000000..ba047edf1e --- /dev/null +++ b/apps/multiplatform/settings.gradle.kts @@ -0,0 +1,21 @@ +pluginManagement { + repositories { + google() + gradlePluginPortal() + mavenCentral() + maven("https://maven.pkg.jetbrains.space/public/p/compose/dev") + } + + plugins { + kotlin("multiplatform").version(extra["kotlin.version"] as String) + kotlin("android").version(extra["kotlin.version"] as String) + id("com.android.application").version(extra["gradle.plugin.version"] as String) + id("com.android.library").version(extra["gradle.plugin.version"] as String) + id("org.jetbrains.compose").version(extra["compose.version"] as String) + id("org.jetbrains.kotlin.plugin.serialization").version(extra["kotlin.version"] as String) + } +} + +rootProject.name = "app" + +include(":android", ":desktop", ":common") diff --git a/cabal.project b/cabal.project index 1119f593e6..82e9949a58 100644 --- a/cabal.project +++ b/cabal.project @@ -7,7 +7,7 @@ constraints: zip +disable-bzip2 +disable-zstd source-repository-package type: git location: https://github.com/simplex-chat/simplexmq.git - tag: 3a74558e8449faa65dd6c72f01d3856d748f8102 + tag: 54dc8d42e7594632daeadf0a0f115936b17165af source-repository-package type: git diff --git a/docs/THEMES.md b/docs/THEMES.md index 6b06a18fd2..693d0c311a 100644 --- a/docs/THEMES.md +++ b/docs/THEMES.md @@ -8,7 +8,7 @@ You can contribute your themes to the repository by creating a pull request. 1. Once you have configured your theme in the app, export it to a file and give it a descriptive name – e.g., `example.theme` -2. Export your app database, and import a [sample chat database](./themes/simplex-chat.sample.zip). +2. Export your app database, and import a [sample chat database](./themes/simplex-chat.sample.zip) - the passphrase is `passphrase`. 3. Make three screenshots - the list of conversations with opened profile picker, conversation and privacy settings. diff --git a/packages/simplex-chat-webrtc/copy b/packages/simplex-chat-webrtc/copy index 1b766264fb..4991cdef45 100755 --- a/packages/simplex-chat-webrtc/copy +++ b/packages/simplex-chat-webrtc/copy @@ -8,7 +8,7 @@ cp ./src/webcall.html ./dist/webcall.html cp ./src/ui.js ./dist/ui.js # copy to android app -cp ./src/call.html ../../apps/android/app/src/main/assets/www/call.html -cp ./src/style.css ../../apps/android/app/src/main/assets/www/style.css -cp ./dist/call.js ../../apps/android/app/src/main/assets/www/call.js -cp ./node_modules/lz-string/libs/lz-string.min.js ../../apps/android/app/src/main/assets/www/lz-string.min.js +cp ./src/call.html ../../apps/multiplatform/android/src/main/assets/www/call.html +cp ./src/style.css ../../apps/multiplatform/android/src/main/assets/www/style.css +cp ./dist/call.js ../../apps/multiplatform/android/src/main/assets/www/call.js +cp ./node_modules/lz-string/libs/lz-string.min.js ../../apps/multiplatform/android/src/main/assets/www/lz-string.min.js diff --git a/scripts/android/build-android-bundle.sh b/scripts/android/build-android-bundle.sh index 18aa03145a..554e64edff 100755 --- a/scripts/android/build-android-bundle.sh +++ b/scripts/android/build-android-bundle.sh @@ -15,13 +15,13 @@ git clone https://github.com/simplex-chat/simplex-chat "$tmp/simplex-chat" git -C "$tmp/simplex-chat" checkout "$commit" # Create missing folders -mkdir -p "$tmp/simplex-chat/apps/android/app/src/main/cpp/libs/arm64-v8a" +mkdir -p "$tmp/simplex-chat/apps/multiplatform/common/src/commonMain/cpp/android/libs/arm64-v8a" curl -sSf "$libsim" -o "$tmp/libsimplex.zip" -unzip -o "$tmp/libsimplex.zip" -d "$tmp/simplex-chat/apps/android/app/src/main/cpp/libs/arm64-v8a" +unzip -o "$tmp/libsimplex.zip" -d "$tmp/simplex-chat/apps/multiplatform/common/src/commonMain/cpp/android/libs/arm64-v8a" curl -sSf "$libsup" -o "$tmp/libsupport.zip" -unzip -o "$tmp/libsupport.zip" -d "$tmp/simplex-chat/apps/android/app/src/main/cpp/libs/arm64-v8a" +unzip -o "$tmp/libsupport.zip" -d "$tmp/simplex-chat/apps/multiplatform/common/src/commonMain/cpp/android/libs/arm64-v8a" -gradle -p "$tmp/simplex-chat/apps/android/" clean build -cp "$tmp/simplex-chat/apps/android/app/build/outputs/apk/release/app-release-unsigned.apk" "$PWD/simplex-chat.apk" +gradle -p "$tmp/simplex-chat/apps/multiplatform/" clean build +cp "$tmp/simplex-chat/apps/multiplatform/android/build/outputs/apk/release/android-release-unsigned.apk" "$PWD/simplex-chat.apk" diff --git a/scripts/android/build-android.sh b/scripts/android/build-android.sh index 87f9776b1b..90916a96fe 100755 --- a/scripts/android/build-android.sh +++ b/scripts/android/build-android.sh @@ -81,21 +81,21 @@ checks() { build() { # Build preparations - sed -i.bak 's/${extract_native_libs}/true/' "$folder/apps/android/app/src/main/AndroidManifest.xml" - sed -i.bak '/android {/a lint {abortOnError false}' "$folder/apps/android/app/build.gradle" + sed -i.bak 's/${extract_native_libs}/true/' "$folder/apps/multiplatform/android/src/main/AndroidManifest.xml" + sed -i.bak '/android {/a lint {abortOnError false}' "$folder/apps/multiplatform/android/build.gradle.kts" for arch in $arches; do android_simplex_lib="${folder}#hydraJobs.${arch}-android:lib:simplex-chat.x86_64-linux" android_support_lib="${folder}#hydraJobs.${arch}-android:lib:support.x86_64-linux" android_simplex_lib_output="${PWD}/result/pkg-${arch}-android-libsimplex.zip" android_support_lib_output="${PWD}/result/pkg-${arch}-android-libsupport.zip" - + arch_map "$arch" android_tmp_folder="${tmp}/android-${arch}" - android_apk_output="${folder}/apps/android/app/build/outputs/apk/release/app-${android_arch}-release-unsigned.apk" + android_apk_output="${folder}/apps/multiplatform/android/build/outputs/apk/release/android-${android_arch}-release-unsigned.apk" android_apk_output_final="simplex-chat-${android_arch}.apk" - libs_folder="$folder/apps/android/app/src/main/cpp/libs" + libs_folder="$folder/apps/multiplatform/common/src/commonMain/cpp/android/libs" # Create missing folders mkdir -p "$libs_folder/$android_arch" @@ -107,12 +107,12 @@ build() { unzip -o "$android_support_lib_output" -d "$libs_folder/$android_arch" # Build only one arch - sed -i.bak "s/include '.*/include '${android_arch}'/" "$folder/apps/android/app/build.gradle" - gradle -p "$folder/apps/android/" clean assembleRelease - + sed -i.bak "s/include '.*/include '${android_arch}'/" "$folder/apps/multiplatform/android/build.gradle.kts" + gradle -p "$folder/apps/multiplatform/" clean assembleRelease + mkdir -p "$android_tmp_folder" unzip -oqd "$android_tmp_folder" "$android_apk_output" - + ( cd "$android_tmp_folder" && \ zip -rq5 "$tmp/$android_apk_output_final" . && \ diff --git a/scripts/android/compress-and-sign-apk.sh b/scripts/android/compress-and-sign-apk.sh index 586511cdfa..e46b8a54f1 100755 --- a/scripts/android/compress-and-sign-apk.sh +++ b/scripts/android/compress-and-sign-apk.sh @@ -23,7 +23,7 @@ touch remove_this_file remove_this_FILE #echo Case-insensitive file system: $case_insensitive rm remove_this_file remove_this_FILE 2> /dev/null || true -ORIG_NAMES=( $(echo app*.apk) ) +ORIG_NAMES=( $(echo android*.apk) ) for ORIG_NAME in "${ORIG_NAMES[@]}"; do unzip -o -q -d apk $ORIG_NAME ORIG_NAME_COPY=$ORIG_NAME-copy diff --git a/scripts/android/download-libs.sh b/scripts/android/download-libs.sh index dd1b1fb2d1..f67cc397a7 100755 --- a/scripts/android/download-libs.sh +++ b/scripts/android/download-libs.sh @@ -33,7 +33,7 @@ root_dir="$(dirname "$(dirname "$(readlink "$0")")")" for ((i = 0 ; i < ${#arches[@]}; i++)); do arch="${arches[$i]}" output_arch="${output_arches[$i]}" - output_dir="$root_dir/apps/android/app/src/main/cpp/libs/$output_arch/" + output_dir="$root_dir/apps/multiplatform/common/src/commonMain/cpp/android/libs/$output_arch/" mkdir -p "$output_dir" 2> /dev/null diff --git a/scripts/android/prepare.sh b/scripts/android/prepare.sh index 4947d0c2b3..683d9cbf87 100755 --- a/scripts/android/prepare.sh +++ b/scripts/android/prepare.sh @@ -1,12 +1,12 @@ #!/bin/sh # libsimplex.so and libsupport.so binaries should be in ~/Downloads folder in their directories based on archive name -mkdir -p ./apps/android/app/src/main/cpp/libs/arm64-v8a/ -rm ./apps/android/app/src/main/cpp/libs/arm64-v8a/* -unzip -o ~/Downloads/pkg-aarch64-android-libsupport.zip -d ./apps/android/app/src/main/cpp/libs/arm64-v8a -unzip -o ~/Downloads/pkg-aarch64-android-libsimplex.zip -d ./apps/android/app/src/main/cpp/libs/arm64-v8a/ +mkdir -p ./apps/multiplatform/common/src/commonMain/cpp/android/libs/arm64-v8a/ +rm ./apps/multiplatform/common/src/commonMain/cpp/android/libs/arm64-v8a/* +unzip -o ~/Downloads/pkg-aarch64-android-libsupport.zip -d ./apps/multiplatform/common/src/commonMain/cpp/android/libs/arm64-v8a +unzip -o ~/Downloads/pkg-aarch64-android-libsimplex.zip -d ./apps/multiplatform/common/src/commonMain/cpp/android/libs/arm64-v8a/ -mkdir -p ./apps/android/app/src/main/cpp/libs/armeabi-v7a/ -rm ./apps/android/app/src/main/cpp/libs/armeabi-v7a/* -unzip -o ~/Downloads/pkg-armv7a-android-libsupport.zip -d ./apps/android/app/src/main/cpp/libs/armeabi-v7a/ -unzip -o ~/Downloads/pkg-armv7a-android-libsimplex.zip -d ./apps/android/app/src/main/cpp/libs/armeabi-v7a/ +mkdir -p ./apps/multiplatform/common/src/commonMain/cpp/android/libs/armeabi-v7a/ +rm ./apps/multiplatform/common/src/commonMain/cpp/android/libs/armeabi-v7a/* +unzip -o ~/Downloads/pkg-armv7a-android-libsupport.zip -d ./apps/multiplatform/common/src/commonMain/cpp/android/libs/armeabi-v7a/ +unzip -o ~/Downloads/pkg-armv7a-android-libsimplex.zip -d ./apps/multiplatform/common/src/commonMain/cpp/android/libs/armeabi-v7a/ diff --git a/scripts/nix/sha256map.nix b/scripts/nix/sha256map.nix index e1198e4c81..679b55456a 100644 --- a/scripts/nix/sha256map.nix +++ b/scripts/nix/sha256map.nix @@ -1,5 +1,5 @@ { - "https://github.com/simplex-chat/simplexmq.git"."3a74558e8449faa65dd6c72f01d3856d748f8102" = "1i1g01ipbvb0ajws7k1n1p03wv4ldf63y3zny6c76vpazxbflcpc"; + "https://github.com/simplex-chat/simplexmq.git"."54dc8d42e7594632daeadf0a0f115936b17165af" = "02lhlnwcyr3zmr11drmfwwmn6a54s7id9qzsdlqvi16dr5va245d"; "https://github.com/simplex-chat/hs-socks.git"."a30cc7a79a08d8108316094f8f2f82a0c5e1ac51" = "0yasvnr7g91k76mjkamvzab2kvlb1g5pspjyjn2fr6v83swjhj38"; "https://github.com/kazu-yamamoto/http2.git"."b5a1b7200cf5bc7044af34ba325284271f6dff25" = "0dqb50j57an64nf4qcf5vcz4xkd1vzvghvf8bk529c1k30r9nfzb"; "https://github.com/simplex-chat/direct-sqlcipher.git"."34309410eb2069b029b8fc1872deb1e0db123294" = "0kwkmhyfsn2lixdlgl15smgr1h5gjk7fky6abzh8rng2h5ymnffd"; diff --git a/stack.yaml b/stack.yaml index 9802cd4654..12a92c8fdc 100644 --- a/stack.yaml +++ b/stack.yaml @@ -49,7 +49,7 @@ extra-deps: # - simplexmq-1.0.0@sha256:34b2004728ae396e3ae449cd090ba7410781e2b3cefc59259915f4ca5daa9ea8,8561 # - ../simplexmq - github: simplex-chat/simplexmq - commit: 3a74558e8449faa65dd6c72f01d3856d748f8102 + commit: 54dc8d42e7594632daeadf0a0f115936b17165af - github: kazu-yamamoto/http2 commit: b5a1b7200cf5bc7044af34ba325284271f6dff25 # - ../direct-sqlcipher diff --git a/tests/ChatClient.hs b/tests/ChatClient.hs index 13eb391f00..8970c6304d 100644 --- a/tests/ChatClient.hs +++ b/tests/ChatClient.hs @@ -37,6 +37,7 @@ import Simplex.Messaging.Client (ProtocolClientConfig (..), defaultNetworkConfig import Simplex.Messaging.Server (runSMPServerBlocking) import Simplex.Messaging.Server.Env.STM import Simplex.Messaging.Transport +import Simplex.Messaging.Transport.Server (defaultTransportServerConfig) import Simplex.Messaging.Version import System.Directory (createDirectoryIfMissing, removeDirectoryRecursive) import System.FilePath (()) @@ -125,7 +126,7 @@ testAgentCfgV1 = testAgentCfg { smpClientVRange = mkVersionRange 1 1, smpAgentVRange = mkVersionRange 1 1, - smpCfg = (smpCfg testAgentCfg) {smpServerVRange = mkVersionRange 1 1} + smpCfg = (smpCfg testAgentCfg) {serverVRange = mkVersionRange 1 1} } testCfgV1 :: ChatConfig @@ -318,7 +319,7 @@ serverCfg = serverStatsLogFile = "tests/smp-server-stats.daily.log", serverStatsBackupFile = Nothing, smpServerVRange = supportedSMPServerVRange, - logTLSErrors = True + transportConfig = defaultTransportServerConfig } withSmpServer :: IO () -> IO () @@ -349,7 +350,7 @@ xftpServerConfig = logStatsStartTime = 0, serverStatsLogFile = "tests/tmp/xftp-server-stats.daily.log", serverStatsBackupFile = Nothing, - logTLSErrors = True + transportConfig = defaultTransportServerConfig } withXFTPServer :: IO () -> IO () diff --git a/tests/ChatTests/Direct.hs b/tests/ChatTests/Direct.hs index 0fee08f79a..941fa2aff2 100644 --- a/tests/ChatTests/Direct.hs +++ b/tests/ChatTests/Direct.hs @@ -86,7 +86,7 @@ chatDirectTests = do it "mark contact verified" testMarkContactVerified it "mark group member verified" testMarkGroupMemberVerified describe "message errors" $ do - it "show message decryption error and update count" testMsgDecryptError + xit "show message decryption error and update count" testMsgDecryptError describe "message reactions" $ do it "set message reactions" testSetMessageReactions diff --git a/tests/ChatTests/Groups.hs b/tests/ChatTests/Groups.hs index 1f89dd3df9..1e724ce007 100644 --- a/tests/ChatTests/Groups.hs +++ b/tests/ChatTests/Groups.hs @@ -56,7 +56,7 @@ chatGroupTests = do it "group link member role" testGroupLinkMemberRole it "leaving and deleting the group joined via link should NOT delete previously existing direct contacts" testGroupLinkLeaveDelete describe "group message errors" $ do - it "show message decryption error and update count" testGroupMsgDecryptError + xit "show message decryption error and update count" testGroupMsgDecryptError describe "message reactions" $ do it "set group message reactions" testSetGroupMessageReactions diff --git a/website/src/css/demo.css b/website/src/css/demo.css new file mode 100644 index 0000000000..d4995d2cd7 --- /dev/null +++ b/website/src/css/demo.css @@ -0,0 +1,226 @@ +#demo { + padding-top: 3.5rem; + padding-bottom: 2.5rem; + text-align: center; +} + +#demo .all-users { + position: relative; + height: 640px; +} + +#demo .user { + position: absolute; +} + +#demo h3 { + position: absolute; + top: -60px; + text-align: left; + padding: 0 0 5px 7px; + color: #ffffbb; + mix-blend-mode: difference; + font-size: 1.5rem; + margin-bottom: 2rem; +} + +#demo .user .terminal { + text-align: left; + position: relative; + background-color: rgba(0, 0, 0, 0.8); + border-radius: 10px; + box-sizing: border-box; + padding: 10px 10px; + color: white; + overflow-y: hidden; +} + +#demo .user .terminal:before { + content: ""; + display: block; + position: absolute; + top: 0px; + left: 0px; + width: 100%; + height: 20px; + background: #666 url(/img/topbar.svg) no-repeat 8px center; + border-radius: 10px 10px 0 0; + z-index: 2; +} + +#demo .user .terminal input, +#demo .user .terminal .input, +#demo .user .terminal .display { + font-family: Menlo, "Lucida Console", Monaco, monospace; + font-size: 14px; + letter-spacing: 0.27px; + line-height: 28px; + position: absolute; + left: 10px; + right: 10px; +} + +#demo .user .terminal input, +#demo .user .terminal .input { + color: white; + display: block; + height: 30px; + bottom: 0; +} + +#demo .user .terminal input { + background-color: transparent; + display: none; + left: 25px; + width: calc(100% - 30px); +} + +#demo .user .terminal .display { + color: white; + text-align: left; + bottom: 30px; + z-index: 1; + overflow-x: hidden; + word-wrap: break-word; +} + +#demo .user .terminal .display .info { + color: white; +} + +#demo .user .terminal .display .error { + color: #ff6347; +} + +#demo .user .terminal .display .sent span.group { + color: cyan; +} + +#demo .user .terminal .display .received span.group { + color: yellow; +} + +#demo .user .terminal .display span { + font-family: Menlo, Monaco, "Lucida Console", monospace; + text-align: left; + margin: 0 0; +} + +#demo .user .terminal .display span.recipient { + color: cyan; +} + +#demo .user .terminal .display span.sender { + color: yellow; +} + +#demo .user .terminal .display span.secret { + color: rgba(0, 0, 0, 0.4); + background-color: rgba(0, 0, 0, 0.4); +} + +#demo .alice { + left: -30px; + top: 102px; +} + +#demo .bob { + left: 425px; + top: 0px; +} + +#demo .tom { + left: 425px; + top: 378px; +} + +#demo .all-users .terminal { + width: 410px; +} + +#demo .alice .terminal { + height: 340px; +} + +#demo .bob .terminal { + height: 305px; +} + +#demo .tom .terminal { + height: 280px; +} + + +/* Demo buttons */ +#demo button { + position: absolute; + width: 140px; + height: 40px; + bottom: 85px; + font-size: 16px; + border-radius: 34px; + color: white; + font-weight: 500; + letter-spacing: 0.02em; + padding-top: 0.5rem; + padding-bottom: 0.5rem; + padding-left: 1.25rem; + padding-right: 1.25rem; + background-color: #0053d0; +} + +.dark #demo button { + color: black; + background-color: #70f0f9; +} + +#demo button:disabled { + filter: brightness(75%); +} + +#demo .run-demo { + left: 20px; +} + +#demo .run-faster { + left: 20px; + display: none; +} + +#demo .try-it { + left: 180px; +} + +@media (max-width: 1240px) { + #demo .all-users { + height: 100%; + display: flex; + flex-direction: column; + align-items: center; + } + + #demo .alice { + position: relative !important; + top: 0; + left: 0; + } + + #demo button, + #demo .bob, + #demo .tom { + display: none; + } +} + +@media (max-width: 570px) { + #demo .alice .terminal { + width: 400px; + } +} + +@media (max-width: 440px) { + #demo .alice .terminal { + width: 330px; + height: 420px; + } +} \ No newline at end of file diff --git a/website/src/js/demo.js b/website/src/js/demo.js new file mode 100644 index 0000000000..21beec97f4 --- /dev/null +++ b/website/src/js/demo.js @@ -0,0 +1,347 @@ +(async function () { + let DELAY = 0; + const DISTR = 0.33; + + class User { + constructor(name) { + this.userWindow = document.querySelector(`#demo .user.${name}`); + this.terminal = this.userWindow.querySelector(`.terminal`); + this.input = this.terminal.querySelector(".input"); + this.demoInput = this.terminal.querySelector("input"); + this.resetInput(); + this.setupDemo(); + this.group = []; + this.display = this.terminal.querySelector(".display"); + this.setupMoveWindow(); + this.name = name; + } + + reset() { + this.resetInput(); + this.display.innerHTML = ""; + } + + setGroup(groupName, users) { + this.users = users; + this.group = users.filter((u) => u !== this); + this.groupName = groupName; + } + + tryDemo() { + this.reset(); + show(this.demoInput); + this.demoInput.value = ""; + } + + async send(to, message, typeTo, paste, secret) { + await this._sendMsg(`@${to.name}`, message, typeTo, paste, secret); + await to.receive(this, toSecret(secret, message)); + await delay(20); + } + + async sendGroup(message, typeTo, paste) { + await this._sendMsg(`#${this.groupName}`, message, typeTo, paste); + await this.receiveGroup(message); + await delay(10); + } + + async _sendMsg(toStr, message, typeTo, paste, secret) { + await this.type(`${toStr} `, !typeTo); + if (secret) await this.type("#"); + await this.type(message, paste); + if (secret) await this.type("#"); + await delay(10); + this.resetInput(); + this.show("sent", `${toStr} ${toSecret(secret, message)}`); + } + + async type(str, paste) { + if (paste) { + await delay(10); + this.input.insertAdjacentHTML("beforeend", str); + } else { + for (const char of str) { + await delay(isAlpha(char) ? 1 : 2); + this.input.insertAdjacentHTML("beforeend", char); + } + } + await delay(2); + } + + resetInput() { + this.input.innerHTML = "> "; + show(this.demoInput, false); + } + + async receive(from, message, edit, group) { + await delay(10); + let g = group ? `#${this.groupName} ` : ""; + this.show("received", `${g}${from.name}> ${message}`, edit); + } + + async receiveGroup(message, edit) { + await Promise.all(this.group.map((u) => u.receive(this, message, edit, true))); + } + + show(mode, str, edit) { + if (edit && this.lastMessage) { + this.lastMessage.innerHTML = highlight(str); + return; + } + this.display.insertAdjacentHTML("beforeend", `
${highlight(str)}
`); + } + + setupDemo() { + if (!this.demoInput) return; + let editMode = false; + + on("keypress", this.demoInput, async ({ key }) => { + if (key === "Enter") { + const edit = editMode; + editMode = false; + const [to, ...words] = this.demoInput.value.trim().split(" "); + const message = words.join(" "); + switch (to[0]) { + case undefined: + if (message !== "") { + this.show("error", "Message should start with @user or #group"); + } + break; + case "@": + await this.sendInput(to.slice(1), message, edit); + break; + case "#": + await this.sendInputGroup(to.slice(1), message, edit); + break; + default: + this.show("error", "Message should start with @user or #group"); + } + } else if (this.demoInput.value === "" && key !== "@" && key !== "#") { + const channel = this.currentChannel(); + if (channel) this.demoInput.value = channel + " "; + } + }); + on("keydown", this.demoInput, async (e) => { + switch (e.key) { + case "ArrowUp": + if (this.demoInput.value === "" && this.lastMessage) { + const str = (this.demoInput.value = this.lastMessage.innerText); + editMode = true; + await delay(0); + this.demoInput.selectionStart = str.length; + } + break; + case "Tab": { + e.preventDefault(); + const userIndex = this.users.indexOf(this); + const nextIndex = (userIndex + 1) % this.users.length; + this.users[nextIndex].demoInput.focus(); + } + } + }); + } + + async sendInput(name, message, edit) { + if (name === this.name) { + this.show("error", "Can't send message to yourself"); + return; + } + const recipient = this.group.find((u) => u.name === name); + if (recipient === undefined) { + const knownNames = this.group.map((u) => `@${u.name}`).join(", ") + ` or @${this.name}`; + this.show("error", `Unknown recipient @${name} (try ${knownNames})`); + return; + } + this.show("sent", `@${name} ${message}`, edit); + this.demoInput.value = ""; + await recipient.receive(this, message, edit); + } + + async sendInputGroup(name, message, edit) { + if (name !== this.groupName) { + this.show("error", `Unknown group #${name} (try #team)`); + return; + } + this.show("sent", `#${name} ${message}`, edit); + this.demoInput.value = ""; + await this.receiveGroup(message, edit); + } + + get lastMessage() { + const messages = this.display.childNodes; + return messages[messages.length - 1]; + } + + currentChannel() { + return this.lastMessage && toContact(this.lastMessage.childNodes[0].innerHTML); + } + + setupMoveWindow() { + let moving = false; + let startX, startY; + const user = this.userWindow; + const parent = user.parentNode; + + on("mousedown", this.terminal, (e) => { + if (e.clientY - this.terminal.getBoundingClientRect().top > 20) return; + moving = true; + startX = user.offsetLeft - e.clientX; + startY = user.offsetTop - e.clientY; + parent.removeChild(user); + parent.appendChild(user); + }); + on("mouseup", this.terminal, () => (moving = false)); + on("mouseleave", this.terminal, () => (moving = false)); + on("mousemove", this.terminal, (e) => { + if (!moving) return; + user.style.left = e.clientX + startX + "px"; + user.style.top = e.clientY + startY + "px"; + }); + } + } + + function toContact(str) { + return str.endsWith(">") ? "@" + str.slice(0, -4) : str; + } + + function setGroup(groupName, users) { + users.forEach((u) => u.setGroup(groupName, users)); + } + + const alice = new User("alice"); + const bob = new User("bob"); + const tom = new User("tom"); + const team = [alice, bob, tom]; + setGroup("team", team); + + async function chatDemo() { + team.forEach((u) => u.reset()); + await alice.sendGroup("please review my PR project/site#72", true); + await tom.sendGroup("anybody got application key 🔑?"); + await bob.sendGroup("looking at it now @alice 👀"); + await alice.sendGroup("thanks @bob!"); + await alice.sendGroup("will DM @tom"); + await alice.send(tom, "w3@o6CewoZx#%$SQETXbWnus", true, true, true); + await tom.send(alice, "you're the savior 🙏!"); + await alice.send(bob, "please check the tests too", true); + await bob.send(alice, "all looks good 👍"); + await alice.send(bob, "thank you!"); + DELAY = 80; + } + + const invitation = + "smp::example.com:5223#1XNE1m2E1m0lm92​WG​Ket9CL6+lO742Vy5​G6nsrkvgs8=::St9hPY+k6nfrbaXj::rsa:MII​BoTANBgkqhkiG9w0B​AQEFAAOCAY4AMIIBiQKCAQEA03XGpEqh3faDN​Gl06pPhaT=="; + + async function establishConnection() { + team.forEach((u) => u.reset()); + await alice.type("/add bob"); + await delay(10); + alice.resetInput(); + // alice.show("/add bob"); + alice.show("sent", "pass this invitation to your contact (via any channel):"); + alice.show("sent", " "); + alice.show("sent", invitation); + alice.show("sent", " "); + alice.show("sent", "and ask them to connect:"); + alice.show("sent", "/c name_for_you invitation_above"); + await delay(20); + await bob.type("/connect alice "); + await bob.type(invitation, true); + await delay(20); + bob.resetInput(); + await bob.show("received", "/connect alice " + invitation); + await delay(10); + bob.show("received", "@alice connected"); + await delay(2); + alice.show("received", "@bob connected"); + await alice.send(bob, "hello bob"); + await bob.send(alice, "hi alice"); + } + + await chatDemo(); + const RUN_DEMO = "#demo .run-demo"; + const RUN_FASTER = "#demo .run-faster"; + const TRY_IT = "#demo .try-it"; + onClick(RUN_DEMO, runChatDemo); + // onClick(RUN_DEMO, establishConnection); + onClick(RUN_FASTER, () => (DELAY /= 2)); + onClick(TRY_IT, tryChatDemo); + + async function runChatDemo() { + show(RUN_DEMO, false); + show(RUN_FASTER); + enable(TRY_IT, false); + await chatDemo(); + show(RUN_DEMO); + show(RUN_FASTER, false); + enable(TRY_IT); + } + + function tryChatDemo() { + team.forEach((u) => u.tryDemo()); + alice.demoInput.focus(); + } + + async function delay(units) { + // delay is random with `1 +/- DISTR` range + const ms = units * DELAY * (1 - DISTR + 2 * DISTR * Math.random()); + return new Promise((resolve) => setTimeout(resolve, ms)); + } + + function highlight(str) { + return str + .replace(/(@[a-z]+)([^0-9]|$)/gi, `$1$2`) + .replace(/([a-z]+>)([^0-9]|$)/gi, `$1$2`) + .replace(/(#[a-z]+)([^0-9]|$)/gi, `$1$2`) + .replace(/#([^\s]+)#([\s]|$)/gi, `#$1#$2`); + } + + function toSecret(secret, message) { + return secret ? `#${message}#` : message; + } + + function isAlpha(c) { + c = c.toUpperCase(); + return c >= "A" && c <= "Z"; + } + + let flipper = setInterval(flipProblem, 10000); + + onClick("#problem .pagination", () => { + clearInterval(flipper); + flipper = setInterval(flipProblem, 20000); + }); + + function flipProblem() { + if (isElementInViewport(document.getElementById("problem"))) { + window.location.hash = + window.location.hash === "#problem-explained" ? "#problem-intro" : "#problem-explained"; + } + } + + function isElementInViewport(el) { + if (!el) return false; + const r = el.getBoundingClientRect(); + return r.bottom >= 0 && r.top <= window.innerHeight; + } + + function onClick(selector, handler, enable = true) { + const el = document.querySelector(selector); + if (el) on("click", el, handler, enable); + } + + function on(event, el, handler, enable = true) { + const method = enable ? "addEventListener" : "removeEventListener"; + el[method](event, handler); + } + + function show(selector, visible = true) { + const el = typeof selector === "string" ? document.querySelector(selector) : selector; + if (el) el.style.display = visible ? "block" : "none"; + } + + function enable(selector, enabled = true) { + const el = document.querySelector(selector); + el.disabled = enabled ? "" : "true"; + } +})(); diff --git a/website/src/js/docs.js b/website/src/js/docs.js index 4535fb5ea9..137923482c 100644 --- a/website/src/js/docs.js +++ b/website/src/js/docs.js @@ -1,4 +1,51 @@ document.addEventListener("DOMContentLoaded", function () { + if (window.location.pathname.endsWith('cli.html')) { + const cliHeader = document.querySelector('h1') + const demoSection = document.createElement('section') + demoSection.id = 'demo' + demoSection.innerHTML = ` +
+
+

@alice

+
+
+
+ +
+
+
+

@bob

+
+
+
+ +
+
+
+

@tom

+
+
+
+ +
+
+ + + +
+ ` + cliHeader.parentNode.insertBefore(demoSection, cliHeader.nextSibling) + + const demoScript = document.createElement('script') + demoScript.src = '/js/demo.js' + document.body.appendChild(demoScript) + + const demoStyles = document.createElement('link') + demoStyles.rel = 'stylesheet' + demoStyles.href = '/css/demo.css' + document.head.appendChild(demoStyles) + } + const imgs = document.querySelectorAll('p img') imgs.forEach(img => { console.log(img.height)