diff --git a/build.gradle.kts b/build.gradle.kts index 430c60f..e5258a2 100644 --- a/build.gradle.kts +++ b/build.gradle.kts @@ -4,4 +4,5 @@ plugins { alias(libs.plugins.composeMultiplatform) apply false alias(libs.plugins.composeCompiler) apply false alias(libs.plugins.kotlinMultiplatform) apply false + alias(libs.plugins.jetbrainsKotlinJvm) apply false } \ No newline at end of file diff --git a/demo/build.gradle.kts b/demo/build.gradle.kts new file mode 100644 index 0000000..0960135 --- /dev/null +++ b/demo/build.gradle.kts @@ -0,0 +1,98 @@ +import org.jetbrains.compose.desktop.application.dsl.TargetFormat +import org.jetbrains.kotlin.gradle.dsl.JvmTarget +import org.jetbrains.kotlin.gradle.plugin.mpp.KotlinNativeTarget + +plugins { + alias(libs.plugins.kotlinMultiplatform) + alias(libs.plugins.androidApplication) + alias(libs.plugins.composeMultiplatform) + alias(libs.plugins.composeCompiler) +} + +kotlin { + androidTarget { + compilerOptions { + jvmTarget.set(JvmTarget.JVM_11) + } + } + + listOf( + iosX64(), + iosArm64(), + iosSimulatorArm64() + ).map(KotlinNativeTarget::binaries).forEach { + it.framework { + baseName = "ComposeApp" + isStatic = true + } + } + + jvm("desktop") + + sourceSets { + val desktopMain by getting + + androidMain.dependencies { + implementation(compose.preview) + implementation(libs.androidx.activity.compose) + } + commonMain.dependencies { + implementation(compose.runtime) + implementation(compose.foundation) + implementation(compose.material3) + implementation(compose.ui) + implementation(compose.materialIconsExtended) + implementation(compose.components.resources) + implementation(compose.components.uiToolingPreview) + + implementation(project(":imageselector")) + } + desktopMain.dependencies { + implementation(compose.desktop.currentOs) + implementation(libs.kotlinx.coroutines.swing) + } + } +} + +android { + namespace = "com.wannaverse.imageselector.demo" + compileSdk = libs.versions.android.compileSdk.get().toInt() + + defaultConfig { + applicationId = "com.wannaverse.imageselector.demo" + minSdk = libs.versions.android.minSdk.get().toInt() + targetSdk = libs.versions.android.targetSdk.get().toInt() + versionCode = 1 + versionName = "1.0.0" + } + packaging { + resources { + excludes += "/META-INF/{AL2.0,LGPL2.1}" + } + } + buildTypes { + getByName("release") { + isMinifyEnabled = false + } + } + compileOptions { + sourceCompatibility = JavaVersion.VERSION_11 + targetCompatibility = JavaVersion.VERSION_11 + } +} + +dependencies { + debugImplementation(compose.uiTooling) +} + +compose.desktop { + application { + mainClass = "com.wannaverse.imageselector.demo.MainKt" + + nativeDistributions { + targetFormats(TargetFormat.Dmg, TargetFormat.Msi, TargetFormat.Deb) + packageName = "com.wannaverse.imageselector.demo" + packageVersion = "1.0.0" + } + } +} \ No newline at end of file diff --git a/demo/src/androidMain/AndroidManifest.xml b/demo/src/androidMain/AndroidManifest.xml new file mode 100644 index 0000000..5b28ed7 --- /dev/null +++ b/demo/src/androidMain/AndroidManifest.xml @@ -0,0 +1,23 @@ + + + + + + + + + + + + + + \ No newline at end of file diff --git a/demo/src/androidMain/kotlin/com/wannaverse/imageselector/demo/MainActivity.kt b/demo/src/androidMain/kotlin/com/wannaverse/imageselector/demo/MainActivity.kt new file mode 100644 index 0000000..768b205 --- /dev/null +++ b/demo/src/androidMain/kotlin/com/wannaverse/imageselector/demo/MainActivity.kt @@ -0,0 +1,22 @@ +package com.wannaverse.imageselector.demo + +import android.graphics.Color +import android.os.Bundle +import androidx.activity.ComponentActivity +import androidx.activity.SystemBarStyle +import androidx.activity.compose.setContent +import androidx.activity.enableEdgeToEdge +import com.wannaverse.imageselector.registerImageSelectorLauncher + +class MainActivity : ComponentActivity() { + override fun onCreate(savedInstanceState: Bundle?) { + super.onCreate(savedInstanceState) + + registerImageSelectorLauncher() + + enableEdgeToEdge(SystemBarStyle.auto(Color.TRANSPARENT, Color.TRANSPARENT)) + setContent { + App() + } + } +} \ No newline at end of file diff --git a/demo/src/androidMain/res/drawable-v24/ic_launcher_foreground.xml b/demo/src/androidMain/res/drawable-v24/ic_launcher_foreground.xml new file mode 100644 index 0000000..2b068d1 --- /dev/null +++ b/demo/src/androidMain/res/drawable-v24/ic_launcher_foreground.xml @@ -0,0 +1,30 @@ + + + + + + + + + + + \ No newline at end of file diff --git a/demo/src/androidMain/res/drawable/ic_launcher_background.xml b/demo/src/androidMain/res/drawable/ic_launcher_background.xml new file mode 100644 index 0000000..e93e11a --- /dev/null +++ b/demo/src/androidMain/res/drawable/ic_launcher_background.xml @@ -0,0 +1,170 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/demo/src/androidMain/res/mipmap-anydpi-v26/ic_launcher.xml b/demo/src/androidMain/res/mipmap-anydpi-v26/ic_launcher.xml new file mode 100644 index 0000000..eca70cf --- /dev/null +++ b/demo/src/androidMain/res/mipmap-anydpi-v26/ic_launcher.xml @@ -0,0 +1,5 @@ + + + + + \ No newline at end of file diff --git a/demo/src/androidMain/res/mipmap-anydpi-v26/ic_launcher_round.xml b/demo/src/androidMain/res/mipmap-anydpi-v26/ic_launcher_round.xml new file mode 100644 index 0000000..eca70cf --- /dev/null +++ b/demo/src/androidMain/res/mipmap-anydpi-v26/ic_launcher_round.xml @@ -0,0 +1,5 @@ + + + + + \ No newline at end of file diff --git a/demo/src/androidMain/res/mipmap-hdpi/ic_launcher.png b/demo/src/androidMain/res/mipmap-hdpi/ic_launcher.png new file mode 100644 index 0000000..a571e60 Binary files /dev/null and b/demo/src/androidMain/res/mipmap-hdpi/ic_launcher.png differ diff --git a/demo/src/androidMain/res/mipmap-hdpi/ic_launcher_round.png b/demo/src/androidMain/res/mipmap-hdpi/ic_launcher_round.png new file mode 100644 index 0000000..61da551 Binary files /dev/null and b/demo/src/androidMain/res/mipmap-hdpi/ic_launcher_round.png differ diff --git a/demo/src/androidMain/res/mipmap-mdpi/ic_launcher.png b/demo/src/androidMain/res/mipmap-mdpi/ic_launcher.png new file mode 100644 index 0000000..c41dd28 Binary files /dev/null and b/demo/src/androidMain/res/mipmap-mdpi/ic_launcher.png differ diff --git a/demo/src/androidMain/res/mipmap-mdpi/ic_launcher_round.png b/demo/src/androidMain/res/mipmap-mdpi/ic_launcher_round.png new file mode 100644 index 0000000..db5080a Binary files /dev/null and b/demo/src/androidMain/res/mipmap-mdpi/ic_launcher_round.png differ diff --git a/demo/src/androidMain/res/mipmap-xhdpi/ic_launcher.png b/demo/src/androidMain/res/mipmap-xhdpi/ic_launcher.png new file mode 100644 index 0000000..6dba46d Binary files /dev/null and b/demo/src/androidMain/res/mipmap-xhdpi/ic_launcher.png differ diff --git a/demo/src/androidMain/res/mipmap-xhdpi/ic_launcher_round.png b/demo/src/androidMain/res/mipmap-xhdpi/ic_launcher_round.png new file mode 100644 index 0000000..da31a87 Binary files /dev/null and b/demo/src/androidMain/res/mipmap-xhdpi/ic_launcher_round.png differ diff --git a/demo/src/androidMain/res/mipmap-xxhdpi/ic_launcher.png b/demo/src/androidMain/res/mipmap-xxhdpi/ic_launcher.png new file mode 100644 index 0000000..15ac681 Binary files /dev/null and b/demo/src/androidMain/res/mipmap-xxhdpi/ic_launcher.png differ diff --git a/demo/src/androidMain/res/mipmap-xxhdpi/ic_launcher_round.png b/demo/src/androidMain/res/mipmap-xxhdpi/ic_launcher_round.png new file mode 100644 index 0000000..b216f2d Binary files /dev/null and b/demo/src/androidMain/res/mipmap-xxhdpi/ic_launcher_round.png differ diff --git a/demo/src/androidMain/res/mipmap-xxxhdpi/ic_launcher.png b/demo/src/androidMain/res/mipmap-xxxhdpi/ic_launcher.png new file mode 100644 index 0000000..f25a419 Binary files /dev/null and b/demo/src/androidMain/res/mipmap-xxxhdpi/ic_launcher.png differ diff --git a/demo/src/androidMain/res/mipmap-xxxhdpi/ic_launcher_round.png b/demo/src/androidMain/res/mipmap-xxxhdpi/ic_launcher_round.png new file mode 100644 index 0000000..e96783c Binary files /dev/null and b/demo/src/androidMain/res/mipmap-xxxhdpi/ic_launcher_round.png differ diff --git a/demo/src/androidMain/res/values/strings.xml b/demo/src/androidMain/res/values/strings.xml new file mode 100644 index 0000000..6a596b8 --- /dev/null +++ b/demo/src/androidMain/res/values/strings.xml @@ -0,0 +1,3 @@ + + country-selector + \ No newline at end of file diff --git a/demo/src/commonMain/kotlin/com/wannaverse/imageselector/demo/App.kt b/demo/src/commonMain/kotlin/com/wannaverse/imageselector/demo/App.kt new file mode 100644 index 0000000..d615df8 --- /dev/null +++ b/demo/src/commonMain/kotlin/com/wannaverse/imageselector/demo/App.kt @@ -0,0 +1,46 @@ +package com.wannaverse.imageselector.demo + +import androidx.compose.foundation.Image +import androidx.compose.foundation.layout.Arrangement +import androidx.compose.foundation.layout.Column +import androidx.compose.foundation.layout.fillMaxSize +import androidx.compose.material3.Button +import androidx.compose.material3.Text +import androidx.compose.runtime.Composable +import androidx.compose.runtime.getValue +import androidx.compose.runtime.mutableStateOf +import androidx.compose.runtime.remember +import androidx.compose.runtime.rememberCoroutineScope +import androidx.compose.runtime.setValue +import androidx.compose.ui.Alignment +import androidx.compose.ui.Modifier +import com.wannaverse.imageselector.launchImagePicker +import com.wannaverse.imageselector.toImageBitmap +import kotlinx.coroutines.launch + +@Composable +fun App() { + val scope = rememberCoroutineScope() + var imageData: ByteArray? by remember { mutableStateOf(null) } + + Column( + modifier = Modifier.fillMaxSize(), + verticalArrangement = Arrangement.Center, + horizontalAlignment = Alignment.CenterHorizontally + ) { + imageData?.let { + Image( + bitmap = it.toImageBitmap(), + contentDescription = "Selected Image" + ) + } + + Button( + onClick = { + scope.launch { imageData = launchImagePicker() } + } + ) { + Text("Select Image") + } + } +} \ No newline at end of file diff --git a/demo/src/desktopMain/kotlin/com/wannaverse/imageselector/demo/Main.kt b/demo/src/desktopMain/kotlin/com/wannaverse/imageselector/demo/Main.kt new file mode 100644 index 0000000..05a6375 --- /dev/null +++ b/demo/src/desktopMain/kotlin/com/wannaverse/imageselector/demo/Main.kt @@ -0,0 +1,13 @@ +package com.wannaverse.imageselector.demo + +import androidx.compose.ui.window.Window +import androidx.compose.ui.window.application + +fun main() = application { + Window( + onCloseRequest = ::exitApplication, + title = "country-selector", + ) { + App() + } +} \ No newline at end of file diff --git a/demo/src/iosMain/kotlin/com/wannaverse/imageselector/demo/MainViewController.kt b/demo/src/iosMain/kotlin/com/wannaverse/imageselector/demo/MainViewController.kt new file mode 100644 index 0000000..c82df65 --- /dev/null +++ b/demo/src/iosMain/kotlin/com/wannaverse/imageselector/demo/MainViewController.kt @@ -0,0 +1,5 @@ +package com.wannaverse.imageselector.demo + +import androidx.compose.ui.window.ComposeUIViewController + +fun MainViewController() = ComposeUIViewController { App() } \ No newline at end of file diff --git a/gradle/libs.versions.toml b/gradle/libs.versions.toml index f18e874..b476338 100644 --- a/gradle/libs.versions.toml +++ b/gradle/libs.versions.toml @@ -8,10 +8,11 @@ composeMultiplatform = "1.9.1" kotlin = "2.2.20" kotlinx-coroutines = "1.10.2" skiko = "0.9.30" +jetbrainsKotlinJvm = "2.2.20" [libraries] androidx-activity-compose = { module = "androidx.activity:activity-compose", version.ref = "androidx-activity" } -kotlinx-coroutinesSwing = { module = "org.jetbrains.kotlinx:kotlinx-coroutines-swing", version.ref = "kotlinx-coroutines" } +kotlinx-coroutines-swing = { module = "org.jetbrains.kotlinx:kotlinx-coroutines-swing", version.ref = "kotlinx-coroutines" } skiko = { module = "org.jetbrains.skiko:skiko", version.ref = "skiko" } [plugins] @@ -21,4 +22,5 @@ composeMultiplatform = { id = "org.jetbrains.compose", version.ref = "composeMul composeCompiler = { id = "org.jetbrains.kotlin.plugin.compose", version.ref = "kotlin" } kotlinMultiplatform = { id = "org.jetbrains.kotlin.multiplatform", version.ref = "kotlin" } publishing = { id = "com.vanniktech.maven.publish", version = "0.32.0" } -dokka = { id = "org.jetbrains.dokka", version = "2.0.0" } \ No newline at end of file +dokka = { id = "org.jetbrains.dokka", version = "2.0.0" } +jetbrainsKotlinJvm = { id = "org.jetbrains.kotlin.jvm", version.ref = "jetbrainsKotlinJvm" } \ No newline at end of file diff --git a/imageselector/build.gradle.kts b/imageselector/build.gradle.kts index c3ff74f..075f6c6 100644 --- a/imageselector/build.gradle.kts +++ b/imageselector/build.gradle.kts @@ -35,7 +35,7 @@ kotlin { implementation(compose.ui) } jvmMain.dependencies { - implementation(libs.kotlinx.coroutinesSwing) + implementation(libs.kotlinx.coroutines.swing) } iosMain.dependencies { implementation(libs.skiko) diff --git a/imageselector/src/androidMain/kotlin/com/wannaverse/imageselector/ImageSelector.android.kt b/imageselector/src/androidMain/kotlin/com/wannaverse/imageselector/ImageSelector.android.kt index ddf83eb..df8738f 100644 --- a/imageselector/src/androidMain/kotlin/com/wannaverse/imageselector/ImageSelector.android.kt +++ b/imageselector/src/androidMain/kotlin/com/wannaverse/imageselector/ImageSelector.android.kt @@ -4,48 +4,39 @@ import android.net.Uri import androidx.activity.ComponentActivity import androidx.activity.result.ActivityResultLauncher import androidx.activity.result.contract.ActivityResultContracts +import androidx.compose.runtime.Composable +import kotlinx.coroutines.ExperimentalCoroutinesApi import kotlinx.coroutines.suspendCancellableCoroutine import java.io.IOException +import java.io.InputStream import kotlin.coroutines.resume private var imageSelectorLauncher: ActivityResultLauncher? = null -private var pendingContinuation: ((ImageData?) -> Unit)? = null -private var currentActivity: ComponentActivity? = null - -fun setImageSelectorActivity(activity: ComponentActivity) { - currentActivity = activity -} +private var onSelectCallback: ((ByteArray?) -> Unit)? = null fun ComponentActivity.registerImageSelectorLauncher() { + val activity = this imageSelectorLauncher = registerForActivityResult(ActivityResultContracts.GetContent()) { uri: Uri? -> - val activity = currentActivity ?: return@registerForActivityResult val result = uri?.let { try { - val bytes = activity.contentResolver.openInputStream(it)?.use { s -> s.readBytes() } - ImageData( - bytes = bytes - ) + activity.contentResolver.openInputStream(it)?.use(InputStream::readBytes) } catch (e: IOException) { e.printStackTrace() null } } - pendingContinuation?.invoke(result) - pendingContinuation = null + onSelectCallback?.invoke(result) + onSelectCallback = null } } -actual suspend fun selectImage(): ImageData? = suspendCancellableCoroutine { continuation -> - val launcher = imageSelectorLauncher - if (launcher == null) { - continuation.resume(null) - return@suspendCancellableCoroutine - } - - pendingContinuation = { imageData -> - continuation.resume(imageData) - } +@OptIn(ExperimentalCoroutinesApi::class) +actual suspend fun launchImagePicker(): ByteArray? = suspendCancellableCoroutine { continuation -> + imageSelectorLauncher ?: throw IllegalStateException( + "Image selector launcher is not registered. Make sure to call registerImageSelectorLauncher() in your Activity before using selectImage()." + ) - launcher.launch("image/*") + onSelectCallback = continuation::resume + imageSelectorLauncher!!.launch("image/*") } \ No newline at end of file diff --git a/imageselector/src/commonMain/kotlin/com/wannaverse/imageselector/ImageData.kt b/imageselector/src/commonMain/kotlin/com/wannaverse/imageselector/ImageData.kt deleted file mode 100644 index ebb4afc..0000000 --- a/imageselector/src/commonMain/kotlin/com/wannaverse/imageselector/ImageData.kt +++ /dev/null @@ -1,23 +0,0 @@ -package com.wannaverse.imageselector - -/** - * A container class representing a loaded image. - * - * Example usage: - * - * ```kotlin - * val bitmap = image.value?.bytes?.toImageBitMap() - * bitmap?.let { - * Image( - * bitmap = it, - * contentDescription = null - * ) - * } - * ``` - * - * @property bytes raw bytes read from the image - */ -data class ImageData( - /** The raw bytes read from the file */ - val bytes: ByteArray? -) \ No newline at end of file diff --git a/imageselector/src/commonMain/kotlin/com/wannaverse/imageselector/ImageSelector.kt b/imageselector/src/commonMain/kotlin/com/wannaverse/imageselector/ImageSelector.kt index d43adc5..a57f7ee 100644 --- a/imageselector/src/commonMain/kotlin/com/wannaverse/imageselector/ImageSelector.kt +++ b/imageselector/src/commonMain/kotlin/com/wannaverse/imageselector/ImageSelector.kt @@ -1,7 +1,7 @@ package com.wannaverse.imageselector /** - * Opens the platform's image picker and returns the selected [ImageData], or `null` + * Opens the platform's image picker and returns the selected image as [ByteArray], or `null` * if the user cancels the selection. */ -expect suspend fun selectImage(): ImageData? +expect suspend fun launchImagePicker(): ByteArray? diff --git a/imageselector/src/iosMain/kotlin/com/wannaverse/imageselector/ImageSelector.ios.kt b/imageselector/src/iosMain/kotlin/com/wannaverse/imageselector/ImageSelector.ios.kt index bb46770..edf1f30 100644 --- a/imageselector/src/iosMain/kotlin/com/wannaverse/imageselector/ImageSelector.ios.kt +++ b/imageselector/src/iosMain/kotlin/com/wannaverse/imageselector/ImageSelector.ios.kt @@ -5,19 +5,19 @@ import kotlinx.cinterop.memScoped import kotlinx.cinterop.refTo import kotlinx.coroutines.suspendCancellableCoroutine import platform.Foundation.NSData +import platform.UIKit.UIApplication +import platform.UIKit.UIImage +import platform.UIKit.UIImageJPEGRepresentation import platform.UIKit.UIImagePickerController -import platform.UIKit.UIImagePickerControllerSourceType import platform.UIKit.UIImagePickerControllerDelegateProtocol -import platform.UIKit.UINavigationControllerDelegateProtocol import platform.UIKit.UIImagePickerControllerOriginalImage -import platform.UIKit.UIImageJPEGRepresentation -import platform.UIKit.UIApplication -import platform.UIKit.UIImage +import platform.UIKit.UIImagePickerControllerSourceType +import platform.UIKit.UINavigationControllerDelegateProtocol import platform.darwin.NSObject import platform.posix.memcpy import kotlin.coroutines.resume -actual suspend fun selectImage(): ImageData? = suspendCancellableCoroutine { continuation -> +actual suspend fun launchImagePicker(): ByteArray? = suspendCancellableCoroutine { continuation -> val picker = UIImagePickerController().apply { sourceType = UIImagePickerControllerSourceType.UIImagePickerControllerSourceTypePhotoLibrary @@ -34,11 +34,7 @@ actual suspend fun selectImage(): ImageData? = suspendCancellableCoroutine { con val bytes = data?.toByteArray() picker.dismissViewControllerAnimated(true) { - continuation.resume( - ImageData( - bytes = bytes - ) - ) + continuation.resume(bytes) } } diff --git a/imageselector/src/jvmMain/kotlin/com/wannaverse/imageselector/ImageSelector.jvm.kt b/imageselector/src/jvmMain/kotlin/com/wannaverse/imageselector/ImageSelector.jvm.kt index 037d9c0..84dcdaf 100644 --- a/imageselector/src/jvmMain/kotlin/com/wannaverse/imageselector/ImageSelector.jvm.kt +++ b/imageselector/src/jvmMain/kotlin/com/wannaverse/imageselector/ImageSelector.jvm.kt @@ -5,7 +5,7 @@ import kotlinx.coroutines.withContext import javax.swing.JFileChooser import javax.swing.filechooser.FileNameExtensionFilter -actual suspend fun selectImage(): ImageData? { +actual suspend fun launchImagePicker(): ByteArray? { return withContext(Dispatchers.IO) { try { val chooser = JFileChooser().apply { @@ -14,15 +14,7 @@ actual suspend fun selectImage(): ImageData? { } val result = chooser.showOpenDialog(null) - if (result == JFileChooser.APPROVE_OPTION) { - val file = chooser.selectedFile - val bytes = file.readBytes() - ImageData( - bytes = bytes - ) - } else { - null - } + if (result == JFileChooser.APPROVE_OPTION) chooser.selectedFile.readBytes() else null } catch (e: Exception) { e.printStackTrace() null diff --git a/iosApp/Configuration/Config.xcconfig b/iosApp/Configuration/Config.xcconfig new file mode 100644 index 0000000..0bc4669 --- /dev/null +++ b/iosApp/Configuration/Config.xcconfig @@ -0,0 +1,3 @@ +TEAM_ID= +BUNDLE_ID=com.wannaverse.countrypicker.country-selector +APP_NAME=country-selector \ No newline at end of file diff --git a/iosApp/iosApp.xcodeproj/project.pbxproj b/iosApp/iosApp.xcodeproj/project.pbxproj new file mode 100644 index 0000000..83c80c2 --- /dev/null +++ b/iosApp/iosApp.xcodeproj/project.pbxproj @@ -0,0 +1,393 @@ +// !$*UTF8*$! +{ + archiveVersion = 1; + classes = { + }; + objectVersion = 56; + objects = { + +/* Begin PBXBuildFile section */ + 058557BB273AAA24004C7B11 /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 058557BA273AAA24004C7B11 /* Assets.xcassets */; }; + 058557D9273AAEEB004C7B11 /* Preview Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 058557D8273AAEEB004C7B11 /* Preview Assets.xcassets */; }; + 2152FB042600AC8F00CF470E /* iOSApp.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2152FB032600AC8F00CF470E /* iOSApp.swift */; }; + 7555FF83242A565900829871 /* ContentView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7555FF82242A565900829871 /* ContentView.swift */; }; +/* End PBXBuildFile section */ + +/* Begin PBXFileReference section */ + 058557BA273AAA24004C7B11 /* Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = Assets.xcassets; sourceTree = ""; }; + 058557D8273AAEEB004C7B11 /* Preview Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = "Preview Assets.xcassets"; sourceTree = ""; }; + 2152FB032600AC8F00CF470E /* iOSApp.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = iOSApp.swift; sourceTree = ""; }; + 7555FF7B242A565900829871 /* country-countryselector.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = country-countryselector.app; sourceTree = BUILT_PRODUCTS_DIR; }; + 7555FF82242A565900829871 /* ContentView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ContentView.swift; sourceTree = ""; }; + 7555FF8C242A565B00829871 /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; + AB3632DC29227652001CCB65 /* Config.xcconfig */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.xcconfig; path = Config.xcconfig; sourceTree = ""; }; +/* End PBXFileReference section */ + +/* Begin PBXFrameworksBuildPhase section */ + B92378962B6B1156000C7307 /* Frameworks */ = { + isa = PBXFrameworksBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + runOnlyForDeploymentPostprocessing = 0; + }; +/* End PBXFrameworksBuildPhase section */ + +/* Begin PBXGroup section */ + 058557D7273AAEEB004C7B11 /* Preview Content */ = { + isa = PBXGroup; + children = ( + 058557D8273AAEEB004C7B11 /* Preview Assets.xcassets */, + ); + path = "Preview Content"; + sourceTree = ""; + }; + 42799AB246E5F90AF97AA0EF /* Frameworks */ = { + isa = PBXGroup; + children = ( + ); + name = Frameworks; + sourceTree = ""; + }; + 7555FF72242A565900829871 = { + isa = PBXGroup; + children = ( + AB1DB47929225F7C00F7AF9C /* Configuration */, + 7555FF7D242A565900829871 /* iosApp */, + 7555FF7C242A565900829871 /* Products */, + 42799AB246E5F90AF97AA0EF /* Frameworks */, + ); + sourceTree = ""; + }; + 7555FF7C242A565900829871 /* Products */ = { + isa = PBXGroup; + children = ( + 7555FF7B242A565900829871 /* country-countryselector.app */, + ); + name = Products; + sourceTree = ""; + }; + 7555FF7D242A565900829871 /* iosApp */ = { + isa = PBXGroup; + children = ( + 058557BA273AAA24004C7B11 /* Assets.xcassets */, + 7555FF82242A565900829871 /* ContentView.swift */, + 7555FF8C242A565B00829871 /* Info.plist */, + 2152FB032600AC8F00CF470E /* iOSApp.swift */, + 058557D7273AAEEB004C7B11 /* Preview Content */, + ); + path = iosApp; + sourceTree = ""; + }; + AB1DB47929225F7C00F7AF9C /* Configuration */ = { + isa = PBXGroup; + children = ( + AB3632DC29227652001CCB65 /* Config.xcconfig */, + ); + path = Configuration; + sourceTree = ""; + }; +/* End PBXGroup section */ + +/* Begin PBXNativeTarget section */ + 7555FF7A242A565900829871 /* iosApp */ = { + isa = PBXNativeTarget; + buildConfigurationList = 7555FFA5242A565B00829871 /* Build configuration list for PBXNativeTarget "iosApp" */; + buildPhases = ( + F36B1CEB2AD83DDC00CB74D5 /* Compile Kotlin Framework */, + 7555FF77242A565900829871 /* Sources */, + B92378962B6B1156000C7307 /* Frameworks */, + 7555FF79242A565900829871 /* Resources */, + ); + buildRules = ( + ); + dependencies = ( + ); + name = iosApp; + packageProductDependencies = ( + ); + productName = iosApp; + productReference = 7555FF7B242A565900829871 /* country-countryselector.app */; + productType = "com.apple.product-type.application"; + }; +/* End PBXNativeTarget section */ + +/* Begin PBXProject section */ + 7555FF73242A565900829871 /* Project object */ = { + isa = PBXProject; + attributes = { + BuildIndependentTargetsInParallel = YES; + LastSwiftUpdateCheck = 1130; + LastUpgradeCheck = 1540; + ORGANIZATIONNAME = orgName; + TargetAttributes = { + 7555FF7A242A565900829871 = { + CreatedOnToolsVersion = 11.3.1; + }; + }; + }; + buildConfigurationList = 7555FF76242A565900829871 /* Build configuration list for PBXProject "iosApp" */; + compatibilityVersion = "Xcode 14.0"; + developmentRegion = en; + hasScannedForEncodings = 0; + knownRegions = ( + en, + Base, + ); + mainGroup = 7555FF72242A565900829871; + packageReferences = ( + ); + productRefGroup = 7555FF7C242A565900829871 /* Products */; + projectDirPath = ""; + projectRoot = ""; + targets = ( + 7555FF7A242A565900829871 /* iosApp */, + ); + }; +/* End PBXProject section */ + +/* Begin PBXResourcesBuildPhase section */ + 7555FF79242A565900829871 /* Resources */ = { + isa = PBXResourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + 058557D9273AAEEB004C7B11 /* Preview Assets.xcassets in Resources */, + 058557BB273AAA24004C7B11 /* Assets.xcassets in Resources */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; +/* End PBXResourcesBuildPhase section */ + +/* Begin PBXShellScriptBuildPhase section */ + F36B1CEB2AD83DDC00CB74D5 /* Compile Kotlin Framework */ = { + isa = PBXShellScriptBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + inputFileListPaths = ( + ); + inputPaths = ( + ); + name = "Compile Kotlin Framework"; + outputFileListPaths = ( + ); + outputPaths = ( + ); + runOnlyForDeploymentPostprocessing = 0; + shellPath = /bin/sh; + shellScript = "if [ \"YES\" = \"$OVERRIDE_KOTLIN_BUILD_IDE_SUPPORTED\" ]; then\n echo \"Skipping Gradle build task invocation due to OVERRIDE_KOTLIN_BUILD_IDE_SUPPORTED environment variable set to \\\"YES\\\"\"\n exit 0\nfi\ncd \"$SRCROOT/..\"\n./gradlew :demo:embedAndSignAppleFrameworkForXcode\n"; + }; +/* End PBXShellScriptBuildPhase section */ + +/* Begin PBXSourcesBuildPhase section */ + 7555FF77242A565900829871 /* Sources */ = { + isa = PBXSourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + 2152FB042600AC8F00CF470E /* iOSApp.swift in Sources */, + 7555FF83242A565900829871 /* ContentView.swift in Sources */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; +/* End PBXSourcesBuildPhase section */ + +/* Begin XCBuildConfiguration section */ + 7555FFA3242A565B00829871 /* Debug */ = { + isa = XCBuildConfiguration; + baseConfigurationReference = AB3632DC29227652001CCB65 /* Config.xcconfig */; + buildSettings = { + ALWAYS_SEARCH_USER_PATHS = NO; + CLANG_ANALYZER_NONNULL = YES; + CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE; + CLANG_CXX_LANGUAGE_STANDARD = "gnu++14"; + CLANG_CXX_LIBRARY = "libc++"; + CLANG_ENABLE_MODULES = YES; + CLANG_ENABLE_OBJC_ARC = YES; + CLANG_ENABLE_OBJC_WEAK = YES; + CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES; + CLANG_WARN_BOOL_CONVERSION = YES; + CLANG_WARN_COMMA = YES; + CLANG_WARN_CONSTANT_CONVERSION = YES; + CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES; + CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; + CLANG_WARN_DOCUMENTATION_COMMENTS = YES; + CLANG_WARN_EMPTY_BODY = YES; + CLANG_WARN_ENUM_CONVERSION = YES; + CLANG_WARN_INFINITE_RECURSION = YES; + CLANG_WARN_INT_CONVERSION = YES; + CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES; + CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES; + CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; + CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; + CLANG_WARN_QUOTED_INCLUDE_IN_FRAMEWORK_HEADER = YES; + CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; + CLANG_WARN_STRICT_PROTOTYPES = YES; + CLANG_WARN_SUSPICIOUS_MOVE = YES; + CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE; + CLANG_WARN_UNREACHABLE_CODE = YES; + CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; + COPY_PHASE_STRIP = NO; + DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; + ENABLE_STRICT_OBJC_MSGSEND = YES; + ENABLE_TESTABILITY = YES; + ENABLE_USER_SCRIPT_SANDBOXING = NO; + GCC_C_LANGUAGE_STANDARD = gnu11; + GCC_DYNAMIC_NO_PIC = NO; + GCC_NO_COMMON_BLOCKS = YES; + GCC_OPTIMIZATION_LEVEL = 0; + GCC_PREPROCESSOR_DEFINITIONS = ( + "DEBUG=1", + "$(inherited)", + ); + GCC_WARN_64_TO_32_BIT_CONVERSION = YES; + GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; + GCC_WARN_UNDECLARED_SELECTOR = YES; + GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; + GCC_WARN_UNUSED_FUNCTION = YES; + GCC_WARN_UNUSED_VARIABLE = YES; + IPHONEOS_DEPLOYMENT_TARGET = 15.3; + MTL_ENABLE_DEBUG_INFO = INCLUDE_SOURCE; + MTL_FAST_MATH = YES; + ONLY_ACTIVE_ARCH = YES; + SDKROOT = iphoneos; + SWIFT_ACTIVE_COMPILATION_CONDITIONS = DEBUG; + SWIFT_OPTIMIZATION_LEVEL = "-Onone"; + }; + name = Debug; + }; + 7555FFA4242A565B00829871 /* Release */ = { + isa = XCBuildConfiguration; + baseConfigurationReference = AB3632DC29227652001CCB65 /* Config.xcconfig */; + buildSettings = { + ALWAYS_SEARCH_USER_PATHS = NO; + CLANG_ANALYZER_NONNULL = YES; + CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE; + CLANG_CXX_LANGUAGE_STANDARD = "gnu++14"; + CLANG_CXX_LIBRARY = "libc++"; + CLANG_ENABLE_MODULES = YES; + CLANG_ENABLE_OBJC_ARC = YES; + CLANG_ENABLE_OBJC_WEAK = YES; + CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES; + CLANG_WARN_BOOL_CONVERSION = YES; + CLANG_WARN_COMMA = YES; + CLANG_WARN_CONSTANT_CONVERSION = YES; + CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES; + CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; + CLANG_WARN_DOCUMENTATION_COMMENTS = YES; + CLANG_WARN_EMPTY_BODY = YES; + CLANG_WARN_ENUM_CONVERSION = YES; + CLANG_WARN_INFINITE_RECURSION = YES; + CLANG_WARN_INT_CONVERSION = YES; + CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES; + CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES; + CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; + CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; + CLANG_WARN_QUOTED_INCLUDE_IN_FRAMEWORK_HEADER = YES; + CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; + CLANG_WARN_STRICT_PROTOTYPES = YES; + CLANG_WARN_SUSPICIOUS_MOVE = YES; + CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE; + CLANG_WARN_UNREACHABLE_CODE = YES; + CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; + COPY_PHASE_STRIP = NO; + DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; + ENABLE_NS_ASSERTIONS = NO; + ENABLE_STRICT_OBJC_MSGSEND = YES; + ENABLE_USER_SCRIPT_SANDBOXING = NO; + GCC_C_LANGUAGE_STANDARD = gnu11; + GCC_NO_COMMON_BLOCKS = YES; + GCC_WARN_64_TO_32_BIT_CONVERSION = YES; + GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; + GCC_WARN_UNDECLARED_SELECTOR = YES; + GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; + GCC_WARN_UNUSED_FUNCTION = YES; + GCC_WARN_UNUSED_VARIABLE = YES; + IPHONEOS_DEPLOYMENT_TARGET = 15.3; + MTL_ENABLE_DEBUG_INFO = NO; + MTL_FAST_MATH = YES; + SDKROOT = iphoneos; + SWIFT_COMPILATION_MODE = wholemodule; + SWIFT_OPTIMIZATION_LEVEL = "-O"; + VALIDATE_PRODUCT = YES; + }; + name = Release; + }; + 7555FFA6242A565B00829871 /* Debug */ = { + isa = XCBuildConfiguration; + buildSettings = { + ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; + CODE_SIGN_IDENTITY = "Apple Development"; + CODE_SIGN_STYLE = Automatic; + DEVELOPMENT_ASSET_PATHS = "\"iosApp/Preview Content\""; + DEVELOPMENT_TEAM = "${TEAM_ID}"; + ENABLE_PREVIEWS = YES; + FRAMEWORK_SEARCH_PATHS = ( + "$(inherited)", + "$(SRCROOT)/../shared/build/xcode-frameworks/$(CONFIGURATION)/$(SDK_NAME)\n$(SRCROOT)/../demo/build/xcode-frameworks/$(CONFIGURATION)/$(SDK_NAME)", + ); + INFOPLIST_FILE = iosApp/Info.plist; + IPHONEOS_DEPLOYMENT_TARGET = 15.3; + LD_RUNPATH_SEARCH_PATHS = ( + "$(inherited)", + "@executable_path/Frameworks", + ); + PRODUCT_BUNDLE_IDENTIFIER = "${BUNDLE_ID}${TEAM_ID}"; + PRODUCT_NAME = "${APP_NAME}"; + PROVISIONING_PROFILE_SPECIFIER = ""; + SWIFT_VERSION = 5.0; + TARGETED_DEVICE_FAMILY = "1,2"; + }; + name = Debug; + }; + 7555FFA7242A565B00829871 /* Release */ = { + isa = XCBuildConfiguration; + buildSettings = { + ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; + CODE_SIGN_IDENTITY = "Apple Development"; + CODE_SIGN_STYLE = Automatic; + DEVELOPMENT_ASSET_PATHS = "\"iosApp/Preview Content\""; + DEVELOPMENT_TEAM = "${TEAM_ID}"; + ENABLE_PREVIEWS = YES; + FRAMEWORK_SEARCH_PATHS = ( + "$(inherited)", + "$(SRCROOT)/../shared/build/xcode-frameworks/$(CONFIGURATION)/$(SDK_NAME)\n$(SRCROOT)/../demo/build/xcode-frameworks/$(CONFIGURATION)/$(SDK_NAME)", + ); + INFOPLIST_FILE = iosApp/Info.plist; + IPHONEOS_DEPLOYMENT_TARGET = 15.3; + LD_RUNPATH_SEARCH_PATHS = ( + "$(inherited)", + "@executable_path/Frameworks", + ); + PRODUCT_BUNDLE_IDENTIFIER = "${BUNDLE_ID}${TEAM_ID}"; + PRODUCT_NAME = "${APP_NAME}"; + PROVISIONING_PROFILE_SPECIFIER = ""; + SWIFT_VERSION = 5.0; + TARGETED_DEVICE_FAMILY = "1,2"; + }; + name = Release; + }; +/* End XCBuildConfiguration section */ + +/* Begin XCConfigurationList section */ + 7555FF76242A565900829871 /* Build configuration list for PBXProject "iosApp" */ = { + isa = XCConfigurationList; + buildConfigurations = ( + 7555FFA3242A565B00829871 /* Debug */, + 7555FFA4242A565B00829871 /* Release */, + ); + defaultConfigurationIsVisible = 0; + defaultConfigurationName = Release; + }; + 7555FFA5242A565B00829871 /* Build configuration list for PBXNativeTarget "iosApp" */ = { + isa = XCConfigurationList; + buildConfigurations = ( + 7555FFA6242A565B00829871 /* Debug */, + 7555FFA7242A565B00829871 /* Release */, + ); + defaultConfigurationIsVisible = 0; + defaultConfigurationName = Release; + }; +/* End XCConfigurationList section */ + }; + rootObject = 7555FF73242A565900829871 /* Project object */; +} \ No newline at end of file diff --git a/iosApp/iosApp/Assets.xcassets/AccentColor.colorset/Contents.json b/iosApp/iosApp/Assets.xcassets/AccentColor.colorset/Contents.json new file mode 100644 index 0000000..ee7e3ca --- /dev/null +++ b/iosApp/iosApp/Assets.xcassets/AccentColor.colorset/Contents.json @@ -0,0 +1,11 @@ +{ + "colors" : [ + { + "idiom" : "universal" + } + ], + "info" : { + "author" : "xcode", + "version" : 1 + } +} \ No newline at end of file diff --git a/iosApp/iosApp/Assets.xcassets/AppIcon.appiconset/Contents.json b/iosApp/iosApp/Assets.xcassets/AppIcon.appiconset/Contents.json new file mode 100644 index 0000000..8edf56e --- /dev/null +++ b/iosApp/iosApp/Assets.xcassets/AppIcon.appiconset/Contents.json @@ -0,0 +1,14 @@ +{ + "images" : [ + { + "filename" : "app-icon-1024.png", + "idiom" : "universal", + "platform" : "ios", + "size" : "1024x1024" + } + ], + "info" : { + "author" : "xcode", + "version" : 1 + } +} diff --git a/iosApp/iosApp/Assets.xcassets/AppIcon.appiconset/app-icon-1024.png b/iosApp/iosApp/Assets.xcassets/AppIcon.appiconset/app-icon-1024.png new file mode 100644 index 0000000..53fc536 Binary files /dev/null and b/iosApp/iosApp/Assets.xcassets/AppIcon.appiconset/app-icon-1024.png differ diff --git a/iosApp/iosApp/Assets.xcassets/Contents.json b/iosApp/iosApp/Assets.xcassets/Contents.json new file mode 100644 index 0000000..4aa7c53 --- /dev/null +++ b/iosApp/iosApp/Assets.xcassets/Contents.json @@ -0,0 +1,6 @@ +{ + "info" : { + "author" : "xcode", + "version" : 1 + } +} \ No newline at end of file diff --git a/iosApp/iosApp/ContentView.swift b/iosApp/iosApp/ContentView.swift new file mode 100644 index 0000000..86a0644 --- /dev/null +++ b/iosApp/iosApp/ContentView.swift @@ -0,0 +1,20 @@ +import UIKit +import SwiftUI +import ComposeApp + +struct ComposeView: UIViewControllerRepresentable { + func makeUIViewController(context: Context) -> UIViewController { + MainViewControllerKt.MainViewController() + } + + func updateUIViewController(_ uiViewController: UIViewController, context: Context) {} +} + +struct ContentView: View { + var body: some View { + ComposeView().ignoresSafeArea(.all) + } +} + + + diff --git a/iosApp/iosApp/Info.plist b/iosApp/iosApp/Info.plist new file mode 100644 index 0000000..412e378 --- /dev/null +++ b/iosApp/iosApp/Info.plist @@ -0,0 +1,50 @@ + + + + + CFBundleDevelopmentRegion + $(DEVELOPMENT_LANGUAGE) + CFBundleExecutable + $(EXECUTABLE_NAME) + CFBundleIdentifier + $(PRODUCT_BUNDLE_IDENTIFIER) + CFBundleInfoDictionaryVersion + 6.0 + CFBundleName + $(PRODUCT_NAME) + CFBundlePackageType + $(PRODUCT_BUNDLE_PACKAGE_TYPE) + CFBundleShortVersionString + 1.0 + CFBundleVersion + 1 + LSRequiresIPhoneOS + + CADisableMinimumFrameDurationOnPhone + + UIApplicationSceneManifest + + UIApplicationSupportsMultipleScenes + + + UILaunchScreen + + UIRequiredDeviceCapabilities + + armv7 + + UISupportedInterfaceOrientations + + UIInterfaceOrientationPortrait + UIInterfaceOrientationLandscapeLeft + UIInterfaceOrientationLandscapeRight + + UISupportedInterfaceOrientations~ipad + + UIInterfaceOrientationPortrait + UIInterfaceOrientationPortraitUpsideDown + UIInterfaceOrientationLandscapeLeft + UIInterfaceOrientationLandscapeRight + + + diff --git a/iosApp/iosApp/Preview Content/Preview Assets.xcassets/Contents.json b/iosApp/iosApp/Preview Content/Preview Assets.xcassets/Contents.json new file mode 100644 index 0000000..4aa7c53 --- /dev/null +++ b/iosApp/iosApp/Preview Content/Preview Assets.xcassets/Contents.json @@ -0,0 +1,6 @@ +{ + "info" : { + "author" : "xcode", + "version" : 1 + } +} \ No newline at end of file diff --git a/iosApp/iosApp/iOSApp.swift b/iosApp/iosApp/iOSApp.swift new file mode 100644 index 0000000..d83dca6 --- /dev/null +++ b/iosApp/iosApp/iOSApp.swift @@ -0,0 +1,10 @@ +import SwiftUI + +@main +struct iOSApp: App { + var body: some Scene { + WindowGroup { + ContentView() + } + } +} \ No newline at end of file diff --git a/settings.gradle.kts b/settings.gradle.kts index 5923ef5..8194711 100644 --- a/settings.gradle.kts +++ b/settings.gradle.kts @@ -33,4 +33,5 @@ plugins { id("org.gradle.toolchains.foojay-resolver-convention") version "1.0.0" } -include(":imageselector") \ No newline at end of file +include(":imageselector") +include(":demo")