├── .gitattributes ├── .gitignore ├── CONTRIBUTING.md ├── Demo ├── build.gradle ├── sampledata │ ├── colors │ ├── pen_brushes │ └── tools.json └── src │ └── main │ ├── AndroidManifest.xml │ ├── java │ └── com │ │ └── myscript │ │ ├── certificate │ │ └── MyCertificate.java │ │ └── iink │ │ └── demo │ │ ├── IInkApplication.kt │ │ ├── MainActivity.kt │ │ ├── data │ │ ├── ContentRepository.kt │ │ ├── IContentRepository.kt │ │ └── ToolRepository.kt │ │ ├── di │ │ └── DemoModule.kt │ │ ├── domain │ │ ├── ConfigurationProfile.kt │ │ └── PartEditor.kt │ │ ├── ui │ │ ├── ColorsAdapter.kt │ │ ├── EditorViewModel.kt │ │ ├── ThicknessesAdapter.kt │ │ ├── ToolbarTransformer.kt │ │ └── ToolsAdapter.kt │ │ └── util │ │ ├── Dialogs.kt │ │ ├── autoCloseable.kt │ │ └── viewExt.kt │ └── res │ ├── drawable │ ├── bottom_sheet_handle.xml │ ├── disc.xml │ ├── ic_add.xml │ ├── ic_brush_outlined.xml │ ├── ic_chevron_left.xml │ ├── ic_chevron_right.xml │ ├── ic_colorize_outlined.xml │ ├── ic_delete.xml │ ├── ic_eraser.xml │ ├── ic_hand_outlined.xml │ ├── ic_lasso.xml │ ├── ic_launcher_foreground.xml │ ├── ic_pen_outlined.xml │ ├── ic_redo.xml │ ├── ic_reset_view.xml │ ├── ic_undo.xml │ ├── ic_zoom_in.xml │ ├── ic_zoom_out.xml │ ├── texture_background.png │ ├── texture_stamp.png │ ├── tool_background.xml │ └── tool_background_selected.xml │ ├── font │ ├── myscriptinter.xml │ ├── myscriptinter_bold.otf │ ├── myscriptinter_regular.otf │ ├── stix.xml │ ├── stix_italic.otf │ └── stix_regular.otf │ ├── layout │ ├── editor_prediction_layout.xml │ ├── editor_text_input_layout.xml │ ├── editor_toolbar.xml │ ├── main_activity.xml │ ├── toolbar_color_cell.xml │ ├── toolbar_pen_brush_row.xml │ ├── toolbar_settings_sheet_layout.xml │ ├── toolbar_thickness_cell.xml │ └── toolbar_tool_cell.xml │ ├── menu │ └── main_menu.xml │ ├── mipmap-anydpi-v26 │ ├── ic_launcher.xml │ └── ic_launcher_round.xml │ ├── mipmap-hdpi │ ├── ic_launcher.png │ └── ic_launcher_round.png │ ├── mipmap-mdpi │ ├── ic_launcher.png │ └── ic_launcher_round.png │ ├── mipmap-xhdpi │ ├── ic_launcher.png │ └── ic_launcher_round.png │ ├── mipmap-xxhdpi │ ├── ic_launcher.png │ └── ic_launcher_round.png │ ├── mipmap-xxxhdpi │ ├── ic_launcher.png │ └── ic_launcher_round.png │ ├── raw │ └── theme.css │ ├── values-large │ └── dimens.xml │ ├── values-night │ ├── colors.xml │ └── themes.xml │ ├── values │ ├── colors.xml │ ├── dimens.xml │ ├── ic_launcher_background.xml │ ├── strings.xml │ └── themes.xml │ └── xml │ └── file_paths.xml ├── GetStarted ├── build.gradle └── src │ └── main │ ├── AndroidManifest.xml │ ├── java │ └── com │ │ └── myscript │ │ ├── certificate │ │ └── MyCertificate.java │ │ └── iink │ │ └── getstarted │ │ ├── ErrorActivity.java │ │ ├── IInkApplication.java │ │ └── MainActivity.java │ └── res │ ├── color │ ├── button_text_color.xml │ └── editor_icon_color.xml │ ├── drawable-v24 │ └── ic_launcher_foreground.xml │ ├── drawable │ ├── button_background.xml │ ├── ic_delete.xml │ ├── ic_redo.xml │ └── ic_undo.xml │ ├── layout │ ├── error_activity.xml │ └── main_activity.xml │ ├── menu │ └── main_activity_menu.xml │ ├── mipmap-anydpi-v26 │ └── ic_launcher.xml │ ├── mipmap-hdpi │ └── ic_launcher.png │ ├── mipmap-mdpi │ └── ic_launcher.png │ ├── mipmap-xhdpi │ └── ic_launcher.png │ ├── mipmap-xxhdpi │ └── ic_launcher.png │ ├── mipmap-xxxhdpi │ └── ic_launcher.png │ └── values │ ├── colors.xml │ ├── dimens.xml │ ├── ic_launcher_background.xml │ ├── strings.xml │ └── themes.xml ├── LICENSE ├── LICENSES ├── androidSupportLib.txt ├── inter.txt ├── kotlin.txt ├── materialDesign.txt └── stix.pdf ├── README.md ├── UIReferenceImplementation ├── build.gradle └── src │ └── main │ ├── AndroidManifest.xml │ ├── java │ └── com │ │ └── myscript │ │ └── iink │ │ └── uireferenceimplementation │ │ ├── Canvas.java │ │ ├── ContextualActions.java │ │ ├── ContextualActionsHelper.java │ │ ├── CustomTextSpan.java │ │ ├── EditorBinding.java │ │ ├── EditorData.java │ │ ├── EditorView.java │ │ ├── FontMetricsProvider.java │ │ ├── FontUtils.java │ │ ├── FrameTimeEstimator.java │ │ ├── IInputControllerListener.java │ │ ├── ISizeListener.java │ │ ├── ImageLoader.java │ │ ├── ImagePainter.java │ │ ├── InputController.java │ │ ├── JiixDefinitions.java │ │ ├── LayerView.java │ │ ├── OfflineSurfaceManager.java │ │ ├── Path.java │ │ └── SmartGuideView.java │ └── res │ ├── drawable │ ├── ic_smart_guide_more.xml │ └── smart_guide_bottom_border.xml │ ├── layout │ ├── editor_view.xml │ └── smart_guide_layout.xml │ └── values │ ├── colors.xml │ └── dimens.xml ├── build.gradle ├── configurations ├── Raw Content │ ├── drawing.json │ └── text_math_shape.json └── interactivity.json ├── gradle.properties ├── gradle └── wrapper │ ├── gradle-wrapper.jar │ └── gradle-wrapper.properties ├── gradlew ├── gradlew.bat └── settings.gradle /.gitattributes: -------------------------------------------------------------------------------- 1 | * text=auto 2 | 3 | # git 4 | .gitignore text 5 | 6 | # source & misc files 7 | *.c text diff=cpp whitespace=blank-at-eol,blank-at-eof,space-before-tab,tab-in-indent,no-newline-at-eof 8 | *.cpp text diff=cpp whitespace=blank-at-eol,blank-at-eof,space-before-tab,tab-in-indent,no-newline-at-eof 9 | *.cs text diff=csharp whitespace=blank-at-eol,blank-at-eof,space-before-tab,tab-in-indent 10 | *.h text diff=cpp whitespace=blank-at-eol,blank-at-eof,space-before-tab,tab-in-indent,no-newline-at-eof 11 | *.hpp text diff=cpp whitespace=blank-at-eol,blank-at-eof,space-before-tab,tab-in-indent,no-newline-at-eof 12 | *.html text diff=html whitespace=blank-at-eol,blank-at-eof,space-before-tab,tab-in-indent 13 | *.java text diff=java whitespace=blank-at-eol,blank-at-eof,space-before-tab,tab-in-indent 14 | *.js text whitespace=blank-at-eol,blank-at-eof,space-before-tab,tab-in-indent 15 | *.l text whitespace=blank-at-eol,blank-at-eof,space-before-tab,tab-in-indent 16 | *.m text diff=objc whitespace=blank-at-eol,blank-at-eof,space-before-tab,tab-in-indent,no-newline-at-eof 17 | *.mm text diff=objc whitespace=blank-at-eol,blank-at-eof,space-before-tab,tab-in-indent,no-newline-at-eof 18 | *.pl text diff=perl whitespace=blank-at-eol,blank-at-eof,space-before-tab,tab-in-indent 19 | *.py text diff=python whitespace=blank-at-eol,blank-at-eof,space-before-tab,tab-in-indent 20 | *.rb text diff=ruby whitespace=blank-at-eol,blank-at-eof,space-before-tab,tab-in-indent 21 | *.tex text diff=text whitespace=blank-at-eol,blank-at-eof,space-before-tab,tab-in-indent 22 | *.txt text whitespace=blank-at-eol,blank-at-eof,space-before-tab,tab-in-indent 23 | *.xml text whitespace=blank-at-eol,blank-at-eof,space-before-tab,tab-in-indent 24 | *.y text whitespace=blank-at-eol,blank-at-eof,space-before-tab,tab-in-indent 25 | *.tsv text whitespace=-blank-at-eol,blank-at-eof,-space-before-tab,-tab-in-indent 26 | *.groovy text whitespace=blank-at-eol,blank-at-eof,space-before-tab,tab-in-indent 27 | 28 | # Shell scripts 29 | *.sh diff=bash text whitespace=blank-at-eol,blank-at-eof,space-before-tab,tab-in-indent eol=lf executable=maybe 30 | *.bat text whitespace=blank-at-eol,blank-at-eof,space-before-tab,tab-in-indent eol=crlf executable=maybe 31 | *.ps1 text whitespace=blank-at-eol,blank-at-eof,space-before-tab,tab-in-indent eol=crlf executable=maybe 32 | 33 | # Awk scripts 34 | *.awk text whitespace=blank-at-eol,blank-at-eof,space-before-tab,tab-in-indent eol=lf 35 | 36 | # GNU Makefile 37 | Makefile text whitespace=blank-at-eol,blank-at-eof,space-before-tab eol=lf 38 | 39 | # Android 40 | *.mk text whitespace=blank-at-eol,blank-at-eof,space-before-tab,tab-in-indent eol=lf 41 | *.gradle text whitespace=blank-at-eol,blank-at-eof,space-before-tab,tab-in-indent 42 | gradlew text whitespace=blank-at-eol,blank-at-eof,space-before-tab,tab-in-indent eol=lf executable 43 | gradlew.bat text whitespace=blank-at-eol,blank-at-eof,space-before-tab,tab-in-indent eol=crlf executable=maybe 44 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | bin/ 2 | build/ 3 | 4 | # gradle & android specific files 5 | .gradle/ 6 | *.iml 7 | .idea 8 | local.properties 9 | 10 | /Demo/src/main/assets 11 | /Demo/tmp-assets 12 | /GetStarted/src/main/assets 13 | /GetStarted/tmp-assets 14 | 15 | .DS_Store 16 | -------------------------------------------------------------------------------- /CONTRIBUTING.md: -------------------------------------------------------------------------------- 1 | # Contributing 2 | 3 | We gladly welcome pull requests. If you have any questions, or want help solving a problem, feel free to stop by the [#MyScript forum](https://developer.myscript.com/support/). 4 | 5 | ## License 6 | 7 | Those examples are licensed under the [Apache 2.0](https://opensource.org/license/apache-2-0). 8 | -------------------------------------------------------------------------------- /Demo/build.gradle: -------------------------------------------------------------------------------- 1 | import org.apache.commons.io.FileUtils 2 | import org.apache.commons.io.filefilter.FileFilterUtils 3 | 4 | plugins { 5 | id 'com.android.application' 6 | id 'kotlin-android' 7 | } 8 | 9 | android { 10 | namespace 'com.myscript.iink.demo' 11 | 12 | compileSdk project.ext.compileSdk 13 | 14 | buildFeatures { 15 | viewBinding true 16 | buildConfig true 17 | } 18 | 19 | defaultConfig { 20 | minSdk project.ext.minSdk 21 | targetSdk project.ext.targetSdk 22 | 23 | applicationId 'com.myscript.iink.demo' 24 | versionCode 4100 25 | versionName '4.1.0' 26 | 27 | vectorDrawables.useSupportLibrary true 28 | } 29 | } 30 | 31 | dependencies { 32 | implementation "androidx.appcompat:appcompat:${project.ext.appcompatVersion}" 33 | implementation 'androidx.core:core-ktx:1.13.1' 34 | implementation 'androidx.activity:activity-ktx:1.9.2' 35 | implementation 'androidx.documentfile:documentfile:1.0.1' 36 | implementation 'androidx.cardview:cardview:1.0.0' 37 | implementation 'androidx.preference:preference-ktx:1.2.1' 38 | implementation 'com.google.android.material:material:1.12.0' 39 | 40 | def lifecycle_version = '2.8.6' 41 | implementation "androidx.lifecycle:lifecycle-runtime-ktx:$lifecycle_version" 42 | implementation "androidx.lifecycle:lifecycle-viewmodel-ktx:$lifecycle_version" 43 | implementation "androidx.lifecycle:lifecycle-livedata-ktx:$lifecycle_version" 44 | 45 | implementation project(':UIReferenceImplementation') 46 | } 47 | 48 | task DownloadAndExtractAssets(type: Copy) { 49 | def sourceUrls = ['https://download.myscript.com/iink/recognitionAssets_iink_4.1/myscript-iink-recognition-diagram.zip', 50 | 'https://download.myscript.com/iink/recognitionAssets_iink_4.1/myscript-iink-recognition-raw-content.zip', 51 | 'https://download.myscript.com/iink/recognitionAssets_iink_4.1/myscript-iink-recognition-raw-content2.zip', 52 | 'https://download.myscript.com/iink/recognitionAssets_iink_4.1/myscript-iink-recognition-math.zip', 53 | 'https://download.myscript.com/iink/recognitionAssets_iink_4.1/myscript-iink-recognition-math2.zip', 54 | 'https://download.myscript.com/iink/recognitionAssets_iink_4.1/myscript-iink-recognition-text-en_US.zip'] 55 | def targetDir = new File(projectDir, "src/main/assets/") 56 | def diagramConf = new File(targetDir, "conf/diagram.conf") 57 | def rawContentConf = new File(targetDir, "conf/raw-content.conf") 58 | def mathConf = new File(targetDir, "conf/math.conf") 59 | def textConf = new File(targetDir, "conf/en_US.conf") 60 | def rawContent2Conf = new File(targetDir, "conf/raw-content2.conf") 61 | def math2Conf = new File(targetDir, "conf/math2.conf") 62 | 63 | if (!diagramConf.exists() || !rawContentConf.exists() || !mathConf.exists() || !textConf.exists() || !rawContent2Conf.exists() || !math2Conf.exists()) { 64 | def tmpAssetsDir = new File(projectDir, "tmp-assets/") 65 | def zipDir = new File(tmpAssetsDir, "zips") 66 | 67 | if (!tmpAssetsDir.isDirectory()) 68 | tmpAssetsDir.mkdirs() 69 | 70 | if (!zipDir.isDirectory()) 71 | zipDir.mkdirs() 72 | 73 | sourceUrls.each { sourceUrl -> 74 | ant.get(src: sourceUrl, dest: zipDir.getPath()) 75 | } 76 | 77 | File[] zipFiles = FileUtils.listFiles(zipDir, FileFilterUtils.suffixFileFilter("zip"), FileFilterUtils.trueFileFilter()) 78 | zipFiles.each { File zipFile -> 79 | from zipTree(zipFile) 80 | into tmpAssetsDir 81 | } 82 | } 83 | } 84 | 85 | task CopyAssets(type: Copy, dependsOn: DownloadAndExtractAssets) { 86 | def targetDir = new File(projectDir, "src/main/assets/") 87 | def diagramConf = new File(targetDir, "conf/diagram.conf") 88 | def rawContentConf = new File(targetDir, "conf/raw-content.conf") 89 | def mathConf = new File(targetDir, "conf/math.conf") 90 | def textConf = new File(targetDir, "conf/en_US.conf") 91 | def rawContent2Conf = new File(targetDir, "conf/raw-content2.conf") 92 | def math2Conf = new File(targetDir, "conf/math2.conf") 93 | 94 | if (!diagramConf.exists() || !rawContentConf.exists() || !mathConf.exists() || !textConf.exists() || !rawContent2Conf.exists() || !math2Conf.exists()) { 95 | def tmpAssetsDir = new File(projectDir, "tmp-assets/") 96 | 97 | if (!tmpAssetsDir.isDirectory()) 98 | tmpAssetsDir.mkdirs() 99 | 100 | def recognitionAssetDir = new File(tmpAssetsDir, "recognition-assets/") 101 | 102 | println "Copying downloaded assets from $recognitionAssetDir to $targetDir" 103 | from recognitionAssetDir 104 | into targetDir 105 | 106 | doLast { 107 | tmpAssetsDir.deleteDir() 108 | } 109 | } 110 | } 111 | 112 | task CopyConfigurations(type: Copy) { 113 | from "${projectDir}/../configurations" 114 | into "${projectDir}/src/main/assets/parts" 115 | include( 116 | "**/*.json" 117 | ) 118 | } 119 | 120 | preBuild.dependsOn(CopyAssets, CopyConfigurations) 121 | -------------------------------------------------------------------------------- /Demo/sampledata/colors: -------------------------------------------------------------------------------- 1 | #000000 2 | #FBBC05 3 | #EA4335 4 | #34A853 5 | #4285F4 -------------------------------------------------------------------------------- /Demo/sampledata/pen_brushes: -------------------------------------------------------------------------------- 1 | Felt Pen 2 | Fountain Pen 3 | Calligraphic Brush -------------------------------------------------------------------------------- /Demo/sampledata/tools.json: -------------------------------------------------------------------------------- 1 | { 2 | "tools": [ 3 | { "image" : "@drawable/ic_hand_outlined" }, 4 | { "image" : "@drawable/ic_pen_outlined" }, 5 | { "image" : "@drawable/ic_brush_outlined" }, 6 | { "image" : "@drawable/ic_eraser" }, 7 | { "image" : "@drawable/ic_lasso" }, 8 | ] 9 | } 10 | -------------------------------------------------------------------------------- /Demo/src/main/AndroidManifest.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 12 | 13 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 29 | 32 | 33 | 34 | 35 | -------------------------------------------------------------------------------- /Demo/src/main/java/com/myscript/certificate/MyCertificate.java: -------------------------------------------------------------------------------- 1 | // Copyright @ MyScript. All rights reserved. 2 | 3 | package com.myscript.certificate; 4 | 5 | public final class MyCertificate 6 | { 7 | public static final byte[] getBytes() 8 | { 9 | throw new RuntimeException("Please replace the content of MyCertificate.java with the certificate you received from the developer portal"); 10 | } 11 | } 12 | -------------------------------------------------------------------------------- /Demo/src/main/java/com/myscript/iink/demo/IInkApplication.kt: -------------------------------------------------------------------------------- 1 | // Copyright @ MyScript. All rights reserved. 2 | 3 | package com.myscript.iink.demo 4 | 5 | import android.app.Application 6 | import com.myscript.iink.demo.di.DemoModule 7 | 8 | class IInkApplication : Application() { 9 | 10 | companion object { 11 | lateinit var DemoModule: DemoModule 12 | } 13 | 14 | override fun onCreate() { 15 | super.onCreate() 16 | DemoModule = DemoModule(this) 17 | } 18 | 19 | override fun onTerminate() { 20 | DemoModule.close() 21 | super.onTerminate() 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /Demo/src/main/java/com/myscript/iink/demo/data/ContentRepository.kt: -------------------------------------------------------------------------------- 1 | // Copyright @ MyScript. All rights reserved. 2 | 3 | package com.myscript.iink.demo.data 4 | 5 | import android.content.SharedPreferences 6 | import android.content.res.AssetManager 7 | import androidx.core.content.edit 8 | import com.myscript.iink.ContentPart 9 | import com.myscript.iink.Engine 10 | import com.myscript.iink.demo.domain.PartType 11 | import com.myscript.iink.demo.domain.getConfigurationProfile 12 | import com.myscript.iink.demo.domain.setConfigurationProfile 13 | import java.io.File 14 | import java.io.FileNotFoundException 15 | import java.io.Reader 16 | 17 | class ContentRepository( 18 | private val rootDir: File, 19 | private val engine: Engine?, 20 | private val preferences: SharedPreferences, 21 | private val assetManager: AssetManager? = null 22 | ) : IContentRepository { 23 | 24 | init { 25 | rootDir.mkdirs() 26 | } 27 | 28 | override val allParts: List 29 | get() = preferences.getStringSet(ALL_PARTS_TYPE_KEY, null) 30 | ?.toList() 31 | ?.sorted() 32 | ?: emptyList() 33 | 34 | private fun contentFile(contentId: String): File = File(rootDir, "$contentId.iink") 35 | 36 | private fun savePartId(contentId: String) { 37 | val ids = preferences.getStringSet(ALL_PARTS_TYPE_KEY, null)?.toMutableSet() ?: mutableSetOf() 38 | ids.add(contentId) 39 | preferences.edit { 40 | putStringSet(ALL_PARTS_TYPE_KEY, ids) 41 | } 42 | } 43 | 44 | private fun removePartId(contentId: String) { 45 | val ids = preferences.getStringSet(ALL_PARTS_TYPE_KEY, null)?.toMutableSet() ?: mutableSetOf() 46 | ids.remove(contentId) 47 | preferences.edit { 48 | putStringSet(ALL_PARTS_TYPE_KEY, ids) 49 | } 50 | } 51 | 52 | override fun getPart(contentId: String): ContentPart { 53 | val engine = checkNotNull(engine) { "Cannot get part without valid engine" } 54 | 55 | engine.openPackage(contentFile(contentId)).use { contentPackage -> 56 | return contentPackage.getPart(0) 57 | } 58 | } 59 | 60 | override fun copyPart(contentId: String, outputDir: File): File { 61 | val engine = checkNotNull(engine) { "Cannot copy part without valid engine" } 62 | 63 | val outputFile = File(outputDir, "${contentId}_${System.currentTimeMillis()}.iink") 64 | engine.createPackage(outputFile).use { targetPackage -> 65 | getPart(contentId).use { sourcePart -> 66 | // enforce a save in case such part is being edited at the moment 67 | sourcePart.getPackage().save() 68 | targetPackage.clonePart(sourcePart).also(ContentPart::close) 69 | } 70 | targetPackage.save() 71 | } 72 | return outputFile 73 | } 74 | 75 | override fun createPart(partType: PartType): String { 76 | val engine = checkNotNull(engine) { "Cannot create part without valid engine" } 77 | 78 | // Here we use a human readable name to ease readability in Demo UI. 79 | // Ideally, application should use *stable* naming such as a UUID and link such file path to 80 | // an application data layer (such a Room database) to deal with part metadata (title, type, 81 | // language, last modification date or anything linked to application logic). 82 | var index = allParts.size 83 | var contentId: String 84 | do { 85 | ++index 86 | contentId = "Part$index" 87 | } while (allParts.contains(contentId)) 88 | engine.createPackage(contentFile(contentId)).use { contentPackage -> 89 | contentPackage.createPart(partType.iinkPartType).use { part -> 90 | part.setConfigurationProfile(partType.configurationProfile) 91 | } 92 | // TODO async 93 | contentPackage.save() 94 | } 95 | savePartId(contentId) 96 | return contentId 97 | } 98 | 99 | override fun deletePart(contentId: String) { 100 | removePartId(contentId) 101 | contentFile(contentId).delete() 102 | } 103 | 104 | override fun hasPart(contentId: String): Boolean { 105 | return contentFile(contentId).exists() 106 | } 107 | 108 | override fun savePart(contentId: String) { 109 | engine?.openPackage(contentFile(contentId))?.use { contentPackage -> 110 | contentPackage.save() 111 | } 112 | } 113 | 114 | override fun requestPartTypes(): List { 115 | val configurationProfiles = mutableListOf() 116 | 117 | engine?.supportedPartTypes?.forEach { partName -> 118 | if (partName != null) { 119 | configurationProfiles.add(PartType(partName)) 120 | 121 | val availableProfiles = assetManager?.list(File(CONFIGURATION_PROFILE_DIRECTORY, partName).path) 122 | if (!availableProfiles.isNullOrEmpty()) { 123 | availableProfiles.forEach { profile -> 124 | configurationProfiles.add(PartType(partName, File(profile).nameWithoutExtension)) 125 | } 126 | } 127 | } 128 | } 129 | return configurationProfiles 130 | } 131 | 132 | override fun getConfiguration(contentPart: ContentPart): String? { 133 | val assetManager = assetManager ?: return null 134 | 135 | val defaultFile = File(CONFIGURATION_PROFILE_DIRECTORY, DEFAULT_RAW_CONTENT_CONFIGURATION_FILE_NAME) 136 | 137 | val configurationProfile = contentPart.getConfigurationProfile() 138 | val configurationFile = if (configurationProfile != null) { 139 | val parent = File(CONFIGURATION_PROFILE_DIRECTORY, contentPart.type.toString()) 140 | val confFile = assetManager.list(parent.path)?.firstOrNull { 141 | File(it).nameWithoutExtension == configurationProfile 142 | } 143 | if (confFile != null) { 144 | File(parent, confFile) 145 | } else { 146 | defaultFile 147 | } 148 | } else { 149 | defaultFile 150 | } 151 | 152 | return try { 153 | assetManager.open(configurationFile.path).bufferedReader().use(Reader::readText) 154 | } catch (e: FileNotFoundException) { 155 | null 156 | } 157 | } 158 | 159 | override fun importContent(file: File): List { 160 | val engine = checkNotNull(engine) { "Cannot import content without valid engine" } 161 | require(file.exists()) { "File '$file' does not exist" } 162 | val ids = mutableListOf() 163 | engine.openPackage(file).use { sourcePackage -> 164 | for (partIndex in 0 until sourcePackage.partCount) { 165 | sourcePackage.getPart(partIndex).use { sourcePart -> 166 | // See `createPart` for part id definition, here made simple for Demo purpose 167 | val contentId = "Part${System.currentTimeMillis()}" 168 | engine.createPackage(contentFile(contentId)).use { targetPackage -> 169 | targetPackage.clonePart(sourcePart).also(ContentPart::close) 170 | targetPackage.save() 171 | } 172 | savePartId(contentId) 173 | ids += contentId 174 | } 175 | } 176 | } 177 | return ids 178 | } 179 | 180 | override var lastOpenedPartId: String? 181 | get() = preferences.getString(LAST_PART_ID_KEY, null) 182 | set(value) { 183 | preferences.edit { 184 | putString(LAST_PART_ID_KEY, value) 185 | } 186 | } 187 | 188 | override var lastChosenPartTypeIndex: Int 189 | get() = preferences.getInt(LAST_PART_TYPE_INDEX_KEY, -1) 190 | set(value) { 191 | preferences.edit { 192 | putInt(LAST_PART_TYPE_INDEX_KEY, value) 193 | } 194 | } 195 | 196 | companion object { 197 | private const val LAST_PART_ID_KEY = "iink.demo.lastPartId" 198 | private const val LAST_PART_TYPE_INDEX_KEY = "iink.demo.lastPartTypeIndex" 199 | private const val ALL_PARTS_TYPE_KEY = "iink.demo.allparts" 200 | 201 | private const val CONFIGURATION_PROFILE_DIRECTORY = "parts" 202 | private const val DEFAULT_RAW_CONTENT_CONFIGURATION_FILE_NAME = "interactivity.json" 203 | } 204 | } -------------------------------------------------------------------------------- /Demo/src/main/java/com/myscript/iink/demo/data/IContentRepository.kt: -------------------------------------------------------------------------------- 1 | // Copyright @ MyScript. All rights reserved. 2 | 3 | package com.myscript.iink.demo.data 4 | 5 | import com.myscript.iink.ContentPart 6 | import com.myscript.iink.demo.domain.PartType 7 | import java.io.File 8 | 9 | interface IContentRepository { 10 | 11 | /** 12 | * Returns the identifiers of all the available parts. 13 | * 14 | * @return A list of all the parts' identifiers. 15 | */ 16 | val allParts: List 17 | 18 | /** 19 | * Returns the identifier of the newly created part. 20 | * Such identifier can be further reused in other [IContentRepository] APIs. 21 | * 22 | * @param partType the type of the part to create. 23 | * @return the identifier of the newly created part. 24 | */ 25 | fun createPart(partType: PartType): String 26 | 27 | /** 28 | * Import all [ContentPart] contained in the given [ContentPackage] file. 29 | * Each individual [ContentPart] from source [ContentPackage] is split in its own [ContentPackage] 30 | * locally. 31 | * 32 | * @param file the content package where to find content part to import. 33 | * @return the list of content id imported (that can be used with [getPart]). 34 | * 35 | * @see ContentPackage 36 | * @see ContentPart 37 | * @see allParts 38 | */ 39 | fun importContent(file: File): List 40 | 41 | /** 42 | * Deletes the part file defined by the given identifier. 43 | * 44 | * @param contentId the part identifier of the part to consider. 45 | */ 46 | fun deletePart(contentId: String) 47 | 48 | /** 49 | * Tells whether the given part identifier departs an existing part or not. 50 | * 51 | * @param contentId the part identifier of the part to consider. 52 | * @return `true` if a part matching the given identifier exists, `false` otherwise. 53 | */ 54 | fun hasPart(contentId: String): Boolean // TODO remove 55 | 56 | /** 57 | * Saves the part on disk. 58 | * 59 | * @param contentId the part identifier of the part to consider. 60 | */ 61 | fun savePart(contentId: String) 62 | 63 | /** 64 | * Loads the content part identified by the given content id. 65 | * 66 | * @param contentId the part identifier of the part to consider. 67 | * @return the content part identified by the given content id. 68 | */ 69 | fun getPart(contentId: String): ContentPart 70 | 71 | /** 72 | * Copies the content part within a content package file. 73 | * 74 | * @param contentId the part identifier of the part to consider. 75 | * @param outputDir the directory where to copy the part. 76 | * @return the file where the part was copied. 77 | */ 78 | fun copyPart(contentId: String, outputDir: File): File 79 | 80 | var lastOpenedPartId: String? 81 | 82 | var lastChosenPartTypeIndex: Int 83 | 84 | fun requestPartTypes(): List 85 | 86 | fun getConfiguration(contentPart: ContentPart): String? 87 | } -------------------------------------------------------------------------------- /Demo/src/main/java/com/myscript/iink/demo/data/ToolRepository.kt: -------------------------------------------------------------------------------- 1 | // Copyright @ MyScript. All rights reserved. 2 | 3 | package com.myscript.iink.demo.data 4 | 5 | import android.content.SharedPreferences 6 | import android.graphics.Color 7 | import androidx.annotation.ColorInt 8 | import androidx.core.content.edit 9 | 10 | class ToolRepository(private val preferences: SharedPreferences) { 11 | 12 | @ColorInt 13 | fun getToolColor(toolKey: String): Int { 14 | return preferences.getInt("$toolKey-color", Color.TRANSPARENT) 15 | } 16 | 17 | fun saveToolColor(toolKey: String, @ColorInt color: Int) { 18 | preferences.edit { 19 | putInt("$toolKey-color", color) 20 | } 21 | } 22 | 23 | fun getToolThickness(toolKey: String): Float { 24 | return preferences.getFloat("$toolKey-thickness", 0f) 25 | } 26 | 27 | fun saveToolThickness(toolKey: String, thickness: Float) { 28 | preferences.edit { 29 | putFloat("$toolKey-thickness", thickness) 30 | } 31 | } 32 | 33 | fun getPenBrush(toolKey: String): String? { 34 | return preferences.getString("$toolKey-pen-brush", null) 35 | } 36 | 37 | fun savePenBrush(toolKey: String, brush: String) { 38 | preferences.edit { 39 | putString("$toolKey-pen-brush", brush) 40 | } 41 | } 42 | } 43 | -------------------------------------------------------------------------------- /Demo/src/main/java/com/myscript/iink/demo/domain/ConfigurationProfile.kt: -------------------------------------------------------------------------------- 1 | package com.myscript.iink.demo.domain 2 | 3 | import com.myscript.iink.ContentPart 4 | import java.io.File 5 | 6 | fun ContentPart.getConfigurationProfile(): String? { 7 | return metadata.getString("configuration-profile", "").takeIf(String::isNotEmpty) 8 | } 9 | 10 | fun ContentPart.setConfigurationProfile(configurationProfile: String?) { 11 | metadata = metadata.apply { 12 | setString("configuration-profile", configurationProfile?.takeUnless(String::isNullOrBlank) ?: "") 13 | } 14 | } 15 | 16 | open class PartType(val iinkPartType: String, val configurationProfile: String? = null) { 17 | 18 | data object Diagram : PartType("Diagram") 19 | data object Math : PartType("Math") 20 | data object RawContent : PartType("Raw Content") 21 | data object Text : PartType("Text") 22 | data object TextDocument : PartType("Text Document") 23 | data object Drawing : PartType("Drawing") 24 | 25 | override fun toString(): String { 26 | return if (configurationProfile.isNullOrEmpty()) { 27 | iinkPartType 28 | } else { 29 | "$iinkPartType ($configurationProfile)" 30 | } 31 | } 32 | } 33 | -------------------------------------------------------------------------------- /Demo/src/main/java/com/myscript/iink/demo/ui/ColorsAdapter.kt: -------------------------------------------------------------------------------- 1 | // Copyright @ MyScript. All rights reserved. 2 | 3 | package com.myscript.iink.demo.ui 4 | 5 | import android.content.res.ColorStateList 6 | import android.view.LayoutInflater 7 | import android.view.ViewGroup 8 | import android.widget.ImageView 9 | import androidx.recyclerview.widget.DiffUtil 10 | import androidx.recyclerview.widget.ListAdapter 11 | import androidx.recyclerview.widget.RecyclerView 12 | import com.myscript.iink.demo.R 13 | 14 | 15 | class ColorViewHolder(parent: ViewGroup) : RecyclerView.ViewHolder( 16 | LayoutInflater.from(parent.context).inflate(R.layout.toolbar_color_cell, parent, false) 17 | ) { 18 | private val colorView: ImageView = itemView.findViewById(R.id.toolbar_color_icon) 19 | 20 | fun bind(state: ColorState, onColorSelected: (ColorState) -> Unit) { 21 | colorView.imageTintList = ColorStateList.valueOf(state.color.opaque) 22 | 23 | itemView.isSelected = state.isSelected 24 | itemView.setOnClickListener { onColorSelected(state) } 25 | } 26 | 27 | fun unbind() { 28 | itemView.setOnClickListener(null) 29 | } 30 | } 31 | 32 | class ColorsAdapter(private val onColorSelected: (ColorState) -> Unit) 33 | : ListAdapter(COLOR_DIFF_UTIL_CALLBACK) { 34 | 35 | override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): ColorViewHolder { 36 | return ColorViewHolder(parent) 37 | } 38 | 39 | override fun onBindViewHolder(holder: ColorViewHolder, position: Int) { 40 | holder.bind(getItem(position), onColorSelected) 41 | } 42 | 43 | override fun onViewRecycled(holder: ColorViewHolder) { 44 | holder.unbind() 45 | } 46 | 47 | override fun submitList(list: List?) { 48 | if (list == null) { 49 | super.submitList(null) 50 | } else { 51 | super.submitList(list.toList()) // force DiffUtil by creating a new list 52 | } 53 | } 54 | 55 | companion object { 56 | private val COLOR_DIFF_UTIL_CALLBACK = object : DiffUtil.ItemCallback() { 57 | override fun areItemsTheSame(oldItem: ColorState, newItem: ColorState): Boolean { 58 | return oldItem.color == newItem.color 59 | } 60 | 61 | override fun areContentsTheSame(oldItem: ColorState, newItem: ColorState): Boolean { 62 | return oldItem == newItem 63 | } 64 | } 65 | } 66 | } -------------------------------------------------------------------------------- /Demo/src/main/java/com/myscript/iink/demo/ui/ThicknessesAdapter.kt: -------------------------------------------------------------------------------- 1 | // Copyright @ MyScript. All rights reserved. 2 | 3 | package com.myscript.iink.demo.ui 4 | 5 | import android.view.LayoutInflater 6 | import android.view.ViewGroup 7 | import android.widget.ImageView 8 | import androidx.recyclerview.widget.DiffUtil 9 | import androidx.recyclerview.widget.ListAdapter 10 | import androidx.recyclerview.widget.RecyclerView 11 | import com.myscript.iink.demo.R 12 | 13 | private fun ThicknessState.toScale(): Float = when (this.thickness) { 14 | Thickness.THIN -> .25f 15 | Thickness.MEDIUM -> .5f 16 | Thickness.LARGE -> 1f 17 | } 18 | 19 | class ThicknessViewHolder(parent: ViewGroup) : RecyclerView.ViewHolder( 20 | LayoutInflater.from(parent.context).inflate(R.layout.toolbar_thickness_cell, parent, false) 21 | ) { 22 | private val thicknessView: ImageView = itemView.findViewById(R.id.toolbar_thickness_icon) 23 | 24 | fun bind(state: ThicknessState, onThicknessSelected: (ThicknessState) -> Unit) { 25 | val scale = state.toScale() 26 | thicknessView.scaleX = scale 27 | thicknessView.scaleY = scale 28 | 29 | itemView.isSelected = state.isSelected 30 | itemView.setOnClickListener { onThicknessSelected(state) } 31 | } 32 | 33 | fun unbind() { 34 | itemView.setOnClickListener(null) 35 | } 36 | } 37 | 38 | class ThicknessesAdapter(private val onThicknessSelected: (ThicknessState) -> Unit) 39 | : ListAdapter(THICKNESS_DIFF_UTIL_CALLBACK) { 40 | 41 | override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): ThicknessViewHolder { 42 | return ThicknessViewHolder(parent) 43 | } 44 | 45 | override fun onBindViewHolder(holder: ThicknessViewHolder, position: Int) { 46 | holder.bind(getItem(position), onThicknessSelected) 47 | } 48 | 49 | override fun onViewRecycled(holder: ThicknessViewHolder) { 50 | holder.unbind() 51 | } 52 | 53 | override fun submitList(list: List?) { 54 | if (list == null) { 55 | super.submitList(null) 56 | } else { 57 | super.submitList(list.toList()) // force DiffUtil by creating a new list 58 | } 59 | } 60 | 61 | companion object { 62 | private val THICKNESS_DIFF_UTIL_CALLBACK = object : DiffUtil.ItemCallback() { 63 | override fun areItemsTheSame(oldItem: ThicknessState, newItem: ThicknessState): Boolean { 64 | return oldItem.thickness == newItem.thickness 65 | } 66 | 67 | override fun areContentsTheSame(oldItem: ThicknessState, newItem: ThicknessState): Boolean { 68 | return oldItem == newItem 69 | } 70 | } 71 | } 72 | } -------------------------------------------------------------------------------- /Demo/src/main/java/com/myscript/iink/demo/ui/ToolbarTransformer.kt: -------------------------------------------------------------------------------- 1 | // Copyright @ MyScript. All rights reserved. 2 | 3 | package com.myscript.iink.demo.ui 4 | 5 | import android.graphics.Color 6 | import androidx.annotation.ColorInt 7 | import androidx.core.graphics.ColorUtils 8 | import com.myscript.iink.demo.domain.ToolType 9 | import com.myscript.iink.graphics.Color as IInkColor 10 | 11 | @get:ColorInt 12 | val IInkColor.androidColor: Int 13 | get() = Color.argb(a(), r(), g(), b()) 14 | 15 | val Int.iinkColor: IInkColor 16 | get() { 17 | val r = this shr 16 and 0xff 18 | val g = this shr 8 and 0xff 19 | val b = this and 0xff 20 | val a = this shr 24 and 0xff 21 | return IInkColor(r, g, b, a) 22 | } 23 | 24 | @get:ColorInt 25 | val Int.opaque: Int 26 | get() = ColorUtils.setAlphaComponent(this, 0xFF) 27 | 28 | fun ToolType.toToolState(isSelected: Boolean, isEnable: Boolean) = ToolState(this, isSelected, isEnable) 29 | 30 | fun Thickness.toFloat(toolType: ToolType) = when (toolType) { 31 | ToolType.PEN -> when (this) { 32 | Thickness.THIN -> .25f 33 | Thickness.MEDIUM -> .65f 34 | Thickness.LARGE -> 1.65f 35 | } 36 | ToolType.HIGHLIGHTER -> when (this) { 37 | Thickness.THIN -> 1.67f 38 | Thickness.MEDIUM -> 5f 39 | Thickness.LARGE -> 15f 40 | } 41 | else -> 0f 42 | } 43 | 44 | fun Float.toThickness(toolType: ToolType?) = when (toolType) { 45 | ToolType.PEN -> when { 46 | this <= .25f -> Thickness.THIN 47 | this == .65f -> Thickness.MEDIUM 48 | this >= 1.65f -> Thickness.LARGE 49 | else -> Thickness.THIN 50 | } 51 | ToolType.HIGHLIGHTER -> when { 52 | this <= 1.67f -> Thickness.THIN 53 | this == 5f -> Thickness.MEDIUM 54 | this >= 15f -> Thickness.LARGE 55 | else -> null 56 | } 57 | else -> Thickness.MEDIUM 58 | } 59 | -------------------------------------------------------------------------------- /Demo/src/main/java/com/myscript/iink/demo/ui/ToolsAdapter.kt: -------------------------------------------------------------------------------- 1 | // Copyright @ MyScript. All rights reserved. 2 | 3 | package com.myscript.iink.demo.ui 4 | 5 | import android.view.LayoutInflater 6 | import android.view.ViewGroup 7 | import android.widget.ImageView 8 | import androidx.annotation.DrawableRes 9 | import androidx.annotation.StringRes 10 | import androidx.recyclerview.widget.DiffUtil 11 | import androidx.recyclerview.widget.ListAdapter 12 | import androidx.recyclerview.widget.RecyclerView 13 | import com.myscript.iink.demo.R 14 | import com.myscript.iink.demo.domain.ToolType 15 | import com.myscript.iink.demo.util.setContentDescription 16 | import com.myscript.iink.demo.util.setTooltipText 17 | 18 | @get:DrawableRes 19 | private val ToolType.asDrawable: Int 20 | get() = when (this) { 21 | ToolType.HAND -> R.drawable.ic_hand_outlined 22 | ToolType.PEN -> R.drawable.ic_pen_outlined 23 | ToolType.ERASER -> R.drawable.ic_eraser 24 | ToolType.HIGHLIGHTER -> R.drawable.ic_brush_outlined 25 | ToolType.LASSO -> R.drawable.ic_lasso 26 | } 27 | 28 | @get:StringRes 29 | private val ToolType.label: Int 30 | get() = when (this) { 31 | ToolType.HAND -> R.string.tool_hand 32 | ToolType.PEN -> R.string.tool_pen 33 | ToolType.ERASER -> R.string.tool_eraser 34 | ToolType.HIGHLIGHTER -> R.string.tool_highlighter 35 | ToolType.LASSO -> R.string.tool_lasso 36 | } 37 | 38 | class ToolStateViewHolder(parent: ViewGroup) : RecyclerView.ViewHolder( 39 | LayoutInflater.from(parent.context).inflate(R.layout.toolbar_tool_cell, parent, false) 40 | ) { 41 | private val toolView: ImageView = itemView.findViewById(R.id.toolbar_tool_icon) 42 | 43 | fun bind(state: ToolState, onToolSelected: (ToolState) -> Unit) { 44 | toolView.setImageResource(state.type.asDrawable) 45 | toolView.setContentDescription(state.type.label) 46 | 47 | itemView.setTooltipText(state.type.label) 48 | itemView.isEnabled = state.isEnabled 49 | itemView.isSelected = state.isSelected 50 | itemView.setOnClickListener { onToolSelected(state) } 51 | } 52 | 53 | fun unbind() { 54 | itemView.setOnClickListener(null) 55 | } 56 | } 57 | 58 | class ToolsAdapter(private val onToolSelected: (ToolState) -> Unit) 59 | : ListAdapter(TOOL_DIFF_UTIL_CALLBACK) { 60 | 61 | override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): ToolStateViewHolder { 62 | return ToolStateViewHolder(parent) 63 | } 64 | 65 | override fun onBindViewHolder(holder: ToolStateViewHolder, position: Int) { 66 | holder.bind(getItem(position), onToolSelected) 67 | } 68 | 69 | override fun onViewRecycled(holder: ToolStateViewHolder) { 70 | holder.unbind() 71 | } 72 | 73 | override fun submitList(list: List?) { 74 | if (list == null) { 75 | super.submitList(null) 76 | } else { 77 | super.submitList(list.toList()) // force DiffUtil by creating a new list 78 | } 79 | } 80 | 81 | companion object { 82 | private val TOOL_DIFF_UTIL_CALLBACK = object : DiffUtil.ItemCallback() { 83 | override fun areItemsTheSame(oldItem: ToolState, newItem: ToolState): Boolean { 84 | return oldItem.type == newItem.type 85 | } 86 | 87 | override fun areContentsTheSame(oldItem: ToolState, newItem: ToolState): Boolean { 88 | return oldItem == newItem 89 | } 90 | } 91 | } 92 | } -------------------------------------------------------------------------------- /Demo/src/main/java/com/myscript/iink/demo/util/Dialogs.kt: -------------------------------------------------------------------------------- 1 | // Copyright @ MyScript. All rights reserved. 2 | 3 | package com.myscript.iink.demo.util 4 | 5 | import android.app.AlertDialog 6 | import android.content.Context 7 | import android.view.LayoutInflater 8 | import android.widget.EditText 9 | import android.widget.SeekBar 10 | import android.widget.SeekBar.OnSeekBarChangeListener 11 | import android.widget.TextView 12 | import androidx.annotation.StringRes 13 | import androidx.appcompat.widget.SwitchCompat 14 | import com.myscript.iink.demo.R 15 | 16 | fun Context.launchSingleChoiceDialog( 17 | @StringRes titleRes: Int, 18 | items: List, 19 | selectedIndex: Int = -1, 20 | onItemSelected: (selected: Int) -> Unit 21 | ) { 22 | var newIndex = selectedIndex 23 | AlertDialog.Builder(this) 24 | .setTitle(titleRes) 25 | .setSingleChoiceItems( 26 | items.toTypedArray(), 27 | selectedIndex 28 | ) { _, which -> 29 | newIndex = which 30 | } 31 | .setPositiveButton(R.string.dialog_ok) { _, _ -> 32 | if (newIndex in items.indices) { 33 | onItemSelected(newIndex) 34 | } 35 | } 36 | .setNegativeButton(R.string.dialog_cancel, null) 37 | .show() 38 | } 39 | 40 | fun Context.launchActionChoiceDialog( 41 | items: List, 42 | onItemSelected: (selected: Int) -> Unit 43 | ) { 44 | AlertDialog.Builder(this) 45 | .setItems(items.toTypedArray()) { _, which -> 46 | if (which in items.indices) { 47 | onItemSelected(which) 48 | } 49 | } 50 | .show() 51 | } 52 | 53 | fun Context.launchTextBlockInputDialog(onInputDone: (text: String) -> Unit) { 54 | val editTextLayout = LayoutInflater.from(this).inflate(R.layout.editor_text_input_layout, null) 55 | val editText = editTextLayout.findViewById(R.id.editor_text_input) 56 | val builder = AlertDialog.Builder(this) 57 | .setView(editTextLayout) 58 | .setTitle(R.string.editor_dialog_insert_text_title) 59 | .setPositiveButton(R.string.editor_dialog_insert_text_action) { _, _ -> 60 | val text = editText.text.toString() 61 | if (text.isNotBlank()) { 62 | onInputDone(text) 63 | } 64 | } 65 | .setNegativeButton(R.string.dialog_cancel, null) 66 | .create() 67 | editText.requestFocus() 68 | builder.show() 69 | } 70 | 71 | fun Context.launchPredictionDialog( 72 | enabled: Boolean, 73 | durationMs: Int, 74 | onInputDone: (Boolean, Int) -> Unit 75 | ) { 76 | val editPredictionLayout = LayoutInflater.from(this).inflate(R.layout.editor_prediction_layout, null) 77 | val enablePredictionSwitch = editPredictionLayout.findViewById(R.id.editor_dialog_prediction_toggle) 78 | val durationLabel = editPredictionLayout.findViewById(R.id.editor_dialog_prediction_duration_label) 79 | durationLabel.text = getString(R.string.editor_dialog_prediction_duration, durationMs) 80 | val durationSeekBar = editPredictionLayout.findViewById(R.id.editor_dialog_prediction_duration).apply { 81 | setOnSeekBarChangeListener(object : OnSeekBarChangeListener { 82 | override fun onProgressChanged(seekBar: SeekBar?, progress: Int, fromUser: Boolean) { 83 | if (fromUser) { 84 | durationLabel.text = getString(R.string.editor_dialog_prediction_duration, seekBar?.progress ?: 0) 85 | } 86 | } 87 | 88 | override fun onStartTrackingTouch(seekBar: SeekBar?) = Unit 89 | override fun onStopTrackingTouch(seekBar: SeekBar?) = Unit 90 | }) 91 | } 92 | 93 | enablePredictionSwitch.isChecked = enabled 94 | durationSeekBar.progress = durationMs 95 | 96 | AlertDialog.Builder(this) 97 | .setView(editPredictionLayout) 98 | .setTitle(R.string.editor_dialog_prediction_title) 99 | .setPositiveButton(R.string.dialog_ok) { _, _ -> 100 | durationSeekBar.setOnSeekBarChangeListener(null) 101 | onInputDone(enablePredictionSwitch.isChecked, durationSeekBar.progress) 102 | } 103 | .setNegativeButton(R.string.dialog_cancel, null) 104 | .show() 105 | } -------------------------------------------------------------------------------- /Demo/src/main/java/com/myscript/iink/demo/util/autoCloseable.kt: -------------------------------------------------------------------------------- 1 | // Copyright @ MyScript. All rights reserved. 2 | 3 | package com.myscript.iink.demo.util 4 | 5 | import kotlin.properties.ReadWriteProperty 6 | import kotlin.reflect.KProperty 7 | 8 | 9 | /** 10 | * Property delegate to enforce [AutoCloseable.close] being called when overwriting a previous value. 11 | * This ensures native resources cleanup in any circumstances when storing [AutoCloseable] objects 12 | * as member variables. 13 | * 14 | * ```kotlin 15 | * private var myAutoCloseable by autoCloseable(null) { oldValue -> 16 | * // oldValue (if not null) can be used if needed 17 | * } 18 | * 19 | * private var myOtherAutoCloseable by autoCloseable(MyAutoCloseable()) 20 | * 21 | * fun doSomething() { 22 | * myAutoCloseable = MyAutoCloseable() 23 | * myOtherAutoCloseable = null // ensures previous value is closed 24 | * } 25 | * ``` 26 | * 27 | * It is similar to [use] (or Java try-with-resources) when handling [AutoCloseable]. 28 | * 29 | * ```kotlin 30 | * object.getAutoCloseableObject().use { obj -> 31 | * // obj.close() automatically closed at the end of `use` block scope. 32 | * } 33 | * ``` 34 | * 35 | * @param initialValue initial value to set to underlying backing field 36 | * @param onUpdate callback called when the value is updated. The `oldValue` will be closed *after* 37 | * this callback is called and *after* the new value is set to underlying backing field. 38 | * This allows working safely with `oldValue` before it's closed. 39 | * 40 | * @see https://docs.oracle.com/javase/tutorial/essential/exceptions/tryResourceClose.html 41 | * @see https://kotlinlang.org/api/latest/jvm/stdlib/kotlin/use.html 42 | * @see https://kotlinlang.org/docs/delegated-properties.html 43 | */ 44 | class AutoCloseableDelegate(initialValue: T?, private val onUpdate: ((oldValue: T?) -> Unit)? = null) 45 | : ReadWriteProperty 46 | where T : AutoCloseable? { 47 | private var value: T? = initialValue 48 | 49 | override fun getValue(thisRef: Any, property: KProperty<*>): T? { 50 | return value 51 | } 52 | 53 | @Suppress("ConvertTryFinallyToUseCall") 54 | override fun setValue(thisRef: Any, property: KProperty<*>, value: T?) { 55 | val oldValue = this.value 56 | // overwriting with same value is no-op 57 | if (oldValue === value) 58 | return 59 | try { 60 | this.value = value 61 | onUpdate?.invoke(oldValue) 62 | } finally { 63 | oldValue?.close() 64 | } 65 | } 66 | } 67 | 68 | /** 69 | * Convenience helper for [AutoCloseableDelegate]. 70 | * 71 | * @see [AutoCloseableDelegate] 72 | */ 73 | fun autoCloseable(initialValue: T? = null, onUpdate: ((oldValue: T?) -> Unit)? = null): AutoCloseableDelegate 74 | where T : AutoCloseable? { 75 | return AutoCloseableDelegate(initialValue, onUpdate) 76 | } 77 | -------------------------------------------------------------------------------- /Demo/src/main/java/com/myscript/iink/demo/util/viewExt.kt: -------------------------------------------------------------------------------- 1 | // Copyright @ MyScript. All rights reserved. 2 | 3 | package com.myscript.iink.demo.util 4 | 5 | import android.view.View 6 | import android.widget.ImageView 7 | import androidx.annotation.StringRes 8 | import androidx.appcompat.widget.TooltipCompat 9 | 10 | fun View.setTooltipText(@StringRes textRes: Int) { 11 | TooltipCompat.setTooltipText(this, context.getString(textRes)) 12 | } 13 | 14 | fun ImageView.setContentDescription(@StringRes textRes: Int) { 15 | contentDescription = context.getString(textRes) 16 | } -------------------------------------------------------------------------------- /Demo/src/main/res/drawable/bottom_sheet_handle.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | -------------------------------------------------------------------------------- /Demo/src/main/res/drawable/disc.xml: -------------------------------------------------------------------------------- 1 | 7 | 10 | 11 | -------------------------------------------------------------------------------- /Demo/src/main/res/drawable/ic_add.xml: -------------------------------------------------------------------------------- 1 | 7 | 10 | 11 | -------------------------------------------------------------------------------- /Demo/src/main/res/drawable/ic_brush_outlined.xml: -------------------------------------------------------------------------------- 1 | 7 | 12 | 13 | -------------------------------------------------------------------------------- /Demo/src/main/res/drawable/ic_chevron_left.xml: -------------------------------------------------------------------------------- 1 | 7 | 10 | 11 | -------------------------------------------------------------------------------- /Demo/src/main/res/drawable/ic_chevron_right.xml: -------------------------------------------------------------------------------- 1 | 7 | 10 | 11 | -------------------------------------------------------------------------------- /Demo/src/main/res/drawable/ic_colorize_outlined.xml: -------------------------------------------------------------------------------- 1 | 7 | 10 | 11 | -------------------------------------------------------------------------------- /Demo/src/main/res/drawable/ic_delete.xml: -------------------------------------------------------------------------------- 1 | 7 | 10 | 11 | -------------------------------------------------------------------------------- /Demo/src/main/res/drawable/ic_eraser.xml: -------------------------------------------------------------------------------- 1 | 7 | 10 | 11 | -------------------------------------------------------------------------------- /Demo/src/main/res/drawable/ic_hand_outlined.xml: -------------------------------------------------------------------------------- 1 | 7 | 10 | 11 | -------------------------------------------------------------------------------- /Demo/src/main/res/drawable/ic_lasso.xml: -------------------------------------------------------------------------------- 1 | 7 | 25 | 26 | -------------------------------------------------------------------------------- /Demo/src/main/res/drawable/ic_launcher_foreground.xml: -------------------------------------------------------------------------------- 1 | 7 | 8 | 12 | 13 | -------------------------------------------------------------------------------- /Demo/src/main/res/drawable/ic_pen_outlined.xml: -------------------------------------------------------------------------------- 1 | 7 | 10 | 11 | -------------------------------------------------------------------------------- /Demo/src/main/res/drawable/ic_redo.xml: -------------------------------------------------------------------------------- 1 | 7 | 10 | 11 | -------------------------------------------------------------------------------- /Demo/src/main/res/drawable/ic_reset_view.xml: -------------------------------------------------------------------------------- 1 | 7 | 10 | 11 | -------------------------------------------------------------------------------- /Demo/src/main/res/drawable/ic_undo.xml: -------------------------------------------------------------------------------- 1 | 7 | 10 | 11 | -------------------------------------------------------------------------------- /Demo/src/main/res/drawable/ic_zoom_in.xml: -------------------------------------------------------------------------------- 1 | 7 | 10 | -------------------------------------------------------------------------------- /Demo/src/main/res/drawable/ic_zoom_out.xml: -------------------------------------------------------------------------------- 1 | 7 | 10 | -------------------------------------------------------------------------------- /Demo/src/main/res/drawable/texture_background.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/MyScript/interactive-ink-examples-android/c9f3eac9d7dabba9449d1225ebdc219672baf651/Demo/src/main/res/drawable/texture_background.png -------------------------------------------------------------------------------- /Demo/src/main/res/drawable/texture_stamp.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/MyScript/interactive-ink-examples-android/c9f3eac9d7dabba9449d1225ebdc219672baf651/Demo/src/main/res/drawable/texture_stamp.png -------------------------------------------------------------------------------- /Demo/src/main/res/drawable/tool_background.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 6 | 7 | 8 | 9 | 10 | -------------------------------------------------------------------------------- /Demo/src/main/res/drawable/tool_background_selected.xml: -------------------------------------------------------------------------------- 1 | 7 | 10 | 11 | -------------------------------------------------------------------------------- /Demo/src/main/res/font/myscriptinter.xml: -------------------------------------------------------------------------------- 1 | 2 | 4 | 8 | 12 | -------------------------------------------------------------------------------- /Demo/src/main/res/font/myscriptinter_bold.otf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/MyScript/interactive-ink-examples-android/c9f3eac9d7dabba9449d1225ebdc219672baf651/Demo/src/main/res/font/myscriptinter_bold.otf -------------------------------------------------------------------------------- /Demo/src/main/res/font/myscriptinter_regular.otf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/MyScript/interactive-ink-examples-android/c9f3eac9d7dabba9449d1225ebdc219672baf651/Demo/src/main/res/font/myscriptinter_regular.otf -------------------------------------------------------------------------------- /Demo/src/main/res/font/stix.xml: -------------------------------------------------------------------------------- 1 | 2 | 4 | 8 | 12 | -------------------------------------------------------------------------------- /Demo/src/main/res/font/stix_italic.otf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/MyScript/interactive-ink-examples-android/c9f3eac9d7dabba9449d1225ebdc219672baf651/Demo/src/main/res/font/stix_italic.otf -------------------------------------------------------------------------------- /Demo/src/main/res/font/stix_regular.otf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/MyScript/interactive-ink-examples-android/c9f3eac9d7dabba9449d1225ebdc219672baf651/Demo/src/main/res/font/stix_regular.otf -------------------------------------------------------------------------------- /Demo/src/main/res/layout/editor_prediction_layout.xml: -------------------------------------------------------------------------------- 1 | 2 | 8 | 9 | 14 | 15 | 23 | 24 | 30 | 31 | -------------------------------------------------------------------------------- /Demo/src/main/res/layout/editor_text_input_layout.xml: -------------------------------------------------------------------------------- 1 | 2 | 6 | 10 | 11 | -------------------------------------------------------------------------------- /Demo/src/main/res/layout/editor_toolbar.xml: -------------------------------------------------------------------------------- 1 | 2 | 9 | 10 | 16 | 17 | 21 | 22 | 31 | 32 | 41 | 42 | 51 | 52 | 61 | 62 | 71 | 72 | 81 | 82 | 83 | -------------------------------------------------------------------------------- /Demo/src/main/res/layout/main_activity.xml: -------------------------------------------------------------------------------- 1 | 2 | 7 | 8 | 12 | 13 | 19 | 20 | 24 | 25 | 28 | 29 | 30 | 31 | 34 | 35 | 46 | 47 | 48 | 49 | 50 | 51 | 52 | 53 | 54 | 57 | 58 | 59 | -------------------------------------------------------------------------------- /Demo/src/main/res/layout/toolbar_color_cell.xml: -------------------------------------------------------------------------------- 1 | 2 | -------------------------------------------------------------------------------- /Demo/src/main/res/layout/toolbar_pen_brush_row.xml: -------------------------------------------------------------------------------- 1 | 2 | -------------------------------------------------------------------------------- /Demo/src/main/res/layout/toolbar_settings_sheet_layout.xml: -------------------------------------------------------------------------------- 1 | 2 | 19 | 20 | 28 | 29 | 38 | 39 | 50 | 51 | 62 | 63 | 70 | 71 | 76 | 77 | 86 | 87 | 88 | -------------------------------------------------------------------------------- /Demo/src/main/res/layout/toolbar_thickness_cell.xml: -------------------------------------------------------------------------------- 1 | 2 | 11 | 18 | 19 | -------------------------------------------------------------------------------- /Demo/src/main/res/layout/toolbar_tool_cell.xml: -------------------------------------------------------------------------------- 1 | 2 | -------------------------------------------------------------------------------- /Demo/src/main/res/menu/main_menu.xml: -------------------------------------------------------------------------------- 1 | 2 | 5 | 6 | 12 | 18 | 24 | 28 | 32 | 36 | 40 | 44 | 48 | 49 | 50 | -------------------------------------------------------------------------------- /Demo/src/main/res/mipmap-anydpi-v26/ic_launcher.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | -------------------------------------------------------------------------------- /Demo/src/main/res/mipmap-anydpi-v26/ic_launcher_round.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | -------------------------------------------------------------------------------- /Demo/src/main/res/mipmap-hdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/MyScript/interactive-ink-examples-android/c9f3eac9d7dabba9449d1225ebdc219672baf651/Demo/src/main/res/mipmap-hdpi/ic_launcher.png -------------------------------------------------------------------------------- /Demo/src/main/res/mipmap-hdpi/ic_launcher_round.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/MyScript/interactive-ink-examples-android/c9f3eac9d7dabba9449d1225ebdc219672baf651/Demo/src/main/res/mipmap-hdpi/ic_launcher_round.png -------------------------------------------------------------------------------- /Demo/src/main/res/mipmap-mdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/MyScript/interactive-ink-examples-android/c9f3eac9d7dabba9449d1225ebdc219672baf651/Demo/src/main/res/mipmap-mdpi/ic_launcher.png -------------------------------------------------------------------------------- /Demo/src/main/res/mipmap-mdpi/ic_launcher_round.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/MyScript/interactive-ink-examples-android/c9f3eac9d7dabba9449d1225ebdc219672baf651/Demo/src/main/res/mipmap-mdpi/ic_launcher_round.png -------------------------------------------------------------------------------- /Demo/src/main/res/mipmap-xhdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/MyScript/interactive-ink-examples-android/c9f3eac9d7dabba9449d1225ebdc219672baf651/Demo/src/main/res/mipmap-xhdpi/ic_launcher.png -------------------------------------------------------------------------------- /Demo/src/main/res/mipmap-xhdpi/ic_launcher_round.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/MyScript/interactive-ink-examples-android/c9f3eac9d7dabba9449d1225ebdc219672baf651/Demo/src/main/res/mipmap-xhdpi/ic_launcher_round.png -------------------------------------------------------------------------------- /Demo/src/main/res/mipmap-xxhdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/MyScript/interactive-ink-examples-android/c9f3eac9d7dabba9449d1225ebdc219672baf651/Demo/src/main/res/mipmap-xxhdpi/ic_launcher.png -------------------------------------------------------------------------------- /Demo/src/main/res/mipmap-xxhdpi/ic_launcher_round.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/MyScript/interactive-ink-examples-android/c9f3eac9d7dabba9449d1225ebdc219672baf651/Demo/src/main/res/mipmap-xxhdpi/ic_launcher_round.png -------------------------------------------------------------------------------- /Demo/src/main/res/mipmap-xxxhdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/MyScript/interactive-ink-examples-android/c9f3eac9d7dabba9449d1225ebdc219672baf651/Demo/src/main/res/mipmap-xxxhdpi/ic_launcher.png -------------------------------------------------------------------------------- /Demo/src/main/res/mipmap-xxxhdpi/ic_launcher_round.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/MyScript/interactive-ink-examples-android/c9f3eac9d7dabba9449d1225ebdc219672baf651/Demo/src/main/res/mipmap-xxxhdpi/ic_launcher_round.png -------------------------------------------------------------------------------- /Demo/src/main/res/raw/theme.css: -------------------------------------------------------------------------------- 1 | /* 2 | * see https://developer.myscript.com/docs/interactive-ink/latest/reference/styling for styling reference 3 | */ 4 | 5 | glyph { 6 | /* Font family must be aligned with the key used to register the corresponding Typeface in typeface map (see DemoModule) */ 7 | font-family: MyScriptInter; 8 | } 9 | 10 | .math { 11 | /* Font family must be aligned with the key used to register the corresponding Typeface in typeface map (see DemoModule) */ 12 | font-family: STIX; 13 | } 14 | 15 | .math-variable { 16 | font-style: italic; 17 | } 18 | -------------------------------------------------------------------------------- /Demo/src/main/res/values-large/dimens.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 72dp 5 | 56dp 6 | 7 | -------------------------------------------------------------------------------- /Demo/src/main/res/values-night/colors.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | #1C1C1C 5 | 6 | 7 | -------------------------------------------------------------------------------- /Demo/src/main/res/values-night/themes.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 13 | 14 | 15 | -------------------------------------------------------------------------------- /Demo/src/main/res/values/colors.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | #009fe3 5 | #F5F6F7 6 | 7 | 8 | -------------------------------------------------------------------------------- /Demo/src/main/res/values/dimens.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 64dp 5 | 24dp 6 | 7 | -------------------------------------------------------------------------------- /Demo/src/main/res/values/ic_launcher_background.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | #FFFFFF 4 | -------------------------------------------------------------------------------- /Demo/src/main/res/values/strings.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | iink Demo 5 | 6 | OK 7 | Cancel 8 | 9 | Invalid certificate 10 | Please check certificate data provided at Engine creation. 11 | Error while inserting image 12 | Unsupported file type 13 | %s not supported (only image content). 14 | %s not supported (only iink content). 15 | [%1$s] %2$s 16 | 17 | New part… 18 | New part type 19 | Previous part 20 | Next part 21 | 22 | Convert 23 | Prediction… 24 | Export… 25 | %1$s (%2$s) 26 | Export error 27 | Save 28 | Import file… 29 | Share file… 30 | 31 | Active pen 32 | Undo 33 | Redo 34 | Clear content 35 | Zoom in 36 | Zoom out 37 | Reset view 38 | 39 | Copy 40 | Paste 41 | Delete 42 | Convert 43 | Export… 44 | Add… 45 | Format text… 46 | As heading level 1 47 | As heading level 2 48 | As paragraph 49 | As bullet list 50 | As check list 51 | As numbered list 52 | Selection mode… 53 | None 54 | Lasso 55 | Item 56 | Resize 57 | Reflow 58 | Selection type… 59 | Text 60 | Text - Single block 61 | Math 62 | Math - Single block 63 | 64 | Tool thickness 65 | Choose a color 66 | Add text block 67 | Insert 68 | Prediction settings 69 | Enable 70 | Duration (%d ms) 71 | 72 | Hand 73 | Pen 74 | Highlighter 75 | Lasso 76 | Eraser 77 | 78 | Brush 79 | Felt Pen 80 | Fountain Pen 81 | Calligraphic Brush 82 | Pencil Brush 83 | 84 | 85 | -------------------------------------------------------------------------------- /Demo/src/main/res/values/themes.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 13 | 14 | 15 | -------------------------------------------------------------------------------- /Demo/src/main/res/xml/file_paths.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | -------------------------------------------------------------------------------- /GetStarted/build.gradle: -------------------------------------------------------------------------------- 1 | import org.apache.commons.io.FileUtils 2 | import org.apache.commons.io.filefilter.FileFilterUtils 3 | 4 | plugins { 5 | id 'com.android.application' 6 | id 'kotlin-android' 7 | } 8 | 9 | android { 10 | namespace 'com.myscript.iink.getstarted' 11 | 12 | compileSdk project.ext.compileSdk 13 | 14 | buildFeatures { 15 | viewBinding true 16 | buildConfig true 17 | } 18 | 19 | defaultConfig { 20 | minSdk project.ext.minSdk 21 | targetSdk project.ext.targetSdk 22 | 23 | applicationId 'com.myscript.iink.getstarted' 24 | versionCode 4100 25 | versionName '4.1.0' 26 | 27 | vectorDrawables.useSupportLibrary true 28 | } 29 | } 30 | 31 | dependencies { 32 | implementation "androidx.appcompat:appcompat:${project.ext.appcompatVersion}" 33 | implementation project(':UIReferenceImplementation') 34 | } 35 | 36 | task DownloadAndExtractAssets(type: Copy) { 37 | def sourceUrls = ['https://download.myscript.com/iink/recognitionAssets_iink_4.1/myscript-iink-recognition-diagram.zip', 38 | 'https://download.myscript.com/iink/recognitionAssets_iink_4.1/myscript-iink-recognition-raw-content.zip', 39 | 'https://download.myscript.com/iink/recognitionAssets_iink_4.1/myscript-iink-recognition-raw-content2.zip', 40 | 'https://download.myscript.com/iink/recognitionAssets_iink_4.1/myscript-iink-recognition-math.zip', 41 | 'https://download.myscript.com/iink/recognitionAssets_iink_4.1/myscript-iink-recognition-math2.zip', 42 | 'https://download.myscript.com/iink/recognitionAssets_iink_4.1/myscript-iink-recognition-text-en_US.zip'] 43 | def targetDir = new File(projectDir, "src/main/assets/") 44 | def diagramConf = new File(targetDir, "conf/diagram.conf") 45 | def rawContentConf = new File(targetDir, "conf/raw-content.conf") 46 | def mathConf = new File(targetDir, "conf/math.conf") 47 | def textConf = new File(targetDir, "conf/en_US.conf") 48 | def rawContent2Conf = new File(targetDir, "conf/raw-content2.conf") 49 | def math2Conf = new File(targetDir, "conf/math2.conf") 50 | 51 | if (!diagramConf.exists() || !rawContentConf.exists() || !mathConf.exists() || !textConf.exists() || !rawContent2Conf.exists() || !math2Conf.exists()) { 52 | def tmpAssetsDir = new File(projectDir, "tmp-assets/") 53 | def zipDir = new File(tmpAssetsDir, "zips") 54 | 55 | if (!tmpAssetsDir.isDirectory()) 56 | tmpAssetsDir.mkdirs() 57 | 58 | if (!zipDir.isDirectory()) 59 | zipDir.mkdirs() 60 | 61 | sourceUrls.each { sourceUrl -> 62 | ant.get(src: sourceUrl, dest: zipDir.getPath()) 63 | } 64 | 65 | File[] zipFiles = FileUtils.listFiles(zipDir, FileFilterUtils.suffixFileFilter("zip"), FileFilterUtils.trueFileFilter()) 66 | zipFiles.each { File zipFile -> 67 | from zipTree(zipFile) 68 | into tmpAssetsDir 69 | } 70 | } 71 | } 72 | 73 | task CopyAssets(type: Copy, dependsOn: DownloadAndExtractAssets) { 74 | def targetDir = new File(projectDir, "src/main/assets/") 75 | def diagramConf = new File(targetDir, "conf/diagram.conf") 76 | def rawContentConf = new File(targetDir, "conf/raw-content.conf") 77 | def mathConf = new File(targetDir, "conf/math.conf") 78 | def textConf = new File(targetDir, "conf/en_US.conf") 79 | def rawContent2Conf = new File(targetDir, "conf/raw-content2.conf") 80 | def math2Conf = new File(targetDir, "conf/math2.conf") 81 | 82 | if (!diagramConf.exists() || !rawContentConf.exists() || !mathConf.exists() || !textConf.exists() || !rawContent2Conf.exists() || !math2Conf.exists()) { 83 | def tmpAssetsDir = new File(projectDir, "tmp-assets/") 84 | 85 | if (!tmpAssetsDir.isDirectory()) 86 | tmpAssetsDir.mkdirs() 87 | 88 | def recognitionAssetDir = new File(tmpAssetsDir, "recognition-assets/") 89 | 90 | println "Copying downloaded assets from $recognitionAssetDir to $targetDir" 91 | from recognitionAssetDir 92 | into targetDir 93 | 94 | doLast { 95 | tmpAssetsDir.deleteDir() 96 | } 97 | } 98 | } 99 | 100 | preBuild.dependsOn(CopyAssets) 101 | -------------------------------------------------------------------------------- /GetStarted/src/main/AndroidManifest.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 11 | 12 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 28 | 29 | 30 | 31 | 32 | 33 | -------------------------------------------------------------------------------- /GetStarted/src/main/java/com/myscript/certificate/MyCertificate.java: -------------------------------------------------------------------------------- 1 | // Copyright MyScript. All rights reserved. 2 | 3 | package com.myscript.certificate; 4 | 5 | public final class MyCertificate 6 | { 7 | public static final byte[] getBytes() 8 | { 9 | throw new RuntimeException("Please replace the content of MyCertificate.java with the certificate you received from the developer portal"); 10 | } 11 | } 12 | -------------------------------------------------------------------------------- /GetStarted/src/main/java/com/myscript/iink/getstarted/ErrorActivity.java: -------------------------------------------------------------------------------- 1 | // Copyright @ MyScript. All rights reserved. 2 | 3 | package com.myscript.iink.getstarted; 4 | 5 | import android.app.Activity; 6 | import android.content.Intent; 7 | import android.os.Bundle; 8 | import androidx.annotation.Nullable; 9 | import android.text.method.ScrollingMovementMethod; 10 | import android.widget.TextView; 11 | 12 | import java.io.PrintWriter; 13 | import java.io.StringWriter; 14 | 15 | 16 | /** 17 | * This activity displays an error message when an uncaught exception is thrown within an activity 18 | * that installed the associated exception handler. Since this application targets developers it's 19 | * better to clearly explain what happened. 20 | * The code is inspired by: 21 | * https://trivedihardik.wordpress.com/2011/08/20/how-to-avoid-force-close-error-in-android/ 22 | */ 23 | public class ErrorActivity extends Activity 24 | { 25 | public static final String INTENT_EXTRA_MESSAGE = "message"; 26 | public static final String INTENT_EXTRA_DETAILS = "details"; 27 | 28 | @Override 29 | protected void onCreate(@Nullable Bundle savedInstanceState) 30 | { 31 | super.onCreate(savedInstanceState); 32 | 33 | setContentView(R.layout.error_activity); 34 | 35 | TextView messageView = findViewById(R.id.error_message); 36 | messageView.setText(getIntent().getStringExtra(INTENT_EXTRA_MESSAGE)); 37 | 38 | TextView detailsView = findViewById(R.id.error_details); 39 | detailsView.setText(getIntent().getStringExtra(INTENT_EXTRA_DETAILS)); 40 | detailsView.setMovementMethod(new ScrollingMovementMethod()); 41 | } 42 | 43 | public static void start(Activity context, String message, String details) 44 | { 45 | Intent intent = new Intent(context, ErrorActivity.class); 46 | intent.putExtra(INTENT_EXTRA_MESSAGE, message); 47 | intent.putExtra(INTENT_EXTRA_DETAILS, details); 48 | context.startActivity(intent); 49 | } 50 | 51 | public static void installHandler(Activity context) 52 | { 53 | Thread.setDefaultUncaughtExceptionHandler(new ExceptionHandler(context)); 54 | } 55 | 56 | private static class ExceptionHandler implements Thread.UncaughtExceptionHandler 57 | { 58 | private final Activity context; 59 | 60 | public ExceptionHandler(Activity context) 61 | { 62 | this.context = context; 63 | } 64 | 65 | @Override 66 | public void uncaughtException(Thread thread, Throwable throwable) 67 | { 68 | // Get the message of the root cause 69 | Throwable rootCause = throwable; 70 | while (rootCause.getCause() != null) 71 | rootCause = rootCause.getCause(); 72 | String message = rootCause.getMessage(); 73 | 74 | // Print the stack trace to a string 75 | StringWriter stackTraceWriter = new StringWriter(); 76 | throwable.printStackTrace(new PrintWriter(stackTraceWriter)); 77 | String stackTrace = stackTraceWriter.toString(); 78 | 79 | // Launch the error activity with the message and stack trace 80 | start(context, message, stackTrace); 81 | 82 | // Kill the current activity 83 | android.os.Process.killProcess(android.os.Process.myPid()); 84 | System.exit(10); 85 | } 86 | } 87 | } 88 | -------------------------------------------------------------------------------- /GetStarted/src/main/java/com/myscript/iink/getstarted/IInkApplication.java: -------------------------------------------------------------------------------- 1 | // Copyright @ MyScript. All rights reserved. 2 | 3 | package com.myscript.iink.getstarted; 4 | 5 | import android.app.Application; 6 | 7 | import com.myscript.certificate.MyCertificate; 8 | import com.myscript.iink.Engine; 9 | 10 | public class IInkApplication extends Application 11 | { 12 | private static Engine engine; 13 | 14 | public static synchronized Engine getEngine() 15 | { 16 | if (engine == null) 17 | { 18 | engine = Engine.create(MyCertificate.getBytes()); 19 | } 20 | 21 | return engine; 22 | } 23 | 24 | } 25 | -------------------------------------------------------------------------------- /GetStarted/src/main/res/color/button_text_color.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /GetStarted/src/main/res/color/editor_icon_color.xml: -------------------------------------------------------------------------------- 1 | 2 | 5 | 6 | 7 | 8 | 9 | -------------------------------------------------------------------------------- /GetStarted/src/main/res/drawable-v24/ic_launcher_foreground.xml: -------------------------------------------------------------------------------- 1 | 7 | 8 | 12 | 13 | -------------------------------------------------------------------------------- /GetStarted/src/main/res/drawable/button_background.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /GetStarted/src/main/res/drawable/ic_delete.xml: -------------------------------------------------------------------------------- 1 | 7 | 10 | 11 | -------------------------------------------------------------------------------- /GetStarted/src/main/res/drawable/ic_redo.xml: -------------------------------------------------------------------------------- 1 | 7 | 10 | 11 | -------------------------------------------------------------------------------- /GetStarted/src/main/res/drawable/ic_undo.xml: -------------------------------------------------------------------------------- 1 | 7 | 10 | 11 | -------------------------------------------------------------------------------- /GetStarted/src/main/res/layout/error_activity.xml: -------------------------------------------------------------------------------- 1 | 2 | 8 | 9 | 16 | 17 | 24 | 25 | -------------------------------------------------------------------------------- /GetStarted/src/main/res/layout/main_activity.xml: -------------------------------------------------------------------------------- 1 | 2 | 7 | 8 | 9 | 10 | 11 | 18 | 19 |