dima.avdeev
2 years ago
committed by
GitHub
53 changed files with 1460 additions and 10 deletions
@ -0,0 +1,24 @@ |
|||||||
|
# Libraries for Compose Multiplatform |
||||||
|
|
||||||
|
## Resources |
||||||
|
Library to load resources, like images. |
||||||
|
|
||||||
|
### How to run demo project: |
||||||
|
*Prerequisites*: to run on iOS and Android, you should have "Kotlin Multiplatform Mobile" plugin installed either |
||||||
|
in Android Studio or in AppCode with [installed CocoaPods](https://kotlinlang.org/docs/native-cocoapods.html). |
||||||
|
|
||||||
|
### Run on desktop via Gradle: |
||||||
|
`./gradlew :resources:demo:desktopApp:run` |
||||||
|
|
||||||
|
### Run JS in browser with WebAssembly Skia via Gradle: |
||||||
|
`./gradlew :resources:demo:shared:jsBrowserDevelopmentRun` |
||||||
|
|
||||||
|
### Run MacOS via Gradle: |
||||||
|
- on Intel CPU: `./gradlew :resources:demo:shared:runDebugExecutableMacosX64` |
||||||
|
- on Apple Silicon: `./gradlew :resources:demo:shared:runDebugExecutableMacosArm64` |
||||||
|
|
||||||
|
# Tests |
||||||
|
Run script: |
||||||
|
```bash |
||||||
|
./test.sh |
||||||
|
``` |
@ -1,5 +1,5 @@ |
|||||||
distributionBase=GRADLE_USER_HOME |
distributionBase=GRADLE_USER_HOME |
||||||
distributionPath=wrapper/dists |
distributionPath=wrapper/dists |
||||||
distributionUrl=https\://services.gradle.org/distributions/gradle-7.3.3-bin.zip |
distributionUrl=https\://services.gradle.org/distributions/gradle-7.5.1-bin.zip |
||||||
zipStoreBase=GRADLE_USER_HOME |
zipStoreBase=GRADLE_USER_HOME |
||||||
zipStorePath=wrapper/dists |
zipStorePath=wrapper/dists |
||||||
|
@ -0,0 +1,6 @@ |
|||||||
|
iosApp/Podfile.lock |
||||||
|
iosApp/Pods/* |
||||||
|
iosApp/ResourcesDemo.xcworkspace/* |
||||||
|
iosApp/ResourcesDemo.xcodeproj/* |
||||||
|
!iosApp/ResourcesDemo.xcodeproj/project.pbxproj |
||||||
|
shared/shared.podspec |
@ -0,0 +1,33 @@ |
|||||||
|
plugins { |
||||||
|
id("com.android.application") |
||||||
|
kotlin("android") |
||||||
|
} |
||||||
|
|
||||||
|
dependencies { |
||||||
|
implementation(project(":resources:demo:shared")) |
||||||
|
implementation("androidx.appcompat:appcompat:1.5.1") |
||||||
|
implementation("androidx.activity:activity-compose:1.6.1") |
||||||
|
implementation("androidx.compose.foundation:foundation:1.3.1") |
||||||
|
implementation("androidx.compose.ui:ui:1.3.1") |
||||||
|
} |
||||||
|
|
||||||
|
android { |
||||||
|
buildFeatures { |
||||||
|
compose = true |
||||||
|
} |
||||||
|
composeOptions { |
||||||
|
kotlinCompilerExtensionVersion = "1.3.2" |
||||||
|
} |
||||||
|
compileSdk = 33 |
||||||
|
defaultConfig { |
||||||
|
applicationId = "me.user.androidApp" |
||||||
|
minSdk = 24 |
||||||
|
targetSdk = 33 |
||||||
|
versionCode = 1 |
||||||
|
versionName = "1.0" |
||||||
|
} |
||||||
|
compileOptions { |
||||||
|
sourceCompatibility = JavaVersion.VERSION_1_8 |
||||||
|
targetCompatibility = JavaVersion.VERSION_1_8 |
||||||
|
} |
||||||
|
} |
@ -0,0 +1,20 @@ |
|||||||
|
<?xml version="1.0" encoding="utf-8"?> |
||||||
|
<manifest xmlns:android="http://schemas.android.com/apk/res/android" |
||||||
|
package="org.jetbrains.compose.resources.demo"> |
||||||
|
|
||||||
|
<application |
||||||
|
android:allowBackup="true" |
||||||
|
android:label="@string/app_name" |
||||||
|
android:supportsRtl="true" |
||||||
|
android:theme="@style/Theme.AppCompat.Light.NoActionBar"> |
||||||
|
<activity |
||||||
|
android:exported="true" |
||||||
|
android:name=".MainActivity"> |
||||||
|
<intent-filter> |
||||||
|
<action android:name="android.intent.action.MAIN"/> |
||||||
|
<category android:name="android.intent.category.LAUNCHER"/> |
||||||
|
</intent-filter> |
||||||
|
</activity> |
||||||
|
</application> |
||||||
|
|
||||||
|
</manifest> |
@ -0,0 +1,15 @@ |
|||||||
|
package org.jetbrains.compose.resources.demo |
||||||
|
|
||||||
|
import android.os.Bundle |
||||||
|
import androidx.activity.compose.setContent |
||||||
|
import androidx.appcompat.app.AppCompatActivity |
||||||
|
import org.jetbrains.compose.resources.demo.shared.MainView |
||||||
|
|
||||||
|
class MainActivity : AppCompatActivity() { |
||||||
|
override fun onCreate(savedInstanceState: Bundle?) { |
||||||
|
super.onCreate(savedInstanceState) |
||||||
|
setContent { |
||||||
|
MainView() |
||||||
|
} |
||||||
|
} |
||||||
|
} |
@ -0,0 +1,3 @@ |
|||||||
|
<resources> |
||||||
|
<string name="app_name">Resources demo</string> |
||||||
|
</resources> |
@ -0,0 +1,22 @@ |
|||||||
|
plugins { |
||||||
|
kotlin("multiplatform") |
||||||
|
id("org.jetbrains.compose") |
||||||
|
} |
||||||
|
|
||||||
|
kotlin { |
||||||
|
jvm {} |
||||||
|
sourceSets { |
||||||
|
val jvmMain by getting { |
||||||
|
dependencies { |
||||||
|
implementation(compose.desktop.currentOs) |
||||||
|
implementation(project(":resources:demo:shared")) |
||||||
|
} |
||||||
|
} |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
compose.desktop { |
||||||
|
application { |
||||||
|
mainClass = "MainKt" |
||||||
|
} |
||||||
|
} |
@ -0,0 +1,18 @@ |
|||||||
|
/* |
||||||
|
* Copyright 2020-2022 JetBrains s.r.o. and respective authors and developers. |
||||||
|
* Use of this source code is governed by the Apache 2.0 license that can be found in the LICENSE.txt file. |
||||||
|
*/ |
||||||
|
|
||||||
|
import androidx.compose.ui.unit.DpSize |
||||||
|
import androidx.compose.ui.unit.dp |
||||||
|
import androidx.compose.ui.window.WindowState |
||||||
|
import androidx.compose.ui.window.singleWindowApplication |
||||||
|
import org.jetbrains.compose.resources.demo.shared.MainView |
||||||
|
|
||||||
|
fun main() = |
||||||
|
singleWindowApplication( |
||||||
|
title = "Resources demo", |
||||||
|
state = WindowState(size = DpSize(800.dp, 800.dp)) |
||||||
|
) { |
||||||
|
MainView() |
||||||
|
} |
@ -0,0 +1 @@ |
|||||||
|
TEAM_ID= |
@ -0,0 +1,5 @@ |
|||||||
|
target 'ResourcesDemo' do |
||||||
|
use_frameworks! |
||||||
|
platform :ios, '14.1' |
||||||
|
pod 'shared', :path => '../shared' |
||||||
|
end |
@ -0,0 +1,398 @@ |
|||||||
|
// !$*UTF8*$! |
||||||
|
{ |
||||||
|
archiveVersion = 1; |
||||||
|
classes = { |
||||||
|
}; |
||||||
|
objectVersion = 50; |
||||||
|
objects = { |
||||||
|
|
||||||
|
/* Begin PBXBuildFile section */ |
||||||
|
2152FB042600AC8F00CF470E /* iosApp.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2152FB032600AC8F00CF470E /* iosApp.swift */; }; |
||||||
|
C1FC908188C4E8695729CB06 /* Pods_ResourcesDemo.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 8DE96E47030356CE6AD9794A /* Pods_ResourcesDemo.framework */; }; |
||||||
|
/* End PBXBuildFile section */ |
||||||
|
|
||||||
|
/* Begin PBXFileReference section */ |
||||||
|
1EB65E27D2C0F884D0A1A133 /* Pods-ResourcesDemo.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-ResourcesDemo.debug.xcconfig"; path = "Target Support Files/Pods-ResourcesDemo/Pods-ResourcesDemo.debug.xcconfig"; sourceTree = "<group>"; }; |
||||||
|
2152FB032600AC8F00CF470E /* iosApp.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = iosApp.swift; sourceTree = "<group>"; }; |
||||||
|
3D7A606AB0AD7636269BD9D0 /* Pods-ResourcesDemo.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-ResourcesDemo.release.xcconfig"; path = "Target Support Files/Pods-ResourcesDemo/Pods-ResourcesDemo.release.xcconfig"; sourceTree = "<group>"; }; |
||||||
|
7555FF7B242A565900829871 /* ResourcesDemo.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = ResourcesDemo.app; sourceTree = BUILT_PRODUCTS_DIR; }; |
||||||
|
7555FF8C242A565B00829871 /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = "<group>"; }; |
||||||
|
8DE96E47030356CE6AD9794A /* Pods_ResourcesDemo.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = Pods_ResourcesDemo.framework; sourceTree = BUILT_PRODUCTS_DIR; }; |
||||||
|
AB3632DC29227652001CCB65 /* TeamId.xcconfig */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.xcconfig; path = TeamId.xcconfig; sourceTree = "<group>"; }; |
||||||
|
/* End PBXFileReference section */ |
||||||
|
|
||||||
|
/* Begin PBXFrameworksBuildPhase section */ |
||||||
|
9964867F0862B4D9FB6ABFC7 /* Frameworks */ = { |
||||||
|
isa = PBXFrameworksBuildPhase; |
||||||
|
buildActionMask = 2147483647; |
||||||
|
files = ( |
||||||
|
C1FC908188C4E8695729CB06 /* Pods_ResourcesDemo.framework in Frameworks */, |
||||||
|
); |
||||||
|
runOnlyForDeploymentPostprocessing = 0; |
||||||
|
}; |
||||||
|
/* End PBXFrameworksBuildPhase section */ |
||||||
|
|
||||||
|
/* Begin PBXGroup section */ |
||||||
|
7555FF72242A565900829871 = { |
||||||
|
isa = PBXGroup; |
||||||
|
children = ( |
||||||
|
AB1DB47929225F7C00F7AF9C /* Configuration */, |
||||||
|
7555FF7D242A565900829871 /* iosApp */, |
||||||
|
7555FF7C242A565900829871 /* Products */, |
||||||
|
E1DAFBE8E1CFC0878361EF0E /* Pods */, |
||||||
|
B62309C7396AD7BF607A63B2 /* Frameworks */, |
||||||
|
); |
||||||
|
sourceTree = "<group>"; |
||||||
|
}; |
||||||
|
7555FF7C242A565900829871 /* Products */ = { |
||||||
|
isa = PBXGroup; |
||||||
|
children = ( |
||||||
|
7555FF7B242A565900829871 /* ResourcesDemo.app */, |
||||||
|
); |
||||||
|
name = Products; |
||||||
|
sourceTree = "<group>"; |
||||||
|
}; |
||||||
|
7555FF7D242A565900829871 /* iosApp */ = { |
||||||
|
isa = PBXGroup; |
||||||
|
children = ( |
||||||
|
7555FF8C242A565B00829871 /* Info.plist */, |
||||||
|
2152FB032600AC8F00CF470E /* iosApp.swift */, |
||||||
|
); |
||||||
|
path = iosApp; |
||||||
|
sourceTree = "<group>"; |
||||||
|
}; |
||||||
|
AB1DB47929225F7C00F7AF9C /* Configuration */ = { |
||||||
|
isa = PBXGroup; |
||||||
|
children = ( |
||||||
|
AB3632DC29227652001CCB65 /* TeamId.xcconfig */, |
||||||
|
); |
||||||
|
path = Configuration; |
||||||
|
sourceTree = "<group>"; |
||||||
|
}; |
||||||
|
B62309C7396AD7BF607A63B2 /* Frameworks */ = { |
||||||
|
isa = PBXGroup; |
||||||
|
children = ( |
||||||
|
8DE96E47030356CE6AD9794A /* Pods_ResourcesDemo.framework */, |
||||||
|
); |
||||||
|
name = Frameworks; |
||||||
|
sourceTree = "<group>"; |
||||||
|
}; |
||||||
|
E1DAFBE8E1CFC0878361EF0E /* Pods */ = { |
||||||
|
isa = PBXGroup; |
||||||
|
children = ( |
||||||
|
1EB65E27D2C0F884D0A1A133 /* Pods-ResourcesDemo.debug.xcconfig */, |
||||||
|
3D7A606AB0AD7636269BD9D0 /* Pods-ResourcesDemo.release.xcconfig */, |
||||||
|
); |
||||||
|
path = Pods; |
||||||
|
sourceTree = "<group>"; |
||||||
|
}; |
||||||
|
/* End PBXGroup section */ |
||||||
|
|
||||||
|
/* Begin PBXNativeTarget section */ |
||||||
|
7555FF7A242A565900829871 /* ResourcesDemo */ = { |
||||||
|
isa = PBXNativeTarget; |
||||||
|
buildConfigurationList = 7555FFA5242A565B00829871 /* Build configuration list for PBXNativeTarget "ResourcesDemo" */; |
||||||
|
buildPhases = ( |
||||||
|
E8D673591E7196AEA2EA10E2 /* [CP] Check Pods Manifest.lock */, |
||||||
|
7555FF77242A565900829871 /* Sources */, |
||||||
|
7555FF79242A565900829871 /* Resources */, |
||||||
|
9964867F0862B4D9FB6ABFC7 /* Frameworks */, |
||||||
|
A51DDDB74597C98E89765935 /* [CP] Copy Pods Resources */, |
||||||
|
); |
||||||
|
buildRules = ( |
||||||
|
); |
||||||
|
dependencies = ( |
||||||
|
); |
||||||
|
name = ResourcesDemo; |
||||||
|
productName = iosApp; |
||||||
|
productReference = 7555FF7B242A565900829871 /* ResourcesDemo.app */; |
||||||
|
productType = "com.apple.product-type.application"; |
||||||
|
}; |
||||||
|
/* End PBXNativeTarget section */ |
||||||
|
|
||||||
|
/* Begin PBXProject section */ |
||||||
|
7555FF73242A565900829871 /* Project object */ = { |
||||||
|
isa = PBXProject; |
||||||
|
attributes = { |
||||||
|
LastSwiftUpdateCheck = 1130; |
||||||
|
LastUpgradeCheck = 1130; |
||||||
|
ORGANIZATIONNAME = org.jetbrains; |
||||||
|
TargetAttributes = { |
||||||
|
7555FF7A242A565900829871 = { |
||||||
|
CreatedOnToolsVersion = 11.3.1; |
||||||
|
}; |
||||||
|
}; |
||||||
|
}; |
||||||
|
buildConfigurationList = 7555FF76242A565900829871 /* Build configuration list for PBXProject "ResourcesDemo" */; |
||||||
|
compatibilityVersion = "Xcode 9.3"; |
||||||
|
developmentRegion = en; |
||||||
|
hasScannedForEncodings = 0; |
||||||
|
knownRegions = ( |
||||||
|
en, |
||||||
|
Base, |
||||||
|
); |
||||||
|
mainGroup = 7555FF72242A565900829871; |
||||||
|
productRefGroup = 7555FF7C242A565900829871 /* Products */; |
||||||
|
projectDirPath = ""; |
||||||
|
projectRoot = ""; |
||||||
|
targets = ( |
||||||
|
7555FF7A242A565900829871 /* ResourcesDemo */, |
||||||
|
); |
||||||
|
}; |
||||||
|
/* End PBXProject section */ |
||||||
|
|
||||||
|
/* Begin PBXResourcesBuildPhase section */ |
||||||
|
7555FF79242A565900829871 /* Resources */ = { |
||||||
|
isa = PBXResourcesBuildPhase; |
||||||
|
buildActionMask = 2147483647; |
||||||
|
files = ( |
||||||
|
); |
||||||
|
runOnlyForDeploymentPostprocessing = 0; |
||||||
|
}; |
||||||
|
/* End PBXResourcesBuildPhase section */ |
||||||
|
|
||||||
|
/* Begin PBXShellScriptBuildPhase section */ |
||||||
|
A51DDDB74597C98E89765935 /* [CP] Copy Pods Resources */ = { |
||||||
|
isa = PBXShellScriptBuildPhase; |
||||||
|
buildActionMask = 2147483647; |
||||||
|
files = ( |
||||||
|
); |
||||||
|
inputFileListPaths = ( |
||||||
|
"${PODS_ROOT}/Target Support Files/Pods-ResourcesDemo/Pods-ResourcesDemo-resources-${CONFIGURATION}-input-files.xcfilelist", |
||||||
|
); |
||||||
|
name = "[CP] Copy Pods Resources"; |
||||||
|
outputFileListPaths = ( |
||||||
|
"${PODS_ROOT}/Target Support Files/Pods-ResourcesDemo/Pods-ResourcesDemo-resources-${CONFIGURATION}-output-files.xcfilelist", |
||||||
|
); |
||||||
|
runOnlyForDeploymentPostprocessing = 0; |
||||||
|
shellPath = /bin/sh; |
||||||
|
shellScript = "\"${PODS_ROOT}/Target Support Files/Pods-ResourcesDemo/Pods-ResourcesDemo-resources.sh\"\n"; |
||||||
|
showEnvVarsInLog = 0; |
||||||
|
}; |
||||||
|
E8D673591E7196AEA2EA10E2 /* [CP] Check Pods Manifest.lock */ = { |
||||||
|
isa = PBXShellScriptBuildPhase; |
||||||
|
buildActionMask = 2147483647; |
||||||
|
files = ( |
||||||
|
); |
||||||
|
inputFileListPaths = ( |
||||||
|
); |
||||||
|
inputPaths = ( |
||||||
|
"${PODS_PODFILE_DIR_PATH}/Podfile.lock", |
||||||
|
"${PODS_ROOT}/Manifest.lock", |
||||||
|
); |
||||||
|
name = "[CP] Check Pods Manifest.lock"; |
||||||
|
outputFileListPaths = ( |
||||||
|
); |
||||||
|
outputPaths = ( |
||||||
|
"$(DERIVED_FILE_DIR)/Pods-ResourcesDemo-checkManifestLockResult.txt", |
||||||
|
); |
||||||
|
runOnlyForDeploymentPostprocessing = 0; |
||||||
|
shellPath = /bin/sh; |
||||||
|
shellScript = "diff \"${PODS_PODFILE_DIR_PATH}/Podfile.lock\" \"${PODS_ROOT}/Manifest.lock\" > /dev/null\nif [ $? != 0 ] ; then\n # print error to STDERR\n echo \"error: The sandbox is not in sync with the Podfile.lock. Run 'pod install' or update your CocoaPods installation.\" >&2\n exit 1\nfi\n# This output is used by Xcode 'outputs' to avoid re-running this script phase.\necho \"SUCCESS\" > \"${SCRIPT_OUTPUT_FILE_0}\"\n"; |
||||||
|
showEnvVarsInLog = 0; |
||||||
|
}; |
||||||
|
/* End PBXShellScriptBuildPhase section */ |
||||||
|
|
||||||
|
/* Begin PBXSourcesBuildPhase section */ |
||||||
|
7555FF77242A565900829871 /* Sources */ = { |
||||||
|
isa = PBXSourcesBuildPhase; |
||||||
|
buildActionMask = 2147483647; |
||||||
|
files = ( |
||||||
|
2152FB042600AC8F00CF470E /* iosApp.swift in Sources */, |
||||||
|
); |
||||||
|
runOnlyForDeploymentPostprocessing = 0; |
||||||
|
}; |
||||||
|
/* End PBXSourcesBuildPhase section */ |
||||||
|
|
||||||
|
/* Begin XCBuildConfiguration section */ |
||||||
|
7555FFA3242A565B00829871 /* Debug */ = { |
||||||
|
isa = XCBuildConfiguration; |
||||||
|
baseConfigurationReference = AB3632DC29227652001CCB65 /* TeamId.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; |
||||||
|
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 = 14.1; |
||||||
|
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 /* TeamId.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; |
||||||
|
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 = 14.1; |
||||||
|
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; |
||||||
|
baseConfigurationReference = 1EB65E27D2C0F884D0A1A133 /* Pods-ResourcesDemo.debug.xcconfig */; |
||||||
|
buildSettings = { |
||||||
|
ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; |
||||||
|
CODE_SIGN_IDENTITY = "Apple Development"; |
||||||
|
CODE_SIGN_STYLE = Automatic; |
||||||
|
DEVELOPMENT_ASSET_PATHS = ""; |
||||||
|
DEVELOPMENT_TEAM = "${TEAM_ID}"; |
||||||
|
ENABLE_PREVIEWS = YES; |
||||||
|
INFOPLIST_FILE = iosApp/Info.plist; |
||||||
|
IPHONEOS_DEPLOYMENT_TARGET = 14.1; |
||||||
|
LD_RUNPATH_SEARCH_PATHS = ( |
||||||
|
"$(inherited)", |
||||||
|
"@executable_path/Frameworks", |
||||||
|
); |
||||||
|
PRODUCT_BUNDLE_IDENTIFIER = "org.jetbrains.ResourcesDemo${TEAM_ID}"; |
||||||
|
PRODUCT_NAME = "$(TARGET_NAME)"; |
||||||
|
PROVISIONING_PROFILE_SPECIFIER = ""; |
||||||
|
SWIFT_VERSION = 5.0; |
||||||
|
TARGETED_DEVICE_FAMILY = "1,2"; |
||||||
|
}; |
||||||
|
name = Debug; |
||||||
|
}; |
||||||
|
7555FFA7242A565B00829871 /* Release */ = { |
||||||
|
isa = XCBuildConfiguration; |
||||||
|
baseConfigurationReference = 3D7A606AB0AD7636269BD9D0 /* Pods-ResourcesDemo.release.xcconfig */; |
||||||
|
buildSettings = { |
||||||
|
ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; |
||||||
|
CODE_SIGN_IDENTITY = "Apple Development"; |
||||||
|
CODE_SIGN_STYLE = Automatic; |
||||||
|
DEVELOPMENT_ASSET_PATHS = ""; |
||||||
|
DEVELOPMENT_TEAM = "${TEAM_ID}"; |
||||||
|
ENABLE_PREVIEWS = YES; |
||||||
|
INFOPLIST_FILE = iosApp/Info.plist; |
||||||
|
IPHONEOS_DEPLOYMENT_TARGET = 14.1; |
||||||
|
LD_RUNPATH_SEARCH_PATHS = ( |
||||||
|
"$(inherited)", |
||||||
|
"@executable_path/Frameworks", |
||||||
|
); |
||||||
|
PRODUCT_BUNDLE_IDENTIFIER = "org.jetbrains.ResourcesDemo${TEAM_ID}"; |
||||||
|
PRODUCT_NAME = "$(TARGET_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 "ResourcesDemo" */ = { |
||||||
|
isa = XCConfigurationList; |
||||||
|
buildConfigurations = ( |
||||||
|
7555FFA3242A565B00829871 /* Debug */, |
||||||
|
7555FFA4242A565B00829871 /* Release */, |
||||||
|
); |
||||||
|
defaultConfigurationIsVisible = 0; |
||||||
|
defaultConfigurationName = Release; |
||||||
|
}; |
||||||
|
7555FFA5242A565B00829871 /* Build configuration list for PBXNativeTarget "ResourcesDemo" */ = { |
||||||
|
isa = XCConfigurationList; |
||||||
|
buildConfigurations = ( |
||||||
|
7555FFA6242A565B00829871 /* Debug */, |
||||||
|
7555FFA7242A565B00829871 /* Release */, |
||||||
|
); |
||||||
|
defaultConfigurationIsVisible = 0; |
||||||
|
defaultConfigurationName = Release; |
||||||
|
}; |
||||||
|
/* End XCConfigurationList section */ |
||||||
|
}; |
||||||
|
rootObject = 7555FF73242A565900829871 /* Project object */; |
||||||
|
} |
@ -0,0 +1,48 @@ |
|||||||
|
<?xml version="1.0" encoding="UTF-8"?> |
||||||
|
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd"> |
||||||
|
<plist version="1.0"> |
||||||
|
<dict> |
||||||
|
<key>CFBundleDevelopmentRegion</key> |
||||||
|
<string>$(DEVELOPMENT_LANGUAGE)</string> |
||||||
|
<key>CFBundleExecutable</key> |
||||||
|
<string>$(EXECUTABLE_NAME)</string> |
||||||
|
<key>CFBundleIdentifier</key> |
||||||
|
<string>$(PRODUCT_BUNDLE_IDENTIFIER)</string> |
||||||
|
<key>CFBundleInfoDictionaryVersion</key> |
||||||
|
<string>6.0</string> |
||||||
|
<key>CFBundleName</key> |
||||||
|
<string>$(PRODUCT_NAME)</string> |
||||||
|
<key>CFBundlePackageType</key> |
||||||
|
<string>$(PRODUCT_BUNDLE_PACKAGE_TYPE)</string> |
||||||
|
<key>CFBundleShortVersionString</key> |
||||||
|
<string>1.0</string> |
||||||
|
<key>CFBundleVersion</key> |
||||||
|
<string>1</string> |
||||||
|
<key>LSRequiresIPhoneOS</key> |
||||||
|
<true/> |
||||||
|
<key>UIApplicationSceneManifest</key> |
||||||
|
<dict> |
||||||
|
<key>UIApplicationSupportsMultipleScenes</key> |
||||||
|
<false/> |
||||||
|
</dict> |
||||||
|
<key>UILaunchScreen</key> |
||||||
|
<dict/> |
||||||
|
<key>UIRequiredDeviceCapabilities</key> |
||||||
|
<array> |
||||||
|
<string>armv7</string> |
||||||
|
</array> |
||||||
|
<key>UISupportedInterfaceOrientations</key> |
||||||
|
<array> |
||||||
|
<string>UIInterfaceOrientationPortrait</string> |
||||||
|
<string>UIInterfaceOrientationLandscapeLeft</string> |
||||||
|
<string>UIInterfaceOrientationLandscapeRight</string> |
||||||
|
</array> |
||||||
|
<key>UISupportedInterfaceOrientations~ipad</key> |
||||||
|
<array> |
||||||
|
<string>UIInterfaceOrientationPortrait</string> |
||||||
|
<string>UIInterfaceOrientationPortraitUpsideDown</string> |
||||||
|
<string>UIInterfaceOrientationLandscapeLeft</string> |
||||||
|
<string>UIInterfaceOrientationLandscapeRight</string> |
||||||
|
</array> |
||||||
|
</dict> |
||||||
|
</plist> |
@ -0,0 +1,15 @@ |
|||||||
|
import UIKit |
||||||
|
import shared |
||||||
|
|
||||||
|
@UIApplicationMain |
||||||
|
class AppDelegate: UIResponder, UIApplicationDelegate { |
||||||
|
var window: UIWindow? |
||||||
|
|
||||||
|
func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?) -> Bool { |
||||||
|
window = UIWindow(frame: UIScreen.main.bounds) |
||||||
|
let mainViewController = Main_iosKt.MainViewController() |
||||||
|
window?.rootViewController = mainViewController |
||||||
|
window?.makeKeyAndVisible() |
||||||
|
return true |
||||||
|
} |
||||||
|
} |
@ -0,0 +1,101 @@ |
|||||||
|
plugins { |
||||||
|
kotlin("multiplatform") |
||||||
|
kotlin("native.cocoapods") |
||||||
|
id("com.android.library") |
||||||
|
id("org.jetbrains.compose") |
||||||
|
} |
||||||
|
|
||||||
|
version = "1.0-SNAPSHOT" |
||||||
|
|
||||||
|
kotlin { |
||||||
|
android() |
||||||
|
jvm("desktop") |
||||||
|
ios() |
||||||
|
iosSimulatorArm64() |
||||||
|
js(IR) { |
||||||
|
browser() |
||||||
|
binaries.executable() |
||||||
|
} |
||||||
|
macosX64 { |
||||||
|
binaries { |
||||||
|
executable { |
||||||
|
entryPoint = "main" |
||||||
|
} |
||||||
|
} |
||||||
|
} |
||||||
|
macosArm64 { |
||||||
|
binaries { |
||||||
|
executable { |
||||||
|
entryPoint = "main" |
||||||
|
} |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
cocoapods { |
||||||
|
summary = "Shared code for the sample" |
||||||
|
homepage = "https://github.com/JetBrains/compose-jb" |
||||||
|
ios.deploymentTarget = "14.1" |
||||||
|
podfile = project.file("../iosApp/Podfile") |
||||||
|
framework { |
||||||
|
baseName = "shared" |
||||||
|
isStatic = true |
||||||
|
} |
||||||
|
extraSpecAttributes["resources"] = "['src/commonMain/resources/**', 'src/iosMain/resources/**']" |
||||||
|
} |
||||||
|
|
||||||
|
sourceSets { |
||||||
|
val commonMain by getting { |
||||||
|
dependencies { |
||||||
|
implementation(compose.ui) |
||||||
|
implementation(compose.foundation) |
||||||
|
implementation(compose.material) |
||||||
|
implementation(compose.runtime) |
||||||
|
implementation(project(":resources:library")) |
||||||
|
} |
||||||
|
} |
||||||
|
val iosMain by getting |
||||||
|
val iosTest by getting |
||||||
|
val iosSimulatorArm64Main by getting { |
||||||
|
dependsOn(iosMain) |
||||||
|
} |
||||||
|
val iosSimulatorArm64Test by getting { |
||||||
|
dependsOn(iosTest) |
||||||
|
} |
||||||
|
val desktopMain by getting { |
||||||
|
dependencies { |
||||||
|
implementation(compose.desktop.common) |
||||||
|
} |
||||||
|
} |
||||||
|
val macosMain by creating { |
||||||
|
dependsOn(commonMain) |
||||||
|
} |
||||||
|
val macosX64Main by getting { |
||||||
|
dependsOn(macosMain) |
||||||
|
} |
||||||
|
val macosArm64Main by getting { |
||||||
|
dependsOn(macosMain) |
||||||
|
} |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
android { |
||||||
|
compileSdk = 33 |
||||||
|
sourceSets["main"].manifest.srcFile("src/androidMain/AndroidManifest.xml") |
||||||
|
defaultConfig { |
||||||
|
minSdk = 24 |
||||||
|
targetSdk = 33 |
||||||
|
} |
||||||
|
compileOptions { |
||||||
|
sourceCompatibility = JavaVersion.VERSION_1_8 |
||||||
|
targetCompatibility = JavaVersion.VERSION_1_8 |
||||||
|
} |
||||||
|
sourceSets { |
||||||
|
named("main") { |
||||||
|
resources.srcDir("src/commonMain/resources") |
||||||
|
} |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
compose.experimental { |
||||||
|
web.application {} |
||||||
|
} |
@ -0,0 +1,2 @@ |
|||||||
|
<?xml version="1.0" encoding="utf-8"?> |
||||||
|
<manifest package="org.jetbrains.compose.resources.demo.shared"/> |
@ -0,0 +1,13 @@ |
|||||||
|
/* |
||||||
|
* Copyright 2020-2022 JetBrains s.r.o. and respective authors and developers. |
||||||
|
* Use of this source code is governed by the Apache 2.0 license that can be found in the LICENSE.txt file. |
||||||
|
*/ |
||||||
|
|
||||||
|
package org.jetbrains.compose.resources.demo.shared |
||||||
|
|
||||||
|
import androidx.compose.runtime.Composable |
||||||
|
|
||||||
|
@Composable |
||||||
|
fun MainView() { |
||||||
|
UseResources() |
||||||
|
} |
@ -0,0 +1,23 @@ |
|||||||
|
package org.jetbrains.compose.resources.demo.shared |
||||||
|
|
||||||
|
import androidx.compose.foundation.Image |
||||||
|
import androidx.compose.foundation.layout.* |
||||||
|
import androidx.compose.material.Text |
||||||
|
import androidx.compose.runtime.Composable |
||||||
|
import org.jetbrains.compose.resources.* |
||||||
|
|
||||||
|
@OptIn(ExperimentalResourceApi::class) |
||||||
|
@Composable |
||||||
|
internal fun UseResources() { |
||||||
|
Column { |
||||||
|
Text("Hello, resources") |
||||||
|
Image( |
||||||
|
bitmap = resource("dir/img.png").rememberImageBitmap().orEmpty(), |
||||||
|
contentDescription = null, |
||||||
|
) |
||||||
|
Image( |
||||||
|
bitmap = resource("img.webp").rememberImageBitmap().orEmpty(), |
||||||
|
contentDescription = null, |
||||||
|
) |
||||||
|
} |
||||||
|
} |
After Width: | Height: | Size: 31 KiB |
After Width: | Height: | Size: 10 KiB |
@ -0,0 +1,20 @@ |
|||||||
|
/* |
||||||
|
* Copyright 2020-2022 JetBrains s.r.o. and respective authors and developers. |
||||||
|
* Use of this source code is governed by the Apache 2.0 license that can be found in the LICENSE.txt file. |
||||||
|
*/ |
||||||
|
|
||||||
|
package org.jetbrains.compose.resources.demo.shared |
||||||
|
|
||||||
|
import androidx.compose.desktop.ui.tooling.preview.Preview |
||||||
|
import androidx.compose.runtime.Composable |
||||||
|
|
||||||
|
@Composable |
||||||
|
fun MainView() { |
||||||
|
UseResources() |
||||||
|
} |
||||||
|
|
||||||
|
@Preview |
||||||
|
@Composable |
||||||
|
fun Preview() { |
||||||
|
MainView() |
||||||
|
} |
@ -0,0 +1,24 @@ |
|||||||
|
/* |
||||||
|
* Copyright 2020-2022 JetBrains s.r.o. and respective authors and developers. |
||||||
|
* Use of this source code is governed by the Apache 2.0 license that can be found in the LICENSE.txt file. |
||||||
|
*/ |
||||||
|
|
||||||
|
import androidx.compose.foundation.layout.Box |
||||||
|
import androidx.compose.foundation.layout.Column |
||||||
|
import androidx.compose.foundation.layout.height |
||||||
|
import androidx.compose.ui.Modifier |
||||||
|
import androidx.compose.ui.unit.dp |
||||||
|
import androidx.compose.ui.window.Application |
||||||
|
import org.jetbrains.compose.resources.demo.shared.UseResources |
||||||
|
import platform.UIKit.UIViewController |
||||||
|
|
||||||
|
fun MainViewController(): UIViewController = |
||||||
|
Application("Resources demo") { |
||||||
|
Column { |
||||||
|
Box( |
||||||
|
modifier = Modifier |
||||||
|
.height(100.dp) |
||||||
|
) |
||||||
|
UseResources() |
||||||
|
} |
||||||
|
} |
@ -0,0 +1,31 @@ |
|||||||
|
/* |
||||||
|
* Copyright 2020-2022 JetBrains s.r.o. and respective authors and developers. |
||||||
|
* Use of this source code is governed by the Apache 2.0 license that can be found in the LICENSE.txt file. |
||||||
|
*/ |
||||||
|
|
||||||
|
import androidx.compose.foundation.layout.Column |
||||||
|
import androidx.compose.foundation.layout.Spacer |
||||||
|
import androidx.compose.foundation.layout.fillMaxSize |
||||||
|
import androidx.compose.foundation.layout.height |
||||||
|
import androidx.compose.runtime.Composable |
||||||
|
import androidx.compose.ui.Modifier |
||||||
|
import androidx.compose.ui.unit.dp |
||||||
|
import androidx.compose.ui.window.Window |
||||||
|
import org.jetbrains.compose.resources.demo.shared.UseResources |
||||||
|
import org.jetbrains.skiko.wasm.onWasmReady |
||||||
|
|
||||||
|
fun main() { |
||||||
|
onWasmReady { |
||||||
|
Window("Resources demo") { |
||||||
|
MainView() |
||||||
|
} |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
@Composable |
||||||
|
fun MainView() { |
||||||
|
Column(modifier = Modifier.fillMaxSize()) { |
||||||
|
Spacer(modifier = Modifier.height(24.dp)) |
||||||
|
UseResources() |
||||||
|
} |
||||||
|
} |
@ -0,0 +1,16 @@ |
|||||||
|
<!DOCTYPE html> |
||||||
|
<html lang="en"> |
||||||
|
<head> |
||||||
|
<meta charset="UTF-8"> |
||||||
|
<title>compose multiplatform web demo</title> |
||||||
|
<script src="skiko.js"> </script> |
||||||
|
<link type="text/css" rel="stylesheet" href="styles.css" /> |
||||||
|
</head> |
||||||
|
<body> |
||||||
|
<h1>compose multiplatform web demo</h1> |
||||||
|
<div> |
||||||
|
<canvas id="ComposeTarget" width="800" height="600"></canvas> |
||||||
|
</div> |
||||||
|
<script src="shared.js"> </script> |
||||||
|
</body> |
||||||
|
</html> |
@ -0,0 +1,8 @@ |
|||||||
|
#root { |
||||||
|
width: 100%; |
||||||
|
height: 100vh; |
||||||
|
} |
||||||
|
|
||||||
|
#root > .compose-web-column > div { |
||||||
|
position: relative; |
||||||
|
} |
@ -0,0 +1,17 @@ |
|||||||
|
/* |
||||||
|
* Copyright 2020-2022 JetBrains s.r.o. and respective authors and developers. |
||||||
|
* Use of this source code is governed by the Apache 2.0 license that can be found in the LICENSE.txt file. |
||||||
|
*/ |
||||||
|
|
||||||
|
import androidx.compose.ui.window.Window |
||||||
|
import org.jetbrains.compose.resources.demo.shared.UseResources |
||||||
|
import platform.AppKit.NSApp |
||||||
|
import platform.AppKit.NSApplication |
||||||
|
|
||||||
|
fun main() { |
||||||
|
NSApplication.sharedApplication() |
||||||
|
Window("Resources demo") { |
||||||
|
UseResources() |
||||||
|
} |
||||||
|
NSApp?.run() |
||||||
|
} |
@ -0,0 +1,61 @@ |
|||||||
|
package org.jetbrains.compose.resources |
||||||
|
|
||||||
|
import androidx.compose.foundation.Image |
||||||
|
import androidx.compose.runtime.Composable |
||||||
|
import androidx.compose.runtime.collectAsState |
||||||
|
import androidx.compose.runtime.getValue |
||||||
|
import androidx.compose.ui.graphics.ImageBitmap |
||||||
|
import androidx.compose.ui.test.junit4.createComposeRule |
||||||
|
import kotlinx.coroutines.ExperimentalCoroutinesApi |
||||||
|
import kotlinx.coroutines.delay |
||||||
|
import kotlinx.coroutines.flow.MutableStateFlow |
||||||
|
import kotlinx.coroutines.test.UnconfinedTestDispatcher |
||||||
|
import kotlinx.coroutines.test.runTest |
||||||
|
import org.junit.Assert.assertEquals |
||||||
|
import org.junit.Rule |
||||||
|
import org.junit.Test |
||||||
|
|
||||||
|
@OptIn(ExperimentalResourceApi::class, ExperimentalCoroutinesApi::class) |
||||||
|
class ComposeResourceTest { |
||||||
|
|
||||||
|
@get:Rule |
||||||
|
val rule = createComposeRule() |
||||||
|
|
||||||
|
@Test |
||||||
|
fun testMissingResource() = runTest (UnconfinedTestDispatcher()) { |
||||||
|
var recompositionCount = 0 |
||||||
|
rule.setContent { |
||||||
|
CountRecompositions(resource("missing.png").rememberImageBitmap().orEmpty()) { |
||||||
|
recompositionCount++ |
||||||
|
} |
||||||
|
} |
||||||
|
rule.awaitIdle() |
||||||
|
assertEquals(2, recompositionCount) |
||||||
|
} |
||||||
|
|
||||||
|
@Test |
||||||
|
fun testCountRecompositions() = runTest (UnconfinedTestDispatcher()) { |
||||||
|
val mutableStateFlow = MutableStateFlow(true) |
||||||
|
var recompositionCount = 0 |
||||||
|
rule.setContent { |
||||||
|
val state: Boolean by mutableStateFlow.collectAsState(true) |
||||||
|
val resource = resource(if (state) "1.png" else "2.png") |
||||||
|
CountRecompositions(resource.rememberImageBitmap().orEmpty()) { |
||||||
|
recompositionCount++ |
||||||
|
} |
||||||
|
} |
||||||
|
rule.awaitIdle() |
||||||
|
mutableStateFlow.value = false |
||||||
|
rule.awaitIdle() |
||||||
|
assertEquals(4, recompositionCount) |
||||||
|
} |
||||||
|
|
||||||
|
} |
||||||
|
|
||||||
|
@Composable |
||||||
|
private fun CountRecompositions(imageBitmap: ImageBitmap?, onRecomposition: () -> Unit) { |
||||||
|
onRecomposition() |
||||||
|
if (imageBitmap != null) { |
||||||
|
Image(bitmap = imageBitmap, contentDescription = null) |
||||||
|
} |
||||||
|
} |
After Width: | Height: | Size: 946 B |
After Width: | Height: | Size: 785 B |
@ -0,0 +1,2 @@ |
|||||||
|
<?xml version="1.0" encoding="utf-8"?> |
||||||
|
<manifest package="org.jetbrains.compose.components.resources"/> |
@ -0,0 +1,18 @@ |
|||||||
|
/* |
||||||
|
* Copyright 2020-2022 JetBrains s.r.o. and respective authors and developers. |
||||||
|
* Use of this source code is governed by the Apache 2.0 license that can be found in the LICENSE.txt file. |
||||||
|
*/ |
||||||
|
|
||||||
|
package org.jetbrains.compose.resources |
||||||
|
|
||||||
|
import android.graphics.Bitmap |
||||||
|
import android.graphics.BitmapFactory |
||||||
|
import androidx.compose.ui.graphics.ImageBitmap |
||||||
|
import androidx.compose.ui.graphics.asImageBitmap |
||||||
|
|
||||||
|
@ExperimentalResourceApi |
||||||
|
internal actual fun ByteArray.toImageBitmap(): ImageBitmap = toAndroidBitmap().asImageBitmap() |
||||||
|
|
||||||
|
private fun ByteArray.toAndroidBitmap(): Bitmap { |
||||||
|
return BitmapFactory.decodeByteArray(this, 0, size); |
||||||
|
} |
@ -0,0 +1,26 @@ |
|||||||
|
/* |
||||||
|
* Copyright 2020-2022 JetBrains s.r.o. and respective authors and developers. |
||||||
|
* Use of this source code is governed by the Apache 2.0 license that can be found in the LICENSE.txt file. |
||||||
|
*/ |
||||||
|
|
||||||
|
package org.jetbrains.compose.resources |
||||||
|
|
||||||
|
import java.io.IOException |
||||||
|
|
||||||
|
@ExperimentalResourceApi |
||||||
|
actual fun resource(path: String): Resource = AndroidResourceImpl(path) |
||||||
|
|
||||||
|
@ExperimentalResourceApi |
||||||
|
private class AndroidResourceImpl(path: String) : AbstractResourceImpl(path) { |
||||||
|
override suspend fun readBytes(): ByteArray { |
||||||
|
val resource = (::AndroidResourceImpl.javaClass.classLoader).getResourceAsStream(path) |
||||||
|
if (resource != null) { |
||||||
|
return resource.readBytes() |
||||||
|
} else { |
||||||
|
throw MissingResourceException(path) |
||||||
|
} |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
internal actual class MissingResourceException actual constructor(path: String) : |
||||||
|
IOException("Missing resource with path: $path") |
@ -0,0 +1,40 @@ |
|||||||
|
/* |
||||||
|
* Copyright 2020-2022 JetBrains s.r.o. and respective authors and developers. |
||||||
|
* Use of this source code is governed by the Apache 2.0 license that can be found in the LICENSE.txt file. |
||||||
|
*/ |
||||||
|
|
||||||
|
package org.jetbrains.compose.resources |
||||||
|
|
||||||
|
import androidx.compose.runtime.* |
||||||
|
import androidx.compose.ui.graphics.ImageBitmap |
||||||
|
|
||||||
|
private val emptyImageBitmap: ImageBitmap by lazy { ImageBitmap(1, 1) } |
||||||
|
|
||||||
|
/** |
||||||
|
* Get and remember resource. While loading and if resource not exists result will be null. |
||||||
|
*/ |
||||||
|
@ExperimentalResourceApi |
||||||
|
@Composable |
||||||
|
fun Resource.rememberImageBitmap(): LoadState<ImageBitmap> { |
||||||
|
val state: MutableState<LoadState<ImageBitmap>> = remember(this) { mutableStateOf(LoadState.Loading()) } |
||||||
|
LaunchedEffect(this) { |
||||||
|
state.value = try { |
||||||
|
LoadState.Success(readBytes().toImageBitmap()) |
||||||
|
} catch (e: Exception) { |
||||||
|
LoadState.Error(e) |
||||||
|
} |
||||||
|
} |
||||||
|
return state.value |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* return current ImageBitmap or return empty while loading |
||||||
|
*/ |
||||||
|
@ExperimentalResourceApi |
||||||
|
fun LoadState<ImageBitmap>.orEmpty(): ImageBitmap = when (this) { |
||||||
|
is LoadState.Loading -> emptyImageBitmap |
||||||
|
is LoadState.Success -> this.value |
||||||
|
is LoadState.Error -> emptyImageBitmap |
||||||
|
} |
||||||
|
|
||||||
|
internal expect fun ByteArray.toImageBitmap(): ImageBitmap |
@ -0,0 +1,9 @@ |
|||||||
|
/* |
||||||
|
* Copyright 2020-2022 JetBrains s.r.o. and respective authors and developers. |
||||||
|
* Use of this source code is governed by the Apache 2.0 license that can be found in the LICENSE.txt file. |
||||||
|
*/ |
||||||
|
|
||||||
|
package org.jetbrains.compose.resources |
||||||
|
|
||||||
|
@RequiresOptIn("This API is experimental and is likely to change in the future.") |
||||||
|
annotation class ExperimentalResourceApi |
@ -0,0 +1,38 @@ |
|||||||
|
/* |
||||||
|
* Copyright 2020-2022 JetBrains s.r.o. and respective authors and developers. |
||||||
|
* Use of this source code is governed by the Apache 2.0 license that can be found in the LICENSE.txt file. |
||||||
|
*/ |
||||||
|
|
||||||
|
package org.jetbrains.compose.resources |
||||||
|
|
||||||
|
/** |
||||||
|
* Should implement equals() and hashCode() |
||||||
|
*/ |
||||||
|
@ExperimentalResourceApi |
||||||
|
interface Resource { |
||||||
|
suspend fun readBytes(): ByteArray //todo in future use streaming |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* Get a resource from <sourceSet>/resources (for example, from commonMain/resources). |
||||||
|
*/ |
||||||
|
@ExperimentalResourceApi |
||||||
|
expect fun resource(path: String): Resource |
||||||
|
|
||||||
|
internal expect class MissingResourceException(path: String) |
||||||
|
|
||||||
|
@OptIn(ExperimentalResourceApi::class) |
||||||
|
internal abstract class AbstractResourceImpl(val path: String) : Resource { |
||||||
|
override fun equals(other: Any?): Boolean { |
||||||
|
if (this === other) return true |
||||||
|
return if (other is AbstractResourceImpl) { |
||||||
|
path == other.path |
||||||
|
} else { |
||||||
|
false |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
override fun hashCode(): Int { |
||||||
|
return path.hashCode() |
||||||
|
} |
||||||
|
} |
@ -0,0 +1,23 @@ |
|||||||
|
/* |
||||||
|
* Copyright 2020-2022 JetBrains s.r.o. and respective authors and developers. |
||||||
|
* Use of this source code is governed by the Apache 2.0 license that can be found in the LICENSE.txt file. |
||||||
|
*/ |
||||||
|
|
||||||
|
package org.jetbrains.compose.resources |
||||||
|
|
||||||
|
import kotlin.test.Test |
||||||
|
import kotlin.test.assertEquals |
||||||
|
import kotlin.test.assertNotEquals |
||||||
|
|
||||||
|
@OptIn(ExperimentalResourceApi::class) |
||||||
|
class ResourceTest { |
||||||
|
@Test |
||||||
|
fun testResourceEquals() { |
||||||
|
assertEquals(resource("a"), resource("a")) |
||||||
|
} |
||||||
|
|
||||||
|
@Test |
||||||
|
fun testResourceNotEquals() { |
||||||
|
assertNotEquals(resource("a"), resource("b")) |
||||||
|
} |
||||||
|
} |
@ -0,0 +1,26 @@ |
|||||||
|
/* |
||||||
|
* Copyright 2020-2022 JetBrains s.r.o. and respective authors and developers. |
||||||
|
* Use of this source code is governed by the Apache 2.0 license that can be found in the LICENSE.txt file. |
||||||
|
*/ |
||||||
|
|
||||||
|
package org.jetbrains.compose.resources |
||||||
|
|
||||||
|
import java.io.IOException |
||||||
|
|
||||||
|
@ExperimentalResourceApi |
||||||
|
actual fun resource(path: String): Resource = DesktopResourceImpl(path) |
||||||
|
|
||||||
|
@ExperimentalResourceApi |
||||||
|
private class DesktopResourceImpl(path: String) : AbstractResourceImpl(path) { |
||||||
|
override suspend fun readBytes(): ByteArray { |
||||||
|
val resource = (::DesktopResourceImpl.javaClass.classLoader).getResourceAsStream(path) |
||||||
|
if (resource != null) { |
||||||
|
return resource.readBytes() |
||||||
|
} else { |
||||||
|
throw MissingResourceException(path) |
||||||
|
} |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
internal actual class MissingResourceException actual constructor(path: String) : |
||||||
|
IOException("Missing resource with path: $path") |
@ -0,0 +1,61 @@ |
|||||||
|
package org.jetbrains.compose.resources |
||||||
|
|
||||||
|
import androidx.compose.foundation.Image |
||||||
|
import androidx.compose.runtime.Composable |
||||||
|
import androidx.compose.runtime.collectAsState |
||||||
|
import androidx.compose.runtime.getValue |
||||||
|
import androidx.compose.ui.graphics.ImageBitmap |
||||||
|
import androidx.compose.ui.test.junit4.createComposeRule |
||||||
|
import kotlinx.coroutines.ExperimentalCoroutinesApi |
||||||
|
import kotlinx.coroutines.delay |
||||||
|
import kotlinx.coroutines.flow.MutableStateFlow |
||||||
|
import kotlinx.coroutines.test.UnconfinedTestDispatcher |
||||||
|
import kotlinx.coroutines.test.runTest |
||||||
|
import org.junit.Assert.assertEquals |
||||||
|
import org.junit.Rule |
||||||
|
import org.junit.Test |
||||||
|
|
||||||
|
@OptIn(ExperimentalResourceApi::class, ExperimentalCoroutinesApi::class) |
||||||
|
class ComposeResourceTest { |
||||||
|
|
||||||
|
@get:Rule |
||||||
|
val rule = createComposeRule() |
||||||
|
|
||||||
|
@Test |
||||||
|
fun testMissingResource() = runTest (UnconfinedTestDispatcher()) { |
||||||
|
var recompositionCount = 0 |
||||||
|
rule.setContent { |
||||||
|
CountRecompositions(resource("missing.png").rememberImageBitmap().orEmpty()) { |
||||||
|
recompositionCount++ |
||||||
|
} |
||||||
|
} |
||||||
|
rule.awaitIdle() |
||||||
|
assertEquals(2, recompositionCount) |
||||||
|
} |
||||||
|
|
||||||
|
@Test |
||||||
|
fun testCountRecompositions() = runTest (UnconfinedTestDispatcher()) { |
||||||
|
val mutableStateFlow = MutableStateFlow(true) |
||||||
|
var recompositionCount = 0 |
||||||
|
rule.setContent { |
||||||
|
val state: Boolean by mutableStateFlow.collectAsState(true) |
||||||
|
val resource = resource(if (state) "1.png" else "2.png") |
||||||
|
CountRecompositions(resource.rememberImageBitmap().orEmpty()) { |
||||||
|
recompositionCount++ |
||||||
|
} |
||||||
|
} |
||||||
|
rule.awaitIdle() |
||||||
|
mutableStateFlow.value = false |
||||||
|
rule.awaitIdle() |
||||||
|
assertEquals(4, recompositionCount) |
||||||
|
} |
||||||
|
|
||||||
|
} |
||||||
|
|
||||||
|
@Composable |
||||||
|
private fun CountRecompositions(imageBitmap: ImageBitmap?, onRecomposition: () -> Unit) { |
||||||
|
onRecomposition() |
||||||
|
if (imageBitmap != null) { |
||||||
|
Image(bitmap = imageBitmap, contentDescription = null) |
||||||
|
} |
||||||
|
} |
After Width: | Height: | Size: 946 B |
After Width: | Height: | Size: 785 B |
@ -0,0 +1,37 @@ |
|||||||
|
/* |
||||||
|
* Copyright 2020-2022 JetBrains s.r.o. and respective authors and developers. |
||||||
|
* Use of this source code is governed by the Apache 2.0 license that can be found in the LICENSE.txt file. |
||||||
|
*/ |
||||||
|
|
||||||
|
package org.jetbrains.compose.resources |
||||||
|
|
||||||
|
|
||||||
|
import kotlinx.cinterop.addressOf |
||||||
|
import kotlinx.cinterop.usePinned |
||||||
|
import platform.Foundation.NSBundle |
||||||
|
import platform.Foundation.NSData |
||||||
|
import platform.Foundation.NSFileManager |
||||||
|
import platform.posix.memcpy |
||||||
|
|
||||||
|
@ExperimentalResourceApi |
||||||
|
actual fun resource(path: String): Resource = UIKitResourceImpl(path) |
||||||
|
|
||||||
|
@ExperimentalResourceApi |
||||||
|
private class UIKitResourceImpl(path: String) : AbstractResourceImpl(path) { |
||||||
|
override suspend fun readBytes(): ByteArray { |
||||||
|
val absolutePath = NSBundle.mainBundle.resourcePath + "/" + path |
||||||
|
val contentsAtPath: NSData? = NSFileManager.defaultManager().contentsAtPath(absolutePath) |
||||||
|
if (contentsAtPath != null) { |
||||||
|
val byteArray = ByteArray(contentsAtPath.length.toInt()) |
||||||
|
byteArray.usePinned { |
||||||
|
memcpy(it.addressOf(0), contentsAtPath.bytes, contentsAtPath.length) |
||||||
|
} |
||||||
|
return byteArray |
||||||
|
} else { |
||||||
|
throw MissingResourceException(path) |
||||||
|
} |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
internal actual class MissingResourceException actual constructor(path: String) : |
||||||
|
Exception("Missing resource with path: $path") |
@ -0,0 +1,44 @@ |
|||||||
|
/* |
||||||
|
* Copyright 2020-2022 JetBrains s.r.o. and respective authors and developers. |
||||||
|
* Use of this source code is governed by the Apache 2.0 license that can be found in the LICENSE.txt file. |
||||||
|
*/ |
||||||
|
|
||||||
|
package org.jetbrains.compose.resources |
||||||
|
|
||||||
|
import org.khronos.webgl.ArrayBuffer |
||||||
|
import org.khronos.webgl.Int8Array |
||||||
|
import org.w3c.xhr.ARRAYBUFFER |
||||||
|
import org.w3c.xhr.XMLHttpRequest |
||||||
|
import org.w3c.xhr.XMLHttpRequestResponseType |
||||||
|
import kotlin.coroutines.resume |
||||||
|
import kotlin.coroutines.resumeWithException |
||||||
|
import kotlin.coroutines.suspendCoroutine |
||||||
|
|
||||||
|
@ExperimentalResourceApi |
||||||
|
actual fun resource(path: String): Resource = JSResourceImpl(path) |
||||||
|
|
||||||
|
@ExperimentalResourceApi |
||||||
|
private class JSResourceImpl(path: String) : AbstractResourceImpl(path) { |
||||||
|
override suspend fun readBytes(): ByteArray { |
||||||
|
return suspendCoroutine { continuation -> |
||||||
|
val req = XMLHttpRequest() |
||||||
|
req.open("GET", "/$path", true) |
||||||
|
req.responseType = XMLHttpRequestResponseType.ARRAYBUFFER |
||||||
|
|
||||||
|
req.onload = { event -> |
||||||
|
val arrayBuffer = req.response |
||||||
|
if (arrayBuffer is ArrayBuffer) { |
||||||
|
continuation.resume(arrayBuffer.toByteArray()) |
||||||
|
} else { |
||||||
|
continuation.resumeWithException(MissingResourceException(path)) |
||||||
|
} |
||||||
|
} |
||||||
|
req.send(null) |
||||||
|
} |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
private fun ArrayBuffer.toByteArray() = Int8Array(this, 0, byteLength).unsafeCast<ByteArray>() |
||||||
|
|
||||||
|
internal actual class MissingResourceException actual constructor(path: String) : |
||||||
|
Exception("Missing resource with path: $path") |
@ -0,0 +1,39 @@ |
|||||||
|
/* |
||||||
|
* Copyright 2020-2022 JetBrains s.r.o. and respective authors and developers. |
||||||
|
* Use of this source code is governed by the Apache 2.0 license that can be found in the LICENSE.txt file. |
||||||
|
*/ |
||||||
|
|
||||||
|
package org.jetbrains.compose.resources |
||||||
|
|
||||||
|
import kotlinx.cinterop.addressOf |
||||||
|
import kotlinx.cinterop.usePinned |
||||||
|
import platform.Foundation.NSData |
||||||
|
import platform.Foundation.NSFileManager |
||||||
|
import platform.posix.memcpy |
||||||
|
|
||||||
|
@ExperimentalResourceApi |
||||||
|
actual fun resource(path: String): Resource = MacOSResourceImpl(path) |
||||||
|
|
||||||
|
@ExperimentalResourceApi |
||||||
|
private class MacOSResourceImpl(path: String) : AbstractResourceImpl(path) { |
||||||
|
override suspend fun readBytes(): ByteArray { |
||||||
|
val currentDirectoryPath = NSFileManager.defaultManager().currentDirectoryPath |
||||||
|
val contentsAtPath: NSData? = NSFileManager.defaultManager().run { |
||||||
|
//todo in future bundle resources with app and use all sourceSets (skikoMain, nativeMain) |
||||||
|
contentsAtPath("$currentDirectoryPath/src/macosMain/resources/$path") |
||||||
|
?: contentsAtPath("$currentDirectoryPath/src/commonMain/resources/$path") |
||||||
|
} |
||||||
|
if (contentsAtPath != null) { |
||||||
|
val byteArray = ByteArray(contentsAtPath.length.toInt()) |
||||||
|
byteArray.usePinned { |
||||||
|
memcpy(it.addressOf(0), contentsAtPath.bytes, contentsAtPath.length) |
||||||
|
} |
||||||
|
return byteArray |
||||||
|
} else { |
||||||
|
throw MissingResourceException(path) |
||||||
|
} |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
internal actual class MissingResourceException actual constructor(path: String) : |
||||||
|
Exception("Missing resource with path: $path") |
@ -0,0 +1,13 @@ |
|||||||
|
/* |
||||||
|
* Copyright 2020-2022 JetBrains s.r.o. and respective authors and developers. |
||||||
|
* Use of this source code is governed by the Apache 2.0 license that can be found in the LICENSE.txt file. |
||||||
|
*/ |
||||||
|
|
||||||
|
package org.jetbrains.compose.resources |
||||||
|
|
||||||
|
import androidx.compose.ui.graphics.ImageBitmap |
||||||
|
import androidx.compose.ui.graphics.toComposeImageBitmap |
||||||
|
import org.jetbrains.skia.Image |
||||||
|
|
||||||
|
internal actual fun ByteArray.toImageBitmap(): ImageBitmap = |
||||||
|
Image.makeFromEncoded(this).toComposeImageBitmap() |
@ -0,0 +1,11 @@ |
|||||||
|
#!/bin/bash |
||||||
|
cd "$(dirname "$0")" # Run always in current dir |
||||||
|
set -euo pipefail # Fail fast |
||||||
|
|
||||||
|
# Unit tests |
||||||
|
./gradlew :resources:library:test |
||||||
|
./gradlew :resources:library:desktopTest |
||||||
|
|
||||||
|
# Android integration tests |
||||||
|
./gradlew :resources:library:pixel5DebugAndroidTest |
||||||
|
|
@ -0,0 +1,13 @@ |
|||||||
|
#!/bin/bash |
||||||
|
|
||||||
|
cd "$(dirname "$0")" |
||||||
|
|
||||||
|
if [[ -z "$COMPOSE_CUSTOM_VERSION" ]]; then |
||||||
|
echo "Must provide COMPOSE_CUSTOM_VERSION in environment" 1>&2 |
||||||
|
exit 1 |
||||||
|
fi |
||||||
|
|
||||||
|
pushd ../../components |
||||||
|
./gradlew publishToMavenLocal -Pcompose.version="$COMPOSE_CUSTOM_VERSION" || exit 1 |
||||||
|
popd |
||||||
|
|
Loading…
Reference in new issue