├── .gitignore ├── README.md ├── build.gradle.kts ├── buildSrc ├── build.gradle.kts └── src │ └── main │ └── kotlin │ ├── Dependencies.kt │ └── Versions.kt ├── gradle.properties ├── settings.gradle.kts └── src ├── main ├── kotlin │ └── io │ │ └── sketchware │ │ ├── annotations │ │ └── ExperimentalSWManagerAPI.kt │ │ ├── exceptions │ │ ├── ComponentsNotFoundException.kt │ │ ├── EventAlreadyExistsException.kt │ │ ├── EventNotFoundException.kt │ │ ├── EventsNotFoundException.kt │ │ ├── InvalidMenuArgumentTypeException.kt │ │ ├── LibraryNotFoundException.kt │ │ ├── MoreblockNotFoundException.kt │ │ ├── MoreblocksNotFoundException.kt │ │ ├── SpecFieldArgumentException.kt │ │ ├── ValueRequireException.kt │ │ ├── VariableNameExistException.kt │ │ ├── VariablesNotFoundException.kt │ │ ├── ViewAlreadyExistsException.kt │ │ └── ViewNotFoundException.kt │ │ ├── interfaces │ │ ├── BaseCollectionManager.kt │ │ ├── CollectionItem.kt │ │ ├── ComponentIdProvider.kt │ │ ├── CustomMenusManager.kt │ │ ├── Editor.kt │ │ ├── FileExportable.kt │ │ ├── FileImportable.kt │ │ ├── IdInterface.kt │ │ ├── ListenerNameProvider.kt │ │ ├── Manager.kt │ │ ├── MenuNameProvider.kt │ │ ├── ProjectsManager.kt │ │ └── listeners │ │ │ └── ActionFinishListener.kt │ │ ├── manager │ │ ├── SketchwareManager.kt │ │ ├── SketchwareProManager.kt │ │ ├── SketchwareStudioManager.kt │ │ ├── collections │ │ │ ├── CollectionsManager.kt │ │ │ └── managers │ │ │ │ ├── BlockCollectionManager.kt │ │ │ │ ├── CollectionManager.kt │ │ │ │ ├── FileCollectionManager.kt │ │ │ │ └── MoreblockCollectionManager.kt │ │ ├── customs │ │ │ ├── SketchwareProCustomManager.kt │ │ │ ├── SketchwareStudioCustomManager.kt │ │ │ ├── blocks │ │ │ │ └── SketchwareCustomBlocksManager.kt │ │ │ ├── components │ │ │ │ └── SketchwareCustomComponentsManager.kt │ │ │ ├── listeners │ │ │ │ └── SketchwareCustomListenersManager.kt │ │ │ └── menus │ │ │ │ ├── SketchwareProCustomMenusManager.kt │ │ │ │ └── SketchwareStudioCustomMenusManager.kt │ │ ├── exporters │ │ │ ├── ProjectExporter.kt │ │ │ └── SketchwareProExporter.kt │ │ └── projects │ │ │ ├── SketchwareProProjectsManager.kt │ │ │ ├── SketchwareProjectsManager.kt │ │ │ ├── SketchwareStudioManager.kt │ │ │ ├── data │ │ │ ├── FileManager.kt │ │ │ ├── LibraryManager.kt │ │ │ ├── LogicManager.kt │ │ │ ├── ResourcesManager.kt │ │ │ └── ViewManager.kt │ │ │ └── entities │ │ │ ├── SketchwareProProject.kt │ │ │ ├── SketchwareProject.kt │ │ │ └── SketchwareStudioProject.kt │ │ ├── models │ │ ├── ExportableItem.kt │ │ ├── ProjectType.kt │ │ ├── collections │ │ │ ├── BlockCollectionItem.kt │ │ │ ├── FileCollectionItem.kt │ │ │ └── MoreblockCollectionItem.kt │ │ ├── customs │ │ │ ├── BlockInputMenu.kt │ │ │ ├── CustomBlock.kt │ │ │ ├── CustomBlockGroup.kt │ │ │ ├── CustomComponent.kt │ │ │ ├── CustomEvent.kt │ │ │ ├── CustomListenerGroup.kt │ │ │ ├── CustomMenu.kt │ │ │ ├── MenuData.kt │ │ │ ├── Palette.kt │ │ │ ├── SWCustomSettings.kt │ │ │ └── SWStudioMenu.kt │ │ ├── export │ │ │ ├── ExportConfig.kt │ │ │ ├── ExportFilesConfig.kt │ │ │ └── ExportedCustomFilesConfig.kt │ │ ├── projects │ │ │ ├── ActivityOption.kt │ │ │ ├── ActivityTheme.kt │ │ │ ├── BlockModel.kt │ │ │ ├── ComponentModel.kt │ │ │ ├── FileType.kt │ │ │ ├── KeyboardSetting.kt │ │ │ ├── MenuArgumentType.kt │ │ │ ├── Orientation.kt │ │ │ ├── ProjectConfigModel.kt │ │ │ ├── ProjectFilesLocations.kt │ │ │ ├── ProjectResource.kt │ │ │ ├── ScaleType.kt │ │ │ ├── SketchwareDataFileModel.kt │ │ │ ├── SketchwareEventModel.kt │ │ │ ├── SketchwareLibraryDataModel.kt │ │ │ ├── SketchwareLibraryModel.kt │ │ │ ├── SketchwareMoreblockModel.kt │ │ │ ├── SpecField.kt │ │ │ └── VariableModel.kt │ │ └── view │ │ │ ├── Image.kt │ │ │ ├── Layout.kt │ │ │ ├── LayoutOrientation.kt │ │ │ ├── Text.kt │ │ │ ├── WidgetRoot.kt │ │ │ └── WidgetType.kt │ │ └── utils │ │ ├── CustomProjectImportManager.kt │ │ ├── Exportable.kt │ │ ├── LogicOpCodes.kt │ │ ├── ProjectImportManager.kt │ │ ├── SWConst.kt │ │ ├── SketchwareEncryptor.kt │ │ ├── ViewBuilder.kt │ │ ├── WidgetBuilder.kt │ │ ├── delegates │ │ └── lazyInit.kt │ │ ├── internal │ │ ├── BytesUtil.kt │ │ ├── FileUtil.kt │ │ ├── Json.kt │ │ ├── ListUtils.kt │ │ ├── LogicIdChanger.kt │ │ ├── ProjectCustomExporter.kt │ │ ├── SketchwareProjectsUtil.kt │ │ ├── TagFormatter.kt │ │ └── TextUtils.kt │ │ └── serializers │ │ ├── ActivityOptionSerializer.kt │ │ ├── IdEnumSerializer.kt │ │ ├── ListBlockModelSerializer.kt │ │ ├── PathToFileSerializer.kt │ │ ├── SpecSerializer.kt │ │ ├── StringBooleanSerializer.kt │ │ └── StringNumberConvertor.kt └── resources │ └── logicOpCodes └── test ├── kotlin └── io │ └── sketchware │ └── manager │ ├── collections │ └── managers │ │ ├── BlockCollectionManagerTest.kt │ │ ├── FileCollectionManagerTest.kt │ │ └── MoreblockCollectionManagerTest.kt │ ├── customs │ ├── blocks │ │ └── SketchwareCustomBlocksManagerTest.kt │ ├── components │ │ └── SketchwareCustomComponentsManagerTest.kt │ └── listeners │ │ └── SketchwareCustomListenersManagerTest.kt │ └── projects │ └── data │ ├── FileManagerTest.kt │ ├── LibraryManagerTest.kt │ ├── LogicManagerTest.kt │ ├── ResourcesManagerTest.kt │ └── ViewManagerTest.kt └── resources ├── collections └── moreblocks ├── customs ├── blocks │ ├── block.json │ └── palette.json ├── component.json ├── events.json └── listeners.json └── project └── data └── view /.gitignore: -------------------------------------------------------------------------------- 1 | /shelf/ 2 | /.idea/ 3 | /.gradle/ 4 | /gradle/ 5 | /gradlew 6 | /gradlew.bat 7 | /build/ 8 | /buildSrc/build/ 9 | /local.properties -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | > Development on the library has been suspended due to its uselessness and possible irrelevance for nextgen of Sketchware. Also because of marriage motivation. 2 | 3 | 4 | ![image](https://user-images.githubusercontent.com/32961194/112753044-b9f0a680-8fde-11eb-9af2-a57b10369477.png) 5 | A coroutine-based library for Sketchware Management. Provides the ability to work with projects(its logic, views, and so 6 | on.), collections and custom elements of Sketchware mods as custom components. Mods such as Pro and Studio (CodeGo) are 7 | now supported. 8 | 9 | Why this particular library? 10 | 11 | - **Fast**: Due to the most optimized work with Sketchware (and mods) and Kotlin's coroutines, the library works as 12 | quickly as possible. 13 | - **Easy to use**: Due to coroutines and convenient Kotlin syntax, working with the library is as pleasant as possible. 14 | - **Modern**: SketchwareManager is Kotlin-first and uses modern libraries including Coroutines, Kotlin Serialization. 15 | 16 | ## Implementation 17 | 18 | ```kotlin 19 | repositories { 20 | maven("https://dl.kotlingang.fun") 21 | } 22 | dependencies { 23 | implementation("io.sketchware:Sketchware-Manager:dev-2.4.2") 24 | } 25 | ``` 26 | 27 | ## Quick Start 28 | 29 | To get a list of projects just make next: 30 | 31 | ```kotlin 32 | // let's initialize base manager for sketchware: 33 | val manager = SketchwareManager(sketchwareFolder) 34 | // let's get projects manager from base manager for sketchware 35 | // and get projects: 36 | val projects = manager.projectsManager.getProjects() 37 | // let's print all: 38 | projects.forEach { project: SketchwareProject -> 39 | // get config or current project 40 | val projectConfig = project.getConfig() 41 | // print id of project what was found. 42 | println("Project with id ${projectConfig.projectId} found.") 43 | } 44 | ``` 45 | 46 | Also, we can get some sketchware collections, for example, moreblocks: 47 | 48 | ```kotlin 49 | // let's initialize base manager for sketchware: 50 | val manager = SketchwareManager(sketchwareFolder) 51 | // get moreblocks as List: 52 | val moreblocks = manager.collectionsManager.getMoreblocksManager().all 53 | // let's print moreblocks data: 54 | moreblocks.forEach(::println) 55 | ``` 56 | 57 | Full documentation you can get [here](https://swmanager.kotlingang.fun). 58 | 59 | ## R8 / Proguard 60 | 61 | If you use Proguard, you may need to add rules 62 | for [Coroutines](https://github.com/Kotlin/kotlinx.coroutines/blob/master/kotlinx-coroutines-core/jvm/resources/META-INF/proguard/coroutines.pro) 63 | . 64 | 65 | # 🤙 Contacts 66 | 67 | Sketchware community chat - https://t.me/sketchware_community 68 | 69 | Me in Telegram - https://t.me/y9neon 70 | -------------------------------------------------------------------------------- /build.gradle.kts: -------------------------------------------------------------------------------- 1 | import org.gradle.util.GUtil.loadProperties 2 | 3 | buildscript { 4 | repositories { mavenCentral() } 5 | 6 | dependencies { 7 | 8 | classpath(kotlin("gradle-plugin", version = kotlinVersion)) 9 | classpath(kotlin("serialization", version = kotlinVersion)) 10 | } 11 | } 12 | 13 | plugins { 14 | kotlin("jvm") version kotlinVersion 15 | kotlin("plugin.serialization") version kotlinVersion 16 | `maven-publish` 17 | } 18 | 19 | group = "io.sketchware.manager" 20 | version = libraryVersion 21 | 22 | repositories { 23 | mavenCentral() 24 | } 25 | 26 | dependencies { 27 | implementation(kotlin("stdlib")) 28 | implementation(kotlinSerializationJson) 29 | implementation(kotlinCoroutines) 30 | implementation("org.junit.jupiter:junit-jupiter:5.7.0") 31 | } 32 | 33 | val localProperties = project.rootProject.file("local.properties") 34 | .takeIf(File::exists) 35 | ?.let(::loadProperties) 36 | 37 | tasks.withType { 38 | useJUnitPlatform() 39 | } 40 | 41 | allprojects { 42 | group = "io.sketchware" 43 | version = libraryVersion 44 | 45 | apply(plugin = "maven-publish") 46 | 47 | publishing { 48 | apply(plugin = "maven-publish") 49 | publications { 50 | create("Deploy") { 51 | groupId = group as String 52 | artifactId = "SketchwareManager" 53 | version = libraryVersion 54 | } 55 | } 56 | 57 | repositories { 58 | maven { 59 | name = "sketchware-api" 60 | url = uri(localProperties!!["serverURI"]!!) 61 | credentials { 62 | username = (localProperties["username"] as String?)!! 63 | password = (localProperties["password"] as String?)!! 64 | } 65 | } 66 | } 67 | } 68 | } 69 | 70 | 71 | tasks.withType { 72 | kotlinOptions { 73 | useIR = true 74 | freeCompilerArgs += "-Xopt-in=kotlin.RequiresOptIn" 75 | } 76 | } 77 | -------------------------------------------------------------------------------- /buildSrc/build.gradle.kts: -------------------------------------------------------------------------------- 1 | plugins { 2 | `kotlin-dsl` 3 | } 4 | 5 | repositories { 6 | jcenter() 7 | } -------------------------------------------------------------------------------- /buildSrc/src/main/kotlin/Dependencies.kt: -------------------------------------------------------------------------------- 1 | const val kotlinSerializationJson = 2 | "org.jetbrains.kotlinx:kotlinx-serialization-json:$kotlinSerializationVer" 3 | const val kotlinCoroutines = "org.jetbrains.kotlinx:kotlinx-coroutines-core:$kotlinCoroutinesVersion" -------------------------------------------------------------------------------- /buildSrc/src/main/kotlin/Versions.kt: -------------------------------------------------------------------------------- 1 | const val kotlinSerializationVer = "1.1.0" 2 | const val kotlinCoroutinesVersion = "1.4.3" 3 | const val libraryVersion = "dev-2.4.3" 4 | const val kotlinVersion = "1.4.30" -------------------------------------------------------------------------------- /gradle.properties: -------------------------------------------------------------------------------- 1 | 2 | -------------------------------------------------------------------------------- /settings.gradle.kts: -------------------------------------------------------------------------------- 1 | rootProject.name = "SketchwareManagerLib" 2 | -------------------------------------------------------------------------------- /src/main/kotlin/io/sketchware/annotations/ExperimentalSWManagerAPI.kt: -------------------------------------------------------------------------------- 1 | package io.sketchware.annotations 2 | 3 | /** 4 | * Prepares that the original API can be changed at any time 5 | * or is not stable in operation (for example, if the functionality 6 | * of a particular moment has not been developed or tested to the end). 7 | * Be careful with this. 8 | */ 9 | @Target(AnnotationTarget.CLASS, AnnotationTarget.PROPERTY, AnnotationTarget.FUNCTION, AnnotationTarget.TYPEALIAS) 10 | @RequiresOptIn(level = RequiresOptIn.Level.WARNING) 11 | annotation class ExperimentalSWManagerAPI 12 | -------------------------------------------------------------------------------- /src/main/kotlin/io/sketchware/exceptions/ComponentsNotFoundException.kt: -------------------------------------------------------------------------------- 1 | package io.sketchware.exceptions 2 | 3 | class ComponentsNotFoundException(activityName: String) : 4 | Exception("Components not found for activity $activityName.") -------------------------------------------------------------------------------- /src/main/kotlin/io/sketchware/exceptions/EventAlreadyExistsException.kt: -------------------------------------------------------------------------------- 1 | package io.sketchware.exceptions 2 | 3 | class EventAlreadyExistsException(activity: String, targetId: String, eventName: String) : 4 | Exception("Event for $targetId with name $eventName already exists in $activity.") -------------------------------------------------------------------------------- /src/main/kotlin/io/sketchware/exceptions/EventNotFoundException.kt: -------------------------------------------------------------------------------- 1 | package io.sketchware.exceptions 2 | 3 | class EventNotFoundException(activity: String, targetId: String, eventName: String) : 4 | Exception("Event with name $eventName not found in $activity for $targetId.") -------------------------------------------------------------------------------- /src/main/kotlin/io/sketchware/exceptions/EventsNotFoundException.kt: -------------------------------------------------------------------------------- 1 | package io.sketchware.exceptions 2 | 3 | class EventsNotFoundException(activity: String) : 4 | Exception( 5 | "Events does not exist for activity $activity. " + 6 | "Make sure that you have activity with name $activity or there is contains any event." 7 | ) -------------------------------------------------------------------------------- /src/main/kotlin/io/sketchware/exceptions/InvalidMenuArgumentTypeException.kt: -------------------------------------------------------------------------------- 1 | package io.sketchware.exceptions 2 | 3 | class InvalidMenuArgumentTypeException(argumentName: String) : 4 | Exception( 5 | "The unexpected type menu's argument under the name: $argumentName." + 6 | " Are you sure this is not a custom argument?" 7 | ) -------------------------------------------------------------------------------- /src/main/kotlin/io/sketchware/exceptions/LibraryNotFoundException.kt: -------------------------------------------------------------------------------- 1 | package io.sketchware.exceptions 2 | 3 | class LibraryNotFoundException(libraryName: String) : 4 | Exception("Library with name $libraryName not found.") -------------------------------------------------------------------------------- /src/main/kotlin/io/sketchware/exceptions/MoreblockNotFoundException.kt: -------------------------------------------------------------------------------- 1 | package io.sketchware.exceptions 2 | 3 | class MoreblockNotFoundException(activityName: String, moreblockName: String) : 4 | Exception("Moreblock for name $moreblockName not found in $activityName.") -------------------------------------------------------------------------------- /src/main/kotlin/io/sketchware/exceptions/MoreblocksNotFoundException.kt: -------------------------------------------------------------------------------- 1 | package io.sketchware.exceptions 2 | 3 | class MoreblocksNotFoundException(activityName: String) : 4 | Exception("Moreblocks not found in activity $activityName. Does activity exist?") -------------------------------------------------------------------------------- /src/main/kotlin/io/sketchware/exceptions/SpecFieldArgumentException.kt: -------------------------------------------------------------------------------- 1 | package io.sketchware.exceptions 2 | 3 | class SpecFieldArgumentException(fieldText: String) : 4 | Exception("Spec field isn't an argument. Spec field value: $fieldText") -------------------------------------------------------------------------------- /src/main/kotlin/io/sketchware/exceptions/ValueRequireException.kt: -------------------------------------------------------------------------------- 1 | package io.sketchware.exceptions 2 | 3 | class ValueRequireException(valueName: String, description: String = "") : 4 | Exception("Value for name [$valueName] should be specified. $description") -------------------------------------------------------------------------------- /src/main/kotlin/io/sketchware/exceptions/VariableNameExistException.kt: -------------------------------------------------------------------------------- 1 | package io.sketchware.exceptions 2 | 3 | class VariableNameExistException(activityName: String, variableName: String) : 4 | Exception("Variable with name $variableName already exist in $activityName.") -------------------------------------------------------------------------------- /src/main/kotlin/io/sketchware/exceptions/VariablesNotFoundException.kt: -------------------------------------------------------------------------------- 1 | package io.sketchware.exceptions 2 | 3 | class VariablesNotFoundException(activity: String) : 4 | Exception("Variables not found for activity $activity. Does your activity exist?") -------------------------------------------------------------------------------- /src/main/kotlin/io/sketchware/exceptions/ViewAlreadyExistsException.kt: -------------------------------------------------------------------------------- 1 | package io.sketchware.exceptions 2 | 3 | class ViewAlreadyExistsException(viewName: String, widgetName: String? = null) : 4 | Exception("View with name $viewName " 5 | .plus(widgetName?.let { "and widget name $widgetName" } ?: "") 6 | .plus("already exists.") 7 | ) -------------------------------------------------------------------------------- /src/main/kotlin/io/sketchware/exceptions/ViewNotFoundException.kt: -------------------------------------------------------------------------------- 1 | package io.sketchware.exceptions 2 | 3 | class ViewNotFoundException( 4 | private val filePath: String, 5 | private val viewName: String, 6 | private val widget: String? = null 7 | ) : Exception() { 8 | override val message: String 9 | get() { 10 | return if (widget != null) 11 | "Widget $widget not found in view $viewName at path $filePath." 12 | else "View $viewName not found at path $filePath" 13 | } 14 | } -------------------------------------------------------------------------------- /src/main/kotlin/io/sketchware/interfaces/BaseCollectionManager.kt: -------------------------------------------------------------------------------- 1 | package io.sketchware.interfaces 2 | 3 | interface BaseCollectionManager { 4 | /** 5 | * @return list of all collection items. 6 | */ 7 | val all: List 8 | 9 | /** 10 | * Adds item to collection 11 | * @param entity - Entity to add. 12 | */ 13 | fun addItem(entity: Entity) 14 | 15 | /** 16 | * Removes item which matches [entity]. 17 | */ 18 | fun removeItem(entity: Entity) 19 | 20 | } -------------------------------------------------------------------------------- /src/main/kotlin/io/sketchware/interfaces/CollectionItem.kt: -------------------------------------------------------------------------------- 1 | package io.sketchware.interfaces 2 | 3 | interface CollectionItem { 4 | /** 5 | * Contains name of the item 6 | */ 7 | val name: String 8 | 9 | /** 10 | * Contains data about item. In collections it's a file name. 11 | */ 12 | val data: DataModel 13 | 14 | /** 15 | * Not null only if it moreblock 16 | */ 17 | val reserved1: Reserved1Model? 18 | } 19 | -------------------------------------------------------------------------------- /src/main/kotlin/io/sketchware/interfaces/ComponentIdProvider.kt: -------------------------------------------------------------------------------- 1 | package io.sketchware.interfaces 2 | 3 | fun interface ComponentIdProvider { 4 | fun provide(conflictId: Int): Int 5 | } -------------------------------------------------------------------------------- /src/main/kotlin/io/sketchware/interfaces/CustomMenusManager.kt: -------------------------------------------------------------------------------- 1 | package io.sketchware.interfaces 2 | 3 | import io.sketchware.interfaces.listeners.ActionFinishListener 4 | import io.sketchware.models.customs.CustomMenu 5 | import kotlinx.coroutines.Job 6 | import java.io.File 7 | 8 | interface CustomMenusManager : FileExportable, Editor { 9 | 10 | /** 11 | * @return list of custom menus. 12 | */ 13 | val menus: List 14 | 15 | /** 16 | * Adds custom menu. 17 | * @param menu menu to add 18 | */ 19 | fun addCustomMenu(menu: CustomMenu) 20 | 21 | /** 22 | * Removes custom menu by id. 23 | * @param id menu's string id. 24 | */ 25 | fun removeMenuById(id: String) 26 | 27 | /** 28 | * Edits custom menu. 29 | * @param id menu string id. 30 | * @param builder Lambda with [CustomMenu] in context to edit already exists menu data. 31 | */ 32 | fun editMenu(id: String, builder: CustomMenu.() -> Unit) 33 | 34 | /** 35 | * Edits custom menu. 36 | * @param id menu string id. 37 | * @param menu new menu data. 38 | */ 39 | fun editMenu(id: String, menu: CustomMenu) 40 | 41 | /** 42 | * Imports custom menus from the [file]. 43 | * @param file - file with data about menus. 44 | * @param conflictProvider - provider for conflict names. 45 | * If provider isn't specified, prime menu will be removed. 46 | */ 47 | suspend fun import(file: File, conflictProvider: ((conflictId: String) -> String)? = null) 48 | 49 | /** 50 | * Imports custom menus from the [file]. 51 | * @param file - file with data about menus. 52 | * @param conflictProvider - provider for conflict names. 53 | * If provider isn't specified, prime menu will be removed. 54 | */ 55 | fun import( 56 | file: File, 57 | conflictProvider: ((conflictId: String) -> String)? = null, 58 | callback: ActionFinishListener 59 | ): Job 60 | 61 | } -------------------------------------------------------------------------------- /src/main/kotlin/io/sketchware/interfaces/Editor.kt: -------------------------------------------------------------------------------- 1 | package io.sketchware.interfaces 2 | 3 | import io.sketchware.interfaces.listeners.ActionFinishListener 4 | import kotlinx.coroutines.Job 5 | 6 | interface Editor { 7 | 8 | /** 9 | * Updates data in Editor async. 10 | */ 11 | fun fetch(callback: ActionFinishListener? = null): Job 12 | 13 | /** 14 | * Updates data in Editor. 15 | */ 16 | suspend fun fetch() 17 | 18 | /** 19 | * Saves data which was edited async. 20 | */ 21 | fun save(callback: ActionFinishListener? = null): Job 22 | 23 | /** 24 | * Saves data which was edited. 25 | */ 26 | suspend fun save() 27 | 28 | } -------------------------------------------------------------------------------- /src/main/kotlin/io/sketchware/interfaces/FileExportable.kt: -------------------------------------------------------------------------------- 1 | package io.sketchware.interfaces 2 | 3 | import io.sketchware.interfaces.listeners.ActionFinishListener 4 | import kotlinx.coroutines.Job 5 | import java.io.File 6 | 7 | interface FileExportable { 8 | /** 9 | * Exports data into file. 10 | * @param destination - file to which will be written data. 11 | */ 12 | suspend fun export(destination: File) 13 | 14 | /** 15 | * Exports data into file. 16 | * @param destination - file to which will be written data. 17 | * @param callback - call back when export will be finished. 18 | */ 19 | fun export(destination: File, callback: ActionFinishListener): Job 20 | } -------------------------------------------------------------------------------- /src/main/kotlin/io/sketchware/interfaces/FileImportable.kt: -------------------------------------------------------------------------------- 1 | package io.sketchware.interfaces 2 | 3 | import io.sketchware.interfaces.listeners.ActionFinishListener 4 | import kotlinx.coroutines.Job 5 | import java.io.File 6 | 7 | interface FileImportable { 8 | /** 9 | * Imports data from [file]. 10 | * @param file - File with data to import. 11 | */ 12 | suspend fun import(file: File) 13 | 14 | /** 15 | * Imports data from [file]. 16 | * @param file - File with data to import. 17 | * @param callback - call back when import will be finished. 18 | */ 19 | fun import(file: File, callback: ActionFinishListener): Job 20 | } -------------------------------------------------------------------------------- /src/main/kotlin/io/sketchware/interfaces/IdInterface.kt: -------------------------------------------------------------------------------- 1 | package io.sketchware.interfaces 2 | 3 | /** 4 | * Interface which uses for serialization enums into custom numbers. 5 | */ 6 | interface IdInterface { 7 | /** 8 | * Id which will be serialized/deserialized 9 | */ 10 | val id: Int 11 | } -------------------------------------------------------------------------------- /src/main/kotlin/io/sketchware/interfaces/ListenerNameProvider.kt: -------------------------------------------------------------------------------- 1 | package io.sketchware.interfaces 2 | 3 | fun interface ListenerNameProvider { 4 | fun provide(conflictName: String): String 5 | } -------------------------------------------------------------------------------- /src/main/kotlin/io/sketchware/interfaces/Manager.kt: -------------------------------------------------------------------------------- 1 | package io.sketchware.interfaces 2 | 3 | import io.sketchware.manager.collections.CollectionsManager 4 | 5 | 6 | interface Manager { 7 | /** 8 | * Sketchware projects manager for this instance. 9 | * Responsible for sketchware projects. 10 | */ 11 | val projectsManager: ProjectsManager 12 | 13 | /** 14 | * Collections container with all collection managers: 15 | */ 16 | val collectionsManager: CollectionsManager 17 | } -------------------------------------------------------------------------------- /src/main/kotlin/io/sketchware/interfaces/MenuNameProvider.kt: -------------------------------------------------------------------------------- 1 | package io.sketchware.interfaces 2 | 3 | interface MenuNameProvider { 4 | fun provide(conflictName: String): String 5 | } -------------------------------------------------------------------------------- /src/main/kotlin/io/sketchware/interfaces/ProjectsManager.kt: -------------------------------------------------------------------------------- 1 | package io.sketchware.interfaces 2 | 3 | import io.sketchware.manager.projects.entities.SketchwareProject 4 | 5 | interface ProjectsManager { 6 | /** 7 | * Gets projects. 8 | * @return List of [SketchwareProject]. 9 | */ 10 | suspend fun getProjects(): List 11 | 12 | /** 13 | * Checks next free id and returns it. 14 | * @param startId - Specific id from which function 15 | * will check next free id. By default it is 601. 16 | * @return next free for new project id. 17 | */ 18 | suspend fun nextFreeId(startId: Int): Int 19 | 20 | } -------------------------------------------------------------------------------- /src/main/kotlin/io/sketchware/interfaces/listeners/ActionFinishListener.kt: -------------------------------------------------------------------------------- 1 | package io.sketchware.interfaces.listeners 2 | 3 | fun interface ActionFinishListener { 4 | fun onFinish() 5 | } -------------------------------------------------------------------------------- /src/main/kotlin/io/sketchware/manager/SketchwareManager.kt: -------------------------------------------------------------------------------- 1 | package io.sketchware.manager 2 | 3 | import io.sketchware.interfaces.Manager 4 | import io.sketchware.manager.collections.CollectionsManager 5 | import io.sketchware.manager.projects.SketchwareProjectsManager 6 | import java.io.File 7 | 8 | /** 9 | * Automatically determines what type of Manager 10 | * should be used to work with Sketchware (i.e. it determines whether it is a mod or not). 11 | * Attention: the function does not guarantee that Sketchware can be a mod or not, 12 | * the function checks if there are files created by 13 | * Sketchware Pro / Studio mods (later it will be specified what type of Sketchware) files. 14 | * If you can, avoid this feature as it can be very time consuming. 15 | * 16 | * @param sketchwareFolder - Sketchware root folder. 17 | */ 18 | //suspend fun sketchwareManager(sketchwareFolder: File): Nothing = TODO() 19 | 20 | /** 21 | * Basic class for access with sketchware. 22 | * @param sketchwareFolder - basic sketchware folder (example: /storage/emulated/0/.sketchware). 23 | */ 24 | open class SketchwareManager(private val sketchwareFolder: File) : Manager { 25 | /** 26 | * @param folderPath - path to sketchware folder. 27 | */ 28 | constructor(folderPath: String) : this(File(folderPath)) 29 | 30 | /** 31 | * Sketchware projects manager for this instance. 32 | * Responsible for sketchware projects. 33 | */ 34 | override val projectsManager = SketchwareProjectsManager(sketchwareFolder) 35 | 36 | /** 37 | * The manager responsible for the Sketchware collections. 38 | */ 39 | override val collectionsManager = CollectionsManager(File(sketchwareFolder, "collections")) 40 | 41 | } -------------------------------------------------------------------------------- /src/main/kotlin/io/sketchware/manager/SketchwareProManager.kt: -------------------------------------------------------------------------------- 1 | package io.sketchware.manager 2 | 3 | import io.sketchware.interfaces.Manager 4 | import io.sketchware.manager.collections.CollectionsManager 5 | import io.sketchware.manager.customs.SketchwareProCustomManager 6 | import io.sketchware.manager.projects.SketchwareProProjectsManager 7 | import java.io.File 8 | 9 | class SketchwareProManager( 10 | sketchwareFolder: File 11 | ) : Manager { 12 | /** 13 | * @param folderPath - path to sketchware folder. 14 | */ 15 | constructor(folderPath: String) : this(File(folderPath)) 16 | 17 | /** 18 | * Sketchware projects manager for this instance. 19 | * Responsible for sketchware projects. 20 | */ 21 | override val projectsManager = SketchwareProProjectsManager(sketchwareFolder) 22 | 23 | /** 24 | * Sketchware collections manager. 25 | * Responsible for sketchware collections. 26 | */ 27 | override val collectionsManager = CollectionsManager(File(sketchwareFolder, "collections")) 28 | 29 | /** 30 | * Sketchware Pro Customs Manager. Responsible for sketchware pro customs (components, blocks, etc) 31 | */ 32 | val customsManager = SketchwareProCustomManager( 33 | File(sketchwareFolder, "data/system"), 34 | File(sketchwareFolder, "data/settings.json"), File(sketchwareFolder, "resources/block/Menu Block") 35 | ) 36 | 37 | } -------------------------------------------------------------------------------- /src/main/kotlin/io/sketchware/manager/SketchwareStudioManager.kt: -------------------------------------------------------------------------------- 1 | package io.sketchware.manager 2 | 3 | import io.sketchware.interfaces.Manager 4 | import io.sketchware.manager.collections.CollectionsManager 5 | import io.sketchware.manager.customs.SketchwareStudioCustomManager 6 | import io.sketchware.manager.projects.SketchwareStudioProjectsManager 7 | import java.io.File 8 | 9 | class SketchwareStudioManager( 10 | sketchwareFolder: File 11 | ) : Manager { 12 | 13 | /** 14 | * @param folderPath - path to sketchware folder. 15 | */ 16 | constructor(folderPath: String) : this(File(folderPath)) 17 | 18 | 19 | /** 20 | * Sketchware Studio Customs Manager. Responsible for sketchware studio customs (components, blocks, etc) 21 | */ 22 | val customsManager = SketchwareStudioCustomManager( 23 | File(sketchwareFolder, "data/system"), 24 | File(sketchwareFolder, "data/settings.json"), File(sketchwareFolder, "resources/block/My Block") 25 | ) 26 | 27 | /** 28 | * Sketchware projects manager for this instance. 29 | * Responsible for sketchware projects. 30 | */ 31 | override val projectsManager = SketchwareStudioProjectsManager(sketchwareFolder) 32 | 33 | /** 34 | * Sketchware collections manager. 35 | * Responsible for sketchware collections. 36 | */ 37 | override val collectionsManager = CollectionsManager(File(sketchwareFolder, "collections")) 38 | 39 | } -------------------------------------------------------------------------------- /src/main/kotlin/io/sketchware/manager/collections/CollectionsManager.kt: -------------------------------------------------------------------------------- 1 | package io.sketchware.manager.collections 2 | 3 | import io.sketchware.manager.collections.managers.BlockCollectionManager 4 | import io.sketchware.manager.collections.managers.FileCollectionManager 5 | import io.sketchware.manager.collections.managers.MoreblockCollectionManager 6 | import java.io.File 7 | 8 | class CollectionsManager(private val rootFolder: File) { 9 | 10 | private var moreblocksManager: MoreblockCollectionManager? = null 11 | private var blocksManager: BlockCollectionManager? = null 12 | private var fontsManager: FileCollectionManager? = null 13 | private var soundsManager: FileCollectionManager? = null 14 | private var imagesManager: FileCollectionManager? = null 15 | 16 | /** 17 | * @return instance of [MoreblockCollectionManager]. 18 | */ 19 | suspend fun getMoreblocksManager(): MoreblockCollectionManager { 20 | moreblocksManager = moreblocksManager 21 | ?: MoreblockCollectionManager(File(rootFolder, "more_block/list")) 22 | return moreblocksManager ?: error("manager shouldn't initialized") 23 | } 24 | 25 | /** 26 | * @return instance of [BlockCollectionManager]. 27 | */ 28 | suspend fun getBlocksManager(): BlockCollectionManager { 29 | blocksManager = blocksManager 30 | ?: BlockCollectionManager(File(rootFolder, "block/list")) 31 | return blocksManager ?: error("manager shouldn't initialized") 32 | } 33 | 34 | /** 35 | * @return instance of [FileCollectionManager]. 36 | */ 37 | suspend fun getFontsManager(): FileCollectionManager { 38 | fontsManager = fontsManager 39 | ?: FileCollectionManager(File(rootFolder, "font/list")) 40 | return fontsManager ?: error("manager shouldn't initialized") 41 | } 42 | 43 | /** 44 | * @return instance of [FileCollectionManager]. 45 | */ 46 | suspend fun getSoundsManager(): FileCollectionManager { 47 | soundsManager = soundsManager 48 | ?: FileCollectionManager(File(rootFolder, "sound/list")) 49 | return soundsManager ?: error("manager shouldn't initialized") 50 | } 51 | 52 | /** 53 | * @return instance of [FileCollectionManager]. 54 | */ 55 | suspend fun getImagesManager(): FileCollectionManager { 56 | imagesManager = imagesManager 57 | ?: FileCollectionManager(File(rootFolder, "image/list")) 58 | return imagesManager ?: error("manager shouldn't initialized") 59 | } 60 | 61 | } -------------------------------------------------------------------------------- /src/main/kotlin/io/sketchware/manager/collections/managers/BlockCollectionManager.kt: -------------------------------------------------------------------------------- 1 | package io.sketchware.manager.collections.managers 2 | 3 | import io.sketchware.models.collections.BlockCollectionItem 4 | import io.sketchware.utils.internal.byteArrayToString 5 | import io.sketchware.utils.internal.read 6 | import java.io.File 7 | 8 | class BlockCollectionManager( 9 | value: String, 10 | file: File 11 | ) : CollectionManager(value, file, BlockCollectionItem.serializer()) { 12 | companion object { 13 | suspend operator fun invoke(file: File) = 14 | BlockCollectionManager(file.read().byteArrayToString(), file) 15 | } 16 | } -------------------------------------------------------------------------------- /src/main/kotlin/io/sketchware/manager/collections/managers/CollectionManager.kt: -------------------------------------------------------------------------------- 1 | package io.sketchware.manager.collections.managers 2 | 3 | import io.sketchware.interfaces.BaseCollectionManager 4 | import io.sketchware.interfaces.Editor 5 | import io.sketchware.interfaces.listeners.ActionFinishListener 6 | import io.sketchware.utils.SketchwareEncryptor.decrypt 7 | import io.sketchware.utils.SketchwareEncryptor.encrypt 8 | import io.sketchware.utils.delegates.lazyInit 9 | import io.sketchware.utils.internal.* 10 | import kotlinx.coroutines.CoroutineScope 11 | import kotlinx.coroutines.Dispatchers 12 | import kotlinx.coroutines.launch 13 | import kotlinx.serialization.KSerializer 14 | import java.io.File 15 | 16 | open class CollectionManager( 17 | private var value: String, 18 | private val file: File, 19 | private val serializer: KSerializer 20 | ) : BaseCollectionManager, Editor, CoroutineScope { 21 | 22 | private val allProperty = lazyInit { 23 | TagFormatter.parseAsArray(value, serializer) 24 | } 25 | 26 | /** 27 | * @return list of all collection items. 28 | */ 29 | override val all: List by allProperty 30 | 31 | /** 32 | * Adds item to collection 33 | * @param entity - Entity to add. 34 | */ 35 | override fun addItem(entity: Item) = saveItems( 36 | all.toMutableList().apply { add(entity) } 37 | ) 38 | 39 | /** 40 | * Removes item which matches [entity]. 41 | */ 42 | override fun removeItem(entity: Item) = saveItems( 43 | all.toMutableList().apply { remove(entity) } 44 | ) 45 | 46 | /** 47 | * Saves [items] locally to [value]. 48 | */ 49 | private fun saveItems(items: List) { 50 | value = items.joinToString("\n") { it.deserialize(serializer) } 51 | allProperty.reset() 52 | } 53 | 54 | /** 55 | * Updates data in Editor async. 56 | */ 57 | override fun fetch(callback: ActionFinishListener?) = launch { 58 | fetch() 59 | callback?.onFinish() 60 | } 61 | 62 | /** 63 | * Updates data in Editor. 64 | */ 65 | override suspend fun fetch() { 66 | value = file.read().decrypt().byteArrayToString() 67 | allProperty.reset() 68 | } 69 | 70 | /** 71 | * Saves data which was edited async. 72 | */ 73 | override fun save(callback: ActionFinishListener?) = launch { 74 | save() 75 | callback?.onFinish() 76 | } 77 | 78 | /** 79 | * Saves data which was edited. 80 | */ 81 | override suspend fun save() { 82 | file.write(value.toByteArray().encrypt()) 83 | } 84 | 85 | override val coroutineContext = Dispatchers.Main 86 | 87 | } -------------------------------------------------------------------------------- /src/main/kotlin/io/sketchware/manager/collections/managers/FileCollectionManager.kt: -------------------------------------------------------------------------------- 1 | package io.sketchware.manager.collections.managers 2 | 3 | import io.sketchware.models.collections.FileCollectionItem 4 | import io.sketchware.utils.internal.byteArrayToString 5 | import io.sketchware.utils.internal.read 6 | import java.io.File 7 | 8 | class FileCollectionManager( 9 | value: String, 10 | private val file: File 11 | ) : CollectionManager(value, file, FileCollectionItem.serializer()) { 12 | 13 | companion object { 14 | suspend operator fun invoke(file: File) = 15 | FileCollectionManager(file.read().byteArrayToString(), file) 16 | } 17 | 18 | /** 19 | * Gets file by [name]. 20 | */ 21 | @Suppress("unused") 22 | fun getFile(name: String) = File(file, "data/$name") 23 | } -------------------------------------------------------------------------------- /src/main/kotlin/io/sketchware/manager/collections/managers/MoreblockCollectionManager.kt: -------------------------------------------------------------------------------- 1 | package io.sketchware.manager.collections.managers 2 | 3 | import io.sketchware.models.collections.MoreblockCollectionItem 4 | import io.sketchware.utils.internal.byteArrayToString 5 | import io.sketchware.utils.internal.read 6 | import java.io.File 7 | 8 | 9 | class MoreblockCollectionManager( 10 | value: String, 11 | file: File 12 | ) : CollectionManager(value, file, MoreblockCollectionItem.serializer()) { 13 | companion object { 14 | suspend operator fun invoke(file: File) = 15 | MoreblockCollectionManager(file.read().byteArrayToString(), file) 16 | } 17 | } -------------------------------------------------------------------------------- /src/main/kotlin/io/sketchware/manager/customs/SketchwareProCustomManager.kt: -------------------------------------------------------------------------------- 1 | package io.sketchware.manager.customs 2 | 3 | import io.sketchware.interfaces.CustomMenusManager 4 | import io.sketchware.manager.customs.blocks.SketchwareCustomBlocksManager 5 | import io.sketchware.manager.customs.components.SketchwareCustomComponentsManager 6 | import io.sketchware.manager.customs.listeners.SketchwareCustomListenersManager 7 | import io.sketchware.manager.customs.menus.SketchwareProCustomMenusManager 8 | import io.sketchware.models.customs.SWCustomSettings 9 | import io.sketchware.utils.internal.byteArrayToString 10 | import io.sketchware.utils.internal.readOrNull 11 | import io.sketchware.utils.internal.serialize 12 | import java.io.File 13 | 14 | open class SketchwareProCustomManager( 15 | internal open val dataSystemFolder: File, 16 | internal open val dataSettingsFile: File, 17 | internal open val menuFolder: File 18 | ) { 19 | 20 | private var config: SWCustomSettings? = null 21 | 22 | private var componentManager: SketchwareCustomComponentsManager? = null 23 | private var blocksManager: SketchwareCustomBlocksManager? = null 24 | private var listenersManager: SketchwareCustomListenersManager? = null 25 | 26 | private var menusManager: SketchwareProCustomMenusManager? = null 27 | 28 | private suspend fun getConfig(): SWCustomSettings { 29 | config = config ?: dataSettingsFile.readOrNull()?.byteArrayToString()?.serialize() 30 | return config ?: error("Config should be initialized.") 31 | } 32 | 33 | /** 34 | * @return instance of Custom Component Manager. 35 | */ 36 | open suspend fun getComponentManager(): SketchwareCustomComponentsManager { 37 | componentManager = componentManager 38 | ?: SketchwareCustomComponentsManager(File(dataSystemFolder, "component.json")) 39 | return componentManager ?: error("componentManager should be initialized.") 40 | } 41 | 42 | /** 43 | * @return instance of Custom Blocks Manager. 44 | */ 45 | open suspend fun getBlocksManager(): SketchwareCustomBlocksManager { 46 | blocksManager = blocksManager 47 | ?: SketchwareCustomBlocksManager(getConfig().blockFile, getConfig().paletteFile) 48 | return blocksManager ?: error("blocksManager should be initialized.") 49 | } 50 | 51 | open suspend fun getMenusManager(): CustomMenusManager { 52 | menusManager = menusManager 53 | ?: SketchwareProCustomMenusManager( 54 | File(menuFolder, "block.json"), File(menuFolder, "data.json") 55 | ) 56 | return menusManager ?: error("menusManager should be initialized") 57 | } 58 | 59 | suspend fun getListenersManager(): SketchwareCustomListenersManager { 60 | listenersManager = listenersManager 61 | ?: SketchwareCustomListenersManager( 62 | File(dataSystemFolder, "events.json"), File(dataSystemFolder, "listeners.json") 63 | ) 64 | return listenersManager ?: error("listenersManager should be initialized") 65 | } 66 | 67 | } -------------------------------------------------------------------------------- /src/main/kotlin/io/sketchware/manager/customs/SketchwareStudioCustomManager.kt: -------------------------------------------------------------------------------- 1 | package io.sketchware.manager.customs 2 | 3 | import io.sketchware.interfaces.CustomMenusManager 4 | import io.sketchware.manager.customs.menus.SketchwareStudioCustomMenusManager 5 | import java.io.File 6 | 7 | class SketchwareStudioCustomManager( 8 | override val dataSystemFolder: File, 9 | override val dataSettingsFile: File, 10 | override val menuFolder: File 11 | ) : SketchwareProCustomManager(dataSystemFolder, dataSettingsFile, menuFolder) { 12 | 13 | private var menusManager: SketchwareStudioCustomMenusManager? = null 14 | 15 | override suspend fun getMenusManager(): CustomMenusManager { 16 | menusManager = menusManager ?: SketchwareStudioCustomMenusManager( 17 | File(menuFolder, "menu.json") 18 | ) 19 | return menusManager ?: error("menusManager should be initialized.") 20 | } 21 | } -------------------------------------------------------------------------------- /src/main/kotlin/io/sketchware/manager/customs/blocks/SketchwareCustomBlocksManager.kt: -------------------------------------------------------------------------------- 1 | package io.sketchware.manager.customs.blocks 2 | 3 | import io.sketchware.interfaces.Editor 4 | import io.sketchware.interfaces.FileExportable 5 | import io.sketchware.interfaces.FileImportable 6 | import io.sketchware.interfaces.listeners.ActionFinishListener 7 | import io.sketchware.models.customs.CustomBlock 8 | import io.sketchware.models.customs.CustomBlockGroup 9 | import io.sketchware.models.customs.Palette 10 | import io.sketchware.utils.delegates.lazyInit 11 | import io.sketchware.utils.internal.* 12 | import kotlinx.coroutines.CoroutineScope 13 | import kotlinx.coroutines.Dispatchers 14 | import kotlinx.coroutines.launch 15 | import kotlinx.coroutines.withContext 16 | import java.io.File 17 | 18 | class SketchwareCustomBlocksManager( 19 | private var blockValue: String, 20 | private var paletteValue: String, 21 | private val blockFile: File, 22 | private val paletteFile: File 23 | ) : CoroutineScope, Editor, FileExportable, FileImportable { 24 | 25 | companion object { 26 | suspend operator fun invoke(blockFile: File, paletteFile: File) = 27 | SketchwareCustomBlocksManager( 28 | blockFile.read().byteArrayToString(), paletteFile.read().byteArrayToString(), 29 | blockFile, paletteFile 30 | ) 31 | } 32 | 33 | override val coroutineContext = Dispatchers.IO 34 | 35 | private val blocksProperty = lazyInit { 36 | val blocks = blockValue.serialize>() 37 | val palettes = paletteValue.serialize>() 38 | return@lazyInit palettes.mapIndexed { index, palette -> 39 | val id = index + 9 // The first group of blocks has a digital identifier with the number 9. 40 | CustomBlockGroup( 41 | id, 42 | palette.name, 43 | palette.color, 44 | blocks.filter { it.palette == id } 45 | ) 46 | } 47 | } 48 | 49 | /** 50 | * @return list of custom blocks 51 | */ 52 | val blocks by blocksProperty 53 | 54 | /** 55 | * Checks the availability of id between 9 (the initial identifier of any group of custom blocks) 56 | * and the last, if there are no free identifiers, it will return the most recent + 1. 57 | * @return free custom group id. 58 | */ 59 | val freeId 60 | get() = blocks.freeBetweenOrDefault( 61 | 9, blocks.size + 9, blocks.size + 9 + 1, transformer = CustomBlockGroup::groupId 62 | ) 63 | 64 | /** 65 | * Resets all existing identifiers to their primary view (first group - 9, all others +1). 66 | * It is used to fix problems when saving (since in reality, Sketchware Pro binds blocks at index + 9, 67 | * when you delete a group, the blocks can move to another group). Always called in [save], [export]. 68 | */ 69 | fun trimIds() = saveBlocks(blocks.mapIndexed { index, customBlockGroup -> 70 | customBlockGroup.groupId = index + 9 71 | return@mapIndexed customBlockGroup 72 | }) 73 | 74 | /** 75 | * Adds block to the resources. 76 | * @param blockGroup group to add. 77 | */ 78 | fun addBlocksGroup(blockGroup: CustomBlockGroup) = saveBlocks( 79 | blocks.toMutableList().apply { add(blockGroup) } 80 | ) 81 | 82 | /** 83 | * Removes group of blocks. 84 | * @param blockGroupId Custom block group id. 85 | */ 86 | fun removeBlocksGroup(blockGroupId: Int) = saveBlocks( 87 | blocks.toMutableList().apply { 88 | removeIf { 89 | it.groupId == blockGroupId 90 | } 91 | } 92 | ) 93 | 94 | /** 95 | * Removes block from the resources. 96 | * @param groupId Custom block group id. 97 | * @param name Custom block name. 98 | */ 99 | fun removeBlock(groupId: Int, name: String) { 100 | val list = blocks.toMutableList() 101 | val group = list.first { it.groupId == groupId } 102 | val newBlocks = group.blocks.toMutableList().apply { 103 | removeIf { it.palette == groupId && it.name == name } 104 | } 105 | saveBlocks(list.apply { 106 | set(indexOf(group), group.copy(blocks = newBlocks)) 107 | }) 108 | } 109 | 110 | fun editBlocksGroup(groupId: Int, builder: (CustomBlockGroup) -> Unit) = saveBlocks( 111 | blocks.toMutableList().apply { 112 | val group = first { it.groupId == groupId } 113 | set(indexOf(group), group.apply(builder)) 114 | } 115 | ) 116 | 117 | /** 118 | * Adds block to specific group. 119 | * @param groupId group id to which block will be added. 120 | * @param block block to add. 121 | */ 122 | fun addBlockToGroup(groupId: Int, block: CustomBlock) { 123 | val list = blocks.toMutableList() 124 | val group = list.first { it.groupId == groupId } 125 | val newBlocks = group.blocks.toMutableList().apply { 126 | add(block) 127 | } 128 | saveBlocks(list.apply { 129 | set(indexOf(group), group.copy(blocks = newBlocks)) 130 | }) 131 | } 132 | 133 | private fun saveBlocks(list: List) { 134 | val allBlocks = mutableListOf() 135 | val allPalettes = mutableListOf() 136 | list.forEach { 137 | allBlocks.addAll(it.blocks) 138 | allPalettes.add(Palette(it.hexColor, it.name)) 139 | } 140 | blockValue = allBlocks.deserialize() 141 | paletteValue = allPalettes.deserialize() 142 | blocksProperty.reset() 143 | } 144 | 145 | /** 146 | * Updates data in Editor async. 147 | */ 148 | override fun fetch(callback: ActionFinishListener?) = launch { 149 | fetch() 150 | callback?.onFinish() 151 | } 152 | 153 | /** 154 | * Updates data in Editor. 155 | */ 156 | override suspend fun fetch() { 157 | blockValue = blockFile.read().byteArrayToString() 158 | paletteValue = paletteFile.read().byteArrayToString() 159 | blocksProperty.reset() 160 | } 161 | 162 | /** 163 | * Saves data which was edited async. 164 | */ 165 | override fun save(callback: ActionFinishListener?) = launch { 166 | save() 167 | callback?.onFinish() 168 | } 169 | 170 | /** 171 | * Saves data which was edited. 172 | */ 173 | override suspend fun save() { 174 | withContext(Dispatchers.Default) { trimIds() } 175 | blockFile.write(blockValue.toByteArray()) 176 | paletteFile.write(paletteValue.toByteArray()) 177 | } 178 | 179 | /** 180 | * Exports list of custom blocks into file. 181 | * @param destination - file to which will be written data. 182 | */ 183 | override suspend fun export(destination: File) { 184 | withContext(Dispatchers.Default) { trimIds() } 185 | destination.write(blocks.deserialize().toByteArray()) 186 | } 187 | 188 | /** 189 | * Exports list of custom blocks into file. 190 | * @param destination - file to which will be written data. 191 | * @param callback - call back when export will be finished. 192 | */ 193 | override fun export(destination: File, callback: ActionFinishListener) = launch { 194 | export(destination) 195 | callback.onFinish() 196 | } 197 | 198 | /** 199 | * Imports data from [file]. 200 | * @param file - File with data to import. 201 | */ 202 | override suspend fun import(file: File) = 203 | saveBlocks( 204 | blocks.toMutableList().plus(file.read().byteArrayToString() 205 | .serialize>().map { 206 | it.groupId = freeId 207 | return@map it 208 | }) 209 | ).also { blocksProperty.reset() } 210 | 211 | /** 212 | * Imports data from [file]. 213 | * @param file - File with data to import. 214 | * @param callback - call back when import will be finished. 215 | */ 216 | override fun import(file: File, callback: ActionFinishListener) = launch { 217 | import(file) 218 | callback.onFinish() 219 | } 220 | } -------------------------------------------------------------------------------- /src/main/kotlin/io/sketchware/manager/customs/components/SketchwareCustomComponentsManager.kt: -------------------------------------------------------------------------------- 1 | package io.sketchware.manager.customs.components 2 | 3 | import io.sketchware.interfaces.Editor 4 | import io.sketchware.interfaces.FileExportable 5 | import io.sketchware.interfaces.listeners.ActionFinishListener 6 | import io.sketchware.models.customs.CustomComponent 7 | import io.sketchware.utils.delegates.lazyInit 8 | import io.sketchware.utils.internal.* 9 | import kotlinx.coroutines.CoroutineScope 10 | import kotlinx.coroutines.Dispatchers 11 | import kotlinx.coroutines.launch 12 | import java.io.File 13 | 14 | class SketchwareCustomComponentsManager( 15 | private var value: String, 16 | private val file: File 17 | ) : CoroutineScope, Editor, FileExportable { 18 | override val coroutineContext = Dispatchers.IO 19 | 20 | companion object { 21 | suspend operator fun invoke(file: File) = 22 | SketchwareCustomComponentsManager(file.read().byteArrayToString(), file) 23 | } 24 | 25 | private val componentProperty = lazyInit { value.serialize>() } 26 | 27 | /** 28 | * @return List of [CustomComponent] with data about component. 29 | */ 30 | val components by componentProperty 31 | 32 | /** 33 | * Adds component to custom components list. 34 | */ 35 | fun addComponent(component: CustomComponent) = saveComponents( 36 | components.toMutableList().apply { 37 | add(component) 38 | } 39 | ) 40 | 41 | /** 42 | * Removes component from custom components list. 43 | */ 44 | fun removeComponent(component: CustomComponent) = saveComponents( 45 | components.toMutableList().apply { 46 | remove(component) 47 | } 48 | ) 49 | 50 | private fun saveComponents(list: List) { 51 | value = list.deserialize() 52 | componentProperty.reset() 53 | } 54 | 55 | /** 56 | * Updates data in Editor async. 57 | */ 58 | override fun fetch(callback: ActionFinishListener?) = launch { 59 | fetch() 60 | callback?.onFinish() 61 | } 62 | 63 | /** 64 | * Updates data in Editor. 65 | */ 66 | override suspend fun fetch() { 67 | value = file.read().byteArrayToString() 68 | componentProperty.reset() 69 | } 70 | 71 | /** 72 | * Saves data which was edited async. 73 | */ 74 | override fun save(callback: ActionFinishListener?) = launch { 75 | save() 76 | callback?.onFinish() 77 | } 78 | 79 | /** 80 | * Saves data which was edited. 81 | */ 82 | override suspend fun save() { 83 | file.write(value.toByteArray()) 84 | } 85 | 86 | /** 87 | * Imports data from [file]. 88 | * @param file - File with data to import. 89 | * @param idProvider - Provider for already exist ids. 90 | * If not specified, already exist will be removed and replaced with new one. 91 | */ 92 | suspend fun import( 93 | file: File, idProvider: ((conflictedId: Int) -> Int)? = null 94 | ) { 95 | val list = file.read().byteArrayToString().serialize>() 96 | return run { 97 | val allComponents = components.toMutableList() 98 | list.forEach { iComponent -> 99 | if (allComponents.any { it.id == iComponent.id }) { 100 | val currentId = iComponent.id 101 | iComponent.id = idProvider?.invoke(iComponent.id) ?: iComponent.id 102 | if (currentId == iComponent.id) 103 | allComponents.removeIf { it.id == currentId } 104 | } 105 | saveComponents(list.toMutableList().plus(allComponents)) 106 | componentProperty.reset() 107 | } 108 | } 109 | } 110 | 111 | /** 112 | * Imports data from [file]. 113 | * @param file - File with data to import. 114 | * @param callback - call back when import will be finished. 115 | */ 116 | fun import(file: File, idProvider: (conflictedId: Int) -> Int, callback: ActionFinishListener) = launch { 117 | import(file, idProvider) 118 | callback.onFinish() 119 | } 120 | 121 | /** 122 | * Exports data into file. 123 | * @param destination - file to which will be written data. 124 | */ 125 | override suspend fun export(destination: File) { 126 | destination.write(components.deserialize().toByteArray()) 127 | } 128 | 129 | /** 130 | * Exports data into file. 131 | * @param destination - file to which will be written data. 132 | * @param callback - call back when export will be finished. 133 | */ 134 | override fun export(destination: File, callback: ActionFinishListener) = launch { 135 | export(destination) 136 | callback.onFinish() 137 | } 138 | } -------------------------------------------------------------------------------- /src/main/kotlin/io/sketchware/manager/customs/listeners/SketchwareCustomListenersManager.kt: -------------------------------------------------------------------------------- 1 | package io.sketchware.manager.customs.listeners 2 | 3 | import io.sketchware.interfaces.Editor 4 | import io.sketchware.interfaces.FileExportable 5 | import io.sketchware.interfaces.listeners.ActionFinishListener 6 | import io.sketchware.models.customs.CustomEvent 7 | import io.sketchware.models.customs.CustomListenerGroup 8 | import io.sketchware.utils.delegates.lazyInit 9 | import io.sketchware.utils.internal.* 10 | import io.sketchware.utils.serializers.toSpecFields 11 | import kotlinx.coroutines.CoroutineScope 12 | import kotlinx.coroutines.Dispatchers 13 | import kotlinx.coroutines.launch 14 | import java.io.File 15 | 16 | class SketchwareCustomListenersManager( 17 | private var eventsValue: String, 18 | private var listenersValue: String, 19 | private val eventsFile: File, 20 | private val listenersFile: File 21 | ) : CoroutineScope, Editor, FileExportable { 22 | override val coroutineContext = Dispatchers.IO 23 | 24 | companion object { 25 | suspend operator fun invoke( 26 | eventsFile: File, 27 | listenersFile: File 28 | ) = SketchwareCustomListenersManager( 29 | eventsFile.read().byteArrayToString(), listenersFile.read().byteArrayToString(), 30 | eventsFile, listenersFile 31 | ) 32 | } 33 | 34 | private val activityListenerMap by lazy { 35 | mapOf( 36 | "name" to "", "s" to "true", "imports" to "", "code" to "" 37 | ) 38 | } 39 | 40 | private val listenerProperty = lazyInit { 41 | val events = eventsValue.serialize>>() 42 | val listeners = listenersValue.serialize>>().plus(activityListenerMap) 43 | return@lazyInit listeners.map { 44 | CustomListenerGroup( 45 | it.getValue("name"), it.getValue("s").toBoolean(), 46 | it.getValue("imports"), it.getValue("code"), events.filter { eventMap -> 47 | it.getValue("name") == eventMap.getValue("listener") 48 | }.map { map -> 49 | CustomEvent( 50 | map.getValue("headerSpec").toSpecFields(), map.getValue("icon").toInt(), 51 | map.getValue("var"), map.getValue("description"), map.getValue("parameters"), 52 | map.getValue("name"), map.getValue("code") 53 | ) 54 | }) 55 | } 56 | } 57 | 58 | /** 59 | * @return list of listeners group. 60 | */ 61 | val listeners by listenerProperty 62 | 63 | /** 64 | * Adds listener group. 65 | * @param group - group to save. 66 | */ 67 | fun addListenerGroup(group: CustomListenerGroup) = saveLocally( 68 | listeners.toMutableList().apply { add(group) } 69 | ) 70 | 71 | /** 72 | * Removes listener group by [name]. 73 | */ 74 | fun removeListenerGroup(name: String) = saveLocally( 75 | listeners.toMutableList().apply { 76 | removeIf { it.name == name } 77 | } 78 | ) 79 | 80 | /** 81 | * Edits listener group. 82 | */ 83 | fun editListenerGroup( 84 | name: String, 85 | editor: (CustomListenerGroup) -> Unit 86 | ) = saveLocally(listeners.toMutableList().apply { 87 | val index = indexOfFirst { it.name == name } 88 | set(index, get(index).apply(editor)) 89 | }) 90 | 91 | private fun saveLocally(list: List) = synchronized(this) { 92 | listenersValue = list 93 | .filter { it.name != "" } 94 | .map { 95 | mapOf( 96 | "name" to it.name, 97 | "code" to it.code, 98 | "s" to it.independent.toString(), 99 | "imports" to it.customImport 100 | ) 101 | }.deserialize() 102 | eventsValue = list.map { group -> 103 | group.events.map { 104 | mapOf( 105 | "headerSpec" to it.spec.deserialize(), 106 | "icon" to it.iconId.toString(), 107 | "var" to it.id, 108 | "description" to it.description, 109 | "parameters" to it.parameters, 110 | "name" to it.name, 111 | "code" to it.code, 112 | "listener" to group.name 113 | ) 114 | } 115 | }.flatten().deserialize() 116 | listenerProperty.reset() 117 | } 118 | 119 | /** 120 | * Updates data in Editor async. 121 | */ 122 | override fun fetch(callback: ActionFinishListener?) = launch { 123 | fetch() 124 | callback?.onFinish() 125 | } 126 | 127 | /** 128 | * Updates data in Editor. 129 | */ 130 | override suspend fun fetch() { 131 | eventsValue = eventsFile.read().byteArrayToString() 132 | listenersValue = listenersFile.read().byteArrayToString() 133 | listenerProperty.reset() 134 | } 135 | 136 | /** 137 | * Saves data which was edited async. 138 | */ 139 | override fun save(callback: ActionFinishListener?) = launch { 140 | save() 141 | callback?.onFinish() 142 | } 143 | 144 | /** 145 | * Saves data which was edited. 146 | */ 147 | override suspend fun save() { 148 | eventsFile.write(eventsValue.toByteArray()) 149 | } 150 | 151 | /** 152 | * Imports Sketchware Custom Listeners 153 | * @param file - custom listeners file destination. 154 | * @param listenerNameProvider - provider for name conflicts. 155 | * If provider won't be specified, exists listener in current scope will be deleted. 156 | * @param callback - will be invoked when action will be finished. 157 | */ 158 | fun import( 159 | file: File, 160 | listenerNameProvider: ((String) -> String)? = null, 161 | callback: ActionFinishListener? = null 162 | ) = launch { 163 | import(file, listenerNameProvider) 164 | callback?.onFinish() 165 | } 166 | 167 | /** 168 | * Imports Sketchware Custom Listeners 169 | * @param file - custom listeners file destination. 170 | * @param listenerNameProvider - provider for name conflicts. 171 | * If provider won't be specified, exists listener in current scope will be deleted. 172 | */ 173 | suspend fun import(file: File, listenerNameProvider: ((String) -> String)? = null) { 174 | val imports = file.read().byteArrayToString().serialize>() 175 | val allListeners = listeners.toMutableList() 176 | imports.forEach { newGroup -> 177 | allListeners.find { it.name == newGroup.name }?.let { group -> 178 | if (group != newGroup) { 179 | newGroup.name = listenerNameProvider?.invoke(group.name) ?: group.name.also { 180 | allListeners.remove(group) 181 | } 182 | } 183 | } 184 | } 185 | saveLocally(allListeners.plus(imports)) 186 | listenerProperty.reset() 187 | } 188 | 189 | /** 190 | * Exports data into file. 191 | * @param destination - file to which will be written data. 192 | */ 193 | override suspend fun export(destination: File) { 194 | destination.write(listeners.deserialize().toByteArray()) 195 | } 196 | 197 | /** 198 | * Exports data into file. 199 | * @param destination - file to which will be written data. 200 | * @param callback - call back when export will be finished. 201 | */ 202 | override fun export(destination: File, callback: ActionFinishListener) = launch { 203 | export(destination) 204 | callback.onFinish() 205 | } 206 | } -------------------------------------------------------------------------------- /src/main/kotlin/io/sketchware/manager/customs/menus/SketchwareProCustomMenusManager.kt: -------------------------------------------------------------------------------- 1 | package io.sketchware.manager.customs.menus 2 | 3 | import io.sketchware.interfaces.CustomMenusManager 4 | import io.sketchware.interfaces.Editor 5 | import io.sketchware.interfaces.listeners.ActionFinishListener 6 | import io.sketchware.models.customs.BlockInputMenu 7 | import io.sketchware.models.customs.CustomMenu 8 | import io.sketchware.models.customs.MenuData 9 | import io.sketchware.utils.delegates.lazyInit 10 | import io.sketchware.utils.internal.* 11 | import kotlinx.coroutines.CoroutineScope 12 | import kotlinx.coroutines.Dispatchers 13 | import kotlinx.coroutines.launch 14 | import java.io.File 15 | 16 | class SketchwareProCustomMenusManager( 17 | private var menuBlockValue: String, 18 | private var menuDataValue: String, 19 | private val menuBlockFile: File, 20 | private val menuDataFile: File 21 | ) : CustomMenusManager, Editor, CoroutineScope { 22 | 23 | companion object { 24 | suspend operator fun invoke(menuBlockFile: File, menuDataFile: File) = 25 | SketchwareProCustomMenusManager( 26 | menuBlockFile.read().byteArrayToString(), menuDataFile.read().byteArrayToString(), 27 | menuBlockFile, menuDataFile 28 | ) 29 | } 30 | 31 | private val menusProperty = lazyInit { 32 | val menus = menuBlockValue.serialize>() 33 | val menusData = menuDataValue.serialize>>() 34 | 35 | return@lazyInit menusData.keys.toList().map { key -> 36 | val menu = menusData[key]!! 37 | val values = menu.map { it.value.split("+") }.flatten() 38 | 39 | return@map CustomMenu( 40 | menus.map(BlockInputMenu::id).first(key::equals), 41 | menus.find { key == it.id }?.name 42 | ?: error("Unexpected error while getting custom menu. Name is unspecified."), 43 | menu[0].title, 44 | values 45 | ) 46 | } 47 | } 48 | 49 | /** 50 | * @return list of custom menus. 51 | */ 52 | override val menus by menusProperty 53 | 54 | /** 55 | * Adds custom menu. 56 | * @param menu menu to add 57 | */ 58 | override fun addCustomMenu(menu: CustomMenu) = saveCustomMenus( 59 | menus.toMutableList().also { it.add(menu) } 60 | ) 61 | 62 | /** 63 | * Removes custom menu by id. 64 | * @param id menu's string id. 65 | */ 66 | override fun removeMenuById(id: String) = saveCustomMenus( 67 | menus.toMutableList().also { menus -> 68 | menus.removeIf { it.id == id } 69 | } 70 | ) 71 | 72 | /** 73 | * Edits custom menu. 74 | * @param id menu string id. 75 | * @param builder Lambda with [CustomMenu] in context to edit already exists menu data. 76 | */ 77 | override fun editMenu(id: String, builder: CustomMenu.() -> Unit) = editMenu( 78 | id, menus.toMutableList().first { it.id == id }.apply(builder) 79 | ) 80 | 81 | /** 82 | * Edits custom menu. 83 | * @param id menu string id. 84 | * @param menu new menu data. 85 | */ 86 | override fun editMenu(id: String, menu: CustomMenu) = saveCustomMenus( 87 | menus.toMutableList().apply { 88 | val oldMenu = first { it.id == id } 89 | set(indexOf(oldMenu), menu) 90 | } 91 | ) 92 | 93 | /** 94 | * Imports custom menus from the [file]. 95 | * @param file - file with data about menus. 96 | * @param conflictProvider - provider for conflict names. 97 | * If provider isn't specified, prime menu will be removed. 98 | */ 99 | override suspend fun import(file: File, conflictProvider: ((conflictId: String) -> String)?) { 100 | val newMenus = file.read().byteArrayToString().serialize>() 101 | val allMenus = menus.toMutableList() 102 | newMenus.forEach { menu -> 103 | allMenus.find { it.id == menu.id }?.let { 104 | val currentId = it.id 105 | it.id = conflictProvider?.invoke(currentId) ?: currentId 106 | if (currentId == it.id) 107 | allMenus.remove(it) 108 | } 109 | } 110 | saveCustomMenus(allMenus.plus(newMenus)) 111 | menusProperty.reset() 112 | } 113 | 114 | /** 115 | * Imports custom menus from the [file]. 116 | * @param file - file with data about menus. 117 | * @param conflictProvider - provider for conflict names. 118 | * If provider isn't specified, prime menu will be removed. 119 | */ 120 | override fun import( 121 | file: File, 122 | conflictProvider: ((conflictId: String) -> String)?, 123 | callback: ActionFinishListener 124 | ) = launch { 125 | import(file, conflictProvider) 126 | callback.onFinish() 127 | } 128 | 129 | /** 130 | * Exports data into file. 131 | * @param destination - file to which will be written data. 132 | */ 133 | override suspend fun export(destination: File) { 134 | destination.write(menus.deserialize().toByteArray()) 135 | } 136 | 137 | /** 138 | * Exports data into file. 139 | * @param destination - file to which will be written data. 140 | * @param callback - call back when export will be finished. 141 | */ 142 | override fun export(destination: File, callback: ActionFinishListener) = launch { 143 | export(destination) 144 | callback.onFinish() 145 | } 146 | 147 | private fun saveCustomMenus(list: List) { 148 | val blocks = mutableListOf() 149 | val dataList = mutableListOf>>() 150 | list.forEach { 151 | blocks.add(BlockInputMenu(it.id, it.name)) 152 | val data = mutableMapOf>() 153 | data[it.id] = listOf(MenuData(it.title, it.options.joinToString("+"))) 154 | dataList.add(data) 155 | } 156 | menuBlockValue = blocks.deserialize() 157 | menuDataValue = dataList.deserialize() 158 | menusProperty.reset() 159 | } 160 | 161 | /** 162 | * Updates data in Editor async. 163 | */ 164 | override fun fetch(callback: ActionFinishListener?) = launch { 165 | fetch() 166 | callback?.onFinish() 167 | } 168 | 169 | /** 170 | * Updates data in Editor. 171 | */ 172 | override suspend fun fetch() { 173 | menuBlockValue = menuBlockFile.read().byteArrayToString() 174 | menuDataValue = menuDataFile.read().byteArrayToString() 175 | menusProperty.reset() 176 | } 177 | 178 | /** 179 | * Saves data which was edited async. 180 | */ 181 | override fun save(callback: ActionFinishListener?) = launch { 182 | save() 183 | callback?.onFinish() 184 | } 185 | 186 | /** 187 | * Saves data which was edited. 188 | */ 189 | override suspend fun save() { 190 | menuBlockFile.write(menuBlockValue.toByteArray()) 191 | menuDataFile.write(menuDataValue.toByteArray()) 192 | } 193 | 194 | override val coroutineContext = Dispatchers.IO 195 | } -------------------------------------------------------------------------------- /src/main/kotlin/io/sketchware/manager/customs/menus/SketchwareStudioCustomMenusManager.kt: -------------------------------------------------------------------------------- 1 | package io.sketchware.manager.customs.menus 2 | 3 | import io.sketchware.interfaces.CustomMenusManager 4 | import io.sketchware.interfaces.Editor 5 | import io.sketchware.interfaces.listeners.ActionFinishListener 6 | import io.sketchware.models.customs.CustomMenu 7 | import io.sketchware.models.customs.SWStudioMenu 8 | import io.sketchware.utils.delegates.lazyInit 9 | import io.sketchware.utils.internal.* 10 | import kotlinx.coroutines.CoroutineScope 11 | import kotlinx.coroutines.Dispatchers 12 | import kotlinx.coroutines.launch 13 | import java.io.File 14 | 15 | class SketchwareStudioCustomMenusManager( 16 | private var menusValue: String, 17 | private val menusFile: File 18 | ) : CustomMenusManager, Editor, CoroutineScope { 19 | 20 | companion object { 21 | suspend operator fun invoke(menusFile: File) = 22 | SketchwareStudioCustomMenusManager(menusFile.read().byteArrayToString(), menusFile) 23 | } 24 | 25 | private val menusProperty = lazyInit { 26 | menusValue.serialize>().map { 27 | CustomMenu(it.name, it.name, it.title, it.data) 28 | } 29 | } 30 | 31 | /** 32 | * @return list of custom menus. 33 | */ 34 | override val menus by menusProperty 35 | 36 | /** 37 | * Adds custom menu. 38 | * @param menu menu to add 39 | */ 40 | override fun addCustomMenu(menu: CustomMenu) = saveCustomMenus( 41 | menus.toMutableList().also { it.add(menu) } 42 | ) 43 | 44 | /** 45 | * Removes custom menu by id. 46 | * @param id menu's string id. 47 | */ 48 | override fun removeMenuById(id: String) = saveCustomMenus( 49 | menus.toMutableList().also { menus -> 50 | menus.removeIf { it.id == id } 51 | } 52 | ) 53 | 54 | /** 55 | * Edits custom menu. 56 | * @param id menu string id. 57 | * @param builder Lambda with [CustomMenu] in context to edit already exists menu data. 58 | */ 59 | override fun editMenu(id: String, builder: CustomMenu.() -> Unit) = editMenu( 60 | id, menus.toMutableList().first { it.id == id }.apply(builder) 61 | ) 62 | 63 | /** 64 | * Edits custom menu. 65 | * @param id menu string id. 66 | * @param menu new menu data. 67 | */ 68 | override fun editMenu(id: String, menu: CustomMenu) = saveCustomMenus( 69 | menus.toMutableList().apply { 70 | val oldMenu = first { it.id == id } 71 | set(indexOf(oldMenu), menu) 72 | } 73 | ) 74 | 75 | /** 76 | * Imports custom menus from the [file]. 77 | * @param file - file with data about menus. 78 | * @param conflictProvider - provider for conflict names. 79 | * If provider isn't specified or it is returning same value as specified 80 | * in [conflictProvider#conflicId],prime menu will be removed. 81 | */ 82 | override suspend fun import(file: File, conflictProvider: ((conflictId: String) -> String)?) { 83 | val newMenus = file.read().byteArrayToString().serialize>() 84 | val allMenus = menus.toMutableList() 85 | newMenus.forEach { menu -> 86 | allMenus.find { it.id == menu.id }?.let { 87 | val currentId = it.id 88 | it.id = conflictProvider?.invoke(currentId) ?: currentId 89 | if (currentId == it.id) 90 | allMenus.remove(it) 91 | } 92 | } 93 | menusProperty.reset() 94 | saveCustomMenus(allMenus.plus(newMenus)) 95 | } 96 | 97 | /** 98 | * Imports custom menus from the [file]. 99 | * @param file - file with data about menus. 100 | * @param conflictProvider - provider for conflict names. 101 | * If provider isn't specified or it is returning same value as specified 102 | * in [conflictProvider#conflicId],prime menu will be removed. 103 | * @param callback - will be called when import will be finished. 104 | */ 105 | override fun import( 106 | file: File, 107 | conflictProvider: ((conflictId: String) -> String)?, 108 | callback: ActionFinishListener 109 | ) = launch { 110 | import(file, conflictProvider) 111 | callback.onFinish() 112 | } 113 | 114 | /** 115 | * Exports data into file. 116 | * @param destination - file to which will be written data. 117 | */ 118 | override suspend fun export(destination: File) { 119 | destination.write(menus.deserialize().toByteArray()) 120 | } 121 | 122 | /** 123 | * Exports data into file. 124 | * @param destination - file to which will be written data. 125 | * @param callback - call back when export will be finished. 126 | */ 127 | override fun export(destination: File, callback: ActionFinishListener) = launch { 128 | export(destination) 129 | callback.onFinish() 130 | } 131 | 132 | private fun saveCustomMenus(list: List) { 133 | menusValue = list.map { 134 | it.toSWStudioMenu() 135 | }.deserialize() 136 | menusProperty.reset() 137 | } 138 | 139 | /** 140 | * Updates data in Editor async. 141 | */ 142 | override fun fetch(callback: ActionFinishListener?) = launch { 143 | fetch() 144 | callback?.onFinish() 145 | } 146 | 147 | /** 148 | * Updates data in Editor. 149 | */ 150 | override suspend fun fetch() { 151 | menusValue = menusFile.read().byteArrayToString() 152 | menusProperty.reset() 153 | } 154 | 155 | /** 156 | * Saves data which was edited async. 157 | */ 158 | override fun save(callback: ActionFinishListener?) = launch { 159 | save() 160 | callback?.onFinish() 161 | } 162 | 163 | /** 164 | * Saves data which was edited. 165 | */ 166 | override suspend fun save() { 167 | menusFile.write(menusValue.toByteArray()) 168 | } 169 | 170 | override val coroutineContext = Dispatchers.IO 171 | } -------------------------------------------------------------------------------- /src/main/kotlin/io/sketchware/manager/exporters/ProjectExporter.kt: -------------------------------------------------------------------------------- 1 | package io.sketchware.manager.exporters 2 | 3 | import io.sketchware.models.ProjectType 4 | import io.sketchware.models.export.ExportConfig 5 | import io.sketchware.models.export.ExportFilesConfig 6 | import io.sketchware.models.projects.ProjectFilesLocations 7 | import io.sketchware.models.projects.ProjectResourcesFiles 8 | import io.sketchware.utils.internal.copy 9 | import io.sketchware.utils.internal.deserialize 10 | import io.sketchware.utils.internal.write 11 | import java.io.File 12 | 13 | open class ProjectExporter( 14 | open val locations: ProjectFilesLocations, 15 | open val projectType: ProjectType = ProjectType.SKETCHWARE 16 | ) { 17 | companion object { 18 | /** 19 | * Const with version of project exporter. 20 | */ 21 | const val PROJECT_EXPORTER_VERSION = 0.1 22 | } 23 | 24 | /** 25 | * Exports project into [destinationFolder]. 26 | */ 27 | open suspend fun exportAsFolder(destinationFolder: File) = exportAsFolder( 28 | destinationFolder, File(destinationFolder, "config"), getDefaultDestLocations(destinationFolder) 29 | ) 30 | 31 | /** 32 | * Exports project into a folder by [destLocations] paths. 33 | */ 34 | open suspend fun exportAsFolder( 35 | rootFolder: File, 36 | configFile: File = File(rootFolder, "config"), 37 | destLocations: ProjectFilesLocations 38 | ) = with(locations) { 39 | dataFolder.copy(destLocations.dataFolder) 40 | projectFile.copy(destLocations.projectFile) 41 | resources.soundsFolder.copy(destLocations.resources.soundsFolder) 42 | resources.fontsFolder.copy(destLocations.resources.fontsFolder) 43 | resources.iconsFolder.copy(destLocations.resources.iconsFolder) 44 | resources.imagesFolder.copy(destLocations.resources.iconsFolder) 45 | configFile.write(getDefaultConfig().deserialize().toByteArray()) 46 | } 47 | 48 | private fun getDefaultDestLocations(root: File) = ProjectFilesLocations( 49 | root, File(root, "project"), File(root, "data"), ProjectResourcesFiles( 50 | File(root, "resources/icons"), File(root, "resources/images"), 51 | File(root, "resources/sounds"), File(root, "resources/fonts") 52 | ) 53 | ) 54 | 55 | private fun getDefaultConfig() = ExportConfig(PROJECT_EXPORTER_VERSION, projectType, ExportFilesConfig()) 56 | } -------------------------------------------------------------------------------- /src/main/kotlin/io/sketchware/manager/exporters/SketchwareProExporter.kt: -------------------------------------------------------------------------------- 1 | package io.sketchware.manager.exporters 2 | 3 | import io.sketchware.manager.customs.SketchwareProCustomManager 4 | import io.sketchware.manager.projects.entities.SketchwareProProject 5 | import io.sketchware.models.ProjectType 6 | import io.sketchware.models.projects.ProjectFilesLocations 7 | import io.sketchware.utils.internal.ProjectCustomExporter 8 | import io.sketchware.utils.internal.copy 9 | import io.sketchware.utils.internal.deserialize 10 | import io.sketchware.utils.internal.write 11 | import java.io.File 12 | 13 | class SketchwareProExporter( 14 | override val locations: ProjectFilesLocations, 15 | override val projectType: ProjectType = ProjectType.SKETCHWARE_PRO, 16 | val project: SketchwareProProject, 17 | val customsManager: SketchwareProCustomManager 18 | ) : ProjectExporter(locations, projectType) { 19 | private val customExporter by lazy { ProjectCustomExporter(project, customsManager) } 20 | 21 | override suspend fun exportAsFolder(rootFolder: File, configFile: File, destLocations: ProjectFilesLocations) { 22 | super.exportAsFolder(rootFolder, configFile, destLocations) 23 | File(rootFolder, "/customs").apply { 24 | mkdir() 25 | File(this, "blocks.json") 26 | .write(customExporter.getBlocksToExport().deserialize().toByteArray()) 27 | File(this, "components.json") 28 | .write(customExporter.getComponentsToExport().deserialize().toByteArray()) 29 | File(this, "menus.json") 30 | .write(customExporter.getMenusToExport().deserialize().toByteArray()) 31 | File(this, "listeners.json") 32 | .write(customExporter.getListenersGroupsToExport().deserialize().toByteArray()) 33 | } 34 | File(rootFolder, "/SourceEdited/").apply { 35 | mkdir() 36 | project.getSourceEditedFiles().forEach { 37 | it?.copy(File(this, it.name)) 38 | } 39 | } 40 | } 41 | 42 | } -------------------------------------------------------------------------------- /src/main/kotlin/io/sketchware/manager/projects/SketchwareProProjectsManager.kt: -------------------------------------------------------------------------------- 1 | package io.sketchware.manager.projects 2 | 3 | import io.sketchware.interfaces.ProjectsManager 4 | import io.sketchware.manager.customs.SketchwareProCustomManager 5 | import io.sketchware.manager.projects.entities.SketchwareProProject 6 | import io.sketchware.manager.projects.entities.SketchwareProject 7 | import io.sketchware.manager.projects.entities.toSketchwareProProject 8 | import io.sketchware.models.export.ExportConfig 9 | import io.sketchware.models.export.ExportedCustomFilesConfig 10 | import io.sketchware.models.projects.ProjectFilesLocations 11 | import io.sketchware.utils.CustomProjectImportManager 12 | import io.sketchware.utils.ProjectImportManager 13 | import io.sketchware.utils.internal.SketchwareProjectsUtil 14 | import io.sketchware.utils.internal.byteArrayToString 15 | import io.sketchware.utils.internal.readOrNull 16 | import io.sketchware.utils.internal.serialize 17 | import java.io.File 18 | 19 | class SketchwareProProjectsManager( 20 | private val sketchwareFolder: File 21 | ) : ProjectsManager { 22 | /** 23 | * @param folderPath - path to sketchware folder. 24 | */ 25 | constructor(folderPath: String) : this(File(folderPath)) 26 | 27 | /** 28 | * Gets projects. 29 | * @return List of [SketchwareProject]. 30 | */ 31 | override suspend fun getProjects() = SketchwareProjectsUtil.getProjectsList(sketchwareFolder).map { 32 | it.toSketchwareProProject( 33 | File(sketchwareFolder, "data/SourceEdited/${it.locations.projectMainDirectory!!.name}") 34 | ) 35 | } 36 | 37 | /** 38 | * Checks next free id and returns it. 39 | * @param startId - Specific id from which function 40 | * will check next free id. By default it is 601. 41 | * @return next free for new project id. 42 | */ 43 | override suspend fun nextFreeId(startId: Int) = 44 | SketchwareProjectsUtil.nextFreeProjectId(File(sketchwareFolder, "mysc/list")) 45 | 46 | /** 47 | * @return instance of [ProjectImportManager] to import project. 48 | */ 49 | suspend fun getImporter(exportedFolder: File, newId: Int, customManager: SketchwareProCustomManager) = 50 | CustomProjectImportManager( 51 | ProjectFilesLocations.getDefaultExport(exportedFolder), 52 | ProjectFilesLocations.getSWDefault(sketchwareFolder, newId), 53 | newId, customManager, 54 | File(exportedFolder, "config").readOrNull()?.byteArrayToString()?.serialize() 55 | ?.customFilesConfig?.toExportedCustomFiles(exportedFolder) 56 | ?: ExportedCustomFilesConfig().toExportedCustomFiles(exportedFolder), 57 | File(sketchwareFolder, "data/SourceEdited") 58 | ) 59 | 60 | } 61 | -------------------------------------------------------------------------------- /src/main/kotlin/io/sketchware/manager/projects/SketchwareProjectsManager.kt: -------------------------------------------------------------------------------- 1 | package io.sketchware.manager.projects 2 | 3 | import io.sketchware.interfaces.ProjectsManager 4 | import io.sketchware.manager.projects.entities.SketchwareProject 5 | import io.sketchware.models.projects.ProjectFilesLocations 6 | import io.sketchware.utils.ProjectImportManager 7 | import io.sketchware.utils.internal.SketchwareProjectsUtil 8 | import java.io.File 9 | 10 | open class SketchwareProjectsManager(private val sketchwareFolder: File) : ProjectsManager { 11 | 12 | /** 13 | * @param folderPath - path to sketchware folder. 14 | */ 15 | constructor(folderPath: String) : this(File(folderPath)) 16 | 17 | /** 18 | * Gets projects. 19 | * @return List of [SketchwareProject]. 20 | */ 21 | override suspend fun getProjects() = SketchwareProjectsUtil.getProjectsList(sketchwareFolder) 22 | 23 | 24 | /** 25 | * Checks next free id and returns it. 26 | * @param startId - Specific id from which function 27 | * will check next free id. By default it is 601. 28 | * @return next free for new project id. 29 | */ 30 | override suspend fun nextFreeId(startId: Int) = 31 | SketchwareProjectsUtil.nextFreeProjectId(File(sketchwareFolder, "mysc/list")) 32 | 33 | /** 34 | * @return instance of [ProjectImportManager] to import project. 35 | */ 36 | fun getImporter(exportedFolder: File, newId: Int) = ProjectImportManager( 37 | ProjectFilesLocations.getDefaultExport(exportedFolder), 38 | ProjectFilesLocations.getSWDefault(sketchwareFolder, newId), 39 | newId 40 | ) 41 | 42 | } -------------------------------------------------------------------------------- /src/main/kotlin/io/sketchware/manager/projects/SketchwareStudioManager.kt: -------------------------------------------------------------------------------- 1 | package io.sketchware.manager.projects 2 | 3 | import io.sketchware.interfaces.ProjectsManager 4 | import io.sketchware.manager.customs.SketchwareStudioCustomManager 5 | import io.sketchware.manager.projects.entities.SketchwareProject 6 | import io.sketchware.manager.projects.entities.SketchwareStudioProject 7 | import io.sketchware.manager.projects.entities.toSketchwareStudioProject 8 | import io.sketchware.models.export.ExportConfig 9 | import io.sketchware.models.export.ExportedCustomFilesConfig 10 | import io.sketchware.models.projects.ProjectFilesLocations 11 | import io.sketchware.utils.CustomProjectImportManager 12 | import io.sketchware.utils.ProjectImportManager 13 | import io.sketchware.utils.internal.SketchwareProjectsUtil 14 | import io.sketchware.utils.internal.byteArrayToString 15 | import io.sketchware.utils.internal.readOrNull 16 | import io.sketchware.utils.internal.serialize 17 | import java.io.File 18 | 19 | class SketchwareStudioProjectsManager( 20 | private val sketchwareFolder: File 21 | ) : ProjectsManager { 22 | /** 23 | * @param folderPath - path to sketchware folder. 24 | */ 25 | constructor(folderPath: String) : this(File(folderPath)) 26 | 27 | /** 28 | * Gets projects. 29 | * @return List of [SketchwareProject]. 30 | */ 31 | override suspend fun getProjects() = SketchwareProjectsUtil.getProjectsList(sketchwareFolder).map { 32 | it.toSketchwareStudioProject( 33 | File(sketchwareFolder, "data/SourceEdited/${it.locations.projectMainDirectory!!.name}") 34 | ) 35 | } 36 | 37 | /** 38 | * Checks next free id and returns it. 39 | * @param startId - Specific id from which function 40 | * will check next free id. By default it is 601. 41 | * @return next free for new project id. 42 | */ 43 | override suspend fun nextFreeId(startId: Int) = 44 | SketchwareProjectsUtil.nextFreeProjectId(File(sketchwareFolder, "mysc/list")) 45 | 46 | /** 47 | * @return instance of [ProjectImportManager] to import project. 48 | */ 49 | suspend fun getImporter(exportedFolder: File, newId: Int, customManager: SketchwareStudioCustomManager) = 50 | CustomProjectImportManager( 51 | ProjectFilesLocations.getDefaultExport(exportedFolder), 52 | ProjectFilesLocations.getSWDefault(sketchwareFolder, newId), 53 | newId, customManager, 54 | File(exportedFolder, "config").readOrNull()?.byteArrayToString()?.serialize() 55 | ?.customFilesConfig?.toExportedCustomFiles(exportedFolder) 56 | ?: ExportedCustomFilesConfig().toExportedCustomFiles(exportedFolder), 57 | File(sketchwareFolder, "data/SourceEdited") 58 | ) 59 | 60 | } 61 | -------------------------------------------------------------------------------- /src/main/kotlin/io/sketchware/manager/projects/data/FileManager.kt: -------------------------------------------------------------------------------- 1 | package io.sketchware.manager.projects.data 2 | 3 | import io.sketchware.annotations.ExperimentalSWManagerAPI 4 | import io.sketchware.interfaces.Editor 5 | import io.sketchware.interfaces.listeners.ActionFinishListener 6 | import io.sketchware.models.projects.SketchwareDataFileModel 7 | import io.sketchware.utils.SketchwareEncryptor.decrypt 8 | import io.sketchware.utils.SketchwareEncryptor.encrypt 9 | import io.sketchware.utils.delegates.lazyInit 10 | import io.sketchware.utils.internal.* 11 | import io.sketchware.utils.internal.TagFormatter.toSaveableValue 12 | import kotlinx.coroutines.CoroutineScope 13 | import kotlinx.coroutines.Dispatchers 14 | import kotlinx.coroutines.launch 15 | import java.io.File 16 | import kotlin.coroutines.CoroutineContext 17 | 18 | class FileManager( 19 | private var value: String, 20 | private val file: File, 21 | override val coroutineContext: CoroutineContext = Dispatchers.Main 22 | ) : CoroutineScope, Editor { 23 | 24 | companion object { 25 | suspend operator fun invoke(file: File) = FileManager( 26 | file.readOrNull()?.decrypt()?.byteArrayToString() ?: "", file 27 | ) 28 | } 29 | 30 | private val activitiesDelegate = lazyInit { 31 | TagFormatter.getListByTag("activity", value) ?: emptyList() 32 | } 33 | 34 | /** 35 | * Gets activities in current scope. 36 | * @return list of [SketchwareDataFileModel] with data about activity. 37 | */ 38 | val activities by activitiesDelegate 39 | 40 | private val customViewsDelegate = lazyInit { 41 | TagFormatter.getListByTag("customview", value) ?: emptyList() 42 | } 43 | 44 | /** 45 | * Gets custom views in specific scope. 46 | * @return list of [SketchwareDataFileModel] with data about activity. 47 | */ 48 | val customViews by customViewsDelegate 49 | 50 | /** 51 | * Adds activity to current scope. 52 | * @param model - new activity to add. 53 | */ 54 | fun addActivity(model: SketchwareDataFileModel) { 55 | value = TagFormatter.addTag("activity", activities.toMutableList().apply { 56 | add(model) 57 | }.joinToString("\n") { it.deserialize() }, value) 58 | activitiesDelegate.reset() 59 | } 60 | 61 | /** 62 | * Adds custom view to current scope. 63 | * @param model - new custom view to add. 64 | */ 65 | fun addCustomView(model: SketchwareDataFileModel) { 66 | value = TagFormatter.addTag("customview", customViews.toMutableList().apply { 67 | add(model) 68 | }.joinToString("\n") { it.deserialize() }, value) 69 | customViewsDelegate.reset() 70 | } 71 | 72 | /** 73 | * Removes activity from current scope. 74 | * @param fileName - file name (example: main.xml) 75 | */ 76 | @ExperimentalSWManagerAPI 77 | fun removeActivity(fileName: String) { 78 | value = TagFormatter.addTag("activity", activities.toMutableList().apply { 79 | removeIf { it.fileName == fileName } 80 | }.toSaveableValue(), value) 81 | activitiesDelegate.reset() 82 | } 83 | 84 | /** 85 | * Removes customview from current scope. 86 | * @param fileName - file name (example: customview.xml) 87 | */ 88 | @ExperimentalSWManagerAPI 89 | fun removeCustomView(fileName: String) { 90 | value = TagFormatter.addTag("customview", customViews.toMutableList().apply { 91 | removeIf { it.fileName == fileName } 92 | }.toSaveableValue(), value) 93 | customViewsDelegate.reset() 94 | } 95 | 96 | 97 | /** 98 | * Updates data in FileManager by reading [file]. 99 | * @param callback - will be called when the fetch is complete. 100 | */ 101 | override fun fetch(callback: ActionFinishListener?) = launch { 102 | fetch() 103 | callback?.onFinish() 104 | } 105 | 106 | /** 107 | * Updates data in FileManager by reading [file]. 108 | */ 109 | override suspend fun fetch() { 110 | value = file.read().decrypt().byteArrayToString() 111 | activitiesDelegate.reset() 112 | customViewsDelegate.reset() 113 | } 114 | 115 | /** 116 | * Saves data from [value] into [file]. 117 | * @param callback - will be called when the fetch is complete. 118 | */ 119 | override fun save(callback: ActionFinishListener?) = launch { 120 | save() 121 | callback?.onFinish() 122 | } 123 | 124 | /** 125 | * Saves data from [value] into [file]. 126 | */ 127 | override suspend fun save() { 128 | file.write(value.toByteArray().encrypt()) 129 | } 130 | } -------------------------------------------------------------------------------- /src/main/kotlin/io/sketchware/manager/projects/data/LibraryManager.kt: -------------------------------------------------------------------------------- 1 | package io.sketchware.manager.projects.data 2 | 3 | import io.sketchware.exceptions.LibraryNotFoundException 4 | import io.sketchware.interfaces.Editor 5 | import io.sketchware.interfaces.listeners.ActionFinishListener 6 | import io.sketchware.models.projects.SketchwareLibraryDataModel 7 | import io.sketchware.models.projects.SketchwareLibraryModel 8 | import io.sketchware.utils.SketchwareEncryptor.decrypt 9 | import io.sketchware.utils.SketchwareEncryptor.encrypt 10 | import io.sketchware.utils.delegates.lazyInit 11 | import io.sketchware.utils.internal.* 12 | import kotlinx.coroutines.CoroutineScope 13 | import kotlinx.coroutines.Dispatchers 14 | import kotlinx.coroutines.launch 15 | import java.io.File 16 | import kotlin.coroutines.CoroutineContext 17 | 18 | class LibraryManager( 19 | private var value: String, 20 | private val file: File, 21 | override val coroutineContext: CoroutineContext = Dispatchers.Main 22 | ) : CoroutineScope, Editor { 23 | 24 | companion object { 25 | suspend operator fun invoke(file: File) = 26 | LibraryManager(file.readOrNull()?.decrypt()?.byteArrayToString() ?: "", file) 27 | } 28 | 29 | private val librariesDelegate = lazyInit { 30 | mutableListOf().apply { 31 | Regex("(?<=@).*?(?=\\n@|$)", RegexOption.DOT_MATCHES_ALL) 32 | .findAll(value).forEach { matchResult -> 33 | val name = matchResult.value.substring( 34 | 0, matchResult.value.indexOf("\n") 35 | .takeUnless { it == -1 } ?: matchResult.value.length 36 | ) 37 | add( 38 | SketchwareLibraryModel( 39 | name, matchResult.value.replace(name, "").serialize() 40 | ) 41 | ) 42 | } 43 | }.toList() 44 | } 45 | 46 | /** 47 | * @return all libraries in scope. 48 | */ 49 | val libraries by librariesDelegate 50 | 51 | /** 52 | * Edits data about library. 53 | * @param name - library name. 54 | * @param editor - lambda with [SketchwareLibraryDataModel] in context to edit. 55 | * @throws LibraryNotFoundException if library does not exist. 56 | */ 57 | @Throws(LibraryNotFoundException::class) 58 | fun editLibraryData( 59 | name: String, 60 | editor: SketchwareLibraryDataModel.() -> Unit 61 | ) { 62 | val librariesList = libraries.toMutableList() 63 | val libraryIndex = librariesList.indexOfFirst { it.name == name } 64 | .takeIf { it != -1 } ?: throw LibraryNotFoundException(name) 65 | librariesList[libraryIndex].information = librariesList[libraryIndex].information.apply(editor) 66 | } 67 | 68 | private fun saveLibraries(list: List) = list.forEach { library -> 69 | value = "" 70 | value += "@${library.name}\n${library.deserialize()}\n\n" 71 | librariesDelegate.reset() 72 | } 73 | 74 | /** 75 | * Updates data in FileManager by reading [file]. 76 | * @param callback - will be called when the fetch is complete. 77 | */ 78 | override fun fetch(callback: ActionFinishListener?) = launch { 79 | fetch() 80 | callback?.onFinish() 81 | } 82 | 83 | /** 84 | * Updates data in FileManager by reading [file]. 85 | */ 86 | override suspend fun fetch() { 87 | value = file.read().decrypt().byteArrayToString() 88 | librariesDelegate.reset() 89 | } 90 | 91 | /** 92 | * Saves data from [value] into [file]. 93 | * @param callback - will be called when the fetch is complete. 94 | */ 95 | override fun save(callback: ActionFinishListener?) = launch { 96 | save() 97 | callback?.onFinish() 98 | } 99 | 100 | /** 101 | * Saves data from [value] into [file]. 102 | */ 103 | override suspend fun save() { 104 | file.write(value.toByteArray().encrypt()) 105 | } 106 | 107 | } -------------------------------------------------------------------------------- /src/main/kotlin/io/sketchware/manager/projects/data/ResourcesManager.kt: -------------------------------------------------------------------------------- 1 | package io.sketchware.manager.projects.data 2 | 3 | import io.sketchware.interfaces.Editor 4 | import io.sketchware.interfaces.listeners.ActionFinishListener 5 | import io.sketchware.models.projects.ProjectResource 6 | import io.sketchware.utils.SketchwareEncryptor.decrypt 7 | import io.sketchware.utils.delegates.lazyInit 8 | import io.sketchware.utils.internal.* 9 | import io.sketchware.utils.internal.TagFormatter.toSaveableValue 10 | import kotlinx.coroutines.CoroutineScope 11 | import kotlinx.coroutines.Dispatchers 12 | import kotlinx.coroutines.MainCoroutineDispatcher 13 | import kotlinx.coroutines.launch 14 | import java.io.File 15 | 16 | class ResourcesManager( 17 | private var value: String, 18 | private val file: File, 19 | override val coroutineContext: MainCoroutineDispatcher = Dispatchers.Main 20 | ) : CoroutineScope, Editor { 21 | 22 | companion object { 23 | suspend operator fun invoke(file: File) = 24 | ResourcesManager(file.readOrNull()?.decrypt()?.byteArrayToString() ?: "", file) 25 | } 26 | 27 | private val imagesDelegate = lazyInit { 28 | TagFormatter.getListByTag("images", value) ?: emptyList() 29 | } 30 | 31 | /** 32 | * @return list of [ProjectResource] with images. 33 | */ 34 | val images by imagesDelegate 35 | 36 | private val fontsDelegate = lazyInit { 37 | TagFormatter.getListByTag("fonts", value) ?: emptyList() 38 | } 39 | 40 | /** 41 | * @return list of [ProjectResource] with fonts. 42 | */ 43 | val fonts by fontsDelegate 44 | 45 | private val soundsDelegate = lazyInit { 46 | TagFormatter.getListByTag("sounds", value) ?: emptyList() 47 | } 48 | 49 | /** 50 | * @return list of [ProjectResource] with sounds. 51 | */ 52 | val sounds by soundsDelegate 53 | 54 | /** 55 | * Adds image to resource info file. 56 | * Attention: this method does not add a real picture, but only information about the picture. 57 | * @param resource - information about image. 58 | */ 59 | fun addImage(resource: ProjectResource) { 60 | value = TagFormatter.addTag("images", images.toMutableList().apply { 61 | add(resource) 62 | }.toSaveableValue(), value) 63 | imagesDelegate.reset() 64 | } 65 | 66 | /** 67 | * Adds font to resource info file. 68 | * Attention: this method does not add a real font, but only information about the font. 69 | * @param resource - information about font. 70 | */ 71 | fun addFont(resource: ProjectResource) { 72 | value = TagFormatter.addTag("fonts", fonts.toMutableList().apply { 73 | add(resource) 74 | }.toSaveableValue(), value) 75 | fontsDelegate.reset() 76 | } 77 | 78 | /** 79 | * Adds sound to resource info file. 80 | * Attention: this method does not add a real sound, but only information about the sound. 81 | * @param resource - information about sound. 82 | */ 83 | fun addSound(resource: ProjectResource) { 84 | value = TagFormatter.addTag("sounds", sounds.toMutableList().apply { 85 | add(resource) 86 | }.toSaveableValue(), value) 87 | soundsDelegate.reset() 88 | } 89 | 90 | /** 91 | * Removes data about image from project. 92 | * Attention: this method does not delete the actual file in the project, 93 | * but only deletes information about the resource (it's name and so on). 94 | * @param name - name of resource (example: ic_arrow_back). 95 | */ 96 | fun removeImage(name: String) { 97 | value = TagFormatter.addTag("images", images.toMutableList().apply { 98 | removeIf { it.name == name } 99 | }.toSaveableValue(), value) 100 | imagesDelegate.reset() 101 | } 102 | 103 | /** 104 | * Removes data about font from project. 105 | * Attention: this method does not delete the actual file in the project, 106 | * but only deletes information about the resource (it's name and so on). 107 | * @param name - name of resource (example: google_sans). 108 | */ 109 | fun removeFont(name: String) { 110 | value = TagFormatter.addTag("fonts", images.toMutableList().apply { 111 | removeIf { it.name == name } 112 | }.toSaveableValue(), value) 113 | fontsDelegate.reset() 114 | } 115 | 116 | /** 117 | * Removes data about sound from project. 118 | * Attention: this method does not delete the actual file in the project, 119 | * but only deletes information about the resource (its name and so on). 120 | * @param name - name of resource (example: some_ringtone). 121 | */ 122 | fun removeSound(name: String) { 123 | value = TagFormatter.addTag("sounds", images.toMutableList().apply { 124 | removeIf { it.name == name } 125 | }.toSaveableValue(), value) 126 | soundsDelegate.reset() 127 | } 128 | 129 | /** 130 | * Updates data in FileManager by reading [file]. 131 | * @param callback - will be called when the fetch is complete. 132 | */ 133 | override fun fetch(callback: ActionFinishListener?) = launch { 134 | fetch() 135 | callback?.onFinish() 136 | } 137 | 138 | /** 139 | * Updates data in FileManager by reading [file]. 140 | */ 141 | override suspend fun fetch() { 142 | value = file.read().decrypt().byteArrayToString() 143 | imagesDelegate.reset() 144 | soundsDelegate.reset() 145 | fontsDelegate.reset() 146 | } 147 | 148 | /** 149 | * Saves data from [value] into [file]. 150 | * @param callback - will be called when the fetch is complete. 151 | */ 152 | override fun save(callback: ActionFinishListener?) = launch { 153 | save() 154 | callback?.onFinish() 155 | } 156 | 157 | /** 158 | * Saves data from [value] into [file]. 159 | */ 160 | override suspend fun save() { 161 | file.write(value.toByteArray().decrypt()) 162 | } 163 | } -------------------------------------------------------------------------------- /src/main/kotlin/io/sketchware/manager/projects/data/ViewManager.kt: -------------------------------------------------------------------------------- 1 | package io.sketchware.manager.projects.data 2 | 3 | import io.sketchware.annotations.ExperimentalSWManagerAPI 4 | import io.sketchware.exceptions.ViewAlreadyExistsException 5 | import io.sketchware.exceptions.ViewNotFoundException 6 | import io.sketchware.interfaces.Editor 7 | import io.sketchware.interfaces.listeners.ActionFinishListener 8 | import io.sketchware.models.view.WidgetRoot 9 | import io.sketchware.utils.SketchwareEncryptor.decrypt 10 | import io.sketchware.utils.SketchwareEncryptor.encrypt 11 | import io.sketchware.utils.ViewBuilder 12 | import io.sketchware.utils.internal.* 13 | import io.sketchware.utils.internal.TagFormatter.toSaveableValue 14 | import kotlinx.coroutines.CoroutineScope 15 | import kotlinx.coroutines.Dispatchers 16 | import kotlinx.coroutines.launch 17 | import java.io.File 18 | import kotlin.coroutines.CoroutineContext 19 | 20 | class ViewManager( 21 | private var value: String, 22 | private val file: File, 23 | override val coroutineContext: CoroutineContext = Dispatchers.Main 24 | ) : CoroutineScope, Editor { 25 | 26 | companion object { 27 | suspend operator fun invoke(file: File) = ViewManager( 28 | file.readOrNull()?.byteArrayToString() ?: "", file 29 | ) 30 | } 31 | 32 | /** 33 | * Gets view by [viewName] and [widgetName] (if exist). 34 | * @param viewName - View name (for example: main, without .xml). 35 | * @param widgetName - specific widget to get (for example: fab). 36 | * @return list of [WidgetRoot] with data about view. 37 | */ 38 | fun getView(viewName: String, widgetName: String? = null): List? = 39 | value.getByTag("$viewName.xml".plus(widgetName?.let { "_$widgetName" } ?: "")) 40 | ?.let(TagFormatter::parseAsArray) 41 | 42 | /** 43 | * Edits view with name [viewName] and [widgetName] (if exist). 44 | * @param viewName - View name (for example: main, without .xml). 45 | * @param widgetName - specific widget to get in [viewName]. 46 | * @param widgets - new widgets to save. 47 | */ 48 | fun editView( 49 | viewName: String, widgetName: String? = null, widgets: List 50 | ) = saveView(viewName, widgetName, widgets) 51 | 52 | /** 53 | * Adds widget to current scope. 54 | * @param viewName - View name (for example: main, without .xml). 55 | * @param widgetName - specific widget to get in [viewName]. 56 | * @param widgets - widgets to save. 57 | * @throws ViewAlreadyExistsException - if view and/or widget with same name already exists. 58 | */ 59 | @Throws(ViewAlreadyExistsException::class) 60 | fun addView( 61 | viewName: String, 62 | widgetName: String? = null, 63 | widgets: List 64 | ) = if (getView(viewName, widgetName) == null) 65 | editView(viewName, widgetName, widgets) 66 | else throw ViewAlreadyExistsException(viewName, widgetName) 67 | 68 | /** 69 | * Adds view to current scope. 70 | * @throws ViewAlreadyExistsException - if view and/or widget with same name already exists. 71 | */ 72 | @ExperimentalSWManagerAPI 73 | @Throws(ViewAlreadyExistsException::class) 74 | fun addView(builder: ViewBuilder.() -> Unit) = ViewBuilder().apply(builder).apply { 75 | addView(name, layoutName, widgetBuilder.widgets) 76 | } 77 | 78 | /** 79 | * Edits view with name [viewName] and [widgetName] (if exist). 80 | * @param viewName - View name (for example: main, without .xml). 81 | * @param widgetName - specific widget to get in [viewName]. 82 | * @param editor - editor. 83 | */ 84 | fun editView( 85 | viewName: String, 86 | widgetName: String? = null, 87 | editor: (MutableList) -> Unit 88 | ) = editView( 89 | viewName, 90 | widgetName, 91 | getView(viewName, widgetName)?.toMutableList()?.apply(editor) 92 | ?: throw ViewNotFoundException(file.path, viewName, widgetName) 93 | ) 94 | 95 | private fun saveView( 96 | viewName: String, 97 | widget: String? = null, 98 | list: List 99 | ) = synchronized(this) { 100 | val name = "$viewName.xml".plus( 101 | if (widget == null) 102 | "" else "_$widget" 103 | ) 104 | value = value.replaceOrInsertAtTop( 105 | "(@$name.*?)(?=@|\$)".toRegex(), 106 | if (list.isEmpty()) 107 | throw IllegalStateException("list cannot be empty") 108 | else "@$name${list.toSaveableValue()}\n\n" 109 | ) 110 | } 111 | 112 | /** 113 | * Updates data in Editor async. 114 | */ 115 | override fun fetch(callback: ActionFinishListener?) = launch { 116 | fetch() 117 | callback?.onFinish() 118 | } 119 | 120 | /** 121 | * Updates data in Editor. 122 | */ 123 | override suspend fun fetch() { 124 | value = file.read().decrypt().byteArrayToString() 125 | } 126 | 127 | /** 128 | * Saves data which was edited async. 129 | */ 130 | override fun save(callback: ActionFinishListener?) = launch { 131 | save() 132 | callback?.onFinish() 133 | } 134 | 135 | /** 136 | * Saves data which was edited. 137 | */ 138 | override suspend fun save() { 139 | file.write(value.toByteArray().encrypt()) 140 | } 141 | 142 | } -------------------------------------------------------------------------------- /src/main/kotlin/io/sketchware/manager/projects/entities/SketchwareProProject.kt: -------------------------------------------------------------------------------- 1 | package io.sketchware.manager.projects.entities 2 | 3 | import io.sketchware.manager.customs.SketchwareProCustomManager 4 | import io.sketchware.manager.exporters.SketchwareProExporter 5 | import io.sketchware.models.projects.ProjectFilesLocations 6 | import io.sketchware.utils.internal.getFilesOrEmpty 7 | import java.io.File 8 | 9 | open class SketchwareProProject( 10 | override val locations: ProjectFilesLocations, 11 | internal open val sourceEditedFolder: File 12 | ) : SketchwareProject(locations) { 13 | 14 | /** 15 | * Sketchware Pro Project exporter. Exports project fully with all custom components / etc. 16 | */ 17 | fun getExporter(customManager: SketchwareProCustomManager) = 18 | SketchwareProExporter(locations, project = this, customsManager = customManager) 19 | 20 | /** 21 | * Returns files which was edited. 22 | * @return list with files. 23 | */ 24 | suspend fun getSourceEditedFiles() = 25 | sourceEditedFolder.getFilesOrEmpty() 26 | 27 | /** 28 | * Returns files from assets directory 29 | * @return list with files. 30 | */ 31 | suspend fun getAssets() = File(locations.dataFolder, "files/assets").getFilesOrEmpty() 32 | 33 | /** 34 | * Returns list of files with background services. 35 | * @return list with '.java' files. 36 | */ 37 | suspend fun getBackgroundServices() = 38 | File(locations.dataFolder, "files/broadcast").getFilesOrEmpty() 39 | 40 | /** 41 | * Returns instance of [Resources] to access with project resources. 42 | */ 43 | fun getResources() = Resources(File(locations.dataFolder, "files/resource")) 44 | 45 | inner class Resources internal constructor(private val resourceFolder: File) { 46 | /** 47 | * Gets list of files in /files/resource/anim folder. 48 | * @return xml files with animations. 49 | */ 50 | suspend fun getAnimations() = File(resourceFolder, "anim").getFilesOrEmpty() 51 | 52 | /** 53 | * Gets list of xml files in /files/resource/layout folder. 54 | * @return xml files with animations. 55 | */ 56 | suspend fun getLayouts() = File(resourceFolder, "layout").getFilesOrEmpty() 57 | 58 | } 59 | 60 | } -------------------------------------------------------------------------------- /src/main/kotlin/io/sketchware/manager/projects/entities/SketchwareProject.kt: -------------------------------------------------------------------------------- 1 | package io.sketchware.manager.projects.entities 2 | 3 | import io.sketchware.exceptions.ValueRequireException 4 | import io.sketchware.manager.exporters.ProjectExporter 5 | import io.sketchware.manager.projects.data.* 6 | import io.sketchware.models.projects.ProjectConfigModel 7 | import io.sketchware.models.projects.ProjectFilesLocations 8 | import io.sketchware.utils.SketchwareEncryptor.decrypt 9 | import io.sketchware.utils.SketchwareEncryptor.encrypt 10 | import io.sketchware.utils.internal.* 11 | import kotlinx.coroutines.CoroutineScope 12 | import kotlinx.coroutines.CoroutineStart 13 | import kotlinx.coroutines.Dispatchers 14 | import kotlinx.coroutines.async 15 | import java.io.File 16 | 17 | open class SketchwareProject( 18 | open val locations: ProjectFilesLocations 19 | ) : CoroutineScope { 20 | 21 | override val coroutineContext = Dispatchers.Default 22 | 23 | private val logicManager = async(start = CoroutineStart.LAZY) { 24 | LogicManager(File(locations.dataFolder, "logic")) 25 | } 26 | 27 | private val fileManager = async(start = CoroutineStart.LAZY) { 28 | FileManager(File(locations.dataFolder, "file")) 29 | } 30 | 31 | private val libraryManager = async(start = CoroutineStart.LAZY) { 32 | LibraryManager(File(locations.dataFolder, "library")) 33 | } 34 | 35 | private val viewManager = async(start = CoroutineStart.LAZY) { 36 | ViewManager(File(locations.dataFolder, "view")) 37 | } 38 | 39 | private val resourcesManager = async(start = CoroutineStart.LAZY) { 40 | ResourcesManager(File(locations.dataFolder, "resource")) 41 | } 42 | 43 | 44 | suspend fun getLogicManager() = logicManager.await() 45 | suspend fun getFileManager() = fileManager.await() 46 | suspend fun getLibraryManager() = libraryManager.await() 47 | suspend fun getViewManager() = viewManager.await() 48 | suspend fun getResourcesManager() = resourcesManager.await() 49 | 50 | /** 51 | * Copies project to specified locations in [newLocations]. 52 | * @param newProjectId - new project id (config will be overwritten) 53 | * @param newLocations - new project locations. 54 | */ 55 | open suspend fun copy(newProjectId: Int, newLocations: ProjectFilesLocations) = with(locations) { 56 | dataFolder.copy(newLocations.dataFolder) 57 | projectMainDirectory?.copy( 58 | locations.projectMainDirectory 59 | ?: throw ValueRequireException( 60 | "newLocations.projectMainDirectory", 61 | "You should specify [projectMainDirectory] due to it exists in [locations]" 62 | ) 63 | ) 64 | projectFile.copy(locations.projectFile) 65 | resources.soundsFolder.copy(newLocations.resources.soundsFolder) 66 | resources.fontsFolder.copy(newLocations.resources.fontsFolder) 67 | resources.iconsFolder.copy(newLocations.resources.iconsFolder) 68 | resources.imagesFolder.copy(newLocations.resources.iconsFolder) 69 | } 70 | 71 | /** 72 | * Simple project exporter. 73 | * Will not export custom settings or anything other than project files. 74 | */ 75 | open val simpleExporter by lazy { ProjectExporter(locations) } 76 | 77 | /** 78 | * Returns config with data about sketchware project. 79 | * @return [ProjectConfigModel] with data about project. 80 | * @see ProjectConfigModel 81 | */ 82 | open suspend fun getConfig() = 83 | locations.projectFile.read().decrypt().byteArrayToString().serialize() 84 | 85 | /** 86 | * Edits sketchware project config. 87 | * @param editor lambda with [ProjectConfigModel] in context. 88 | */ 89 | open suspend fun editConfig(editor: ProjectConfigModel.() -> Unit) = 90 | locations.projectFile.write( 91 | getConfig().apply(editor).deserialize().toByteArray().encrypt() 92 | ) 93 | 94 | /** 95 | * Deletes all project files. 96 | */ 97 | open suspend fun delete() { 98 | locations.dataFolder.remove() 99 | locations.projectFile.remove() 100 | locations.projectMainDirectory?.remove() 101 | locations.resources.apply { 102 | removeFiles(imagesFolder, iconsFolder, fontsFolder, soundsFolder) 103 | } 104 | } 105 | } 106 | 107 | internal fun SketchwareProject.toSketchwareProProject(sourceEditedFolder: File) = 108 | SketchwareProProject(locations, sourceEditedFolder) 109 | 110 | internal fun SketchwareProject.toSketchwareStudioProject(sourceEditedFolder: File) = 111 | SketchwareStudioProject(locations, sourceEditedFolder) -------------------------------------------------------------------------------- /src/main/kotlin/io/sketchware/manager/projects/entities/SketchwareStudioProject.kt: -------------------------------------------------------------------------------- 1 | package io.sketchware.manager.projects.entities 2 | 3 | import io.sketchware.manager.customs.SketchwareStudioCustomManager 4 | import io.sketchware.manager.exporters.SketchwareProExporter 5 | import io.sketchware.models.ProjectType 6 | import io.sketchware.models.projects.ProjectFilesLocations 7 | import java.io.File 8 | 9 | class SketchwareStudioProject( 10 | override val locations: ProjectFilesLocations, 11 | override val sourceEditedFolder: File, 12 | ) : SketchwareProProject(locations, sourceEditedFolder) { 13 | /** 14 | * Sketchware Studio Project exporter. Exports project fully with all custom components / etc. 15 | */ 16 | fun getExporter(customManager: SketchwareStudioCustomManager): SketchwareProExporter { 17 | return SketchwareProExporter(locations, ProjectType.SKETCHWARE_STUDIO, this, customManager) 18 | } 19 | } -------------------------------------------------------------------------------- /src/main/kotlin/io/sketchware/models/ExportableItem.kt: -------------------------------------------------------------------------------- 1 | package io.sketchware.models 2 | 3 | import java.io.File 4 | 5 | data class ExportableItem( 6 | val internalPath: String, 7 | val file: File 8 | ) 9 | 10 | -------------------------------------------------------------------------------- /src/main/kotlin/io/sketchware/models/ProjectType.kt: -------------------------------------------------------------------------------- 1 | package io.sketchware.models 2 | 3 | import io.sketchware.interfaces.IdInterface 4 | 5 | enum class ProjectType(override val id: Int) : IdInterface { 6 | SKETCHWARE(1), SKETCHWARE_PRO(2), SKETCHWARE_STUDIO(3) 7 | } -------------------------------------------------------------------------------- /src/main/kotlin/io/sketchware/models/collections/BlockCollectionItem.kt: -------------------------------------------------------------------------------- 1 | package io.sketchware.models.collections 2 | 3 | import io.sketchware.interfaces.CollectionItem 4 | import io.sketchware.models.projects.BlockModel 5 | import io.sketchware.utils.serializers.ListBlockModelSerializer 6 | import kotlinx.serialization.Serializable 7 | 8 | /** 9 | * Class with data about moreblock collection item. 10 | */ 11 | @Serializable 12 | data class BlockCollectionItem( 13 | /** 14 | * Name of collection item. 15 | */ 16 | override val name: String, 17 | /** 18 | * Contains data about collection item. 19 | * For moreblocks it is list of blocks in moreblock logic. 20 | */ 21 | @Serializable(ListBlockModelSerializer::class) 22 | override val data: List, 23 | /** 24 | * Does not contain anything for this type of collection. 25 | */ 26 | override val reserved1: String? = null 27 | ) : CollectionItem, String> -------------------------------------------------------------------------------- /src/main/kotlin/io/sketchware/models/collections/FileCollectionItem.kt: -------------------------------------------------------------------------------- 1 | package io.sketchware.models.collections 2 | 3 | import io.sketchware.interfaces.CollectionItem 4 | import kotlinx.serialization.Serializable 5 | 6 | @Serializable 7 | data class FileCollectionItem( 8 | /** 9 | * Name of collection. 10 | */ 11 | override val name: String, 12 | /** 13 | * For this type of collection, it contains name of file. 14 | */ 15 | override val data: String, 16 | /** 17 | * Does not contain anything for this type of collection. 18 | */ 19 | override val reserved1: String? = null 20 | ) : CollectionItem -------------------------------------------------------------------------------- /src/main/kotlin/io/sketchware/models/collections/MoreblockCollectionItem.kt: -------------------------------------------------------------------------------- 1 | package io.sketchware.models.collections 2 | 3 | import io.sketchware.interfaces.CollectionItem 4 | import io.sketchware.models.projects.BlockModel 5 | import io.sketchware.models.projects.SpecField 6 | import io.sketchware.utils.serializers.ListBlockModelSerializer 7 | import io.sketchware.utils.serializers.SpecSerializer 8 | import kotlinx.serialization.Serializable 9 | 10 | /** 11 | * Class with data about moreblock collection item. 12 | */ 13 | @Serializable 14 | data class MoreblockCollectionItem( 15 | /** 16 | * Name of collection item. 17 | */ 18 | override val name: String, 19 | /** 20 | * Contains data about collection item. 21 | * For moreblocks it is list of blocks in moreblock logic. 22 | */ 23 | @Serializable(with = ListBlockModelSerializer::class) 24 | override val data: List, 25 | /** 26 | * Contains data about collection. Consist only for moreblock. 27 | * There is spec for moreblock. 28 | */ 29 | @Serializable(with = SpecSerializer::class) 30 | override val reserved1: List 31 | ) : CollectionItem, List> 32 | -------------------------------------------------------------------------------- /src/main/kotlin/io/sketchware/models/customs/BlockInputMenu.kt: -------------------------------------------------------------------------------- 1 | package io.sketchware.models.customs 2 | 3 | import kotlinx.serialization.Serializable 4 | 5 | /** 6 | * Class with custom menu data. 7 | */ 8 | @Serializable 9 | data class BlockInputMenu( 10 | /** 11 | * Unique string identification of menu 12 | */ 13 | val id: String, 14 | /** 15 | * Menu selector name (shows in block, example: View) 16 | */ 17 | val name: String 18 | ) 19 | -------------------------------------------------------------------------------- /src/main/kotlin/io/sketchware/models/customs/CustomBlock.kt: -------------------------------------------------------------------------------- 1 | package io.sketchware.models.customs 2 | 3 | import io.sketchware.models.projects.SpecField 4 | import io.sketchware.utils.serializers.SpecSerializer 5 | import io.sketchware.utils.serializers.StringNumberConvertor 6 | import kotlinx.serialization.Serializable 7 | 8 | @Serializable 9 | data class CustomBlock( 10 | var typeName: String, 11 | /** 12 | * Hexadecimal string with block's color. 13 | */ 14 | var color: String, 15 | /** 16 | * Block name 17 | */ 18 | var name: String, 19 | /** 20 | * It stores data about the group that contains the block. 21 | */ 22 | @Serializable(with = StringNumberConvertor::class) 23 | var palette: Int, 24 | @Serializable(with = SpecSerializer::class) 25 | var spec: List, 26 | var type: String, 27 | /** 28 | * Source code of block (it logic). 29 | */ 30 | var code: String 31 | ) -------------------------------------------------------------------------------- /src/main/kotlin/io/sketchware/models/customs/CustomBlockGroup.kt: -------------------------------------------------------------------------------- 1 | package io.sketchware.models.customs 2 | 3 | import kotlinx.serialization.Serializable 4 | 5 | /** 6 | * A class with data about a specific group of blocks. 7 | */ 8 | @Serializable 9 | data class CustomBlockGroup( 10 | /** 11 | * Blocks group unique identify. 12 | */ 13 | var groupId: Int, 14 | /** 15 | * Block group name 16 | */ 17 | var name: String, 18 | /** 19 | * Blocks group color (shows in the list of block types, example: Variable, Operator, etc.) 20 | */ 21 | var hexColor: String, 22 | /** 23 | * List of custom blocks in specific group. 24 | */ 25 | var blocks: List 26 | ) { 27 | override fun equals(other: Any?): Boolean { 28 | (other as CustomBlockGroup) 29 | return groupId == other.groupId 30 | && name == other.name 31 | && hexColor == other.hexColor 32 | && blocks == other.blocks 33 | } 34 | 35 | override fun hashCode(): Int { 36 | var result = groupId 37 | result = 31 * result + name.hashCode() 38 | result = 31 * result + hexColor.hashCode() 39 | result = 31 * result + blocks.hashCode() 40 | return result 41 | } 42 | } -------------------------------------------------------------------------------- /src/main/kotlin/io/sketchware/models/customs/CustomComponent.kt: -------------------------------------------------------------------------------- 1 | package io.sketchware.models.customs 2 | 3 | import io.sketchware.utils.serializers.StringNumberConvertor 4 | import kotlinx.serialization.SerialName 5 | import kotlinx.serialization.Serializable 6 | 7 | @Serializable 8 | data class CustomComponent( 9 | @Serializable(StringNumberConvertor::class) 10 | var icon: Int, 11 | @SerialName("class") 12 | var `class`: String, 13 | var description: String, 14 | var defineAdditionalVar: String, 15 | var typeName: String, 16 | @Serializable(StringNumberConvertor::class) 17 | var id: Int, 18 | var url: String, 19 | var name: String, 20 | var additionalVar: String, 21 | var varName: String, 22 | var imports: String, 23 | var buildClass: String 24 | ) -------------------------------------------------------------------------------- /src/main/kotlin/io/sketchware/models/customs/CustomEvent.kt: -------------------------------------------------------------------------------- 1 | package io.sketchware.models.customs 2 | 3 | import io.sketchware.models.projects.SpecField 4 | import kotlinx.serialization.Contextual 5 | import kotlinx.serialization.Serializable 6 | 7 | @Serializable 8 | data class CustomEvent( 9 | /** 10 | * Spec of listener 11 | */ 12 | @Contextual 13 | var spec: List, 14 | /** 15 | * Id of icons which displaying for every listener. 16 | */ 17 | var iconId: Int, 18 | /** 19 | * Unique name of event. 20 | */ 21 | var id: String, 22 | var description: String, 23 | var parameters: String, 24 | var name: String, 25 | var code: String 26 | ) -------------------------------------------------------------------------------- /src/main/kotlin/io/sketchware/models/customs/CustomListenerGroup.kt: -------------------------------------------------------------------------------- 1 | package io.sketchware.models.customs 2 | 3 | import kotlinx.serialization.Serializable 4 | 5 | 6 | @Serializable 7 | data class CustomListenerGroup( 8 | /** 9 | * Event name. 10 | */ 11 | var name: String, 12 | /** 13 | * Independent class/method. 14 | */ 15 | var independent: Boolean, 16 | /** 17 | * Custom import for event. 18 | */ 19 | var customImport: String, 20 | /** 21 | * Code of event group. 22 | */ 23 | var code: String, 24 | var events: List 25 | ) -------------------------------------------------------------------------------- /src/main/kotlin/io/sketchware/models/customs/CustomMenu.kt: -------------------------------------------------------------------------------- 1 | package io.sketchware.models.customs 2 | 3 | import kotlinx.serialization.Serializable 4 | 5 | @Serializable 6 | data class CustomMenu( 7 | /** 8 | * String identify of custom menu. 9 | */ 10 | var id: String, 11 | /** 12 | * Name which shows in block. 13 | */ 14 | var name: String, 15 | /** 16 | * Title which shows in dialog while choosing option. 17 | */ 18 | var title: String, 19 | /** 20 | * Options to choose. 21 | */ 22 | var options: List 23 | ) { 24 | fun toSWStudioMenu() = SWStudioMenu(name, title, options) 25 | } 26 | -------------------------------------------------------------------------------- /src/main/kotlin/io/sketchware/models/customs/MenuData.kt: -------------------------------------------------------------------------------- 1 | package io.sketchware.models.customs 2 | 3 | import kotlinx.serialization.Serializable 4 | 5 | @Serializable 6 | data class MenuData( 7 | val title: String, 8 | val value: String 9 | ) 10 | -------------------------------------------------------------------------------- /src/main/kotlin/io/sketchware/models/customs/Palette.kt: -------------------------------------------------------------------------------- 1 | package io.sketchware.models.customs 2 | 3 | import kotlinx.serialization.Serializable 4 | 5 | @Serializable 6 | data class Palette( 7 | val color: String, 8 | val name: String 9 | ) -------------------------------------------------------------------------------- /src/main/kotlin/io/sketchware/models/customs/SWCustomSettings.kt: -------------------------------------------------------------------------------- 1 | package io.sketchware.models.customs 2 | 3 | import io.sketchware.utils.serializers.PathToFileSerializer 4 | import kotlinx.serialization.SerialName 5 | import kotlinx.serialization.Serializable 6 | import java.io.File 7 | 8 | @Serializable 9 | data class SWCustomSettings( 10 | @SerialName("blockDir") 11 | @Serializable(with = PathToFileSerializer::class) 12 | val blockFile: File, 13 | @SerialName("palletteDir") 14 | @Serializable(with = PathToFileSerializer::class) 15 | val paletteFile: File, 16 | @SerialName("built-in-blocks") 17 | val builtInBlocks: Boolean, 18 | @SerialName("always-show-blocks") 19 | val alwaysShowBlocks: Boolean 20 | ) 21 | -------------------------------------------------------------------------------- /src/main/kotlin/io/sketchware/models/customs/SWStudioMenu.kt: -------------------------------------------------------------------------------- 1 | package io.sketchware.models.customs 2 | 3 | import kotlinx.serialization.Serializable 4 | 5 | @Serializable 6 | data class SWStudioMenu( 7 | val name: String, 8 | val title: String, 9 | val data: List 10 | ) -------------------------------------------------------------------------------- /src/main/kotlin/io/sketchware/models/export/ExportConfig.kt: -------------------------------------------------------------------------------- 1 | package io.sketchware.models.export 2 | 3 | import io.sketchware.models.ProjectType 4 | import kotlinx.serialization.Contextual 5 | import kotlinx.serialization.Serializable 6 | 7 | @Serializable 8 | data class ExportConfig( 9 | val version: Double, 10 | @Contextual 11 | val projectType: ProjectType, 12 | val filesConfig: ExportFilesConfig, 13 | val customFilesConfig: ExportedCustomFilesConfig? = null 14 | ) -------------------------------------------------------------------------------- /src/main/kotlin/io/sketchware/models/export/ExportFilesConfig.kt: -------------------------------------------------------------------------------- 1 | package io.sketchware.models.export 2 | 3 | import kotlinx.serialization.Serializable 4 | 5 | @Serializable 6 | data class ExportFilesConfig( 7 | val projectFileName: String = "project", 8 | val dataFolderName: String = "data", 9 | val resources: ExportDataFilesConfig = ExportDataFilesConfig() 10 | ) { 11 | companion object { 12 | /** 13 | * @return Default config which are uses for SH-Recovery projects backups. 14 | */ 15 | fun getSHRecoveryDefaultConfig() = ExportFilesConfig( 16 | "project", "data", 17 | ExportDataFilesConfig( 18 | "icons", "images", "fonts", "sounds" 19 | ) 20 | ) 21 | } 22 | } 23 | 24 | @Serializable 25 | data class ExportDataFilesConfig( 26 | val iconsFolderName: String = "resources/icons", 27 | val imagesFolderName: String = "resources/images", 28 | val fontsFolderName: String = "resources/fonts", 29 | val soundsFolderName: String = "resources/sounds" 30 | ) -------------------------------------------------------------------------------- /src/main/kotlin/io/sketchware/models/export/ExportedCustomFilesConfig.kt: -------------------------------------------------------------------------------- 1 | package io.sketchware.models.export 2 | 3 | import kotlinx.serialization.Serializable 4 | import java.io.File 5 | 6 | @Serializable 7 | data class ExportedCustomFilesConfig( 8 | val blocksFileName: String = "customs/blocks.json", 9 | val componentsFileName: String = "customs/components.json", 10 | val listenersFileName: String = "custom/listeners.json", 11 | val menusFileName: String = "custom/menus.json", 12 | val sourceEditedFolderName: String = "SourceEdited" 13 | ) { 14 | fun toExportedCustomFiles(rootFolder: File) = 15 | ExportedCustomFiles( 16 | File(rootFolder, blocksFileName), 17 | File(rootFolder, componentsFileName), 18 | File(rootFolder, listenersFileName), 19 | File(rootFolder, menusFileName), 20 | File(rootFolder, sourceEditedFolderName) 21 | ) 22 | } 23 | 24 | data class ExportedCustomFiles( 25 | val blocksFile: File, 26 | val componentsFile: File, 27 | val listenersFile: File, 28 | val menusFile: File, 29 | val sourceEditedFolder: File 30 | ) 31 | -------------------------------------------------------------------------------- /src/main/kotlin/io/sketchware/models/projects/ActivityOption.kt: -------------------------------------------------------------------------------- 1 | package io.sketchware.models.projects 2 | 3 | import io.sketchware.interfaces.IdInterface 4 | import io.sketchware.utils.SWConst 5 | import kotlinx.serialization.Serializable 6 | 7 | @Serializable 8 | enum class ActivityOption(override val id: Int) : IdInterface { 9 | /** 10 | * The option says that Drawer is present in the activity. 11 | */ 12 | DRAWER(SWConst.OPTION_ACTIVITY_DRAWER), 13 | 14 | /** 15 | * The option says that FAB is present in the activity. 16 | */ 17 | FAB(SWConst.OPTION_ACTIVITY_FAB), 18 | 19 | /** 20 | * The option says that Status Bar isn't present in the activity. 21 | */ 22 | FULLSCREEN(SWConst.OPTION_ACTIVITY_FULL_SCREEN), 23 | 24 | /** 25 | * The option says that Toolbar is present in the activity. 26 | */ 27 | TOOLBAR(SWConst.OPTION_ACTIVITY_TOOLBAR) 28 | } -------------------------------------------------------------------------------- /src/main/kotlin/io/sketchware/models/projects/ActivityTheme.kt: -------------------------------------------------------------------------------- 1 | package io.sketchware.models.projects 2 | 3 | import io.sketchware.interfaces.IdInterface 4 | import io.sketchware.utils.SWConst 5 | 6 | enum class ActivityTheme(override val id: Int) : IdInterface { 7 | /** 8 | * Says that activity don't have any special theme. 9 | */ 10 | NONE(SWConst.THEME_NONE), 11 | 12 | /** 13 | * Says that activity has default theme. 14 | */ 15 | DEFAULT(SWConst.THEME_DEFAULT), 16 | 17 | /** 18 | * Says that activity has no action bar. 19 | */ 20 | NO_ACTIONBAR(SWConst.THEME_NO_ACTIONBAR), 21 | 22 | /** 23 | * Says that activity has fullscreen theme. 24 | */ 25 | FULLSCREEN(SWConst.THEME_FULL_SCREEN) 26 | } -------------------------------------------------------------------------------- /src/main/kotlin/io/sketchware/models/projects/BlockModel.kt: -------------------------------------------------------------------------------- 1 | package io.sketchware.models.projects 2 | 3 | import io.sketchware.utils.serializers.SpecSerializer 4 | import io.sketchware.utils.serializers.StringNumberConvertor 5 | import kotlinx.serialization.Serializable 6 | 7 | /** 8 | * The class stores data about a block that is in some kind of logic. 9 | */ 10 | @Serializable 11 | data class BlockModel( 12 | /** 13 | * Block color in [Int] type. 14 | */ 15 | var color: Int, 16 | /** 17 | * Block unique id (in specific scope) 18 | */ 19 | @Serializable(StringNumberConvertor::class) 20 | var id: Int, 21 | /** 22 | * Next block id or '-1' if it doesn't have next block. 23 | */ 24 | var nextBlock: Int, 25 | /** 26 | * Unique block code. 27 | */ 28 | var opCode: String, 29 | /** 30 | * Arguments of block function. 31 | */ 32 | var parameters: List, 33 | /** 34 | * List of [SpecField] with info about block and it's arguments. 35 | */ 36 | @Serializable(with = SpecSerializer::class) 37 | var spec: List, 38 | var subStack1: Int, 39 | var subStack2: Int, 40 | /** 41 | * Name of block type. 42 | */ 43 | var type: String, 44 | /** 45 | * Name of type. 46 | */ 47 | var typeName: String 48 | ) -------------------------------------------------------------------------------- /src/main/kotlin/io/sketchware/models/projects/ComponentModel.kt: -------------------------------------------------------------------------------- 1 | package io.sketchware.models.projects 2 | 3 | import kotlinx.serialization.SerialName 4 | import kotlinx.serialization.Serializable 5 | 6 | /** 7 | * Stores data about a component in a project. 8 | */ 9 | @Serializable 10 | data class ComponentModel( 11 | /** 12 | * The unique name of the component in the activity. 13 | */ 14 | @SerialName("componentId") 15 | val id: String, 16 | /** 17 | * Any data about a component (may be missing) is different everywhere. 18 | */ 19 | val param1: String, 20 | /** 21 | * Any data about a component (may be missing) is different everywhere. 22 | */ 23 | val param2: String, 24 | /** 25 | * Any data about a component (may be missing) is different everywhere. 26 | */ 27 | val param3: String, 28 | /** 29 | * Unique component type id. 30 | */ 31 | val type: Int 32 | ) -------------------------------------------------------------------------------- /src/main/kotlin/io/sketchware/models/projects/FileType.kt: -------------------------------------------------------------------------------- 1 | package io.sketchware.models.projects 2 | 3 | import io.sketchware.interfaces.IdInterface 4 | import io.sketchware.utils.SWConst 5 | 6 | enum class FileType(override val id: Int) : IdInterface { 7 | Activity(SWConst.PROJECT_FILE_TYPE_ACTIVITY), 8 | CustomView(SWConst.PROJECT_FILE_TYPE_CUSTOM_VIEW), 9 | Drawer(SWConst.PROJECT_FILE_TYPE_DRAWER) 10 | } -------------------------------------------------------------------------------- /src/main/kotlin/io/sketchware/models/projects/KeyboardSetting.kt: -------------------------------------------------------------------------------- 1 | package io.sketchware.models.projects 2 | 3 | import io.sketchware.interfaces.IdInterface 4 | import io.sketchware.utils.SWConst 5 | 6 | enum class KeyboardSetting(override val id: Int) : IdInterface { 7 | Unspecified(SWConst.KEYBOARD_STATE_UNSPECIFIED), 8 | Visible(SWConst.KEYBOARD_STATE_VISIBLE), 9 | Hidden(SWConst.KEYBOARD_STATE_HIDDEN) 10 | } -------------------------------------------------------------------------------- /src/main/kotlin/io/sketchware/models/projects/MenuArgumentType.kt: -------------------------------------------------------------------------------- 1 | package io.sketchware.models.projects 2 | 3 | enum class MenuArgumentType(val serialName: String) { 4 | Intent("intent"), 5 | SharedPreferences("sharedpreferences"), 6 | Calendar("calendar"), 7 | Vibrator("vibrator"), 8 | Timer("timer"), 9 | Dialog("dialog"), 10 | MediaPlayer("mediaplayer"), 11 | SoundPool("soundpool"), 12 | ObjectAnimator("objectanimator"), 13 | Camera("camera"), 14 | FilePicker("picker"), 15 | Gyroscope("gyroscope"), 16 | FirebaseDB("firebase"), 17 | FirebaseAuth("firebaseauth"), 18 | FirebaseStorage("firebasestorage"), 19 | 20 | //TODO right serial name 21 | AdmobInterstitialAd("admobInterstitialAd"), 22 | TextToSpeech("texttospeech"), 23 | SpeechToText("speechtotext"), 24 | RequestNetwork("requestnetwork"), 25 | 26 | //TODO right serial name 27 | BluetoothConnect("bluetooth"), 28 | LocationManager("locationmanager") 29 | } -------------------------------------------------------------------------------- /src/main/kotlin/io/sketchware/models/projects/Orientation.kt: -------------------------------------------------------------------------------- 1 | package io.sketchware.models.projects 2 | 3 | import io.sketchware.interfaces.IdInterface 4 | import io.sketchware.utils.SWConst 5 | 6 | enum class Orientation(override val id: Int) : IdInterface { 7 | Portrait(SWConst.ORIENTATION_PORTRAIT), 8 | Landscape(SWConst.ORIENTATION_LANDSCAPE), 9 | Both(SWConst.ORIENTATION_BOTH) 10 | } -------------------------------------------------------------------------------- /src/main/kotlin/io/sketchware/models/projects/ProjectConfigModel.kt: -------------------------------------------------------------------------------- 1 | package io.sketchware.models.projects 2 | 3 | import io.sketchware.utils.serializers.StringNumberConvertor 4 | import kotlinx.serialization.SerialName 5 | import kotlinx.serialization.Serializable 6 | 7 | /** 8 | * Data class with data about the project name, id, settings for the theme, etc. 9 | * It serializes usually from ../.sketchware/mysc/list/[projectId]/project 10 | */ 11 | @Serializable 12 | data class ProjectConfigModel( 13 | /** 14 | * The version of Sketchware on which the project was last modified. 15 | */ 16 | @SerialName("sketchware_ver") 17 | var sketchwareVersion: Double, 18 | /** 19 | * Unique digital identifier for the project. 20 | */ 21 | @SerialName("sc_id") 22 | @Serializable(StringNumberConvertor::class) 23 | var projectId: Int, 24 | /** 25 | * Project's package name (example: com.android.test) 26 | */ 27 | @SerialName("my_sc_pkg_name") 28 | var packageName: String, 29 | /** 30 | * Project's app name 31 | */ 32 | @SerialName("my_app_name") 33 | var appName: String, 34 | /** 35 | * Project's app version code (Used to indicate new versions.) 36 | */ 37 | @SerialName("sc_ver_code") 38 | var appVersionCode: String, 39 | /** 40 | * Project theme property. 41 | * Color for a ripple effect (on buttons, etc). 42 | */ 43 | @SerialName("color_control_highlight") 44 | var colorControlHighLight: Double, 45 | /** 46 | * Project theme property. 47 | * Color for the app bar and other primary UI elements. 48 | */ 49 | @SerialName("color_primary") 50 | var colorPrimary: Double, 51 | /** 52 | * Project theme property. 53 | * A secondary color for controls like checkboxes and text fields. 54 | */ 55 | @SerialName("color_accent") 56 | var colorAccent: Double, 57 | /** 58 | * Project creation date. 59 | */ 60 | @SerialName("my_sc_reg_dt") 61 | var projectCreationDate: String, 62 | /** 63 | * Project theme property. 64 | * Responsible for color in status bar. 65 | */ 66 | @SerialName("color_primary_dark") 67 | var colorPrimaryDark: Double, 68 | /** 69 | * Project theme property. 70 | * Responsible for color while on widget focused. 71 | */ 72 | @SerialName("color_control_normal") 73 | var colorControlNormal: Double, 74 | @SerialName("sc_ver_name") 75 | var appVersionName: String, 76 | /** 77 | * Project name (Not to be confused with [appName].) 78 | */ 79 | @SerialName("my_ws_name") 80 | var projectName: String, 81 | /** 82 | * Is a custom icon installed. 83 | */ 84 | @SerialName("custom_icon") 85 | var customIcon: Boolean 86 | ) 87 | -------------------------------------------------------------------------------- /src/main/kotlin/io/sketchware/models/projects/ProjectFilesLocations.kt: -------------------------------------------------------------------------------- 1 | package io.sketchware.models.projects 2 | 3 | import java.io.File 4 | 5 | /** 6 | * Class with data about files where contains data/resources for some project. 7 | */ 8 | open class ProjectFilesLocations( 9 | /** 10 | * Project directory where "project" file located. 11 | */ 12 | val projectMainDirectory: File? = null, 13 | /** 14 | * File with data about project. 15 | * @see ProjectConfigModel to see what info file contains. 16 | */ 17 | val projectFile: File, 18 | /** 19 | * Folder with data about logic, view, project activities / custom views, etc. 20 | */ 21 | val dataFolder: File, 22 | val resources: ProjectResourcesFiles 23 | ) { 24 | companion object { 25 | fun getSWDefault(swRootFolder: File, id: Int) = ProjectFilesLocations( 26 | File(swRootFolder, "mysc/list/$id"), File(swRootFolder, "mysc/list/$id/project"), 27 | File(swRootFolder, "data/$id"), ProjectResourcesFiles( 28 | File(swRootFolder, "resources/icons/$id"), File(swRootFolder, "resources/images/$id"), 29 | File(swRootFolder, "resources/sounds/$id"), File(swRootFolder, "resources/fonts/$id") 30 | ) 31 | ) 32 | 33 | fun getDefaultExport(rootFolder: File) = ProjectFilesLocations( 34 | null, File(rootFolder, "project"), 35 | File(rootFolder, "data"), ProjectResourcesFiles( 36 | File(rootFolder, "resources/icons"), File(rootFolder, "resources/images"), 37 | File(rootFolder, "resources/sounds"), File(rootFolder, "resources/fonts") 38 | ) 39 | ) 40 | } 41 | } 42 | 43 | /** 44 | * Class with data about project resources. 45 | */ 46 | open class ProjectResourcesFiles( 47 | /** 48 | * Folder with all app icons (usually there only one icon with name app_icon.png). 49 | */ 50 | open val iconsFolder: File, 51 | /** 52 | * Folder with project's images. 53 | */ 54 | open val imagesFolder: File, 55 | /** 56 | * Folder with project's sounds 57 | */ 58 | open val soundsFolder: File, 59 | /** 60 | * Folder with project's fonts. 61 | */ 62 | open val fontsFolder: File 63 | ) 64 | -------------------------------------------------------------------------------- /src/main/kotlin/io/sketchware/models/projects/ProjectResource.kt: -------------------------------------------------------------------------------- 1 | package io.sketchware.models.projects 2 | 3 | import kotlinx.serialization.SerialName 4 | import kotlinx.serialization.Serializable 5 | 6 | @Serializable 7 | data class ProjectResource( 8 | /** 9 | * Full name of resource (example: logo.png) 10 | */ 11 | @SerialName("resFullName") 12 | val fullName: String, 13 | /** 14 | * Resource name (example: logo) 15 | */ 16 | @SerialName("resName") 17 | val name: String, 18 | @SerialName("resType") 19 | val type: Int 20 | ) -------------------------------------------------------------------------------- /src/main/kotlin/io/sketchware/models/projects/ScaleType.kt: -------------------------------------------------------------------------------- 1 | package io.sketchware.models.projects 2 | 3 | import kotlinx.serialization.Serializable 4 | 5 | /** 6 | * ImageView scale type. 7 | */ 8 | @Serializable 9 | enum class ScaleType { 10 | FIT_XY, FIT_START, FIT_CENTER, FIT_END, CENTER, CENTER_CROP, CENTER_INSIDE 11 | } -------------------------------------------------------------------------------- /src/main/kotlin/io/sketchware/models/projects/SketchwareDataFileModel.kt: -------------------------------------------------------------------------------- 1 | package io.sketchware.models.projects 2 | 3 | import io.sketchware.utils.internal.snakeToUpperCamelCase 4 | import kotlinx.serialization.Contextual 5 | import kotlinx.serialization.Serializable 6 | 7 | /** 8 | * Stores data about activity or custom view. 9 | */ 10 | @Serializable 11 | class SketchwareDataFileModel( 12 | /** 13 | * Activity / custom view name. 14 | */ 15 | val fileName: String, 16 | /** 17 | * File type (Activity / Custom View). 18 | */ 19 | @Contextual 20 | val fileType: FileType, 21 | @Contextual 22 | val keyboardSetting: KeyboardSetting, 23 | /** 24 | * Contains data about enabled components in activity: 25 | * For example: is drawer enabled, is FAB enabled, etc. 26 | */ 27 | val options: Int, 28 | /** 29 | * Allowed activity orientation setting. 30 | */ 31 | @Contextual 32 | val orientation: Orientation, 33 | @Contextual 34 | val theme: ActivityTheme 35 | ) { 36 | val activityName get() = "${fileName.snakeToUpperCamelCase()}Activity" 37 | } -------------------------------------------------------------------------------- /src/main/kotlin/io/sketchware/models/projects/SketchwareEventModel.kt: -------------------------------------------------------------------------------- 1 | package io.sketchware.models.projects 2 | 3 | import kotlinx.serialization.SerialName 4 | import kotlinx.serialization.Serializable 5 | 6 | /** 7 | * Stores event data. 8 | */ 9 | @Serializable 10 | data class SketchwareEventModel( 11 | /** 12 | * Event name (example: onClick). 13 | */ 14 | @SerialName("eventName") 15 | var name: String, 16 | /** 17 | * Unique event type id. 18 | */ 19 | @SerialName("eventType") 20 | var type: Int, 21 | /** 22 | * Event target id (example: button1). 23 | */ 24 | var targetId: String, 25 | /** 26 | * Event target type id. 27 | */ 28 | var targetType: Int 29 | ) -------------------------------------------------------------------------------- /src/main/kotlin/io/sketchware/models/projects/SketchwareLibraryDataModel.kt: -------------------------------------------------------------------------------- 1 | package io.sketchware.models.projects 2 | 3 | import kotlinx.serialization.Serializable 4 | 5 | /** 6 | * Sketchware library data. 7 | */ 8 | @Serializable 9 | data class SketchwareLibraryDataModel( 10 | /** 11 | * Ad units list (empty if it isn't admob component. 12 | */ 13 | var adUnits: List, 14 | //TODO description 15 | var data: String, 16 | /** 17 | * Unique library type id. 18 | */ 19 | var libType: Int, 20 | /** 21 | * Any data about a library (may be missing) is different everywhere. 22 | */ 23 | var reserved1: String, 24 | /** 25 | * Any data about a library (may be missing) is different everywhere. 26 | */ 27 | var reserved2: String, 28 | /** 29 | * Any data about a library (may be missing) is different everywhere. 30 | */ 31 | var reserved3: String, 32 | /** 33 | * List of test devices (may be missing if it isn't admob library). 34 | */ 35 | var testDevices: List, 36 | //TODO description 37 | var useYn: String 38 | ) 39 | 40 | @Serializable 41 | data class AdUnit( 42 | val id: String, 43 | val name: String 44 | ) -------------------------------------------------------------------------------- /src/main/kotlin/io/sketchware/models/projects/SketchwareLibraryModel.kt: -------------------------------------------------------------------------------- 1 | package io.sketchware.models.projects 2 | 3 | import kotlinx.serialization.Serializable 4 | 5 | /** 6 | * Stores name of library and it's information. 7 | */ 8 | @Serializable 9 | data class SketchwareLibraryModel( 10 | /** 11 | * Library unique name (example: admob) 12 | */ 13 | var name: String, 14 | /** 15 | * Library information. 16 | */ 17 | var information: SketchwareLibraryDataModel 18 | ) 19 | 20 | -------------------------------------------------------------------------------- /src/main/kotlin/io/sketchware/models/projects/SketchwareMoreblockModel.kt: -------------------------------------------------------------------------------- 1 | package io.sketchware.models.projects 2 | 3 | import kotlinx.serialization.Contextual 4 | import kotlinx.serialization.Serializable 5 | 6 | /** 7 | * Stores data about moreblock (name and data (spec)). 8 | */ 9 | @Serializable 10 | data class SketchwareMoreblockModel( 11 | /** 12 | * Moreblock unique name. 13 | */ 14 | var name: String, 15 | /** 16 | * Moreblock spec. 17 | */ 18 | @Contextual 19 | var data: List 20 | ) { 21 | /** 22 | * Converts to sketchware value format. 23 | */ 24 | override fun toString(): String { 25 | return "$name:${data.toStringValue()}" 26 | } 27 | } 28 | -------------------------------------------------------------------------------- /src/main/kotlin/io/sketchware/models/projects/SpecField.kt: -------------------------------------------------------------------------------- 1 | package io.sketchware.models.projects 2 | 3 | import io.sketchware.exceptions.InvalidMenuArgumentTypeException 4 | import kotlinx.serialization.Serializable 5 | 6 | @Serializable 7 | open class SpecField( 8 | open var text: String 9 | ) { 10 | /** 11 | * [text] is originally formatted of current field. 12 | */ 13 | override fun toString() = text 14 | } 15 | 16 | open class SpecArgument(override var text: String) : SpecField(text) { 17 | /** 18 | * Returns name of argument. 19 | * @return [String] with name of argument. 20 | */ 21 | open val argumentName: String 22 | get() = text.split(".")[1] 23 | } 24 | 25 | data class StringSpecArgument(override var text: String) : SpecArgument(text) 26 | data class BooleanSpecArgument(override var text: String) : SpecArgument(text) 27 | data class NumberSpecArgument(override var text: String) : SpecArgument(text) 28 | 29 | data class MenuSpecArgument(override var text: String) : SpecArgument(text) { 30 | 31 | override val argumentName: String 32 | get() = text.split(".")[2] 33 | 34 | /** 35 | * Tells what type of argument is passed in the list when selected (in Sketchware). 36 | * @throws InvalidMenuArgumentTypeException if argument is custom 37 | * or not implemented yet. 38 | */ 39 | val menuArgumentType: MenuArgumentType 40 | get() = with(menuArgumentTypeName) { 41 | return enumValues().firstOrNull { 42 | it.serialName == this 43 | } ?: throw InvalidMenuArgumentTypeException(this) 44 | } 45 | 46 | /** 47 | * Returns name of menu's argument type. 48 | */ 49 | var menuArgumentTypeName: String 50 | get() = text.split(".")[1] 51 | set(value) { 52 | text = text.split(".").toMutableList().apply { 53 | set(1, value) 54 | }.joinToString(".") 55 | } 56 | 57 | } 58 | 59 | internal fun List.toStringValue() = this.joinToString(" ") -------------------------------------------------------------------------------- /src/main/kotlin/io/sketchware/models/projects/VariableModel.kt: -------------------------------------------------------------------------------- 1 | package io.sketchware.models.projects 2 | 3 | data class VariableModel( 4 | /** 5 | * Variable type id. 6 | */ 7 | val type: Int, 8 | /** 9 | * Name of variable. 10 | */ 11 | val name: String 12 | ) { 13 | /** 14 | * Converts the object to the original Sketchware look. 15 | */ 16 | override fun toString() = "$type:$name" 17 | } 18 | -------------------------------------------------------------------------------- /src/main/kotlin/io/sketchware/models/view/Image.kt: -------------------------------------------------------------------------------- 1 | package io.sketchware.models.view 2 | 3 | import io.sketchware.models.projects.ScaleType 4 | import kotlinx.serialization.Serializable 5 | 6 | @Serializable 7 | data class Image( 8 | var rotate: Int = 0, 9 | var scaleType: ScaleType = ScaleType.CENTER, 10 | var resName: String = "default_image" 11 | ) -------------------------------------------------------------------------------- /src/main/kotlin/io/sketchware/models/view/Layout.kt: -------------------------------------------------------------------------------- 1 | package io.sketchware.models.view 2 | 3 | import kotlinx.serialization.Contextual 4 | import kotlinx.serialization.Serializable 5 | 6 | @Serializable 7 | data class Layout( 8 | var backgroundColor: Int = 0, 9 | var borderColor: Int = 0, 10 | var gravity: Int = 0, 11 | var height: Int = 0, 12 | var layoutGravity: Int = 0, 13 | var marginBottom: Int = 0, 14 | var marginLeft: Int = 0, 15 | var marginRight: Int = 0, 16 | var marginTop: Int = 0, 17 | @Contextual 18 | var orientation: LayoutOrientation = LayoutOrientation.HORIZONTAL, 19 | var paddingBottom: Int = 0, 20 | var paddingLeft: Int = 0, 21 | var paddingRight: Int = 0, 22 | var paddingTop: Int = 0, 23 | var weight: Int = 0, 24 | var weightSum: Int = 0, 25 | var width: Int = 0 26 | ) -------------------------------------------------------------------------------- /src/main/kotlin/io/sketchware/models/view/LayoutOrientation.kt: -------------------------------------------------------------------------------- 1 | package io.sketchware.models.view 2 | 3 | import io.sketchware.interfaces.IdInterface 4 | 5 | enum class LayoutOrientation(override val id: Int) : IdInterface { 6 | VERTICAL(1), HORIZONTAL(-1) 7 | } -------------------------------------------------------------------------------- /src/main/kotlin/io/sketchware/models/view/Text.kt: -------------------------------------------------------------------------------- 1 | package io.sketchware.models.view 2 | 3 | import kotlinx.serialization.Serializable 4 | 5 | @Serializable 6 | data class Text( 7 | var hint: String = "", 8 | var hintColor: Int = -10453621, 9 | var imeOption: Int = 0, 10 | var inputType: Int = 0, 11 | var line: Int = 0, 12 | var singleLine: Int = 0, 13 | var text: String = "", 14 | var textColor: Int = -16777216, 15 | var textFont: String = "default_font", 16 | var textSize: Int = 12, 17 | var textType: Int = 0 18 | ) -------------------------------------------------------------------------------- /src/main/kotlin/io/sketchware/models/view/WidgetRoot.kt: -------------------------------------------------------------------------------- 1 | package io.sketchware.models.view 2 | 3 | import kotlinx.serialization.Serializable 4 | 5 | @Serializable 6 | data class WidgetRoot( 7 | var adSize: String = "", 8 | var adUnitId: String = "", 9 | var alpha: Double = 1.0, 10 | var checked: Int = 0, 11 | var choiceMode: Int = 0, 12 | var clickable: Int = 0, 13 | var convert: String? = null, 14 | var customView: String = "", 15 | var dividerHeight: Int = 0, 16 | var enabled: Int = 0, 17 | var firstDayOfWeek: Int = 0, 18 | var id: String? = null, 19 | var image: Image = Image(), 20 | var indeterminate: String? = null, 21 | var index: Int = 0, 22 | var inject: String? = null, 23 | var layout: Layout? = null, 24 | var parent: String? = null, 25 | var max: Int = 0, 26 | var parentType: Int = 0, 27 | var preId: String? = null, 28 | var preParent: String? = null, 29 | var preIndex: Int = 0, 30 | var preParentType: Int = 0, 31 | var progress: Int = 0, 32 | var progressStyle: String = "?android:progressBarStyle", 33 | var scaleX: Double = 0.0, 34 | var scaleY: Double = 0.0, 35 | var spinnerMode: Int = 0, 36 | var text: Text = Text(), 37 | var translationX: Double = 0.0, 38 | var translationY: Double = 0.0, 39 | var type: Int = 0 40 | ) -------------------------------------------------------------------------------- /src/main/kotlin/io/sketchware/models/view/WidgetType.kt: -------------------------------------------------------------------------------- 1 | package io.sketchware.models.view 2 | 3 | object WidgetType { 4 | /** 5 | * Widget type: LinearLayout (V/H) 6 | */ 7 | const val LINEAR_LAYOUT = 0 8 | 9 | /** 10 | * Widget type: HorizontalScrollView 11 | */ 12 | const val HORIZONTAL_SCROLL = 2 13 | 14 | /** 15 | * Widget type: ScrollView 16 | */ 17 | const val VERTICAL_SCROLL = 12 18 | 19 | /** 20 | * Widget type: Button 21 | */ 22 | const val BUTTON = 3 23 | 24 | /** 25 | * Widget type: TextView 26 | */ 27 | const val TEXT_VIEW = 4 28 | 29 | /** 30 | * Widget type: EditText 31 | */ 32 | const val EDIT_TEXT = 5 33 | 34 | /** 35 | * Widget type: ImageView 36 | */ 37 | const val IMAGE_VIEW = 6 38 | 39 | /** 40 | * Widget type: WebView 41 | */ 42 | const val WEBVIEW = 7 43 | 44 | /** 45 | * Widget type: ProgressBar 46 | */ 47 | const val PROGRESS_BAR = 8 48 | 49 | /** 50 | * Widget type: ListView 51 | */ 52 | const val LISTVIEW = 9 53 | 54 | /** 55 | * Widget type: Spinner 56 | */ 57 | const val SPINNER = 10 58 | 59 | /** 60 | * Widget type: Checkbox 61 | */ 62 | const val CHECKBOX = 11 63 | 64 | /** 65 | * Widget type: Switch 66 | */ 67 | const val SWITCH = 13 68 | 69 | /** 70 | * Widget type: SeekBar 71 | */ 72 | const val SEEKBAR = 14 73 | 74 | /** 75 | * Widget type: CalendarView 76 | */ 77 | const val CALENDAR = 15 78 | 79 | /** 80 | * Widget type: AdView 81 | */ 82 | const val ADVIEW = 17 83 | 84 | /** 85 | * Widget type: MapView 86 | */ 87 | const val MAP_VIEW = 18 88 | 89 | } -------------------------------------------------------------------------------- /src/main/kotlin/io/sketchware/utils/CustomProjectImportManager.kt: -------------------------------------------------------------------------------- 1 | package io.sketchware.utils 2 | 3 | import io.sketchware.manager.customs.SketchwareProCustomManager 4 | import io.sketchware.manager.projects.data.FileManager 5 | import io.sketchware.manager.projects.data.LogicManager 6 | import io.sketchware.models.export.ExportedCustomFiles 7 | import io.sketchware.models.projects.ProjectFilesLocations 8 | import io.sketchware.utils.internal.LogicIdChanger 9 | import io.sketchware.utils.internal.copy 10 | import java.io.File 11 | 12 | open class CustomProjectImportManager( 13 | override val locations: ProjectFilesLocations, 14 | override val destination: ProjectFilesLocations, 15 | override val projectId: Int, 16 | internal open val customsManager: SketchwareProCustomManager, 17 | internal open val exportedCustomFiles: ExportedCustomFiles, 18 | internal open val sourceEditedFolder: File 19 | ) : ProjectImportManager(locations, destination, projectId) { 20 | private var componentIdProvider: ((Int) -> Int)? = null 21 | private var listenerNameProvider: ((String) -> String)? = null 22 | private var menusConflictProvider: ((conflictId: String) -> String)? = null 23 | 24 | private var componentIdsChanges = mutableListOf>() 25 | private var listenerNamesChanges = mutableListOf>() 26 | private var menusNamesChanges = mutableListOf>() 27 | 28 | fun setComponentConflictProvider(builder: (Int) -> Int) { 29 | componentIdProvider = provider@{ 30 | val newId = builder(it) 31 | if (newId != it) 32 | componentIdsChanges.add(Pair(it, newId)) 33 | return@provider newId 34 | } 35 | } 36 | 37 | fun setListenerConflictProvider(builder: (String) -> String) { 38 | listenerNameProvider = provider@{ 39 | val newName = builder(it) 40 | if (newName != it) 41 | listenerNamesChanges.add(Pair(it, newName)) 42 | return@provider newName 43 | } 44 | } 45 | 46 | fun setMenuConflictProvider(builder: (conflictId: String) -> String) { 47 | menusConflictProvider = provider@{ 48 | val newName = builder(it) 49 | if (newName != it) 50 | menusNamesChanges.add(Pair(it, newName)) 51 | return@provider newName 52 | } 53 | } 54 | 55 | private suspend fun editUniques() { 56 | val changer = LogicIdChanger( 57 | LogicManager(File(locations.dataFolder, "logic")), 58 | FileManager(File(locations.dataFolder, "file")).activities 59 | ) 60 | if (componentIdsChanges.isNotEmpty()) 61 | changer.editComponentIds(componentIdsChanges) 62 | if (listenerNamesChanges.isNotEmpty()) 63 | changer.editEventsType(listenerNamesChanges) 64 | if (menusNamesChanges.isNotEmpty()) 65 | changer.editMenuNames(menusNamesChanges) 66 | } 67 | 68 | override suspend fun import(): Unit = with(exportedCustomFiles) { 69 | super.import() 70 | customsManager.getBlocksManager().apply { 71 | import(blocksFile) 72 | save() 73 | } 74 | customsManager.getComponentManager().apply { 75 | import(componentsFile, componentIdProvider) 76 | save() 77 | } 78 | customsManager.getListenersManager().apply { 79 | import(listenersFile, listenerNameProvider) 80 | save() 81 | } 82 | customsManager.getMenusManager().apply { 83 | import(menusFile, menusConflictProvider) 84 | save() 85 | } 86 | exportedCustomFiles.sourceEditedFolder.copy( 87 | File( 88 | this@CustomProjectImportManager.sourceEditedFolder, "$projectId" 89 | ) 90 | ) 91 | editUniques() 92 | } 93 | 94 | } -------------------------------------------------------------------------------- /src/main/kotlin/io/sketchware/utils/Exportable.kt: -------------------------------------------------------------------------------- 1 | package io.sketchware.utils 2 | 3 | import io.sketchware.models.ExportableItem 4 | import java.io.File 5 | 6 | class Exportable { 7 | } 8 | 9 | fun MutableList.add(internalPath: String, file: File) = 10 | add(ExportableItem(internalPath, file)) -------------------------------------------------------------------------------- /src/main/kotlin/io/sketchware/utils/LogicOpCodes.kt: -------------------------------------------------------------------------------- 1 | package io.sketchware.utils 2 | 3 | import kotlinx.coroutines.Dispatchers 4 | import kotlinx.coroutines.withContext 5 | 6 | object LogicOpCodes { 7 | val opcodes by lazy { javaClass.getResource("/opcodes/logic").readText().split("\n") } 8 | 9 | suspend fun getOpCodes() = withContext(Dispatchers.IO) { 10 | return@withContext opcodes 11 | } 12 | 13 | } -------------------------------------------------------------------------------- /src/main/kotlin/io/sketchware/utils/ProjectImportManager.kt: -------------------------------------------------------------------------------- 1 | package io.sketchware.utils 2 | 3 | import io.sketchware.manager.projects.entities.SketchwareProject 4 | import io.sketchware.models.projects.ProjectFilesLocations 5 | 6 | open class ProjectImportManager( 7 | internal open val locations: ProjectFilesLocations, 8 | internal open val destination: ProjectFilesLocations, 9 | internal open val projectId: Int 10 | ) { 11 | open suspend fun import() = 12 | SketchwareProject(locations).copy(projectId, destination) 13 | } -------------------------------------------------------------------------------- /src/main/kotlin/io/sketchware/utils/SWConst.kt: -------------------------------------------------------------------------------- 1 | package io.sketchware.utils 2 | 3 | /** 4 | * Contains Sketchware Activity Info constants. 5 | */ 6 | internal object SWConst { 7 | 8 | //Keyboard Options 9 | const val KEYBOARD_STATE_HIDDEN = 2 10 | const val KEYBOARD_STATE_UNSPECIFIED = 0 11 | const val KEYBOARD_STATE_VISIBLE = 1 12 | 13 | //Theme Features 14 | const val OPTION_ACTIVITY_DRAWER = 4 15 | const val OPTION_ACTIVITY_FAB = 8 16 | const val OPTION_ACTIVITY_FULL_SCREEN = 2 17 | const val OPTION_ACTIVITY_MASK = 15 18 | const val OPTION_ACTIVITY_SHIFT = 0 19 | const val OPTION_ACTIVITY_TOOLBAR = 1 20 | 21 | //Theme Orientation 22 | const val ORIENTATION_BOTH = 2 23 | const val ORIENTATION_LANDSCAPE = 1 24 | const val ORIENTATION_PORTRAIT = 0 25 | 26 | //Project File Type 27 | const val PROJECT_FILE_TYPE_ACTIVITY = 0 28 | const val PROJECT_FILE_TYPE_CUSTOM_VIEW = 1 29 | const val PROJECT_FILE_TYPE_DRAWER = 2 30 | 31 | //Theme Styles 32 | const val THEME_DEFAULT = 0 33 | const val THEME_FULL_SCREEN = 2 34 | const val THEME_NO_ACTIONBAR = 1 35 | const val THEME_NONE = -1 36 | } -------------------------------------------------------------------------------- /src/main/kotlin/io/sketchware/utils/SketchwareEncryptor.kt: -------------------------------------------------------------------------------- 1 | package io.sketchware.utils 2 | 3 | import kotlinx.coroutines.Dispatchers 4 | import kotlinx.coroutines.withContext 5 | import javax.crypto.Cipher 6 | import javax.crypto.spec.IvParameterSpec 7 | import javax.crypto.spec.SecretKeySpec 8 | 9 | object SketchwareEncryptor { 10 | /** 11 | * Sketchware encrypts everything under one static key, 12 | * so it is used in the [encrypt] & [decrypt] method. 13 | */ 14 | private val encryptKey = "sketchwaresecure".toByteArray() 15 | 16 | /** 17 | * Decryption of the incoming [byteArray] by [encryptKey]. 18 | * @return [ByteArray] of decrypted [byteArray]. 19 | */ 20 | suspend fun decrypt(byteArray: ByteArray): ByteArray = withContext(Dispatchers.Default) { 21 | val cipher = Cipher.getInstance("AES/CBC/PKCS5Padding") 22 | cipher.init(2, SecretKeySpec(encryptKey, "AES"), IvParameterSpec(encryptKey)) 23 | return@withContext cipher.doFinal(byteArray) ?: error("Error while decrypting string.") 24 | } 25 | 26 | /** 27 | * Encryption of the incoming [byteArray] by [encryptKey]. 28 | * @return [ByteArray] of encrypted [byteArray]. 29 | */ 30 | suspend fun encrypt(byteArray: ByteArray) = withContext(Dispatchers.Default) { 31 | val cipher: Cipher = Cipher.getInstance("AES/CBC/PKCS5Padding") 32 | cipher.init(1, SecretKeySpec(encryptKey, "AES"), IvParameterSpec(encryptKey)) 33 | return@withContext cipher.doFinal(byteArray) ?: error("Error while encrypting string.") 34 | } 35 | 36 | internal suspend fun ByteArray.decrypt() = decrypt(this) 37 | internal suspend fun ByteArray.encrypt() = encrypt(this) 38 | 39 | internal suspend fun String.decrypt() = String(decrypt(this.toByteArray())) 40 | internal suspend fun String.encrypt() = String(encrypt(this.toByteArray())) 41 | 42 | } -------------------------------------------------------------------------------- /src/main/kotlin/io/sketchware/utils/ViewBuilder.kt: -------------------------------------------------------------------------------- 1 | package io.sketchware.utils 2 | 3 | import io.sketchware.annotations.ExperimentalSWManagerAPI 4 | 5 | @ExperimentalSWManagerAPI 6 | class ViewBuilder { 7 | /** 8 | * View name (for example: main) 9 | */ 10 | var name: String = "" 11 | 12 | /** 13 | * Second view name (for example: layout on fab or custom view.). 14 | */ 15 | var layoutName: String? = null 16 | 17 | /** 18 | * Widget builder. 19 | * Responsible for adding widgets to view. 20 | */ 21 | var widgetBuilder: WidgetBuilder = WidgetBuilder() 22 | 23 | inline fun widgetBuilder(builder: WidgetBuilder.() -> Unit) = builder(widgetBuilder) 24 | } -------------------------------------------------------------------------------- /src/main/kotlin/io/sketchware/utils/WidgetBuilder.kt: -------------------------------------------------------------------------------- 1 | package io.sketchware.utils 2 | 3 | import io.sketchware.models.view.WidgetRoot 4 | 5 | //TODO Widget Builder 6 | class WidgetBuilder( 7 | var widgets: MutableList = mutableListOf() 8 | ) { 9 | 10 | // fun WidgetRoot.addTextView( 11 | // id: String, 12 | // text: String = "TextView", 13 | // textSize: Int = 12, 14 | // textFont: String = "default_font", 15 | // textColor: Int = -16777216 16 | // ) = addTextView( 17 | // id, Text(text = text, textSize = textSize, textFont = textFont, textColor = textColor) 18 | // ) 19 | // 20 | // fun WidgetRoot.addTextViewAfter(widgetId: String, text: Text) = 21 | // widgets.add(WidgetRoot( 22 | // text = text, id = widgetId, type = WidgetType.TEXT_VIEW, preId = this.id, 23 | // )) 24 | // 25 | // fun addEditText( 26 | // id: String, 27 | // text: String = "TextView", 28 | // textSize: Int = 12, 29 | // textFont: String = "default_font", 30 | // textColor: Int = -16777216, 31 | // hintText: String, 32 | // hintColor: Int = -10453621 33 | // ) = addEditText( 34 | // id, Text( 35 | // text = text, textSize = textSize, textFont = textFont, 36 | // textColor = textColor, hint = hintText, hintColor = hintColor 37 | // ) 38 | // ) 39 | // 40 | // fun addEditText(id: String, text: Text) = 41 | // widgets.add(WidgetRoot(text = text, id = id, type = WidgetType.EDIT_TEXT)) 42 | 43 | } -------------------------------------------------------------------------------- /src/main/kotlin/io/sketchware/utils/delegates/lazyInit.kt: -------------------------------------------------------------------------------- 1 | package io.sketchware.utils.delegates 2 | 3 | import kotlin.properties.ReadOnlyProperty 4 | import kotlin.reflect.KProperty 5 | 6 | internal fun lazyInit(initFunction: () -> T) = LazyInit(initFunction) 7 | 8 | internal class LazyInit(private val initFunction: () -> T) : ReadOnlyProperty { 9 | 10 | private var valueSource: T? = null 11 | 12 | fun reset() = synchronized(this) { 13 | valueSource = null 14 | } 15 | 16 | override operator fun getValue(thisRef: Any?, property: KProperty<*>) = synchronized(this) { 17 | valueSource ?: initFunction().also { valueSource = it } 18 | } 19 | 20 | } -------------------------------------------------------------------------------- /src/main/kotlin/io/sketchware/utils/internal/BytesUtil.kt: -------------------------------------------------------------------------------- 1 | package io.sketchware.utils.internal 2 | 3 | internal fun ByteArray.byteArrayToString() = String(this) -------------------------------------------------------------------------------- /src/main/kotlin/io/sketchware/utils/internal/FileUtil.kt: -------------------------------------------------------------------------------- 1 | package io.sketchware.utils.internal 2 | 3 | import kotlinx.coroutines.Dispatchers 4 | import kotlinx.coroutines.withContext 5 | import java.io.File 6 | 7 | internal suspend fun File.getFiles() = getFilesOrEmpty().also { 8 | if (it.isEmpty()) error("error while getting files") 9 | } 10 | 11 | internal suspend fun File.getFilesOrEmpty() = withContext(Dispatchers.IO) { 12 | return@withContext listFiles()?.toList() ?: emptyList() 13 | } 14 | 15 | internal suspend fun File.read() = withContext(Dispatchers.IO) { 16 | return@withContext readBytes() 17 | } 18 | 19 | internal suspend fun File.readOrNull() = try { 20 | read() 21 | } catch (e: Exception) { 22 | System.err.println(e) 23 | null 24 | } 25 | 26 | internal suspend fun File.write(bytes: ByteArray) = withContext(Dispatchers.IO) { 27 | return@withContext writeBytes(bytes) 28 | } 29 | 30 | internal suspend fun File.remove() = withContext(Dispatchers.IO) { 31 | return@withContext if (isFile) delete() else deleteRecursively() 32 | } 33 | 34 | internal suspend fun removeFiles(vararg files: File) = files.forEach { 35 | it.remove() 36 | } 37 | 38 | internal suspend fun File.copy(destFile: File, overwrite: Boolean = true) = withContext(Dispatchers.IO) { 39 | if (isFile) { 40 | if (!destFile.exists()) destFile.parentFile.createFolder() 41 | copyTo(destFile, overwrite) 42 | } else { 43 | if (exists()) { 44 | if (!destFile.exists()) destFile.createFolder() 45 | copyRecursively(destFile, overwrite) 46 | } else { 47 | System.err.println("Folder onto $path path does not exist. Skipped.") 48 | } 49 | } 50 | Unit 51 | } 52 | 53 | internal suspend fun File.createFolder(): Boolean = withContext(Dispatchers.IO) { 54 | mkdirs() 55 | } -------------------------------------------------------------------------------- /src/main/kotlin/io/sketchware/utils/internal/Json.kt: -------------------------------------------------------------------------------- 1 | package io.sketchware.utils.internal 2 | 3 | import io.sketchware.models.projects.ActivityTheme 4 | import io.sketchware.models.projects.FileType 5 | import io.sketchware.models.projects.KeyboardSetting 6 | import io.sketchware.models.projects.Orientation 7 | import io.sketchware.models.view.LayoutOrientation 8 | import io.sketchware.utils.serializers.idSerializer 9 | import kotlinx.serialization.KSerializer 10 | import kotlinx.serialization.decodeFromString 11 | import kotlinx.serialization.encodeToString 12 | import kotlinx.serialization.json.Json 13 | import kotlinx.serialization.modules.SerializersModule 14 | import kotlinx.serialization.modules.contextual 15 | 16 | private val serializationModule = SerializersModule { 17 | contextual(idSerializer()) 18 | contextual(idSerializer()) 19 | contextual(idSerializer()) 20 | contextual(idSerializer(LayoutOrientation.HORIZONTAL)) 21 | contextual(idSerializer()) 22 | } 23 | 24 | internal val json = Json { 25 | serializersModule = serializationModule 26 | } 27 | 28 | internal inline fun String.serialize() = json.decodeFromString(this) 29 | internal fun String.serialize(kSerializer: KSerializer) = 30 | json.decodeFromString(kSerializer, this) 31 | 32 | internal inline fun T.deserialize() = json.encodeToString(this) 33 | internal fun T.deserialize(kSerializer: KSerializer) = 34 | json.encodeToString(kSerializer, this) -------------------------------------------------------------------------------- /src/main/kotlin/io/sketchware/utils/internal/ListUtils.kt: -------------------------------------------------------------------------------- 1 | package io.sketchware.utils.internal 2 | 3 | internal fun List.freeBetweenOrNull(first: Int, second: Int): Int? { 4 | (first..second).toList().forEach { number -> 5 | if (!contains(number)) 6 | return number 7 | } 8 | return null 9 | } 10 | 11 | internal fun List.freeBetweenOrNull(first: Int, max: Int, transformer: (T) -> Int) = 12 | map(transformer).freeBetweenOrNull(first, max) 13 | 14 | internal fun List.freeBetweenOrDefault(first: Int, max: Int, default: Int, transformer: (T) -> Int) = 15 | map(transformer).freeBetweenOrNull(first, max) ?: default -------------------------------------------------------------------------------- /src/main/kotlin/io/sketchware/utils/internal/LogicIdChanger.kt: -------------------------------------------------------------------------------- 1 | package io.sketchware.utils.internal 2 | 3 | import io.sketchware.annotations.ExperimentalSWManagerAPI 4 | import io.sketchware.manager.projects.data.LogicManager 5 | import io.sketchware.models.projects.MenuSpecArgument 6 | import io.sketchware.models.projects.SketchwareDataFileModel 7 | 8 | internal class LogicIdChanger( 9 | private val logicManager: LogicManager, 10 | private val activities: List 11 | ) { 12 | fun editComponentIds(list: List>) = activities.forEach { file -> 13 | val activityName = "${file.fileName.capitalize()}Activity" 14 | logicManager.getComponents(activityName)?.filter { component -> 15 | list.any { it.first == component.type } 16 | }?.forEach { component -> 17 | logicManager.removeComponent(activityName, component.id) 18 | logicManager.addComponent( 19 | activityName, component.copy(type = list.first { it.first == component.type }.second) 20 | ) 21 | } 22 | } 23 | 24 | @OptIn(ExperimentalSWManagerAPI::class) 25 | fun editEventsType(list: List>) = activities.forEach { activity -> 26 | logicManager.getEvents(activity.activityName)?.filter { event -> 27 | list.any { it.first == event.name } 28 | }?.forEach { event -> 29 | logicManager.editEventInfo(activity.activityName, event.targetId, event.name) { 30 | name = list.first { it.first == name }.second 31 | } 32 | } 33 | } 34 | 35 | fun editMenuNames(list: List>) = activities.forEach { activity -> 36 | logicManager.getEvents(activity.activityName)?.forEach { event -> 37 | logicManager.editEventLogic(activity.activityName, event.targetId, event.name) { blocks -> 38 | blocks.forEach { block -> 39 | block.spec.filterIsInstance().forEach { menu -> 40 | list.firstOrNull { it.first == menu.menuArgumentTypeName }?.let { 41 | menu.menuArgumentTypeName = it.second 42 | } 43 | } 44 | } 45 | } 46 | } 47 | } 48 | 49 | suspend fun saveAll() { 50 | logicManager.save() 51 | } 52 | 53 | } -------------------------------------------------------------------------------- /src/main/kotlin/io/sketchware/utils/internal/ProjectCustomExporter.kt: -------------------------------------------------------------------------------- 1 | package io.sketchware.utils.internal 2 | 3 | import io.sketchware.manager.customs.SketchwareProCustomManager 4 | import io.sketchware.manager.projects.entities.SketchwareProProject 5 | import io.sketchware.models.customs.CustomBlockGroup 6 | import io.sketchware.models.customs.CustomComponent 7 | import io.sketchware.models.customs.CustomListenerGroup 8 | import io.sketchware.models.customs.CustomMenu 9 | import io.sketchware.models.projects.MenuSpecArgument 10 | import io.sketchware.utils.LogicOpCodes 11 | 12 | // shit code day is announced open. DON'T LOOK AT THE CODE WHICH GOES NEXT. PLEASE. 13 | internal class ProjectCustomExporter( 14 | val project: SketchwareProProject, 15 | val customsManager: SketchwareProCustomManager 16 | ) { 17 | private suspend fun getComponentsIdsToExport() = project.getFileManager().activities.map { activity -> 18 | val componentsId = mutableListOf() 19 | project.getLogicManager().getComponents(activity.activityName)?.forEach { 20 | if (it.type !in 0..18) 21 | componentsId.add(it.type) 22 | } 23 | return@map componentsId 24 | }.flatten() 25 | 26 | private suspend fun getBlocksOpCodesToExport() = project.getFileManager().activities.map { activity -> 27 | val blockOpCodes = mutableListOf() 28 | project.getLogicManager().getEvents(activity.activityName)?.forEach { 29 | project.getLogicManager().getEventLogic(activity.activityName, it.targetId, it.name) 30 | ?.forEach { block -> 31 | if (!LogicOpCodes.getOpCodes().contains(block.opCode)) 32 | blockOpCodes.add(block.opCode) 33 | } 34 | } 35 | project.getLogicManager().getMoreblocks(activity.activityName)?.forEach { moreblock -> 36 | project.getLogicManager().getMoreblockLogic(activity.activityName, moreblock.name) 37 | ?.forEach { block -> 38 | if (!LogicOpCodes.getOpCodes().contains(block.opCode)) 39 | blockOpCodes.add(block.opCode) 40 | } 41 | } 42 | return@map blockOpCodes 43 | }.flatten() 44 | 45 | private suspend fun getMenusNamesToExport() = project.getFileManager().activities.map { activity -> 46 | val menuNames = mutableListOf() 47 | project.getLogicManager().getEvents(activity.activityName)?.forEach { event -> 48 | project.getLogicManager().getEventLogic(activity.activityName, event.targetId, event.name) 49 | ?.forEach { block -> 50 | block.spec.filterIsInstance().forEach { 51 | try { 52 | it.menuArgumentType 53 | } catch (e: Exception) { 54 | menuNames.add(it.menuArgumentTypeName) 55 | } 56 | } 57 | } 58 | } 59 | project.getLogicManager().getMoreblocks(activity.activityName)?.forEach { moreblock -> 60 | project.getLogicManager().getMoreblockLogic(activity.activityName, moreblock.name)?.forEach { block -> 61 | block.spec.filterIsInstance().forEach { 62 | try { 63 | it.menuArgumentType 64 | } catch (e: Exception) { 65 | menuNames.add(it.menuArgumentTypeName) 66 | } 67 | } 68 | } 69 | } 70 | return@map menuNames 71 | }.flatten() 72 | 73 | private suspend fun getListenersToExport() = project.getFileManager().activities.map { activity -> 74 | val eventsToExport = mutableListOf() 75 | project.getLogicManager().getEvents(activity.activityName)?.forEach { event -> 76 | if (event.type == 3) 77 | eventsToExport.add(event.name) 78 | } 79 | return@map eventsToExport 80 | }.flatten() 81 | 82 | suspend fun getComponentsToExport(): List { 83 | val componentsIds = getComponentsIdsToExport() 84 | val components = customsManager.getComponentManager().components 85 | return components.filter { 86 | componentsIds.contains(it.id) 87 | } 88 | } 89 | 90 | suspend fun getBlocksToExport(): List { 91 | val blocksOpCodes = getBlocksOpCodesToExport() 92 | val customBlocks = customsManager.getBlocksManager().blocks 93 | return customBlocks.filter { blockGroup -> 94 | blockGroup.blocks.any { blocksOpCodes.contains(it.typeName) } 95 | } 96 | } 97 | 98 | suspend fun getMenusToExport(): List { 99 | val menuNames = getMenusNamesToExport() 100 | val menus = customsManager.getMenusManager().menus 101 | return menus.filter { 102 | menuNames.contains(it.id) 103 | } 104 | } 105 | 106 | suspend fun getListenersGroupsToExport(): List { 107 | val eventsToExport = getListenersToExport() 108 | val allEvents = customsManager.getListenersManager().listeners 109 | return allEvents.filter { event -> 110 | event.events.any { eventsToExport.contains(it.name) } 111 | } 112 | } 113 | 114 | } -------------------------------------------------------------------------------- /src/main/kotlin/io/sketchware/utils/internal/SketchwareProjectsUtil.kt: -------------------------------------------------------------------------------- 1 | package io.sketchware.utils.internal 2 | 3 | import io.sketchware.manager.projects.entities.SketchwareProject 4 | import io.sketchware.models.projects.ProjectFilesLocations 5 | import io.sketchware.models.projects.ProjectResourcesFiles 6 | import java.io.File 7 | 8 | internal object SketchwareProjectsUtil { 9 | 10 | suspend fun getProjectsList(baseFolder: File) = File(baseFolder, "mysc/list").getFiles().map { 11 | return@map SketchwareProject( 12 | ProjectFilesLocations( 13 | it, File(it, "project"), File(baseFolder, "data/${it.name}"), 14 | ProjectResourcesFiles( 15 | File(baseFolder, "resources/icons/${it.name}"), 16 | File(baseFolder, "resources/images/${it.name}"), 17 | File(baseFolder, "resources/sounds/${it.name}"), 18 | File(baseFolder, "resources/fonts/${it.name}") 19 | ) 20 | ) 21 | ) 22 | } 23 | 24 | suspend fun nextFreeProjectId(listFolder: File, startId: Int = 601): Int = 25 | if (listFolder.getFiles().any { it.name == startId.toString() }) 26 | nextFreeProjectId(listFolder, startId + 1) 27 | else startId 28 | 29 | } -------------------------------------------------------------------------------- /src/main/kotlin/io/sketchware/utils/internal/TagFormatter.kt: -------------------------------------------------------------------------------- 1 | package io.sketchware.utils.internal 2 | 3 | import kotlinx.serialization.KSerializer 4 | import java.util.regex.Matcher 5 | import java.util.regex.Pattern 6 | 7 | internal object TagFormatter { 8 | private const val regex = "\\{.*?\\}$(?:\\{|\\s|\\Z)" 9 | val pattern: Pattern = Pattern.compile(regex, Pattern.MULTILINE) 10 | 11 | inline fun parseAsArray(string: String): List { 12 | val matcher: Matcher = pattern.matcher(string) 13 | val array = ArrayList() 14 | while (matcher.find()) { 15 | array.add(matcher.group(0).serialize()) 16 | } 17 | return array 18 | } 19 | 20 | fun parseAsArray(string: String, serializer: KSerializer): List { 21 | val matcher: Matcher = pattern.matcher(string) 22 | val array = ArrayList() 23 | while (matcher.find()) { 24 | array.add(matcher.group(0).serialize(serializer)) 25 | } 26 | return array 27 | } 28 | 29 | inline fun getListByTag(tag: String, value: String) = 30 | value.getByTag(tag.normalizeTag())?.let { parseAsArray(it) } 31 | 32 | fun parseTextBlocks(input: String): List> { 33 | return input 34 | .split("\n") 35 | .filter { it.contains(":") } 36 | .map { it.split(":") } 37 | .map { (first, second) -> Pair(first, second) } 38 | } 39 | 40 | fun addTag(name: String, stringToSave: String, value: String): String { 41 | return value.replaceOrInsertAtTop( 42 | "(@${name.normalizeTag()}.*?)(?=@|\$)".toRegex(), 43 | "@$name\n$stringToSave\n\n", 44 | suffix = "\n" 45 | ) 46 | } 47 | 48 | fun removeTag(name: String, value: String): String { 49 | return value.replace( 50 | "(@${name.normalizeTag()}.*?)(?=@|\$)".toRegex(), 51 | "\n" 52 | ) 53 | } 54 | 55 | inline fun List.toSaveableValue() = 56 | joinToString { "\n${it.deserialize()}" } 57 | 58 | } -------------------------------------------------------------------------------- /src/main/kotlin/io/sketchware/utils/internal/TextUtils.kt: -------------------------------------------------------------------------------- 1 | package io.sketchware.utils.internal 2 | 3 | internal fun String.replaceOrInsertAtTop( 4 | regex: Regex, 5 | replacement: String, 6 | prefix: String = "", 7 | suffix: String = "" 8 | ): String { 9 | val content = regex.replace(this, replacement) 10 | return if (this == content) 11 | "${replacement}$content" 12 | else "${prefix}content${suffix}" 13 | } 14 | 15 | internal fun String.getByTag(tag: String): String? { 16 | val regex = Regex( 17 | "(?<=@)($tag\\b)(.*?)(?=\\n@|$)", 18 | RegexOption.DOT_MATCHES_ALL 19 | ) 20 | val result = regex 21 | .find(this) 22 | return result?.value 23 | } 24 | 25 | internal fun String.normalizeTag() = this.replace(".", "\\.") 26 | 27 | internal val camelRegex = "(?<=[a-zA-Z])[A-Z]".toRegex() 28 | internal val snakeRegex = "_[a-zA-Z]".toRegex() 29 | 30 | internal fun String.camelToSnakeCase(): String { 31 | return camelRegex.replace(this) { 32 | "_${it.value}" 33 | }.toLowerCase() 34 | } 35 | 36 | internal fun String.snakeToLowerCamelCase(): String { 37 | return snakeRegex.replace(this) { 38 | it.value.replace("_", "") 39 | .toUpperCase() 40 | } 41 | } 42 | 43 | internal fun String.snakeToUpperCamelCase(): String { 44 | return this.snakeToLowerCamelCase().capitalize() 45 | } -------------------------------------------------------------------------------- /src/main/kotlin/io/sketchware/utils/serializers/ActivityOptionSerializer.kt: -------------------------------------------------------------------------------- 1 | package io.sketchware.utils.serializers 2 | 3 | import io.sketchware.models.projects.ActivityOption 4 | import io.sketchware.utils.SWConst.OPTION_ACTIVITY_DRAWER 5 | import io.sketchware.utils.SWConst.OPTION_ACTIVITY_FAB 6 | import io.sketchware.utils.SWConst.OPTION_ACTIVITY_FULL_SCREEN 7 | import io.sketchware.utils.SWConst.OPTION_ACTIVITY_MASK 8 | import io.sketchware.utils.SWConst.OPTION_ACTIVITY_TOOLBAR 9 | import kotlinx.serialization.KSerializer 10 | import kotlinx.serialization.descriptors.SerialDescriptor 11 | import kotlinx.serialization.descriptors.buildClassSerialDescriptor 12 | import kotlinx.serialization.encoding.Decoder 13 | import kotlinx.serialization.encoding.Encoder 14 | 15 | class ActivityOptionSerializer public constructor() : KSerializer> { 16 | 17 | override val descriptor: SerialDescriptor = buildClassSerialDescriptor("ActivityOptionSerializer") 18 | 19 | override fun deserialize(decoder: Decoder) = mutableListOf().apply { 20 | val value = decoder.decodeInt() 21 | if ((value and OPTION_ACTIVITY_MASK) and OPTION_ACTIVITY_DRAWER == OPTION_ACTIVITY_DRAWER) 22 | add(ActivityOption.DRAWER) 23 | if ((value and OPTION_ACTIVITY_MASK) and OPTION_ACTIVITY_TOOLBAR == OPTION_ACTIVITY_TOOLBAR) 24 | add(ActivityOption.TOOLBAR) 25 | if ((value and OPTION_ACTIVITY_MASK) and OPTION_ACTIVITY_FAB == OPTION_ACTIVITY_FAB) 26 | add(ActivityOption.FAB) 27 | if ((value and OPTION_ACTIVITY_MASK) and OPTION_ACTIVITY_FULL_SCREEN == OPTION_ACTIVITY_FULL_SCREEN) 28 | add(ActivityOption.FULLSCREEN) 29 | }.toList() 30 | 31 | override fun serialize(encoder: Encoder, value: List) { 32 | var output = 0 33 | value.forEach { 34 | output = output or it.id 35 | } 36 | encoder.encodeInt(output) 37 | } 38 | } -------------------------------------------------------------------------------- /src/main/kotlin/io/sketchware/utils/serializers/IdEnumSerializer.kt: -------------------------------------------------------------------------------- 1 | package io.sketchware.utils.serializers 2 | 3 | import io.sketchware.interfaces.IdInterface 4 | import kotlinx.serialization.KSerializer 5 | import kotlinx.serialization.descriptors.SerialDescriptor 6 | import kotlinx.serialization.descriptors.buildClassSerialDescriptor 7 | import kotlinx.serialization.encoding.Decoder 8 | import kotlinx.serialization.encoding.Encoder 9 | 10 | class IdEnumSerializer>( 11 | override val descriptor: SerialDescriptor = buildClassSerialDescriptor("IdEnumSerializer"), 12 | private val map: Map, private val defaultValue: T? = null 13 | ) : KSerializer { 14 | override fun deserialize(decoder: Decoder): T { 15 | return map[decoder.decodeInt()] 16 | ?: defaultValue 17 | ?: error("Unexpected id ${decoder.decodeInt()}, defaultValue isn't specified.") 18 | } 19 | 20 | override fun serialize(encoder: Encoder, value: T) { 21 | encoder.encodeInt((map.values.find { it == value } as IdInterface).id) 22 | } 23 | } 24 | 25 | inline fun > idSerializer(defaultValue: T? = null): IdEnumSerializer { 26 | return IdEnumSerializer(map = enumValues().map { 27 | (it as IdInterface).id to it 28 | }.toMap(), defaultValue = defaultValue) 29 | } -------------------------------------------------------------------------------- /src/main/kotlin/io/sketchware/utils/serializers/ListBlockModelSerializer.kt: -------------------------------------------------------------------------------- 1 | package io.sketchware.utils.serializers 2 | 3 | import io.sketchware.models.projects.BlockModel 4 | import io.sketchware.utils.internal.TagFormatter 5 | import io.sketchware.utils.internal.TagFormatter.toSaveableValue 6 | import kotlinx.serialization.KSerializer 7 | import kotlinx.serialization.descriptors.SerialDescriptor 8 | import kotlinx.serialization.descriptors.buildClassSerialDescriptor 9 | import kotlinx.serialization.encoding.Decoder 10 | import kotlinx.serialization.encoding.Encoder 11 | 12 | object ListBlockModelSerializer : KSerializer> { 13 | 14 | override val descriptor: SerialDescriptor = buildClassSerialDescriptor("listBlockModelSerializer") 15 | 16 | override fun deserialize(decoder: Decoder): List { 17 | return TagFormatter.parseAsArray(decoder.decodeString()) 18 | } 19 | 20 | override fun serialize(encoder: Encoder, value: List) { 21 | encoder.encodeString(value.toSaveableValue()) 22 | } 23 | 24 | } -------------------------------------------------------------------------------- /src/main/kotlin/io/sketchware/utils/serializers/PathToFileSerializer.kt: -------------------------------------------------------------------------------- 1 | package io.sketchware.utils.serializers 2 | 3 | import kotlinx.serialization.KSerializer 4 | import kotlinx.serialization.descriptors.SerialDescriptor 5 | import kotlinx.serialization.descriptors.buildClassSerialDescriptor 6 | import kotlinx.serialization.encoding.Decoder 7 | import kotlinx.serialization.encoding.Encoder 8 | import java.io.File 9 | 10 | class PathToFileSerializer( 11 | override val descriptor: SerialDescriptor = buildClassSerialDescriptor("Path2FileSerializer") 12 | ) : KSerializer { 13 | override fun deserialize(decoder: Decoder) = File(decoder.decodeString()) 14 | override fun serialize(encoder: Encoder, value: File) = encoder.encodeString(value.path) 15 | } -------------------------------------------------------------------------------- /src/main/kotlin/io/sketchware/utils/serializers/SpecSerializer.kt: -------------------------------------------------------------------------------- 1 | package io.sketchware.utils.serializers 2 | 3 | import io.sketchware.models.projects.* 4 | import kotlinx.serialization.KSerializer 5 | import kotlinx.serialization.descriptors.SerialDescriptor 6 | import kotlinx.serialization.descriptors.buildClassSerialDescriptor 7 | import kotlinx.serialization.encoding.Decoder 8 | import kotlinx.serialization.encoding.Encoder 9 | 10 | object SpecSerializer : KSerializer> { 11 | 12 | override val descriptor: SerialDescriptor = buildClassSerialDescriptor("SpecSerializer") 13 | 14 | override fun deserialize(decoder: Decoder): List { 15 | return decoder.decodeString().toSpecFields() 16 | } 17 | 18 | override fun serialize(encoder: Encoder, value: List) { 19 | encoder.encodeString(value.joinToString(" ")) 20 | } 21 | } 22 | 23 | internal fun String.toSpecFields(): List { 24 | return split(" ").map { 25 | return@map when { 26 | "%d" in it -> NumberSpecArgument(it) 27 | "%s" in it -> StringSpecArgument(it) 28 | "%b" in it -> BooleanSpecArgument(it) 29 | "%m" in it -> MenuSpecArgument(it) 30 | else -> SpecField(it) 31 | } 32 | } 33 | } -------------------------------------------------------------------------------- /src/main/kotlin/io/sketchware/utils/serializers/StringBooleanSerializer.kt: -------------------------------------------------------------------------------- 1 | package io.sketchware.utils.serializers 2 | 3 | import kotlinx.serialization.KSerializer 4 | import kotlinx.serialization.descriptors.SerialDescriptor 5 | import kotlinx.serialization.descriptors.buildClassSerialDescriptor 6 | import kotlinx.serialization.encoding.Decoder 7 | import kotlinx.serialization.encoding.Encoder 8 | 9 | class StringBooleanSerializer( 10 | override val descriptor: SerialDescriptor = buildClassSerialDescriptor("stringToBoolean") 11 | ) : KSerializer { 12 | override fun deserialize(decoder: Decoder): Boolean { 13 | return decoder.decodeString().toBoolean() 14 | } 15 | 16 | override fun serialize(encoder: Encoder, value: Boolean) { 17 | encoder.encodeString(value.toString()) 18 | } 19 | } -------------------------------------------------------------------------------- /src/main/kotlin/io/sketchware/utils/serializers/StringNumberConvertor.kt: -------------------------------------------------------------------------------- 1 | package io.sketchware.utils.serializers 2 | 3 | import kotlinx.serialization.KSerializer 4 | import kotlinx.serialization.descriptors.SerialDescriptor 5 | import kotlinx.serialization.descriptors.buildClassSerialDescriptor 6 | import kotlinx.serialization.encoding.Decoder 7 | import kotlinx.serialization.encoding.Encoder 8 | 9 | /** 10 | * Uses to fix numbers in string type. 11 | */ 12 | internal class StringNumberConvertor( 13 | override val descriptor: SerialDescriptor = buildClassSerialDescriptor("stringToInt") 14 | ) : KSerializer { 15 | override fun deserialize(decoder: Decoder): Int { 16 | return decoder.decodeString().toInt() 17 | } 18 | 19 | override fun serialize(encoder: Encoder, value: Int) { 20 | encoder.encodeString(value.toString()) 21 | } 22 | } -------------------------------------------------------------------------------- /src/main/resources/logicOpCodes: -------------------------------------------------------------------------------- 1 | % 2 | && 3 | * 4 | + 5 | - 6 | / 7 | < 8 | = 9 | > 10 | || 11 | addListInt 12 | addListMap 13 | addMapToList 14 | addSourceDirectly 15 | break 16 | calendarFormat 17 | calendarGetNow 18 | calendarViewSetDate 19 | calendarViewSetMaxDate 20 | calendarViewSetMinDate 21 | clearList 22 | copyToClipboard 23 | cropBitmapFileFromCenter 24 | decreaseInt 25 | definedFunc 26 | deleteList 27 | false 28 | filepickerstartpickfiles 29 | fileutilcopy 30 | fileutildelete 31 | fileutildelete 32 | fileutilisdir 33 | fileutillistdir 34 | fileutilmakedir 35 | fileutilmove 36 | fileutilwrite 37 | finishActivity 38 | forever 39 | getAlpha 40 | getArg 41 | getAtListInt 42 | getAtListInt 43 | getAtListMap 44 | getAtListMap 45 | getAtListStr 46 | getChecked 47 | getEnable 48 | getLocationX 49 | getLocationY 50 | getMapInList 51 | getPublicDir 52 | getRotate 53 | getScaleX 54 | getScaleY 55 | getText 56 | getTranslationX 57 | getTranslationY 58 | getVar 59 | if 60 | if 61 | ifElse 62 | increaseInt 63 | indexListInt 64 | indexListStr 65 | insertListInt 66 | insertListMap 67 | insertMapToList 68 | intentGetString 69 | intentSetAction 70 | intentSetData 71 | intentSetScreen 72 | lengthList 73 | lengthList 74 | listMapToStr 75 | listRefresh 76 | listSetCustomViewData 77 | listSetData 78 | listSmoothScrollTo 79 | mapClear 80 | mapContainKey 81 | mapCreateNew 82 | mapGet 83 | mapGetAllKeys 84 | mapPut 85 | mapRemoveKey 86 | mapSize 87 | mapToStr 88 | mapViewAddMarker 89 | mapViewMoveCamera 90 | mapViewSetMapType 91 | mapViewSetMarkerColor 92 | mapViewSetMarkerIcon 93 | mapViewSetMarkerInfo 94 | mapViewSetMarkerPosition 95 | mapViewSetMarkerVisible 96 | mapViewZoomIn 97 | mapViewZoomOut 98 | mapViewZoomTo 99 | mathAcos 100 | mathAsin 101 | mathAtan 102 | mathCeil 103 | mathCos 104 | mathE 105 | mathExp 106 | mathFloor 107 | mathGetDip 108 | mathGetDisplayHeight 109 | mathGetDisplayWidth 110 | mathLog 111 | mathLog10 112 | mathMax 113 | mathMin 114 | mathPi 115 | mathPow 116 | mathRound 117 | mathSin 118 | mathSqrt 119 | mathTan 120 | mathToDegree 121 | mathToRadian 122 | mediaplayerCreate 123 | mediaplayerPause 124 | mediaplayerStart 125 | not 126 | objectanimatorCancel 127 | objectanimatorIsRunning 128 | objectanimatorSetDuration 129 | objectanimatorSetFromTo 130 | objectanimatorSetProperty 131 | objectanimatorSetTarget 132 | progressBarSetIndeterminate 133 | random 134 | repeat 135 | requestFocus 136 | resizeBitmapFileRetainRatio 137 | resizeBitmapFileToCircle 138 | resizeBitmapFileToSquare 139 | resizeBitmapFileWithRoundedBorder 140 | rotateBitmapFile 141 | scaleBitmapFile 142 | seekBarGetMax 143 | seekBarGetProgress 144 | seekBarSetMax 145 | seekBarSetProgress 146 | setAlpha 147 | setBgColor 148 | setBgResource 149 | setBitmapFileBrightness 150 | setBitmapFileColorFilter 151 | setBitmapFileContrast 152 | setChecked 153 | setColorFilter 154 | setEnable 155 | setHint 156 | setHintTextColor 157 | setImage 158 | setImageFilePath 159 | setImageUrl 160 | setListMap 161 | setRotate 162 | setScaleX 163 | setScaleY 164 | setScaleY 165 | setScaleY 166 | setText 167 | setTextColor 168 | setTitle 169 | setTranslationX 170 | setTranslationY 171 | setTranslationY 172 | setTypeface 173 | setVarBoolean 174 | setVarInt 175 | setVarInt 176 | setVarInt 177 | setVarInt 178 | setVarInt 179 | setVarInt 180 | setVarInt 181 | setVarString 182 | setVarString 183 | setVisible 184 | skewBitmapFile 185 | spnGetSelection 186 | spnRefresh 187 | spnSetData 188 | spnSetSelection 189 | startActivity 190 | strToListMap 191 | strToMap 192 | stringContains 193 | stringEquals 194 | stringIndex 195 | stringJoin 196 | stringLastIndex 197 | stringLength 198 | stringReplace 199 | stringSub 200 | timerAfter 201 | timerEvery 202 | toLowerCase 203 | toNumber 204 | toString 205 | toStringFormat 206 | toStringWithDecimal 207 | toUpperCase 208 | trim 209 | true 210 | vibratorAction 211 | webViewClearCache 212 | webViewGetUrl 213 | webViewGoBack 214 | webViewGoForward 215 | webViewLoadUrl 216 | webViewSetCacheMode 217 | webViewStopLoading 218 | webViewZoomIn 219 | webViewZoomOut -------------------------------------------------------------------------------- /src/test/kotlin/io/sketchware/manager/collections/managers/BlockCollectionManagerTest.kt: -------------------------------------------------------------------------------- 1 | package io.sketchware.manager.collections.managers 2 | 3 | import io.sketchware.models.collections.BlockCollectionItem 4 | import org.junit.jupiter.api.Assertions.assertNotNull 5 | import org.junit.jupiter.api.Assertions.assertNull 6 | import org.junit.jupiter.api.Test 7 | import java.io.File 8 | 9 | private const val blockCollectionValue = 10 | "{\"data\":\"{\\\"color\\\":-13851166,\\\"id\\\":\\\"99000012\\\",\\\"nextBlock\\\":-1,\\\"opCode\\\":\\\"finishActivity\\\",\\\"parameters\\\":[],\\\"spec\\\":\\\"Finish Activity\\\",\\\"subStack1\\\":-1,\\\"subStack2\\\":-1,\\\"type\\\":\\\"f\\\",\\\"typeName\\\":\\\"\\\"}\\n\",\"name\":\"vv\"}\n" 11 | 12 | internal class BlockCollectionManagerTest { 13 | private val manager = BlockCollectionManager(blockCollectionValue, File("/")) 14 | 15 | @Test 16 | fun getAll() { 17 | assertNotNull(manager.all) 18 | } 19 | 20 | @Test 21 | fun addItem() { 22 | manager.addItem(BlockCollectionItem("test", listOf())) 23 | assertNotNull(manager.all.find { it.name == "test" }) 24 | } 25 | 26 | @Test 27 | fun removeItem() { 28 | val block = BlockCollectionItem("test1", listOf()) 29 | manager.addItem(block) 30 | assertNotNull(manager.all.find { it.name == "test1" }) 31 | manager.removeItem(block) 32 | assertNull(manager.all.find { it.name == "test1" }) 33 | } 34 | 35 | } -------------------------------------------------------------------------------- /src/test/kotlin/io/sketchware/manager/collections/managers/FileCollectionManagerTest.kt: -------------------------------------------------------------------------------- 1 | package io.sketchware.manager.collections.managers 2 | 3 | import io.sketchware.models.collections.FileCollectionItem 4 | import org.junit.jupiter.api.Assertions.assertNotNull 5 | import org.junit.jupiter.api.Assertions.assertNull 6 | import org.junit.jupiter.api.Test 7 | import java.io.File 8 | 9 | private const val fileCollectionValue = 10 | "{\"data\":\"test.png\",\"name\":\"test\"}\n{\"data\":\"test2.png\",\"name\":\"test2\"}" 11 | 12 | internal class FileCollectionManagerTest { 13 | 14 | private val manager = FileCollectionManager(fileCollectionValue, File("/")) 15 | 16 | @Test 17 | fun getAll() { 18 | assertNotNull(manager.all) 19 | } 20 | 21 | @Test 22 | fun addItem() { 23 | manager.addItem(FileCollectionItem("test", "")) 24 | assertNotNull(manager.all.find { it.name == "test" }) 25 | } 26 | 27 | @Test 28 | fun removeItem() { 29 | val item = FileCollectionItem("test1", "") 30 | manager.addItem(item) 31 | assertNotNull(manager.all.find { it.name == "test1" }) 32 | manager.removeItem(item) 33 | assertNull(manager.all.find { it.name == "test1" }) 34 | } 35 | 36 | } -------------------------------------------------------------------------------- /src/test/kotlin/io/sketchware/manager/collections/managers/MoreblockCollectionManagerTest.kt: -------------------------------------------------------------------------------- 1 | package io.sketchware.manager.collections.managers 2 | 3 | import io.sketchware.models.collections.MoreblockCollectionItem 4 | import org.junit.jupiter.api.Assertions.assertNotNull 5 | import org.junit.jupiter.api.Assertions.assertNull 6 | import org.junit.jupiter.api.Test 7 | import java.io.File 8 | 9 | internal class MoreblockCollectionManagerTest { 10 | private val manager = MoreblockCollectionManager( 11 | String( 12 | javaClass.getResourceAsStream("/collections/moreblocks")!!.readBytes() 13 | ), File("") 14 | ) 15 | 16 | @Test 17 | fun getAll() { 18 | assertNotNull(manager.all) 19 | } 20 | 21 | @Test 22 | fun addItem() { 23 | manager.addItem(MoreblockCollectionItem("test", listOf(), listOf())) 24 | assertNotNull(manager.all.find { it.name == "test" }) 25 | } 26 | 27 | @Test 28 | fun removeItem() { 29 | val moreblock = MoreblockCollectionItem("test1", listOf(), listOf()) 30 | manager.addItem(moreblock) 31 | assertNotNull(manager.all.find { it.name == "test1" }) 32 | manager.all.find { it.name == "test1" }?.let { manager.removeItem(it) } 33 | assertNull(manager.all.find { it.name == "test1" }) 34 | } 35 | 36 | } -------------------------------------------------------------------------------- /src/test/kotlin/io/sketchware/manager/customs/blocks/SketchwareCustomBlocksManagerTest.kt: -------------------------------------------------------------------------------- 1 | package io.sketchware.manager.customs.blocks 2 | 3 | import io.sketchware.models.customs.CustomBlock 4 | import org.junit.jupiter.api.Assertions.* 5 | import org.junit.jupiter.api.Test 6 | import java.io.File 7 | 8 | internal class SketchwareCustomBlocksManagerTest { 9 | 10 | private val manager = SketchwareCustomBlocksManager( 11 | String(javaClass.getResourceAsStream("/customs/blocks/block.json")!!.readBytes()), 12 | String(javaClass.getResourceAsStream("/customs/blocks/palette.json")!!.readBytes()), 13 | File(""), 14 | File("") 15 | ) 16 | 17 | @Test 18 | fun getBlocks() { 19 | assertNotNull(manager.blocks) 20 | } 21 | 22 | @Test 23 | fun getFreeId() { 24 | assertNull(manager.blocks.find { it.groupId == manager.freeId }) 25 | } 26 | 27 | @Test 28 | fun addBlocksGroup() { 29 | val id = manager.freeId 30 | val group = manager.blocks[0].copy(groupId = id) 31 | manager.addBlocksGroup(group) 32 | assertNotNull(manager.blocks.find { it.groupId == id }) 33 | } 34 | 35 | @Test 36 | fun removeBlocksGroup() { 37 | val group = manager.blocks[0] 38 | manager.removeBlocksGroup(group.groupId) 39 | assertFalse(manager.blocks.contains(group)) 40 | } 41 | 42 | @Test 43 | fun removeBlock() { 44 | val group = manager.blocks[0] 45 | manager.removeBlock(group.groupId, group.blocks[0].name) 46 | assertFalse(manager.blocks[0].blocks.contains(group.blocks[0])) 47 | } 48 | 49 | @Test 50 | fun editBlocksGroup() { 51 | val group = manager.blocks[0] 52 | manager.editBlocksGroup(group.groupId) { 53 | it.hexColor = "hex" 54 | } 55 | assertNotNull(manager.blocks.find { it.hexColor == "hex" }) 56 | } 57 | 58 | @Test 59 | fun addBlockToGroup() { 60 | val group = manager.blocks[0] 61 | manager.addBlockToGroup( 62 | group.groupId, 63 | CustomBlock("", "hex", "test", 0, listOf(), "", "") 64 | ) 65 | assertNotNull(manager.blocks.find { it.name == "test" }) 66 | } 67 | } -------------------------------------------------------------------------------- /src/test/kotlin/io/sketchware/manager/customs/components/SketchwareCustomComponentsManagerTest.kt: -------------------------------------------------------------------------------- 1 | package io.sketchware.manager.customs.components 2 | 3 | import io.sketchware.models.customs.CustomComponent 4 | import org.junit.jupiter.api.Assertions.assertFalse 5 | import org.junit.jupiter.api.Assertions.assertNotNull 6 | import org.junit.jupiter.api.Test 7 | import java.io.File 8 | 9 | internal class SketchwareCustomComponentsManagerTest { 10 | 11 | private val manager = SketchwareCustomComponentsManager( 12 | String(javaClass.getResourceAsStream("/customs/component.json")!!.readBytes()), File("") 13 | ) 14 | 15 | @Test 16 | fun getComponents() { 17 | assertNotNull(manager.components) 18 | } 19 | 20 | @Test 21 | fun addComponent() { 22 | manager.addComponent( 23 | CustomComponent( 24 | 0, "class", "desc", "", "", 380, "", 25 | "", "", "", "", "" 26 | ) 27 | ) 28 | assertNotNull(manager.components.find { it.id == 380 }) 29 | } 30 | 31 | @Test 32 | fun removeComponent() { 33 | val component = manager.components[0] 34 | manager.removeComponent(component) 35 | assertFalse(manager.components.contains(component)) 36 | } 37 | } -------------------------------------------------------------------------------- /src/test/kotlin/io/sketchware/manager/customs/listeners/SketchwareCustomListenersManagerTest.kt: -------------------------------------------------------------------------------- 1 | package io.sketchware.manager.customs.listeners 2 | 3 | import io.sketchware.models.customs.CustomEvent 4 | import io.sketchware.models.customs.CustomListenerGroup 5 | import org.junit.jupiter.api.Assertions.assertNotNull 6 | import org.junit.jupiter.api.Assertions.assertNull 7 | import org.junit.jupiter.api.Test 8 | import java.io.File 9 | 10 | internal class SketchwareCustomListenersManagerTest { 11 | 12 | private val manager = SketchwareCustomListenersManager( 13 | String(javaClass.getResourceAsStream("/customs/events.json")!!.readBytes()), 14 | String(javaClass.getResourceAsStream("/customs/listeners.json")!!.readBytes()), 15 | File(""), 16 | File("") 17 | ) 18 | 19 | @Test 20 | fun getListeners() { 21 | assertNotNull(manager.listeners) 22 | } 23 | 24 | @Test 25 | fun addListenerGroup() { 26 | manager.addListenerGroup( 27 | CustomListenerGroup( 28 | "test", false, "", "", 29 | listOf( 30 | CustomEvent( 31 | listOf(), 0, "some_id", "dff", "flkfwwf", "fwkwffw", "wffwfw" 32 | ) 33 | ) 34 | ) 35 | ) 36 | assertNotNull(manager.listeners.find { it.name == "test" }) 37 | } 38 | 39 | @Test 40 | fun removeListenerGroup() { 41 | val listener = manager.listeners[0] 42 | manager.removeListenerGroup(listener.name) 43 | assertNull(manager.listeners.find { it.name == listener.name }) 44 | } 45 | 46 | @Test 47 | fun editListenerGroup() { 48 | val listener = manager.listeners[0] 49 | manager.editListenerGroup(listener.name) { 50 | it.name = "damn" 51 | } 52 | assertNotNull(manager.listeners.find { it.name == "damn" }) 53 | } 54 | 55 | } -------------------------------------------------------------------------------- /src/test/kotlin/io/sketchware/manager/projects/data/FileManagerTest.kt: -------------------------------------------------------------------------------- 1 | package io.sketchware.manager.projects.data 2 | 3 | import io.sketchware.annotations.ExperimentalSWManagerAPI 4 | import io.sketchware.models.projects.* 5 | import org.junit.jupiter.api.Assertions.* 6 | import org.junit.jupiter.api.Test 7 | import java.io.File 8 | 9 | private const val fileValue = "@activity\n" + 10 | "{\"fileName\":\"main\",\"fileType\":0,\"keyboardSetting\":2,\"options\":2,\"orientation\":2,\"theme\":-1}\n" + 11 | "{\"fileName\":\"menu\",\"fileType\":0,\"keyboardSetting\":2,\"options\":2,\"orientation\":2,\"theme\":-1}\n" + 12 | "{\"fileName\":\"editor\",\"fileType\":0,\"keyboardSetting\":2,\"options\":2,\"orientation\":2,\"theme\":-1}\n" + 13 | "{\"fileName\":\"collage\",\"fileType\":0,\"keyboardSetting\":2,\"options\":2,\"orientation\":2,\"theme\":-1}\n" + 14 | "@customview\n" + 15 | "{\"fileName\":\"files\",\"fileType\":1,\"keyboardSetting\":0,\"options\":0,\"orientation\":2,\"theme\":-1}\n" + 16 | "{\"fileName\":\"custom\",\"fileType\":1,\"keyboardSetting\":0,\"options\":0,\"orientation\":2,\"theme\":-1}\n" + 17 | "{\"fileName\":\"_drawer_editor\",\"fileType\":2,\"keyboardSetting\":0,\"options\":0,\"orientation\":2,\"theme\":-1}" 18 | 19 | internal class FileManagerTest { 20 | 21 | private val fileManager = FileManager(fileValue, File("/")) 22 | 23 | @Test 24 | fun getActivities() { 25 | assertNotNull(fileManager.activities) 26 | assertFalse { fileManager.activities.isEmpty() } 27 | } 28 | 29 | @Test 30 | fun getCustomViews() { 31 | assertNotNull(fileManager.customViews) 32 | assertFalse { fileManager.activities.isEmpty() } 33 | } 34 | 35 | @Test 36 | fun addActivity() { 37 | fileManager.addActivity( 38 | SketchwareDataFileModel( 39 | "file_name_test", FileType.Activity, KeyboardSetting.Unspecified, 0, 40 | Orientation.Both, ActivityTheme.NO_ACTIONBAR 41 | ) 42 | ) 43 | assertNotNull(fileManager.activities.find { it.activityName == "FileNameTestActivity" }) 44 | } 45 | 46 | @Test 47 | fun addCustomView() { 48 | fileManager.addCustomView( 49 | SketchwareDataFileModel( 50 | "file_name_test2", FileType.CustomView, KeyboardSetting.Unspecified, 0, 51 | Orientation.Both, ActivityTheme.NO_ACTIONBAR 52 | ) 53 | ) 54 | assertNotNull(fileManager.customViews.find { it.fileName == "file_name_test2" }) 55 | } 56 | 57 | @OptIn(ExperimentalSWManagerAPI::class) 58 | @Test 59 | fun removeActivity() { 60 | fileManager.removeActivity("file_name_test") 61 | assertNull(fileManager.activities.find { it.activityName == "FileNameTestActivity" }) 62 | } 63 | 64 | @OptIn(ExperimentalSWManagerAPI::class) 65 | @Test 66 | fun removeCustomView() { 67 | fileManager.removeCustomView("file_name_test2") 68 | assertNull(fileManager.customViews.find { it.fileName == "file_name_test2" }) 69 | } 70 | } -------------------------------------------------------------------------------- /src/test/kotlin/io/sketchware/manager/projects/data/LibraryManagerTest.kt: -------------------------------------------------------------------------------- 1 | package io.sketchware.manager.projects.data 2 | 3 | import org.junit.jupiter.api.Assertions.assertFalse 4 | import org.junit.jupiter.api.Assertions.assertNotNull 5 | import org.junit.jupiter.api.Test 6 | import java.io.File 7 | 8 | private const val libraryValue = "@firebaseDB\n" + 9 | "{\"adUnits\":[],\"data\":\"\",\"libType\":0,\"reserved1\":\"\",\"reserved2\":\"\",\"reserved3\":\"\",\"testDevices\":[],\"useYn\":\"N\"}\n" + 10 | "@compat\n" + 11 | "{\"adUnits\":[],\"data\":\"\",\"libType\":1,\"reserved1\":\"\",\"reserved2\":\"\",\"reserved3\":\"\",\"testDevices\":[],\"useYn\":\"Y\"}\n" + 12 | "@admob\n" + 13 | "{\"adUnits\":[{\"id\":\"ca-app-pub-3538535077259745/1896727371\",\"name\":\"Banner\"},{\"id\":\"ca-app-pub-3538535077259745/2435783424\",\"name\":\"Intersticial\"}],\"data\":\"\",\"libType\":2,\"reserved1\":\"Banner : ca-app-pub-3538535077259745/1896727371\",\"reserved2\":\"Intersticial : ca-app-pub-3538535077259745/2435783424\",\"reserved3\":\"\",\"testDevices\":[],\"useYn\":\"Y\"}\n" + 14 | "@googleMap\n" + 15 | "{\"adUnits\":[],\"data\":\"\",\"libType\":3,\"reserved1\":\"\",\"reserved2\":\"\",\"reserved3\":\"\",\"testDevices\":[],\"useYn\":\"N\"}" 16 | 17 | internal class LibraryManagerTest { 18 | 19 | private val manager = LibraryManager(libraryValue, File("/")) 20 | 21 | @Test 22 | fun getLibraries() { 23 | println(manager.libraries) 24 | assertNotNull(manager.libraries) 25 | assertFalse { manager.libraries.isEmpty() } 26 | } 27 | 28 | @Test 29 | fun editLibraryData() { 30 | manager.editLibraryData("admob") { 31 | this.data = "test" 32 | } 33 | assertNotNull(manager.libraries.find { it.information.data == "test" }) 34 | } 35 | } -------------------------------------------------------------------------------- /src/test/kotlin/io/sketchware/manager/projects/data/ResourcesManagerTest.kt: -------------------------------------------------------------------------------- 1 | package io.sketchware.manager.projects.data 2 | 3 | import io.sketchware.models.projects.ProjectResource 4 | import org.junit.jupiter.api.Assertions.assertNotNull 5 | import org.junit.jupiter.api.Assertions.assertNull 6 | import org.junit.jupiter.api.Test 7 | import java.io.File 8 | 9 | private const val resourcesValue = "@images\n" + 10 | "{\"resFullName\":\"png.png\",\"resName\":\"png\",\"resType\":1}\n" + 11 | "{\"resFullName\":\"cuadro1.png\",\"resName\":\"cuadro1\",\"resType\":1}" + 12 | "@sounds\n" + 13 | "{\"resFullName\":\"sound.mp3\",\"resName\":\"sound\",\"resType\":1}" + 14 | "@fonts\n" + 15 | "{\"resFullName\":\"texto1.ttf\",\"resName\":\"texto1\",\"resType\":1}\n" + 16 | "{\"resFullName\":\"texto2.ttf\",\"resName\":\"texto2\",\"resType\":1}" 17 | 18 | internal class ResourcesManagerTest { 19 | 20 | private val resourcesManager = ResourcesManager(resourcesValue, File("")) 21 | 22 | @Test 23 | fun getImages() { 24 | assertNotNull(resourcesManager.images) 25 | } 26 | 27 | @Test 28 | fun getFonts() { 29 | assertNotNull(resourcesManager.fonts) 30 | } 31 | 32 | @Test 33 | fun getSounds() { 34 | assertNotNull(resourcesManager.sounds) 35 | } 36 | 37 | @Test 38 | fun addImage() { 39 | resourcesManager.addImage(ProjectResource("image.png", "image", 1)) 40 | assertNotNull(resourcesManager.images.find { it.fullName == "image.png" }) 41 | } 42 | 43 | @Test 44 | fun addFont() { 45 | resourcesManager.addFont(ProjectResource("font.ttf", "font", 1)) 46 | assertNotNull(resourcesManager.fonts.find { it.fullName == "font.ttf" }) 47 | } 48 | 49 | @Test 50 | fun addSound() { 51 | resourcesManager.addSound(ProjectResource("sound1.mp3", "sound1", 1)) 52 | assertNotNull(resourcesManager.sounds.find { it.fullName == "sound1.mp3" }) 53 | } 54 | 55 | @Test 56 | fun removeImage() { 57 | resourcesManager.removeImage("cuadro1") 58 | assertNull(resourcesManager.images.find { it.name == "cuadro1" }) 59 | } 60 | 61 | @Test 62 | fun removeFont() { 63 | resourcesManager.removeFont("texto1") 64 | assertNull(resourcesManager.fonts.find { it.name == "texto1" }) 65 | } 66 | 67 | @Test 68 | fun removeSound() { 69 | resourcesManager.removeSound("sound.mp3") 70 | assertNull(resourcesManager.sounds.find { it.name == "sound" }) 71 | } 72 | 73 | } -------------------------------------------------------------------------------- /src/test/kotlin/io/sketchware/manager/projects/data/ViewManagerTest.kt: -------------------------------------------------------------------------------- 1 | package io.sketchware.manager.projects.data 2 | 3 | import org.junit.jupiter.api.Assertions.assertNotNull 4 | import org.junit.jupiter.api.Assertions.assertTrue 5 | import org.junit.jupiter.api.Test 6 | import java.io.File 7 | 8 | internal class ViewManagerTest { 9 | 10 | private val manager = ViewManager( 11 | String(javaClass.getResourceAsStream("/project/data/view")!!.readBytes()), File("") 12 | ) 13 | 14 | @Test 15 | fun getView() { 16 | assertNotNull(manager.getView("_drawer_main")) 17 | assertNotNull(manager.getView("main")) 18 | assertNotNull(manager.getView("main", "fab")) 19 | } 20 | 21 | @Test 22 | fun editView() { 23 | manager.editView("main") { 24 | it.forEach { widget -> widget.id = "test" } 25 | } 26 | assertTrue { manager.getView("main")?.all { it.id == "test" } == true } 27 | } 28 | } -------------------------------------------------------------------------------- /src/test/resources/collections/moreblocks: -------------------------------------------------------------------------------- 1 | {"data":"{\"color\":-10701022,\"id\":\"10\",\"nextBlock\":-1,\"opCode\":\"addSourceDirectly\",\"parameters\":[\"android.support.design.widget.Snackbar.make(_view, _label.toString(), android.support.design.widget.Snackbar.LENGTH_SHORT).show();\"],\"spec\":\"add source directly %s.inputOnly\",\"subStack1\":-1,\"subStack2\":-1,\"type\":\" \",\"typeName\":\"\"}\n","name":"test","reserved1":"Snackbar on %m.view.view with label %s.label"} 2 | -------------------------------------------------------------------------------- /src/test/resources/customs/blocks/block.json: -------------------------------------------------------------------------------- 1 | [ 2 | { 3 | "typeName": "typeName", 4 | "color": "#FF2196F3", 5 | "name": "testBlockName", 6 | "palette": "9", 7 | "spec": "spec", 8 | "type": "s", 9 | "code": "code" 10 | }, 11 | { 12 | "typeName": "test", 13 | "color": "#FF2196F3", 14 | "name": "test", 15 | "palette": "9", 16 | "spec": "test", 17 | "type": "b", 18 | "code": "nxnxnd" 19 | }, 20 | { 21 | "name": "test2", 22 | "palette": "10", 23 | "spec": "chcjfjfhf %b %d ", 24 | "type": "s", 25 | "typeName": "fhjfjf", 26 | "code": "jdjdjdjd", 27 | "color": "#FFF44336" 28 | } 29 | ] -------------------------------------------------------------------------------- /src/test/resources/customs/blocks/palette.json: -------------------------------------------------------------------------------- 1 | [ 2 | { 3 | "name": "Test Palette", 4 | "color": "#FF2196F3" 5 | }, 6 | { 7 | "name": "Group2", 8 | "color": "#FFF44336" 9 | }, 10 | { 11 | "name": "test", 12 | "color": "#FF9C27B0" 13 | } 14 | ] -------------------------------------------------------------------------------- /src/test/resources/customs/component.json: -------------------------------------------------------------------------------- 1 | [ 2 | { 3 | "icon": "2131165504", 4 | "class": "component1typeClass", 5 | "description": "component1Description", 6 | "defineAdditionalVar": "component1AdditionalVar2", 7 | "typeName": "component1TypeName", 8 | "id": "37", 9 | "url": "component1.docs", 10 | "name": "component1", 11 | "varName": "component1VariableName", 12 | "additionalVar": "component1AdditionalVar1", 13 | "imports": "component1CustomImport", 14 | "buildClass": "component1BuildClass" 15 | }, 16 | { 17 | "class": "component2TypeClass", 18 | "icon": "2131165229", 19 | "description": "component2Desc", 20 | "defineAdditionalVar": "", 21 | "typeName": "component2TypeName", 22 | "id": "38", 23 | "url": "component2Docs", 24 | "name": "component2Name", 25 | "additionalVar": "conponent2AdditionVar", 26 | "varName": "component2VarName", 27 | "imports": "", 28 | "buildClass": "component2BuildClass" 29 | } 30 | ] -------------------------------------------------------------------------------- /src/test/resources/customs/events.json: -------------------------------------------------------------------------------- 1 | [ 2 | { 3 | "headerSpec": "Listener1Event1Spec 1", 4 | "icon": "2131165606", 5 | "var": "listener1Event1ComponentName", 6 | "description": "listener1Event1Desc", 7 | "parameters": "listener1Event1Params", 8 | "name": "listener1event1Name", 9 | "code": "listener1Event1Code", 10 | "listener": "Listener1Name" 11 | }, 12 | { 13 | "headerSpec": "listener1Event2Spec 2", 14 | "icon": "2131165458", 15 | "var": "listener1Event2WOrCName", 16 | "description": "listener1Event2Desc", 17 | "parameters": "listener1Event2Desc", 18 | "name": "listener1Event2Name", 19 | "code": "listener1Event2Code", 20 | "listener": "Listener1Name" 21 | } 22 | ] -------------------------------------------------------------------------------- /src/test/resources/customs/listeners.json: -------------------------------------------------------------------------------- 1 | [ 2 | { 3 | "name": "Listener1Name", 4 | "code": "code", 5 | "s": "false", 6 | "imports": "Listener1CustomImport" 7 | }, 8 | { 9 | "name": "Listener2", 10 | "s": "true", 11 | "code": "//Listener2\ntest2", 12 | "imports": "" 13 | } 14 | ] --------------------------------------------------------------------------------