From 989de49d4f438c27e5abdc1d78bcdb9b89fe9901 Mon Sep 17 00:00:00 2001 From: tverbeke Date: Tue, 13 Aug 2024 14:11:01 +0200 Subject: [PATCH 001/246] docs: add list of supported devices --- readme.md | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/readme.md b/readme.md index 5d776a26..69bde7cb 100644 --- a/readme.md +++ b/readme.md @@ -28,6 +28,7 @@ [Features](#features) • [Download](#download) • [Gestures](#gestures) • +[Supported Devices](#devices) • [Contribute](#contribute) @@ -76,6 +77,13 @@ Notable features intuitive gestures controls within Editor's Mode, to optimize t * **Drag:** Move the selected writing around. * **Double tap:** Copy the selected writing. +## Supported Devices + +The list below lists devices which users confirm to be supported by Notable. It does not imply any +commitment from the developers. Feel free to add your device to the list. + +* [ONYX BOOX Go 10.3](https://onyxboox.com/boox_go103) + ## Contribute Notable is an open-source project and welcomes contributions from the community. To start working with the project, see [the guide on how to start contributing](https://docs.github.com/en/get-started/quickstart/contributing-to-projects) to the project. From f6ec056808fd3625feac8c119ea48b1426fcc3dc Mon Sep 17 00:00:00 2001 From: tverbeke Date: Tue, 13 Aug 2024 14:13:20 +0200 Subject: [PATCH 002/246] fix: link --- readme.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/readme.md b/readme.md index 69bde7cb..0fcb7279 100644 --- a/readme.md +++ b/readme.md @@ -28,7 +28,7 @@ [Features](#features) • [Download](#download) • [Gestures](#gestures) • -[Supported Devices](#devices) • +[Supported Devices](#supported-devices) • [Contribute](#contribute) From cb61f5212e6fd345189a7c5dc302f2af62c2c897 Mon Sep 17 00:00:00 2001 From: alokehpdev Date: Sun, 1 Sep 2024 06:26:42 +0545 Subject: [PATCH 003/246] Several Changes + Export to png and pdf both + Intent to open notable:// links in the app from obsidian or anywhere as floating window + Updated gradle version as well --- .idea/compiler.xml | 2 +- .idea/deploymentTargetSelector.xml | 18 +++++ .idea/gradle.xml | 4 +- .idea/kotlinc.xml | 6 ++ .idea/migrations.xml | 10 +++ .idea/misc.xml | 2 +- app/build.gradle | 26 +++---- app/src/main/AndroidManifest.xml | 16 +++++ .../olup/notable/components/ToolbarMenu.kt | 61 ++++++++++++++-- .../main/java/com/olup/notable/utils/page.kt | 69 +++++++++++++++++++ .../olup/notable/views/FloatingEditorView.kt | 62 +++++++++++++++++ .../java/com/olup/notable/views/HomeView.kt | 37 +++++++++- build.gradle | 4 +- gradle.properties | 3 +- gradle/wrapper/gradle-wrapper.properties | 2 +- 15 files changed, 296 insertions(+), 26 deletions(-) create mode 100644 .idea/deploymentTargetSelector.xml create mode 100644 .idea/kotlinc.xml create mode 100644 .idea/migrations.xml create mode 100644 app/src/main/java/com/olup/notable/views/FloatingEditorView.kt diff --git a/.idea/compiler.xml b/.idea/compiler.xml index fb7f4a8a..b589d56e 100644 --- a/.idea/compiler.xml +++ b/.idea/compiler.xml @@ -1,6 +1,6 @@ - + \ No newline at end of file diff --git a/.idea/deploymentTargetSelector.xml b/.idea/deploymentTargetSelector.xml new file mode 100644 index 00000000..5ba4cf98 --- /dev/null +++ b/.idea/deploymentTargetSelector.xml @@ -0,0 +1,18 @@ + + + + + + + + + \ No newline at end of file diff --git a/.idea/gradle.xml b/.idea/gradle.xml index a2d7c213..0897082f 100644 --- a/.idea/gradle.xml +++ b/.idea/gradle.xml @@ -4,15 +4,15 @@ diff --git a/.idea/kotlinc.xml b/.idea/kotlinc.xml new file mode 100644 index 00000000..b1077fbd --- /dev/null +++ b/.idea/kotlinc.xml @@ -0,0 +1,6 @@ + + + + + \ No newline at end of file diff --git a/.idea/migrations.xml b/.idea/migrations.xml new file mode 100644 index 00000000..f8051a6f --- /dev/null +++ b/.idea/migrations.xml @@ -0,0 +1,10 @@ + + + + + + \ No newline at end of file diff --git a/.idea/misc.xml b/.idea/misc.xml index b1f8730f..0ad17cbd 100644 --- a/.idea/misc.xml +++ b/.idea/misc.xml @@ -1,7 +1,7 @@ - + diff --git a/app/build.gradle b/app/build.gradle index e5dc8fb5..33885f02 100644 --- a/app/build.gradle +++ b/app/build.gradle @@ -28,25 +28,25 @@ android { } - signingConfigs { - debug { - storeFile file(DEBUG_STORE_FILE) - storePassword DEBUG_STORE_PASSWORD - keyAlias DEBUG_KEY_ALIAS - keyPassword DEBUG_KEY_PASSWORD - - // Optional, specify signing versions used - v1SigningEnabled true - v2SigningEnabled true - } - } + // signingConfigs { + // debug { + // storeFile file(DEBUG_STORE_FILE) + // storePassword DEBUG_STORE_PASSWORD + // keyAlias DEBUG_KEY_ALIAS + // keyPassword DEBUG_KEY_PASSWORD + + // // Optional, specify signing versions used + // v1SigningEnabled true + // v2SigningEnabled true + // } + // } buildTypes { debug { minifyEnabled false proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro' - signingConfig signingConfigs.debug + // signingConfig signingConfigs.debug buildConfigField("boolean", "IS_NEXT", IS_NEXT) } release { diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml index 93e48ebb..3909866d 100644 --- a/app/src/main/AndroidManifest.xml +++ b/app/src/main/AndroidManifest.xml @@ -2,6 +2,8 @@ + + + + + + + + + + + + + + bitmap.compress(Bitmap.CompressFormat.PNG, 100, out) + } + bitmap.recycle() +} + +fun exportBookToPng(context: Context, bookId: String) { + val book = BookRepository(context).getById(bookId) ?: return + val pages = PageRepository(context) + + val dirPath = Environment.getExternalStorageDirectory().toPath() / + Environment.DIRECTORY_DOCUMENTS / "notable" / "notebooks" / book.title + File(dirPath.toString()).mkdirs() + + book.pageIds.forEachIndexed { index, pageId -> + val (page, strokes) = pages.getWithStrokeById(pageId) + + val strokeHeight = if (strokes.isEmpty()) 0 else strokes.maxOf(Stroke::bottom).toInt() + 50 + val strokeWidth = if (strokes.isEmpty()) 0 else strokes.maxOf(Stroke::right).toInt() + 50 + + val height = strokeHeight.coerceAtLeast(SCREEN_HEIGHT) + val width = strokeWidth.coerceAtLeast(SCREEN_WIDTH) + + val bitmap = Bitmap.createBitmap(width, height, Bitmap.Config.ARGB_8888) + val canvas = Canvas(bitmap) + + drawBg(canvas, page.nativeTemplate, 0) + + for (stroke in strokes) { + drawStroke(canvas, stroke, IntOffset(0, 0)) + } + + val filePath = dirPath / "page-${index + 1}.png" + FileOutputStream(filePath.toString()).use { out -> + bitmap.compress(Bitmap.CompressFormat.PNG, 100, out) + } + bitmap.recycle() + } +} + private inline fun exportPdf(dir: String, name: String, write: PdfDocument.() -> Unit) { val document = PdfDocument() document.write() diff --git a/app/src/main/java/com/olup/notable/views/FloatingEditorView.kt b/app/src/main/java/com/olup/notable/views/FloatingEditorView.kt new file mode 100644 index 00000000..690e76fb --- /dev/null +++ b/app/src/main/java/com/olup/notable/views/FloatingEditorView.kt @@ -0,0 +1,62 @@ +package com.olup.notable.views + +import androidx.compose.foundation.ExperimentalFoundationApi +import androidx.compose.foundation.background +import androidx.compose.foundation.layout.* +import androidx.compose.material.* +import androidx.compose.material.icons.Icons +import androidx.compose.material.icons.filled.Close +import androidx.compose.runtime.* +import androidx.compose.ui.Alignment +import androidx.compose.ui.Modifier +import androidx.compose.ui.graphics.Color +import androidx.compose.ui.unit.dp +import androidx.compose.ui.window.Dialog +import androidx.compose.ui.window.DialogProperties +import androidx.navigation.NavController +import com.olup.notable.EditorView +import com.olup.notable.ui.theme.InkaTheme + +@OptIn(ExperimentalFoundationApi::class) +@Composable +fun FloatingEditorView( + navController: NavController, + pageId: String, + onDismissRequest: () -> Unit +) { + Dialog( + onDismissRequest = onDismissRequest, + properties = DialogProperties(dismissOnBackPress = true, dismissOnClickOutside = true) + ) { + InkaTheme { + Box( + modifier = Modifier + .fillMaxSize(0.9f) + .background(Color.White) + ) { + Column { + Row( + modifier = Modifier + .fillMaxWidth() + .background(Color.LightGray) + .padding(8.dp), + horizontalArrangement = Arrangement.SpaceBetween, + verticalAlignment = Alignment.CenterVertically + ) { + Text("Floating Editor", style = MaterialTheme.typography.h6) + IconButton(onClick = onDismissRequest) { + Icon(Icons.Default.Close, contentDescription = "Close") + } + } + Box(modifier = Modifier.weight(1f)) { + EditorView( + navController = navController, + _bookId = null, + _pageId = pageId + ) + } + } + } + } + } +} \ No newline at end of file diff --git a/app/src/main/java/com/olup/notable/views/HomeView.kt b/app/src/main/java/com/olup/notable/views/HomeView.kt index 08bc40ac..12118472 100644 --- a/app/src/main/java/com/olup/notable/views/HomeView.kt +++ b/app/src/main/java/com/olup/notable/views/HomeView.kt @@ -34,6 +34,9 @@ import compose.icons.feathericons.Folder import compose.icons.feathericons.Settings import java.net.URL import kotlin.concurrent.thread +import androidx.compose.material.Button +import com.olup.notable.views.FloatingEditorView +import com.olup.notable.AppSettings @ExperimentalFoundationApi @ExperimentalComposeUiApi @@ -60,6 +63,9 @@ fun Library(navController: NavController, folderId: String? = null) { } }) + var showFloatingEditor by remember { mutableStateOf(false) } + var floatingEditorPageId by remember { mutableStateOf(null) } + Column( Modifier.fillMaxSize() ) { @@ -79,7 +85,6 @@ fun Library(navController: NavController, folderId: String? = null) { isSettingsOpen = true }) } - } Row(Modifier.fillMaxWidth(), horizontalArrangement = Arrangement.Center) { Text(text = "Add quick page", @@ -116,6 +121,24 @@ fun Library(navController: NavController, folderId: String? = null) { appRepository.folderRepository.create(folder) } .padding(10.dp)) + + // Add the new "Floating Editor" button here + Text(text = "Floating Editor", + textAlign = TextAlign.Center, + modifier = Modifier + .noRippleClickable { + val page = Page( + notebookId = null, + parentFolderId = folderId, + nativeTemplate = appRepository.kvProxy.get( + "APP_SETTINGS", AppSettings.serializer() + )?.defaultNativeTemplate ?: "blank" + ) + appRepository.pageRepository.create(page) + floatingEditorPageId = page.id + showFloatingEditor = true + } + .padding(10.dp)) } } Row( @@ -266,6 +289,18 @@ fun Library(navController: NavController, folderId: String? = null) { } if (isSettingsOpen) AppSettingsModal(onClose = { isSettingsOpen = false }) + + // Add the FloatingEditorView here + if (showFloatingEditor && floatingEditorPageId != null) { + FloatingEditorView( + navController = navController, + pageId = floatingEditorPageId!!, + onDismissRequest = { + showFloatingEditor = false + floatingEditorPageId = null + } + ) + } } diff --git a/build.gradle b/build.gradle index 1049f7d4..9def8b8f 100644 --- a/build.gradle +++ b/build.gradle @@ -13,8 +13,8 @@ buildscript { } }// Top-level build file where you can add configuration options common to all sub-projects/modules. plugins { - id 'com.android.application' version '7.4.1' apply false - id 'com.android.library' version '7.4.1' apply false + id 'com.android.application' version '8.4.0' apply false + id 'com.android.library' version '8.4.0' apply false id 'org.jetbrains.kotlin.android' version '1.7.10' apply false id 'org.jetbrains.kotlin.jvm' version '1.7.10' id 'org.jetbrains.kotlin.plugin.serialization' version '1.7.10' diff --git a/gradle.properties b/gradle.properties index 30e360f6..f7bdee41 100644 --- a/gradle.properties +++ b/gradle.properties @@ -22,4 +22,5 @@ kotlin.code.style=official # resources declared in the library itself and none from the library's dependencies, # thereby reducing the size of the R class for that library android.nonTransitiveRClass=true -android.defaults.buildfeatures.buildconfig=true \ No newline at end of file +android.defaults.buildfeatures.buildconfig=true +android.nonFinalResIds=false \ No newline at end of file diff --git a/gradle/wrapper/gradle-wrapper.properties b/gradle/wrapper/gradle-wrapper.properties index 3a2c9a79..ed680c57 100644 --- a/gradle/wrapper/gradle-wrapper.properties +++ b/gradle/wrapper/gradle-wrapper.properties @@ -1,6 +1,6 @@ #Sun Jan 29 13:21:50 CET 2023 distributionBase=GRADLE_USER_HOME -distributionUrl=https\://services.gradle.org/distributions/gradle-7.6-bin.zip +distributionUrl=https\://services.gradle.org/distributions/gradle-8.6-bin.zip distributionPath=wrapper/dists zipStorePath=wrapper/dists zipStoreBase=GRADLE_USER_HOME From ebd91c39883c776088eb3a02a3c40a7e95071dfe Mon Sep 17 00:00:00 2001 From: alokehpdev Date: Sun, 1 Sep 2024 06:28:47 +0545 Subject: [PATCH 004/246] Add floating editor activity --- .../olup/notable/FloatingEditorActivity.kt | 95 +++++++++++++++++++ 1 file changed, 95 insertions(+) create mode 100644 app/src/main/java/com/olup/notable/FloatingEditorActivity.kt diff --git a/app/src/main/java/com/olup/notable/FloatingEditorActivity.kt b/app/src/main/java/com/olup/notable/FloatingEditorActivity.kt new file mode 100644 index 00000000..8f837c49 --- /dev/null +++ b/app/src/main/java/com/olup/notable/FloatingEditorActivity.kt @@ -0,0 +1,95 @@ +package com.olup.notable + +import android.content.Intent +import android.net.Uri +import android.os.Bundle +import android.provider.Settings +import androidx.activity.ComponentActivity +import androidx.activity.compose.setContent +import androidx.compose.foundation.layout.fillMaxSize +import androidx.compose.material.MaterialTheme +import androidx.compose.material.Surface +import androidx.compose.runtime.* +import androidx.compose.ui.Modifier +import androidx.navigation.compose.rememberNavController +import com.olup.notable.ui.theme.InkaTheme +import com.olup.notable.views.FloatingEditorView +import com.olup.notable.db.Page +import com.olup.notable.AppSettings + +class FloatingEditorActivity : ComponentActivity() { + private lateinit var appRepository: AppRepository + + override fun onCreate(savedInstanceState: Bundle?) { + super.onCreate(savedInstanceState) + + val pageId = intent.data?.lastPathSegment ?: return + appRepository = AppRepository(this) + + setContent { + InkaTheme { + Surface( + modifier = Modifier.fillMaxSize(), + color = MaterialTheme.colors.background + ) { + val navController = rememberNavController() + var showEditor by remember { mutableStateOf(false) } + + LaunchedEffect(Unit) { + if (!Settings.canDrawOverlays(this@FloatingEditorActivity)) { + val intent = Intent( + Settings.ACTION_MANAGE_OVERLAY_PERMISSION, + Uri.parse("package:${packageName}") + ) + startActivityForResult(intent, OVERLAY_PERMISSION_REQ_CODE) + } else { + showEditor = true + } + } + + if (showEditor) { + FloatingEditorContent(pageId, navController) + } + } + } + } + } + + @Composable + private fun FloatingEditorContent(pageId: String, navController: androidx.navigation.NavController) { + var page = appRepository.pageRepository.getById(pageId) + if (page == null) { + page = Page( + id = pageId, + notebookId = null, + parentFolderId = null, + nativeTemplate = appRepository.kvProxy.get( + "APP_SETTINGS", AppSettings.serializer() + )?.defaultNativeTemplate ?: "blank" + ) + appRepository.pageRepository.create(page) + } + + FloatingEditorView( + navController = navController, + pageId = pageId, + onDismissRequest = { finish() } + ) + } + + override fun onActivityResult(requestCode: Int, resultCode: Int, data: Intent?) { + super.onActivityResult(requestCode, resultCode, data) + if (requestCode == OVERLAY_PERMISSION_REQ_CODE) { + if (Settings.canDrawOverlays(this)) { + recreate() // Recreate the activity to show the editor + } else { + // Permission denied, handle accordingly (e.g., show a message or close the activity) + finish() + } + } + } + + companion object { + private const val OVERLAY_PERMISSION_REQ_CODE = 1234 + } +} \ No newline at end of file From 78caf9dd5b5fbf58bc7402bdd83908711f5631bb Mon Sep 17 00:00:00 2001 From: alokehpdev Date: Sun, 1 Sep 2024 06:42:45 +0545 Subject: [PATCH 005/246] Export note as png by default from a floating window --- .../olup/notable/FloatingEditorActivity.kt | 25 ++++++++++++++++--- 1 file changed, 22 insertions(+), 3 deletions(-) diff --git a/app/src/main/java/com/olup/notable/FloatingEditorActivity.kt b/app/src/main/java/com/olup/notable/FloatingEditorActivity.kt index 8f837c49..d7656e16 100644 --- a/app/src/main/java/com/olup/notable/FloatingEditorActivity.kt +++ b/app/src/main/java/com/olup/notable/FloatingEditorActivity.kt @@ -16,14 +16,21 @@ import com.olup.notable.ui.theme.InkaTheme import com.olup.notable.views.FloatingEditorView import com.olup.notable.db.Page import com.olup.notable.AppSettings +import kotlinx.coroutines.launch class FloatingEditorActivity : ComponentActivity() { private lateinit var appRepository: AppRepository + private var pageId: String? = null override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) - val pageId = intent.data?.lastPathSegment ?: return + pageId = intent.data?.lastPathSegment + if (pageId == null) { + finish() + return + } + appRepository = AppRepository(this) setContent { @@ -48,7 +55,7 @@ class FloatingEditorActivity : ComponentActivity() { } if (showEditor) { - FloatingEditorContent(pageId, navController) + FloatingEditorContent(pageId!!, navController) } } } @@ -73,7 +80,9 @@ class FloatingEditorActivity : ComponentActivity() { FloatingEditorView( navController = navController, pageId = pageId, - onDismissRequest = { finish() } + onDismissRequest = { + finish() + } ) } @@ -89,6 +98,16 @@ class FloatingEditorActivity : ComponentActivity() { } } + override fun onDestroy() { + super.onDestroy() + pageId?.let { id -> + // Auto-export to PNG when the activity is destroyed + Thread { + exportPageToPng(this, id) + }.start() + } + } + companion object { private const val OVERLAY_PERMISSION_REQ_CODE = 1234 } From 8ca8c6457f3a5edf444a872048d5e1815e568f04 Mon Sep 17 00:00:00 2001 From: alokehpdev Date: Sun, 1 Sep 2024 11:14:44 +0545 Subject: [PATCH 006/246] Add color to stroke object --- .idea/deploymentTargetSelector.xml | 4 +- .../com.olup.notable.db.AppDatabase/28.json | 13 +- .../com.olup.notable.db.AppDatabase/29.json | 399 ++++++++++++++++++ .../com/olup/notable/components/StrokeMenu.kt | 37 ++ .../com/olup/notable/components/Toolbar.kt | 41 ++ app/src/main/java/com/olup/notable/db/Db.kt | 3 +- .../main/java/com/olup/notable/db/Stroke.kt | 3 + .../main/java/com/olup/notable/utils/utils.kt | 4 +- app/src/main/res/drawable/line_weight.xml | 5 + app/src/main/res/drawable/palette.xml | 5 + 10 files changed, 507 insertions(+), 7 deletions(-) create mode 100644 app/schemas/com.olup.notable.db.AppDatabase/29.json create mode 100644 app/src/main/res/drawable/line_weight.xml create mode 100644 app/src/main/res/drawable/palette.xml diff --git a/.idea/deploymentTargetSelector.xml b/.idea/deploymentTargetSelector.xml index 5ba4cf98..8a1ae220 100644 --- a/.idea/deploymentTargetSelector.xml +++ b/.idea/deploymentTargetSelector.xml @@ -4,10 +4,10 @@ diff --git a/app/src/main/java/com/olup/notable/utils/utils.kt b/app/src/main/java/com/olup/notable/utils/utils.kt index c09f0c67..0e946a9c 100644 --- a/app/src/main/java/com/olup/notable/utils/utils.kt +++ b/app/src/main/java/com/olup/notable/utils/utils.kt @@ -535,7 +535,7 @@ fun offsetImage(image: Image, offset: Offset): Image { } -public class Provider : FileProvider(R.xml.paths) { +public class Provider : FileProvider(R.xml.file_paths) { } fun shareBitmap(context: Context, bitmap: Bitmap) { @@ -548,7 +548,6 @@ fun shareBitmap(context: Context, bitmap: Bitmap) { val cachePath = File(context.cacheDir, "images") Log.i(TAG, cachePath.toString()) cachePath.mkdirs() - try { val stream = FileOutputStream("$cachePath/share.png") diff --git a/app/src/main/res/xml/paths.xml b/app/src/main/res/xml/file_paths.xml similarity index 100% rename from app/src/main/res/xml/paths.xml rename to app/src/main/res/xml/file_paths.xml From ba4734715ef67cebbf134be651b3554e4a9e8f89 Mon Sep 17 00:00:00 2001 From: Wiktor Wichrowski Date: Sun, 5 Jan 2025 23:32:41 +0100 Subject: [PATCH 073/246] improved image handling --- .../com/olup/notable/classes/DrawCanvas.kt | 36 +--- .../main/java/com/olup/notable/db/Image.kt | 2 + .../main/java/com/olup/notable/db/Select.kt | 184 ++++++++++++++++++ .../main/java/com/olup/notable/utils/draw.kt | 24 +-- .../main/java/com/olup/notable/utils/utils.kt | 131 +------------ 5 files changed, 200 insertions(+), 177 deletions(-) create mode 100644 app/src/main/java/com/olup/notable/db/Select.kt diff --git a/app/src/main/java/com/olup/notable/classes/DrawCanvas.kt b/app/src/main/java/com/olup/notable/classes/DrawCanvas.kt index ba0863ca..64cd07cb 100644 --- a/app/src/main/java/com/olup/notable/classes/DrawCanvas.kt +++ b/app/src/main/java/com/olup/notable/classes/DrawCanvas.kt @@ -13,6 +13,8 @@ import androidx.compose.ui.unit.IntOffset import androidx.compose.ui.unit.dp import com.olup.notable.db.Image import com.olup.notable.db.ImageRepository +import com.olup.notable.db.handleSelect +import com.olup.notable.db.selectImage import com.onyx.android.sdk.api.device.epd.EpdController import com.onyx.android.sdk.data.note.TouchPoint import com.onyx.android.sdk.pen.RawInputCallback @@ -261,7 +263,7 @@ class DrawCanvas( } imageCoordinateToSelect.value = null if (imageToSelect != null) { - selectImage(imageToSelect) + selectImage(page,state, imageToSelect) SnackState.globalSnackFlow.emit(SnackConf( text = "Image selected!", duration = 3000, @@ -392,42 +394,14 @@ class DrawCanvas( drawImage( context, page.windowedCanvas, imageToSave, IntOffset(0, -page.scroll) ) - selectImage(imageToSave) + selectImage(page,state, imageToSave) } else { // Handle cases where the bitmap could not be created Log.e("ImageProcessing", "Failed to create software bitmap from URI.") } } - fun selectImage( - imageToSelect: Image - ) { - //handle selection: - val pageBounds = imageBoundsInt(imageToSelect) - val padding = 30 - pageBounds.inset(-padding, -padding) - val bounds = pageAreaToCanvasArea(pageBounds, page.scroll) - val selectedBitmap = Bitmap.createBitmap( - imageToSelect.width, - imageToSelect.height, - Bitmap.Config.ARGB_8888 - ) - val selectedCanvas = Canvas(selectedBitmap) - drawImage( - context, - selectedCanvas, - imageToSelect, - IntOffset(0, -page.scroll) - ) - // set state - state.selectionState.selectedImages = listOf(imageToSelect) - state.selectionState.selectedBitmap = selectedBitmap - state.selectionState.selectionStartOffset = IntOffset(bounds.left, bounds.top) - state.selectionState.selectionRect = bounds - state.selectionState.selectionDisplaceOffset = IntOffset(0, 0) - state.selectionState.placementMode = PlacementMode.Move - page.drawArea(bounds, ignoredImageIds = listOf(imageToSelect).map { it.id }) - } + fun drawCanvasToView() { val canvas = this.holder.lockCanvas() ?: return diff --git a/app/src/main/java/com/olup/notable/db/Image.kt b/app/src/main/java/com/olup/notable/db/Image.kt index 7bdb93a2..5c6e3e41 100644 --- a/app/src/main/java/com/olup/notable/db/Image.kt +++ b/app/src/main/java/com/olup/notable/db/Image.kt @@ -81,9 +81,11 @@ class ImageRepository(context: Context) { fun create( imageUri: String, + //position on canvas x: Int, y: Int, pageId: String, + //size on canvas width: Int, height: Int ): Long { diff --git a/app/src/main/java/com/olup/notable/db/Select.kt b/app/src/main/java/com/olup/notable/db/Select.kt new file mode 100644 index 00000000..0f0ed5fa --- /dev/null +++ b/app/src/main/java/com/olup/notable/db/Select.kt @@ -0,0 +1,184 @@ +package com.olup.notable.db + +import android.graphics.Bitmap +import android.graphics.Canvas +import androidx.compose.ui.unit.IntOffset +import com.olup.notable.DrawCanvas +import com.olup.notable.EditorState +import com.olup.notable.PageView +import com.olup.notable.PlacementMode +import com.olup.notable.SelectPointPosition +import com.olup.notable.SimplePointF +import com.olup.notable.TAG +import com.olup.notable.divideStrokesFromCut +import com.olup.notable.drawImage +import com.olup.notable.drawStroke +import com.olup.notable.imageBoundsInt +import com.olup.notable.pageAreaToCanvasArea +import com.olup.notable.pointsToPath +import com.olup.notable.selectStrokesFromPath +import com.olup.notable.strokeBounds +import io.shipbook.shipbooksdk.Log +import kotlinx.coroutines.CoroutineScope +import kotlinx.coroutines.launch + +fun selectImage( + page: PageView, + editorState: EditorState, + imageToSelect: Image +) { + //handle selection: + val pageBounds = imageBoundsInt(imageToSelect) + val padding = 0 + pageBounds.inset(-padding, -padding) + val bounds = pageAreaToCanvasArea(pageBounds, page.scroll) + val selectedBitmap = Bitmap.createBitmap( + imageToSelect.width, + imageToSelect.height, + Bitmap.Config.ARGB_8888 + ) + val selectedCanvas = Canvas(selectedBitmap) + drawImage( + page.context, + selectedCanvas, + imageToSelect, + IntOffset(-imageToSelect.x, -imageToSelect.y) + ) + // set state + editorState.selectionState.selectedImages = listOf(imageToSelect) + editorState.selectionState.selectedBitmap = selectedBitmap + editorState.selectionState.selectionStartOffset = IntOffset(bounds.left, bounds.top) + editorState.selectionState.selectionRect = bounds + editorState.selectionState.selectionDisplaceOffset = IntOffset(0, 0) + editorState.selectionState.placementMode = PlacementMode.Move + page.drawArea(bounds, ignoredImageIds = listOf(imageToSelect).map { it.id }) +} + + + +/** Written by GPT: + * Handles selection of strokes and areas on a page, enabling either lasso selection or + * page-cut-based selection for further manipulation or operations. + * + * This function performs the following steps: + * + * 1. **Page Cut Selection**: + * - Identifies if the selection points cross the left or right edge of the page (`Page cut` case). + * - Determines the direction of the cut and creates a complete selection area spanning the page. + * - For the first page cut, it registers the cut coordinates. + * - For the second page cut, it orders the cuts, divides the strokes into sections based on these cuts, + * and assigns the strokes in the middle section to `selectedStrokes`. + * + * 2. **Lasso Selection**: + * - For non-page-cut cases, it performs lasso selection using the provided points. + * - Creates a `Path` from the selection points and identifies strokes within this lasso area. + * - Computes the bounding box (`pageBounds`) for the selected strokes and expands it with padding. + * - Maps the page-relative bounds to the canvas coordinate space. + * - Renders the selected strokes onto a new bitmap using the calculated bounds. + * - Updates the editor's selection state with: + * - The selected strokes. + * - The created bitmap and its position on the canvas. + * - The selection rectangle and displacement offset. + * - Enabling the "Move" placement mode for manipulation. + * - Optionally, redraws the affected area without the selected strokes. + * + * 3. **UI Refresh**: + * - Notifies the UI to refresh and disables the drawing mode. + * + * @param scope The `CoroutineScope` used to perform asynchronous operations, such as UI refresh. + * @param page The `PageView` object representing the current page, including its strokes and dimensions. + * @param editorState The `EditorState` object storing the current state of the editor, such as selected strokes. + * @param points A list of `SimplePointF` objects defining the user's selection path in page coordinates. + * points is in page coodinates + */ +fun handleSelect( + scope: CoroutineScope, + page: PageView, + editorState: EditorState, + points: List +) { + val state = editorState.selectionState + + val firstPointPosition = + if (points.first().x < 50) SelectPointPosition.LEFT else if (points.first().x > page.width - 50) SelectPointPosition.RIGHT else SelectPointPosition.CENTER + val lastPointPosition = + if (points.last().x < 50) SelectPointPosition.LEFT else if (points.last().x > page.width - 50) SelectPointPosition.RIGHT else SelectPointPosition.CENTER + + if (firstPointPosition != SelectPointPosition.CENTER && lastPointPosition != SelectPointPosition.CENTER && firstPointPosition != lastPointPosition) { + // Page cut situation + val correctedPoints = + if (firstPointPosition === SelectPointPosition.LEFT) points else points.reversed() + // lets make this end to end + val completePoints = + listOf(SimplePointF(0f, correctedPoints.first().y)) + correctedPoints + listOf( + SimplePointF(page.width.toFloat(), correctedPoints.last().y) + ) + if (state.firstPageCut == null) { + // this is the first page cut + state.firstPageCut = completePoints + Log.i(TAG, "Registered first curt") + } else { + // this is the second page cut, we can also select the strokes + // first lets have the cuts in the right order + if (completePoints[0].y > state.firstPageCut!![0].y) state.secondPageCut = + completePoints + else { + state.secondPageCut = state.firstPageCut + state.firstPageCut = completePoints + } + // let's get stroke selection from that + val (_, after) = divideStrokesFromCut(page.strokes, state.firstPageCut!!) + val (middle, _) = divideStrokesFromCut(after, state.secondPageCut!!) + state.selectedStrokes = middle + } + } else { + // lasso selection + // padding inside the dashed selection square + val padding = 30 + + // rcreate the lasso selection + val selectionPath = pointsToPath(points) + selectionPath.close() + + // get the selected strokes + val selectedStrokes = selectStrokesFromPath(page.strokes, selectionPath) + if (selectedStrokes.isEmpty()) return + + // TODO collocate with control tower ? + + state.selectedStrokes = selectedStrokes + + // area of implication - in page and view reference + val pageBounds = strokeBounds(selectedStrokes) + pageBounds.inset(-padding, -padding) + + val bounds = pageAreaToCanvasArea(pageBounds, page.scroll) + + // create bitmap and draw strokes + val selectedBitmap = + Bitmap.createBitmap(bounds.width(), bounds.height(), Bitmap.Config.ARGB_8888) + val selectedCanvas = Canvas(selectedBitmap) + selectedStrokes.forEach { + drawStroke( + selectedCanvas, + it, + IntOffset(-pageBounds.left, -pageBounds.top) + ) + } + + // set state + state.selectedBitmap = selectedBitmap + state.selectionStartOffset = IntOffset(bounds.left, bounds.top) + state.selectionRect = bounds + state.selectionDisplaceOffset = IntOffset(0, 0) + state.placementMode = PlacementMode.Move + +// page.removeStrokes(selectedStrokes.map{it.id}) + page.drawArea(bounds, ignoredStrokeIds = selectedStrokes.map { it.id }) + + scope.launch { + DrawCanvas.refreshUi.emit(Unit) + editorState.isDrawing = false + } + } +} diff --git a/app/src/main/java/com/olup/notable/utils/draw.kt b/app/src/main/java/com/olup/notable/utils/draw.kt index d199a0f3..45e57b42 100644 --- a/app/src/main/java/com/olup/notable/utils/draw.kt +++ b/app/src/main/java/com/olup/notable/utils/draw.kt @@ -174,23 +174,15 @@ fun drawImage(context: Context, canvas: Canvas, image: Image, offset: IntOffset) DrawCanvas.addImageByUri.value = null - // Draw the bitmap on the canvas at the center of the page - canvas.drawBitmap( - softwareBitmap, - Rect( - 0, - 0, - imageBitmap.width, - imageBitmap.height - ), // Source rectangle (full image) - Rect( - image.x, - image.y, - image.x + image.width, - image.y + image.height - ), // Destination rectangle (centered) - null // Optional Paint object (null for default) + val rectOnImage = Rect(0, 0, imageBitmap.width, imageBitmap.height) + val rectOnCanvas = Rect( + image.x + offset.x, + image.y + offset.y, + image.x + image.width + offset.x, + image.y + image.height + offset.y ) + // Draw the bitmap on the canvas at the center of the page + canvas.drawBitmap(softwareBitmap, rectOnImage, rectOnCanvas, null) // Log after drawing Log.i(TAG, "Image drawn successfully at center!") diff --git a/app/src/main/java/com/olup/notable/utils/utils.kt b/app/src/main/java/com/olup/notable/utils/utils.kt index 0e946a9c..313f6652 100644 --- a/app/src/main/java/com/olup/notable/utils/utils.kt +++ b/app/src/main/java/com/olup/notable/utils/utils.kt @@ -159,135 +159,6 @@ enum class SelectPointPosition { CENTER } - -/** Written by GPT: - * Handles selection of strokes and areas on a page, enabling either lasso selection or - * page-cut-based selection for further manipulation or operations. - * - * This function performs the following steps: - * - * 1. **Page Cut Selection**: - * - Identifies if the selection points cross the left or right edge of the page (`Page cut` case). - * - Determines the direction of the cut and creates a complete selection area spanning the page. - * - For the first page cut, it registers the cut coordinates. - * - For the second page cut, it orders the cuts, divides the strokes into sections based on these cuts, - * and assigns the strokes in the middle section to `selectedStrokes`. - * - * 2. **Lasso Selection**: - * - For non-page-cut cases, it performs lasso selection using the provided points. - * - Creates a `Path` from the selection points and identifies strokes within this lasso area. - * - Computes the bounding box (`pageBounds`) for the selected strokes and expands it with padding. - * - Maps the page-relative bounds to the canvas coordinate space. - * - Renders the selected strokes onto a new bitmap using the calculated bounds. - * - Updates the editor's selection state with: - * - The selected strokes. - * - The created bitmap and its position on the canvas. - * - The selection rectangle and displacement offset. - * - Enabling the "Move" placement mode for manipulation. - * - Optionally, redraws the affected area without the selected strokes. - * - * 3. **UI Refresh**: - * - Notifies the UI to refresh and disables the drawing mode. - * - * @param scope The `CoroutineScope` used to perform asynchronous operations, such as UI refresh. - * @param page The `PageView` object representing the current page, including its strokes and dimensions. - * @param editorState The `EditorState` object storing the current state of the editor, such as selected strokes. - * @param points A list of `SimplePointF` objects defining the user's selection path in page coordinates. - * points is in page coodinates - */ -fun handleSelect( - scope: CoroutineScope, - page: PageView, - editorState: EditorState, - points: List -) { - val state = editorState.selectionState - - val firstPointPosition = - if (points.first().x < 50) SelectPointPosition.LEFT else if (points.first().x > page.width - 50) SelectPointPosition.RIGHT else SelectPointPosition.CENTER - val lastPointPosition = - if (points.last().x < 50) SelectPointPosition.LEFT else if (points.last().x > page.width - 50) SelectPointPosition.RIGHT else SelectPointPosition.CENTER - - if (firstPointPosition != SelectPointPosition.CENTER && lastPointPosition != SelectPointPosition.CENTER && firstPointPosition != lastPointPosition) { - // Page cut situation - val correctedPoints = - if (firstPointPosition === SelectPointPosition.LEFT) points else points.reversed() - // lets make this end to end - val completePoints = - listOf(SimplePointF(0f, correctedPoints.first().y)) + correctedPoints + listOf( - SimplePointF(page.width.toFloat(), correctedPoints.last().y) - ) - if (state.firstPageCut == null) { - // this is the first page cut - state.firstPageCut = completePoints - Log.i(TAG, "Registered first curt") - } else { - // this is the second page cut, we can also select the strokes - // first lets have the cuts in the right order - if (completePoints[0].y > state.firstPageCut!![0].y) state.secondPageCut = - completePoints - else { - state.secondPageCut = state.firstPageCut - state.firstPageCut = completePoints - } - // let's get stroke selection from that - val (_, after) = divideStrokesFromCut(page.strokes, state.firstPageCut!!) - val (middle, _) = divideStrokesFromCut(after, state.secondPageCut!!) - state.selectedStrokes = middle - } - } else { - // lasso selection - // padding inside the dashed selection square - val padding = 30 - - // rcreate the lasso selection - val selectionPath = pointsToPath(points) - selectionPath.close() - - // get the selected strokes - val selectedStrokes = selectStrokesFromPath(page.strokes, selectionPath) - if (selectedStrokes.isEmpty()) return - - // TODO collocate with control tower ? - - state.selectedStrokes = selectedStrokes - - // area of implication - in page and view reference - val pageBounds = strokeBounds(selectedStrokes) - pageBounds.inset(-padding, -padding) - - val bounds = pageAreaToCanvasArea(pageBounds, page.scroll) - - // create bitmap and draw strokes - val selectedBitmap = - Bitmap.createBitmap(bounds.width(), bounds.height(), Bitmap.Config.ARGB_8888) - val selectedCanvas = Canvas(selectedBitmap) - selectedStrokes.forEach { - drawStroke( - selectedCanvas, - it, - IntOffset(-pageBounds.left, -pageBounds.top) - ) - } - - // set state - state.selectedBitmap = selectedBitmap - state.selectionStartOffset = IntOffset(bounds.left, bounds.top) - state.selectionRect = bounds - state.selectionDisplaceOffset = IntOffset(0, 0) - state.placementMode = PlacementMode.Move - -// page.removeStrokes(selectedStrokes.map{it.id}) - page.drawArea(bounds, ignoredStrokeIds = selectedStrokes.map { it.id }) - - scope.launch { - DrawCanvas.refreshUi.emit(Unit) - editorState.isDrawing = false - } - } -} - - // touchpoints is in wiew coordinates fun handleDraw( page: PageView, @@ -534,7 +405,7 @@ fun offsetImage(image: Image, offset: Offset): Image { ) } - +// Why it is needed? I try to removed it, and sharing bimap seems to work. public class Provider : FileProvider(R.xml.file_paths) { } From dc497bd863130a7823386ad7b25bbf190e319039 Mon Sep 17 00:00:00 2001 From: Wiktor Wichrowski Date: Sun, 5 Jan 2025 23:38:28 +0100 Subject: [PATCH 074/246] fixed image selection --- app/src/main/java/com/olup/notable/classes/DrawCanvas.kt | 4 ++-- app/src/main/java/com/olup/notable/db/Select.kt | 7 ++++++- 2 files changed, 8 insertions(+), 3 deletions(-) diff --git a/app/src/main/java/com/olup/notable/classes/DrawCanvas.kt b/app/src/main/java/com/olup/notable/classes/DrawCanvas.kt index 64cd07cb..b5bee687 100644 --- a/app/src/main/java/com/olup/notable/classes/DrawCanvas.kt +++ b/app/src/main/java/com/olup/notable/classes/DrawCanvas.kt @@ -263,7 +263,7 @@ class DrawCanvas( } imageCoordinateToSelect.value = null if (imageToSelect != null) { - selectImage(page,state, imageToSelect) + selectImage( coroutineScope, page,state, imageToSelect) SnackState.globalSnackFlow.emit(SnackConf( text = "Image selected!", duration = 3000, @@ -394,7 +394,7 @@ class DrawCanvas( drawImage( context, page.windowedCanvas, imageToSave, IntOffset(0, -page.scroll) ) - selectImage(page,state, imageToSave) + selectImage(coroutineScope, page,state, imageToSave) } else { // Handle cases where the bitmap could not be created Log.e("ImageProcessing", "Failed to create software bitmap from URI.") diff --git a/app/src/main/java/com/olup/notable/db/Select.kt b/app/src/main/java/com/olup/notable/db/Select.kt index 0f0ed5fa..078a4d60 100644 --- a/app/src/main/java/com/olup/notable/db/Select.kt +++ b/app/src/main/java/com/olup/notable/db/Select.kt @@ -22,7 +22,7 @@ import io.shipbook.shipbooksdk.Log import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.launch -fun selectImage( +fun selectImage( scope: CoroutineScope, page: PageView, editorState: EditorState, imageToSelect: Image @@ -52,6 +52,11 @@ fun selectImage( editorState.selectionState.selectionDisplaceOffset = IntOffset(0, 0) editorState.selectionState.placementMode = PlacementMode.Move page.drawArea(bounds, ignoredImageIds = listOf(imageToSelect).map { it.id }) + + scope.launch { + DrawCanvas.refreshUi.emit(Unit) + editorState.isDrawing = false + } } From 6c38d98ce269ff3fa77691971eeb80aa59ca1a9d Mon Sep 17 00:00:00 2001 From: Wiktor Wichrowski Date: Tue, 7 Jan 2025 00:14:33 +0100 Subject: [PATCH 075/246] deleting images --- .../notable/classes/EditorControlTower.kt | 13 ++++- .../java/com/olup/notable/classes/PageView.kt | 1 + .../olup/notable/components/SelectorBitmap.kt | 47 +++++++++++++++++++ .../main/java/com/olup/notable/db/Select.kt | 4 +- .../main/java/com/olup/notable/utils/utils.kt | 10 ++-- app/src/main/res/drawable/delete.xml | 9 ++++ app/src/main/res/drawable/minus.xml | 9 ++++ app/src/main/res/drawable/plus.xml | 9 ++++ 8 files changed, 94 insertions(+), 8 deletions(-) create mode 100644 app/src/main/res/drawable/delete.xml create mode 100644 app/src/main/res/drawable/minus.xml create mode 100644 app/src/main/res/drawable/plus.xml diff --git a/app/src/main/java/com/olup/notable/classes/EditorControlTower.kt b/app/src/main/java/com/olup/notable/classes/EditorControlTower.kt index 6ed1a56b..e3968d59 100644 --- a/app/src/main/java/com/olup/notable/classes/EditorControlTower.kt +++ b/app/src/main/java/com/olup/notable/classes/EditorControlTower.kt @@ -120,6 +120,17 @@ class EditorControlTower( DrawCanvas.refreshUi.emit(Unit) } } - + fun deleteSelection(){ + val selectedImages = state.selectionState.selectedImages + if (!selectedImages.isNullOrEmpty()) { + val imageIds: List = selectedImages.map { it.id } + Log.i(TAG, "removing images") + page.removeImage(imageIds) + } + state.selectionState.reset() + scope.launch { + DrawCanvas.refreshUi.emit(Unit) + } + } } \ No newline at end of file diff --git a/app/src/main/java/com/olup/notable/classes/PageView.kt b/app/src/main/java/com/olup/notable/classes/PageView.kt index a6678c64..fe293d12 100644 --- a/app/src/main/java/com/olup/notable/classes/PageView.kt +++ b/app/src/main/java/com/olup/notable/classes/PageView.kt @@ -172,6 +172,7 @@ class PageView( fun removeImage(imageIds: List) { images = images.filter { s -> !imageIds.contains(s.id) } + Log.i(TAG, images.size.toString()) removeImagesFromPersistLayer(imageIds) indexImages() computeHeight() diff --git a/app/src/main/java/com/olup/notable/components/SelectorBitmap.kt b/app/src/main/java/com/olup/notable/components/SelectorBitmap.kt index 9e6c611f..c96e29f2 100644 --- a/app/src/main/java/com/olup/notable/components/SelectorBitmap.kt +++ b/app/src/main/java/com/olup/notable/components/SelectorBitmap.kt @@ -1,14 +1,19 @@ package com.olup.notable +import android.util.Log import androidx.compose.foundation.ExperimentalFoundationApi import androidx.compose.foundation.Image +import androidx.compose.foundation.background import androidx.compose.foundation.combinedClickable import androidx.compose.foundation.gestures.detectDragGestures import androidx.compose.foundation.interaction.MutableInteractionSource import androidx.compose.foundation.layout.* +import androidx.compose.foundation.shape.RoundedCornerShape +import androidx.compose.material.Text import androidx.compose.runtime.* import androidx.compose.ui.ExperimentalComposeUiApi import androidx.compose.ui.Modifier +import androidx.compose.ui.draw.clip import androidx.compose.ui.draw.drawBehind import androidx.compose.ui.geometry.Offset import androidx.compose.ui.graphics.Color @@ -16,6 +21,7 @@ import androidx.compose.ui.graphics.PathEffect import androidx.compose.ui.graphics.asImageBitmap import androidx.compose.ui.input.pointer.pointerInput import androidx.compose.ui.unit.IntOffset +import androidx.compose.ui.unit.dp import androidx.compose.ui.unit.round import com.olup.notable.EditorControlTower import java.util.Date @@ -96,5 +102,46 @@ fun SelectedBitmap( } ) ) + + // If we can calculate offset of buttons show selection handling tools + // TODO: improve this code + selectionState.selectionStartOffset?.let { startOffset -> + selectionState.selectionDisplaceOffset?.let { displaceOffset -> + val xPos = selectionState.selectionRect?.let { rect -> + ((rect.left ?: 0) - (rect.right ?: 0)) / 2 + 35 * 3 + } ?: 0 + val offset = startOffset + displaceOffset + IntOffset(x = -xPos, y = -100) + // Overlay buttons near the selection box + Row( + modifier = Modifier + .offset { offset } + .background(Color.White.copy(alpha = 0.8f)) + .padding(4.dp) + .height(35.dp) + ) { + ToolbarButton( + iconId = R.drawable.delete, + isSelected = false, + onSelect = { + controlTower.deleteSelection() + }, + modifier = Modifier.height(37.dp) + ) + ToolbarButton( + iconId = R.drawable.plus, + isSelected = false, + onSelect = { }, + modifier = Modifier.height(37.dp) + ) + ToolbarButton( + iconId = R.drawable.minus, + isSelected = false, + onSelect = { }, + modifier = Modifier.height(37.dp) + ) + } + } + } + } } \ No newline at end of file diff --git a/app/src/main/java/com/olup/notable/db/Select.kt b/app/src/main/java/com/olup/notable/db/Select.kt index 078a4d60..62d68682 100644 --- a/app/src/main/java/com/olup/notable/db/Select.kt +++ b/app/src/main/java/com/olup/notable/db/Select.kt @@ -22,7 +22,8 @@ import io.shipbook.shipbooksdk.Log import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.launch -fun selectImage( scope: CoroutineScope, +fun selectImage( + scope: CoroutineScope, page: PageView, editorState: EditorState, imageToSelect: Image @@ -60,7 +61,6 @@ fun selectImage( scope: CoroutineScope, } - /** Written by GPT: * Handles selection of strokes and areas on a page, enabling either lasso selection or * page-cut-based selection for further manipulation or operations. diff --git a/app/src/main/java/com/olup/notable/utils/utils.kt b/app/src/main/java/com/olup/notable/utils/utils.kt index 313f6652..c4facd44 100644 --- a/app/src/main/java/com/olup/notable/utils/utils.kt +++ b/app/src/main/java/com/olup/notable/utils/utils.kt @@ -312,12 +312,12 @@ fun strokeBounds(strokes: List): Rect { return rect } -fun imageBoundsInt(image: Image): Rect { +fun imageBoundsInt(image: Image, padding: Int = 0): Rect { return Rect( - image.x, - image.y, - image.x + image.width, - image.y + image.height + image.x + padding, + image.y + padding, + image.x + image.width + padding, + image.y + image.height + padding ) } diff --git a/app/src/main/res/drawable/delete.xml b/app/src/main/res/drawable/delete.xml new file mode 100644 index 00000000..e7591350 --- /dev/null +++ b/app/src/main/res/drawable/delete.xml @@ -0,0 +1,9 @@ + + + diff --git a/app/src/main/res/drawable/minus.xml b/app/src/main/res/drawable/minus.xml new file mode 100644 index 00000000..f9e62769 --- /dev/null +++ b/app/src/main/res/drawable/minus.xml @@ -0,0 +1,9 @@ + + + diff --git a/app/src/main/res/drawable/plus.xml b/app/src/main/res/drawable/plus.xml new file mode 100644 index 00000000..b74729a6 --- /dev/null +++ b/app/src/main/res/drawable/plus.xml @@ -0,0 +1,9 @@ + + + From 48f5e34fa8bf0cc745dc10fbe87a4b9b84c7d9e3 Mon Sep 17 00:00:00 2001 From: Wiktor Wichrowski Date: Tue, 7 Jan 2025 00:20:27 +0100 Subject: [PATCH 076/246] also delete strokes --- .../java/com/olup/notable/classes/EditorControlTower.kt | 7 +++++++ app/src/main/java/com/olup/notable/classes/PageView.kt | 1 - 2 files changed, 7 insertions(+), 1 deletion(-) diff --git a/app/src/main/java/com/olup/notable/classes/EditorControlTower.kt b/app/src/main/java/com/olup/notable/classes/EditorControlTower.kt index e3968d59..4ceaeb38 100644 --- a/app/src/main/java/com/olup/notable/classes/EditorControlTower.kt +++ b/app/src/main/java/com/olup/notable/classes/EditorControlTower.kt @@ -127,7 +127,14 @@ class EditorControlTower( Log.i(TAG, "removing images") page.removeImage(imageIds) } + val selectedStrokes = state.selectionState.selectedStrokes + if (!selectedStrokes.isNullOrEmpty()) { + val imageIds: List = selectedStrokes.map { it.id } + Log.i(TAG, "removing strokes") + page.removeStrokes(imageIds) + } state.selectionState.reset() + state.isDrawing = true scope.launch { DrawCanvas.refreshUi.emit(Unit) } diff --git a/app/src/main/java/com/olup/notable/classes/PageView.kt b/app/src/main/java/com/olup/notable/classes/PageView.kt index fe293d12..a6678c64 100644 --- a/app/src/main/java/com/olup/notable/classes/PageView.kt +++ b/app/src/main/java/com/olup/notable/classes/PageView.kt @@ -172,7 +172,6 @@ class PageView( fun removeImage(imageIds: List) { images = images.filter { s -> !imageIds.contains(s.id) } - Log.i(TAG, images.size.toString()) removeImagesFromPersistLayer(imageIds) indexImages() computeHeight() From 139c994ed2b6dac5ddd94c0b14a8ee7c68d202fa Mon Sep 17 00:00:00 2001 From: Wiktor Wichrowski Date: Tue, 7 Jan 2025 10:55:53 +0100 Subject: [PATCH 077/246] change size of selected images --- .../notable/classes/EditorControlTower.kt | 85 ++++++++++++++++++- .../olup/notable/components/SelectorBitmap.kt | 28 +----- .../com/olup/notable/utils/EditorState.kt | 1 + .../main/java/com/olup/notable/utils/utils.kt | 11 +++ 4 files changed, 99 insertions(+), 26 deletions(-) diff --git a/app/src/main/java/com/olup/notable/classes/EditorControlTower.kt b/app/src/main/java/com/olup/notable/classes/EditorControlTower.kt index 4ceaeb38..e305b4e0 100644 --- a/app/src/main/java/com/olup/notable/classes/EditorControlTower.kt +++ b/app/src/main/java/com/olup/notable/classes/EditorControlTower.kt @@ -1,10 +1,17 @@ package com.olup.notable +import android.graphics.Bitmap +import android.graphics.Canvas import android.graphics.Rect import android.util.Log +import androidx.compose.ui.unit.IntOffset import androidx.compose.ui.unit.toOffset +import com.olup.notable.db.Image +import com.olup.notable.db.selectImage import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.launch +import java.util.Date +import java.util.UUID class EditorControlTower( val scope: CoroutineScope, val page: PageView, val history: History, val state: EditorState @@ -94,7 +101,7 @@ class EditorControlTower( history.addOperationsToHistory(operationList) } } - if(selectedImages!=null){ + if (selectedImages != null) { val displacedImages = selectedImages.map { offsetImage(it, offset = offset.toOffset()) } @@ -120,7 +127,8 @@ class EditorControlTower( DrawCanvas.refreshUi.emit(Unit) } } - fun deleteSelection(){ + + fun deleteSelection() { val selectedImages = state.selectionState.selectedImages if (!selectedImages.isNullOrEmpty()) { val imageIds: List = selectedImages.map { it.id } @@ -140,4 +148,77 @@ class EditorControlTower( } } + fun changeSizeOfSelection(scale: Int) { + val selectedImages = state.selectionState.selectedImages + + // Ensure selected images are not null or empty + if (!selectedImages.isNullOrEmpty()) { + state.selectionState.selectedImages = selectedImages.map { image -> + image.copy( + height = image.height + (image.height * scale / 100), + width = image.width + (image.width * scale / 100) + ) + } + + // Adjust displacement offset by half the size change + val sizeChange = selectedImages.firstOrNull()?.let { image -> + IntOffset( + x = (image.width * scale / 200), + y = (image.height * scale / 200) + ) + } ?: IntOffset.Zero + + val pageBounds = imageBoundsInt(selectedImages) + state.selectionState.selectionRect = pageAreaToCanvasArea(pageBounds, page.scroll) + + state.selectionState.selectionDisplaceOffset = + state.selectionState.selectionDisplaceOffset?.let { it - sizeChange } ?: IntOffset.Zero + + val selectedBitmap = Bitmap.createBitmap( + pageBounds.width(), pageBounds.height(), + Bitmap.Config.ARGB_8888 + ) + val selectedCanvas = Canvas(selectedBitmap) + selectedImages.forEach() { + drawImage( + page.context, + selectedCanvas, + it, + IntOffset(-it.x, -it.y) + ) + } + + // set state + state.selectionState.selectedBitmap = selectedBitmap + + // Emit a refresh signal to update UI + scope.launch { + DrawCanvas.refreshUi.emit(Unit) + } + } + } + + + fun copySelection() { + // finish ongoing movement + applySelectionDisplace() + // set operation to paste only + state.selectionState.placementMode = PlacementMode.Paste + // change the selected stokes' ids - it's a copy + state.selectionState.selectedStrokes = state.selectionState.selectedStrokes!!.map { + it.copy( + id = UUID + .randomUUID() + .toString(), + createdAt = Date() + ) + } + // move the selection a bit, to show the copy + state.selectionState.selectionDisplaceOffset = IntOffset( + x = state.selectionState.selectionDisplaceOffset!!.x + 50, + y = state.selectionState.selectionDisplaceOffset!!.y + 50, + ) + //TODO: implement coping for images + } + } \ No newline at end of file diff --git a/app/src/main/java/com/olup/notable/components/SelectorBitmap.kt b/app/src/main/java/com/olup/notable/components/SelectorBitmap.kt index c96e29f2..96c167bb 100644 --- a/app/src/main/java/com/olup/notable/components/SelectorBitmap.kt +++ b/app/src/main/java/com/olup/notable/components/SelectorBitmap.kt @@ -79,27 +79,7 @@ fun SelectedBitmap( .combinedClickable( indication = null, interactionSource = remember { MutableInteractionSource() }, onClick = {}, - onDoubleClick = { - // finish ongoing movement - controlTower.applySelectionDisplace() - // set operation to paste only - selectionState.placementMode = PlacementMode.Paste - // change the selected stokes' ids - it's a copy - selectionState.selectedStrokes = selectionState.selectedStrokes!!.map { - it.copy( - id = UUID - .randomUUID() - .toString(), - createdAt = Date() - ) - } - // move the selection a bit, to show the copy - selectionState.selectionDisplaceOffset = IntOffset( - x = selectionState.selectionDisplaceOffset!!.x + 50, - y = selectionState.selectionDisplaceOffset!!.y + 50, - ) - //TODO: implement coping for images - } + onDoubleClick = { controlTower.copySelection() } ) ) @@ -123,20 +103,20 @@ fun SelectedBitmap( iconId = R.drawable.delete, isSelected = false, onSelect = { - controlTower.deleteSelection() + controlTower.deleteSelection() }, modifier = Modifier.height(37.dp) ) ToolbarButton( iconId = R.drawable.plus, isSelected = false, - onSelect = { }, + onSelect = { controlTower.changeSizeOfSelection(10) }, modifier = Modifier.height(37.dp) ) ToolbarButton( iconId = R.drawable.minus, isSelected = false, - onSelect = { }, + onSelect = { controlTower.changeSizeOfSelection(-10) }, modifier = Modifier.height(37.dp) ) } diff --git a/app/src/main/java/com/olup/notable/utils/EditorState.kt b/app/src/main/java/com/olup/notable/utils/EditorState.kt index 9811b3f4..3583e02e 100644 --- a/app/src/main/java/com/olup/notable/utils/EditorState.kt +++ b/app/src/main/java/com/olup/notable/utils/EditorState.kt @@ -42,6 +42,7 @@ class EditorState(val bookId: String? = null, val pageId: String, val pageView: val selectionState = SelectionState() } +// if state is Move then applySelectionDisplace() will delete original strokes(images in future) enum class PlacementMode { Move, Paste diff --git a/app/src/main/java/com/olup/notable/utils/utils.kt b/app/src/main/java/com/olup/notable/utils/utils.kt index c4facd44..0d79e708 100644 --- a/app/src/main/java/com/olup/notable/utils/utils.kt +++ b/app/src/main/java/com/olup/notable/utils/utils.kt @@ -321,6 +321,17 @@ fun imageBoundsInt(image: Image, padding: Int = 0): Rect { ) } +fun imageBoundsInt(images: List): Rect { + if (images.isEmpty()) return Rect() + val rect = imageBoundsInt(images[0]) + images.forEach { + rect.union( + imageBoundsInt(it) + ) + } + return rect +} + data class SimplePoint(val x: Int, val y: Int) data class SimplePointF(val x: Float, val y: Float) From 79f36f842d9c0e80f8077fee4a30417f96b6c79e Mon Sep 17 00:00:00 2001 From: Wiktor Wichrowski <77555700+Ethran@users.noreply.github.com> Date: Tue, 7 Jan 2025 13:35:23 +0100 Subject: [PATCH 078/246] Update readme.md --- readme.md | 40 ++++++++++++++++++++++++++++++---------- 1 file changed, 30 insertions(+), 10 deletions(-) diff --git a/readme.md b/readme.md index 7b3e0175..34a2c4d5 100644 --- a/readme.md +++ b/readme.md @@ -39,12 +39,14 @@ Notable is a **custom note-taking app designed specifically for BOOX e-ink devic *⚠️ This is alpha software with a couple of part time individuals pushing it further. We try to make it as stable as possible and to support a smooth update experience, but be prepared for the occasionnal bug and possible breaking changes.* ## Features -* ⚡ **Fast Page Turn with Caching:** Notable leverages caching techniques to ensure smooth and swift page transitions, allowing you to navigate through your notes seamlessly. +* ⚡ **Fast Page Turn with Caching:** Notable leverages caching techniques to ensure smooth and swift page transitions, allowing you to navigate through your notes seamlessly. (next and previus pages are cached) * ↕️ **Infinite Vertical Scroll:** Enjoy a virtually endless canvas for your notes. Scroll vertically without limitations. * 📝 **Quick Pages:** Quickly create a new page using the Quick Pages feature. -* 📒 **Notebooks:** Keep related notes together and easily switch between different notebooks based on your needs. +* 📒 **Notebooks:** Keep related notes together and easily switch between different noteboo︂︂ks based on your needs. * 📁 **Folders:** Create folders to organize your notes. * 🤏 **Editors' Mode Gestures:** [Intuitive gesture controls](#gestures) to enhance the editing experience. +* 🌅 **Images:** Add, move, scale, and remove images +* ︂︂᠋︁ **slection export** share selected text ## Download **Download the latest stable version of the [Notable app here.](https://github.com/olup/notable/releases/latest)** @@ -68,7 +70,7 @@ Notable features intuitive gestures controls within Editor's Mode, to optimize t * **Swipe left or right:** Change to the previous/next page (only available in notebooks). * **Double tap:** Show or hide the toolbar. * **Double tap bottom part of the screen:** Show quick navigation. - +* **Hold** select image #### ✌️ 2 Fingers * **Swipe left or right:** Undo/redo your changes. * **Single tap:** Switch between writing modes and eraser modes. @@ -79,17 +81,35 @@ Notable features intuitive gestures controls within Editor's Mode, to optimize t ## Supported Devices -The list below lists devices which users confirm to be supported by Notable. It does not imply any -commitment from the developers. Feel free to add your device to the list. +The following table lists devices confirmed by users to be compatible with specific versions of Notable. +This does not imply any commitment from the developers. Feel free to add your device to the list if tested successfully. + +| Device Name | v0.0.10 | v0.0.11dev | | | | +|------------------------------------------------|---------|-----------|---------|---------|---------| +| [ONYX BOOX Go 10.3](https://onyxboox.com/boox_go103) | ✔ | ? | | | | +| [Onyx Boox Note Air 4 C](https://onyxboox.pl/en/ebook-readers/onyx-boox-note-air-4-c) | ✘ | ✔ | | | | +| [Onyx Boox Note Air 3 C](https://onyxboox.pl/en/ebook-readers/onyx-boox-note-air-3-c) | ✘ | ✔ | | | | + -* [ONYX BOOX Go 10.3](https://onyxboox.com/boox_go103) -* [Onyx Boox Note Air 4 C](https://onyxboox.pl/en/ebook-readers/onyx-boox-note-air-4-c) ## Screenshots -drawing -drawing -drawing +
+ +screenshot-1 + +screenshot-2 + +screenshot-3 + + +screenshot-5 + +screenshot-6 + +screenshot-7 + +
From e74479922007458c2824eb81ba0ee3e6f870dbf8 Mon Sep 17 00:00:00 2001 From: Wiktor Wichrowski Date: Sat, 11 Jan 2025 23:20:20 +0100 Subject: [PATCH 079/246] allow screen rotation --- .idea/misc.xml | 1 - app/src/main/AndroidManifest.xml | 3 +- .../java/com/olup/notable/MainActivity.kt | 35 ++++++++++++------- .../java/com/olup/notable/classes/PageView.kt | 23 +++++++++--- .../java/com/olup/notable/views/EditorView.kt | 7 ++++ 5 files changed, 51 insertions(+), 18 deletions(-) diff --git a/.idea/misc.xml b/.idea/misc.xml index 0ad17cbd..8978d23d 100644 --- a/.idea/misc.xml +++ b/.idea/misc.xml @@ -1,4 +1,3 @@ - diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml index 9b8f73ca..d62e03bf 100644 --- a/app/src/main/AndroidManifest.xml +++ b/app/src/main/AndroidManifest.xml @@ -20,7 +20,8 @@ android:name=".MainActivity" android:exported="true" android:label="@string/app_name" - android:screenOrientation="nosensor" + android:configChanges="orientation|screenSize" + android:screenOrientation="unspecified" android:theme="@style/Theme.Inka"> diff --git a/app/src/main/java/com/olup/notable/MainActivity.kt b/app/src/main/java/com/olup/notable/MainActivity.kt index b8bf752b..d9e4b57b 100644 --- a/app/src/main/java/com/olup/notable/MainActivity.kt +++ b/app/src/main/java/com/olup/notable/MainActivity.kt @@ -1,6 +1,7 @@ package com.olup.notable import android.content.pm.ActivityInfo +import android.content.res.Configuration import android.os.Bundle import io.shipbook.shipbooksdk.Log import androidx.activity.ComponentActivity @@ -25,6 +26,7 @@ var SCREEN_WIDTH = EpdController.getEpdHeight().toInt() var SCREEN_HEIGHT = EpdController.getEpdWidth().toInt() var TAG = "MainActivity" + @ExperimentalAnimationApi @ExperimentalComposeUiApi @ExperimentalFoundationApi @@ -32,13 +34,15 @@ class MainActivity : ComponentActivity() { override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) - ShipBook.start(this.application, "648adf9364c9825976c1d57e", - "7c53dffa949e3b55e37ab04672138feb"); + ShipBook.start( + this.application, "648adf9364c9825976c1d57e", + "7c53dffa949e3b55e37ab04672138feb" + ); Log.i(TAG, "Notable started") - if(SCREEN_WIDTH == 0){ + if (SCREEN_WIDTH == 0) { SCREEN_WIDTH = applicationContext.resources.displayMetrics.widthPixels SCREEN_HEIGHT = applicationContext.resources.displayMetrics.heightPixels } @@ -54,7 +58,7 @@ class MainActivity : ComponentActivity() { val intentData = intent.data?.lastPathSegment setContent { InkaTheme { - CompositionLocalProvider(SnackContext provides snackState ) { + CompositionLocalProvider(SnackContext provides snackState) { Box( Modifier .background(Color.White) @@ -78,7 +82,7 @@ class MainActivity : ComponentActivity() { super.onRestart() // redraw after device sleep this.lifecycleScope.launch { - DrawCanvas.restartAfterConfChange.emit(Unit) + DrawCanvas.restartAfterConfChange.emit(Unit) } } @@ -96,12 +100,19 @@ class MainActivity : ComponentActivity() { } } - - - - - override fun onContentChanged() { - super.onContentChanged() - setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_SENSOR_PORTRAIT); + // when the screen orientation is changed, set new screen width restart is not necessary, + // as we need first to update page dimensions which is done in EditorView + override fun onConfigurationChanged(newConfig: Configuration) { + super.onConfigurationChanged(newConfig) + if (newConfig.orientation == Configuration.ORIENTATION_LANDSCAPE) { + Log.i(TAG, "Switched to Landscape") + } else if (newConfig.orientation == Configuration.ORIENTATION_PORTRAIT) { + Log.i(TAG, "Switched to Portrait") + } + SCREEN_WIDTH = applicationContext.resources.displayMetrics.widthPixels + SCREEN_HEIGHT = applicationContext.resources.displayMetrics.heightPixels +// this.lifecycleScope.launch { +// DrawCanvas.restartAfterConfChange.emit(Unit) +// } } } \ No newline at end of file diff --git a/app/src/main/java/com/olup/notable/classes/PageView.kt b/app/src/main/java/com/olup/notable/classes/PageView.kt index a6678c64..1bc81919 100644 --- a/app/src/main/java/com/olup/notable/classes/PageView.kt +++ b/app/src/main/java/com/olup/notable/classes/PageView.kt @@ -34,12 +34,12 @@ class PageView( val coroutineScope: CoroutineScope, val id: String, val width: Int, - val viewWidth: Int, - val viewHeight: Int + var viewWidth: Int, + var viewHeight: Int ) { - val windowedBitmap = Bitmap.createBitmap(viewWidth, viewHeight, Bitmap.Config.ARGB_8888) - val windowedCanvas = Canvas(windowedBitmap) + var windowedBitmap = Bitmap.createBitmap(viewWidth, viewHeight, Bitmap.Config.ARGB_8888) + var windowedCanvas = Canvas(windowedBitmap) var strokes = listOf() var strokesById: HashMap = hashMapOf() var images = listOf() @@ -336,6 +336,8 @@ class PageView( saveToPersistLayer() } + // updates page setting in db, (for instance type of background) + // and redraws page to vew. fun updatePageSettings(page: Page) { AppRepository(context).pageRepository.update(page) pageFromDb = AppRepository(context).pageRepository.getById(id) @@ -343,6 +345,19 @@ class PageView( persistBitmapDebounced() } + fun updateDimensions(newWidth: Int, newHeight: Int) { + if (newWidth != viewWidth || newHeight != viewHeight) { + viewWidth = newWidth + viewHeight = newHeight + + // Recreate bitmap and canvas with new dimensions + windowedBitmap = Bitmap.createBitmap(viewWidth, viewHeight, Bitmap.Config.ARGB_8888) + windowedCanvas = Canvas(windowedBitmap) + drawArea(Rect(0, 0, SCREEN_WIDTH, SCREEN_HEIGHT)) + persistBitmapDebounced() + } + } + private fun persistBitmapDebounced() { coroutineScope.launch { saveTopic.emit(Unit) diff --git a/app/src/main/java/com/olup/notable/views/EditorView.kt b/app/src/main/java/com/olup/notable/views/EditorView.kt index cd4b8c50..c26063d5 100644 --- a/app/src/main/java/com/olup/notable/views/EditorView.kt +++ b/app/src/main/java/com/olup/notable/views/EditorView.kt @@ -52,6 +52,13 @@ fun EditorView( viewHeight = height ) } + // Dynamically update the page width when the Box constraints change + LaunchedEffect(width, height) { + if (page.width != width || page.viewHeight != height) { + page.updateDimensions(width,height) + DrawCanvas.refreshUi.emit(Unit) + } + } val editorState = remember { EditorState(bookId = _bookId, pageId = _pageId, pageView = page) } From 64648691cace1856d04eeeec5d3411bcd403e0ac Mon Sep 17 00:00:00 2001 From: Wiktor Wichrowski Date: Sat, 11 Jan 2025 23:40:18 +0100 Subject: [PATCH 080/246] small formating changes --- app/src/main/java/com/olup/notable/classes/PageView.kt | 6 ++---- app/src/main/java/com/olup/notable/utils/history.kt | 1 - 2 files changed, 2 insertions(+), 5 deletions(-) diff --git a/app/src/main/java/com/olup/notable/classes/PageView.kt b/app/src/main/java/com/olup/notable/classes/PageView.kt index 1bc81919..d7dfa550 100644 --- a/app/src/main/java/com/olup/notable/classes/PageView.kt +++ b/app/src/main/java/com/olup/notable/classes/PageView.kt @@ -270,12 +270,10 @@ class PageView( activeCanvas.clipRect(area); activeCanvas.drawColor(Color.BLACK) - val timeToBg = measureTimeMillis { - drawBg(activeCanvas, pageFromDb?.nativeTemplate ?: "blank", scroll) - } - Log.i(TAG, "Took $timeToBg to draw the BG") val timeToDraw = measureTimeMillis { + drawBg(activeCanvas, pageFromDb?.nativeTemplate ?: "blank", scroll) + // Trying to find what throws error when drawing quickly try { strokes.forEach { stroke -> diff --git a/app/src/main/java/com/olup/notable/utils/history.kt b/app/src/main/java/com/olup/notable/utils/history.kt index b82b4b9e..b87abe96 100644 --- a/app/src/main/java/com/olup/notable/utils/history.kt +++ b/app/src/main/java/com/olup/notable/utils/history.kt @@ -66,7 +66,6 @@ class History(coroutineScope: CoroutineScope, pageView: PageView) { if (zoneAffected != null) { pageView.drawArea(pageAreaToCanvasArea(zoneAffected, pageView.scroll)) //moved to refresh after drawing -// DrawCanvas.refreshUi.emit(Unit) DrawCanvas.refreshUi.emit(Unit) } else { SnackState.globalSnackFlow.emit(SnackConf( From 4d267020a829c9ebe3f97bb7d9f123633f7a438e Mon Sep 17 00:00:00 2001 From: Wiktor Wichrowski Date: Sun, 12 Jan 2025 00:07:12 +0100 Subject: [PATCH 081/246] only one vertical/horizontal swipe gesture at once --- .../components/EditorGestureReceiver.kt | 104 ++++++++---------- 1 file changed, 43 insertions(+), 61 deletions(-) diff --git a/app/src/main/java/com/olup/notable/components/EditorGestureReceiver.kt b/app/src/main/java/com/olup/notable/components/EditorGestureReceiver.kt index 4dce81fd..9706fd0c 100644 --- a/app/src/main/java/com/olup/notable/components/EditorGestureReceiver.kt +++ b/app/src/main/java/com/olup/notable/components/EditorGestureReceiver.kt @@ -167,73 +167,55 @@ fun EditorGestureReceiver( val verticalDrag = lastPosition.y - initialPosition.y val horizontalDrag = lastPosition.x - initialPosition.x + // Determine if the movement is primarily vertical or horizontal + val isVerticalMove = kotlin.math.abs(verticalDrag) > kotlin.math.abs(horizontalDrag) + val isHorizontalMove = kotlin.math.abs(horizontalDrag) > kotlin.math.abs(verticalDrag) - if (verticalDrag < -200) { - if (inputsCount == 1) { - coroutineScope.launch { - controlTower.onSingleFingerVerticalSwipe( - SimplePointF( - initialPosition.x, initialPosition.y - ), verticalDrag.toInt() - ) + // Handle vertical movements + if (isVerticalMove && inputsCount == 1) { + coroutineScope.launch { + when { + verticalDrag < -200 -> { + controlTower.onSingleFingerVerticalSwipe( + SimplePointF(initialPosition.x, initialPosition.y), + verticalDrag.toInt() + ) + } + verticalDrag > 200 -> { + controlTower.onSingleFingerVerticalSwipe( + SimplePointF(initialPosition.x, initialPosition.y), + verticalDrag.toInt() + ) + } } } } - if (verticalDrag > 200) { - if (inputsCount == 1) { - coroutineScope.launch { - controlTower.onSingleFingerVerticalSwipe( - SimplePointF( - initialPosition.x, initialPosition.y - ), verticalDrag.toInt() + + // Handle horizontal movements + if (isHorizontalMove) { + when { + horizontalDrag < -200 -> { + resolveGesture( + settings = appSettings, + default = if (inputsCount == 1) AppSettings.defaultSwipeLeftAction else AppSettings.defaultTwoFingerSwipeLeftAction, + override = if (inputsCount == 1) AppSettings::swipeLeftAction else AppSettings::twoFingerSwipeLeftAction, + state = state, + scope = coroutineScope, + previousPage = goToPreviousPage, + nextPage = goToNextPage, + ) + } + horizontalDrag > 200 -> { + resolveGesture( + settings = appSettings, + default = if (inputsCount == 1) AppSettings.defaultSwipeRightAction else AppSettings.defaultTwoFingerSwipeRightAction, + override = if (inputsCount == 1) AppSettings::swipeRightAction else AppSettings::twoFingerSwipeRightAction, + state = state, + scope = coroutineScope, + previousPage = goToPreviousPage, + nextPage = goToNextPage, ) } - } - } - if (horizontalDrag < -200) { - if (inputsCount == 1) { - resolveGesture( - settings = appSettings, - default = AppSettings.defaultSwipeLeftAction, - override = AppSettings::swipeLeftAction, - state = state, - scope = coroutineScope, - previousPage = goToPreviousPage, - nextPage = goToNextPage, - ) - } else if (inputsCount == 2) { - resolveGesture( - settings = appSettings, - default = AppSettings.defaultTwoFingerSwipeLeftAction, - override = AppSettings::twoFingerSwipeLeftAction, - state = state, - scope = coroutineScope, - previousPage = goToPreviousPage, - nextPage = goToNextPage, - ) - } - } - if (horizontalDrag > 200) { - if (inputsCount == 1) { - resolveGesture( - settings = appSettings, - default = AppSettings.defaultSwipeRightAction, - override = AppSettings::swipeRightAction, - state = state, - scope = coroutineScope, - previousPage = goToPreviousPage, - nextPage = goToNextPage, - ) - } else if (inputsCount == 2) { - resolveGesture( - settings = appSettings, - default = AppSettings.defaultTwoFingerSwipeRightAction, - override = AppSettings::twoFingerSwipeRightAction, - state = state, - scope = coroutineScope, - previousPage = goToPreviousPage, - nextPage = goToNextPage, - ) } } } From 62c7e79d21da65b6032cba8d80320ce625271b02 Mon Sep 17 00:00:00 2001 From: Wiktor Wichrowski Date: Sun, 12 Jan 2025 00:22:48 +0100 Subject: [PATCH 082/246] Trying to fix not visible strokes, and maybe instead of exporting to png, just allow to share? --- app/build.gradle | 2 +- .../com/olup/notable/classes/DrawCanvas.kt | 2 + .../java/com/olup/notable/classes/PageView.kt | 8 ++- .../main/java/com/olup/notable/utils/page.kt | 64 +++++++++++++++++-- 4 files changed, 67 insertions(+), 9 deletions(-) diff --git a/app/build.gradle b/app/build.gradle index 669ee409..ba0a7365 100644 --- a/app/build.gradle +++ b/app/build.gradle @@ -16,7 +16,7 @@ android { targetSdk 34 versionCode 11 - versionName '0.0.11g' + versionName '0.0.11h' testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner" vectorDrawables { diff --git a/app/src/main/java/com/olup/notable/classes/DrawCanvas.kt b/app/src/main/java/com/olup/notable/classes/DrawCanvas.kt index b5bee687..e647a4bd 100644 --- a/app/src/main/java/com/olup/notable/classes/DrawCanvas.kt +++ b/app/src/main/java/com/olup/notable/classes/DrawCanvas.kt @@ -348,6 +348,8 @@ class DrawCanvas( strokeHistoryBatch.clear() // give signal that commit was successful commitCompletion.complete(Unit) + //testing if it will help with undo hiding strokes. + drawCanvasToView() } } diff --git a/app/src/main/java/com/olup/notable/classes/PageView.kt b/app/src/main/java/com/olup/notable/classes/PageView.kt index d7dfa550..ca5ba604 100644 --- a/app/src/main/java/com/olup/notable/classes/PageView.kt +++ b/app/src/main/java/com/olup/notable/classes/PageView.kt @@ -273,7 +273,13 @@ class PageView( val timeToDraw = measureTimeMillis { drawBg(activeCanvas, pageFromDb?.nativeTemplate ?: "blank", scroll) - + // Draw the gray edge of the rectangle +// val redPaint = Paint().apply { +// color = Color.GRAY +// style = Paint.Style.STROKE +// strokeWidth = 1f // Adjust the width of the edge +// } +// activeCanvas.drawRect(area, redPaint) // Trying to find what throws error when drawing quickly try { strokes.forEach { stroke -> diff --git a/app/src/main/java/com/olup/notable/utils/page.kt b/app/src/main/java/com/olup/notable/utils/page.kt index caff2b65..36d44bb5 100644 --- a/app/src/main/java/com/olup/notable/utils/page.kt +++ b/app/src/main/java/com/olup/notable/utils/page.kt @@ -74,12 +74,14 @@ import androidx.compose.ui.graphics.asImageBitmap import androidx.compose.ui.layout.ContentScale import androidx.compose.ui.platform.LocalContext import androidx.core.content.ContentProviderCompat.requireContext +import androidx.core.content.ContextCompat +import androidx.core.content.FileProvider suspend fun exportBook(context: Context, bookId: String): String { val book = BookRepository(context).getById(bookId) ?: return "Book ID not found" val pages = PageRepository(context) val message = exportPdf(context, "Notebooks", book.title) { - book.pageIds.forEachIndexed {i, pageId -> writePage(context, i + 1, pages, pageId) } + book.pageIds.forEachIndexed { i, pageId -> writePage(context, i + 1, pages, pageId) } } copyBookPdfLinkForObsidian(context, bookId, book.title) return message @@ -98,13 +100,14 @@ fun copyBookPdfLinkForObsidian(context: Context, bookId: String, bookName: Strin suspend fun exportPage(context: Context, pageId: String): String { val pages = PageRepository(context) return exportPdf(context, "pages", "notable-page-${pageId}") { - writePage(context,1, pages, pageId) + writePage(context, 1, pages, pageId) } } fun exportPageToPng(context: Context, pageId: String): String { val pages = PageRepository(context) val (page, strokes) = pages.getWithStrokeById(pageId) + val (page2, images) = pages.getWithImageById(pageId) val strokeHeight = if (strokes.isEmpty()) 0 else strokes.maxOf(Stroke::bottom).toInt() + 50 val strokeWidth = if (strokes.isEmpty()) 0 else strokes.maxOf(Stroke::right).toInt() + 50 @@ -122,8 +125,56 @@ fun exportPageToPng(context: Context, pageId: String): String { for (stroke in strokes) { drawStroke(canvas, stroke, IntOffset(0, 0)) } + for (image in images) { + drawImage(context, canvas, image, IntOffset(0, 0)) + } //TODO Draw images + if (true) { + val cachePath = File(context.cacheDir, "images") + Log.i(TAG, cachePath.toString()) + cachePath.mkdirs() + try { + val stream = + FileOutputStream("$cachePath/share.png") + bitmap.compress( + Bitmap.CompressFormat.PNG, + 100, + stream + ) + stream.close() + } catch (e: IOException) { + e.printStackTrace() + } + + val bitmapFile = File(cachePath, "share.png") + val contentUri = FileProvider.getUriForFile( + context, + "com.olup.notable.provider", //(use your app signature + ".provider" ) + bitmapFile + ); + + val sendIntent = Intent().apply { + if (contentUri != null) { + action = Intent.ACTION_SEND + addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION) // temp permission for receiving app to read this file + putExtra(Intent.EXTRA_STREAM, contentUri); + type = "image/png"; + } + + context.grantUriPermission( + "android", + contentUri, + Intent.FLAG_GRANT_READ_URI_PERMISSION + ); + } + + ContextCompat.startActivity( + context, + Intent.createChooser(sendIntent, "Choose an app"), + null + ) + } return try { // Save the bitmap as PNG val filePath = Environment.getExternalStorageDirectory().toPath() / @@ -314,7 +365,7 @@ private fun PdfDocument.writePage(context: Context, number: Int, repo: PageRepos // todo do not rely on this anymore // I slightly modified it, should be better val contentHeight = strokeHeight.coerceAtLeast(SCREEN_HEIGHT) - val pageHeight = (contentHeight*scaleFactor).toInt() + val pageHeight = (contentHeight * scaleFactor).toInt() val contentWidth = strokeWidth.coerceAtLeast(SCREEN_WIDTH) @@ -325,16 +376,15 @@ private fun PdfDocument.writePage(context: Context, number: Int, repo: PageRepos val offsetX = (A4_WIDTH - (contentWidth * scaleFactor)) / 2 val offsetY = (A4_HEIGHT - (contentHeight * scaleFactor)) / 2 - documentPage.canvas.scale( scaleFactor,scaleFactor) + documentPage.canvas.scale(scaleFactor, scaleFactor) drawBg(documentPage.canvas, page.nativeTemplate, 0, scaleFactor) for (stroke in strokes) { drawStroke(documentPage.canvas, stroke, IntOffset(0, 0)) } - for(image in images) - { - drawImage(context,documentPage.canvas, image, IntOffset(0, 0)) + for (image in images) { + drawImage(context, documentPage.canvas, image, IntOffset(0, 0)) } finishPage(documentPage) From c46c1a6b2da9008ee9204b46d2faf66e18190246 Mon Sep 17 00:00:00 2001 From: Wiktor Wichrowski Date: Mon, 13 Jan 2025 01:20:28 +0100 Subject: [PATCH 083/246] code cleanup and reformating --- app/src/main/AndroidManifest.xml | 12 +-- .../olup/notable/FloatingEditorActivity.kt | 27 ++--- .../java/com/olup/notable/MainActivity.kt | 11 ++- .../com/olup/notable/classes/AppRepository.kt | 28 ++++-- .../com/olup/notable/classes/DrawCanvas.kt | 62 +++++++----- .../notable/classes/EditorControlTower.kt | 20 ++-- .../java/com/olup/notable/classes/PageView.kt | 63 ++++++------ .../java/com/olup/notable/classes/SnackBar.kt | 37 ++++--- .../com/olup/notable/components/BreadCrumb.kt | 21 ++-- .../components/EditorGestureReceiver.kt | 21 ++-- .../olup/notable/components/EditorSurface.kt | 11 ++- .../notable/components/EraserToolbarButton.kt | 7 +- .../notable/components/LineToolbarButton.kt | 12 +-- .../com/olup/notable/components/PageMenu.kt | 19 ++-- .../olup/notable/components/PagePreview.kt | 20 ++-- .../notable/components/PenToolbarButton.kt | 12 +-- .../com/olup/notable/components/QuickNav.kt | 24 +++-- .../notable/components/ScrollIndicator.kt | 15 ++- .../com/olup/notable/components/Select.kt | 15 ++- .../olup/notable/components/SelectorBitmap.kt | 19 ++-- .../com/olup/notable/components/StrokeMenu.kt | 22 +++-- .../com/olup/notable/components/Toolbar.kt | 52 ++++++---- .../olup/notable/components/ToolbarButton.kt | 7 +- .../olup/notable/components/ToolbarMenu.kt | 21 ++-- .../com/olup/notable/components/Topbar.kt | 9 +- .../datastore/EditorSettingCacheManager.kt | 19 ++-- app/src/main/java/com/olup/notable/db/Db.kt | 60 +++++------ .../main/java/com/olup/notable/db/Folder.kt | 19 ++-- .../main/java/com/olup/notable/db/Image.kt | 23 +++-- app/src/main/java/com/olup/notable/db/Kv.kt | 40 +++----- .../java/com/olup/notable/db/Migrations.kt | 1 - .../main/java/com/olup/notable/db/Notebook.kt | 27 +++-- app/src/main/java/com/olup/notable/db/Page.kt | 19 +++- .../main/java/com/olup/notable/db/Select.kt | 6 +- .../main/java/com/olup/notable/db/Stroke.kt | 18 +++- .../com/olup/notable/modals/AppSettings.kt | 32 ++++-- .../com/olup/notable/modals/FolderConfig.kt | 38 ++++--- .../com/olup/notable/modals/NotebookConfig.kt | 26 +++-- .../com/olup/notable/modals/PageSettings.kt | 20 +++- .../com/olup/notable/utils/EditorState.kt | 7 +- .../main/java/com/olup/notable/utils/draw.kt | 15 ++- .../java/com/olup/notable/utils/eraser.kt | 2 +- .../java/com/olup/notable/utils/history.kt | 13 +-- .../main/java/com/olup/notable/utils/page.kt | 99 ++++--------------- .../main/java/com/olup/notable/utils/pen.kt | 2 +- .../main/java/com/olup/notable/utils/utils.kt | 47 +++++---- .../com/olup/notable/utils/versionChecker.kt | 12 +-- .../java/com/olup/notable/views/EditorView.kt | 34 ++++--- .../olup/notable/views/FloatingEditorView.kt | 23 +++-- .../java/com/olup/notable/views/HomeView.kt | 39 +++++--- .../java/com/olup/notable/views/PagesView.kt | 24 ++++- .../java/com/olup/notable/views/Router.kt | 16 ++- .../ic_launcher.xml | 0 .../ic_launcher_round.xml | 0 readme.md | 16 +-- 55 files changed, 733 insertions(+), 531 deletions(-) rename app/src/main/res/{mipmap-anydpi-v26 => mipmap-anydpi}/ic_launcher.xml (100%) rename app/src/main/res/{mipmap-anydpi-v26 => mipmap-anydpi}/ic_launcher_round.xml (100%) diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml index d62e03bf..deafee70 100644 --- a/app/src/main/AndroidManifest.xml +++ b/app/src/main/AndroidManifest.xml @@ -2,7 +2,7 @@ - + - + @@ -29,12 +27,14 @@ + + - + diff --git a/app/src/main/java/com/olup/notable/MainActivity.kt b/app/src/main/java/com/olup/notable/MainActivity.kt index d9e4b57b..46957ede 100644 --- a/app/src/main/java/com/olup/notable/MainActivity.kt +++ b/app/src/main/java/com/olup/notable/MainActivity.kt @@ -1,15 +1,15 @@ package com.olup.notable -import android.content.pm.ActivityInfo import android.content.res.Configuration import android.os.Bundle -import io.shipbook.shipbooksdk.Log import androidx.activity.ComponentActivity import androidx.activity.compose.setContent import androidx.compose.animation.ExperimentalAnimationApi import androidx.compose.foundation.ExperimentalFoundationApi import androidx.compose.foundation.background -import androidx.compose.foundation.layout.* +import androidx.compose.foundation.layout.Box +import androidx.compose.foundation.layout.fillMaxWidth +import androidx.compose.foundation.layout.height import androidx.compose.runtime.CompositionLocalProvider import androidx.compose.ui.ExperimentalComposeUiApi import androidx.compose.ui.Modifier @@ -18,6 +18,7 @@ import androidx.compose.ui.unit.dp import androidx.lifecycle.lifecycleScope import com.olup.notable.ui.theme.InkaTheme import com.onyx.android.sdk.api.device.epd.EpdController +import io.shipbook.shipbooksdk.Log import io.shipbook.shipbooksdk.ShipBook import kotlinx.coroutines.launch @@ -37,7 +38,7 @@ class MainActivity : ComponentActivity() { ShipBook.start( this.application, "648adf9364c9825976c1d57e", "7c53dffa949e3b55e37ab04672138feb" - ); + ) Log.i(TAG, "Notable started") @@ -58,7 +59,7 @@ class MainActivity : ComponentActivity() { val intentData = intent.data?.lastPathSegment setContent { InkaTheme { - CompositionLocalProvider(SnackContext provides snackState) { + CompositionLocalProvider(LocalSnackContext provides snackState) { Box( Modifier .background(Color.White) diff --git a/app/src/main/java/com/olup/notable/classes/AppRepository.kt b/app/src/main/java/com/olup/notable/classes/AppRepository.kt index 0320c5d7..8d8effbb 100644 --- a/app/src/main/java/com/olup/notable/classes/AppRepository.kt +++ b/app/src/main/java/com/olup/notable/classes/AppRepository.kt @@ -1,8 +1,16 @@ package com.olup.notable import android.content.Context -import com.olup.notable.db.* -import java.util.* +import com.olup.notable.db.BookRepository +import com.olup.notable.db.FolderRepository +import com.olup.notable.db.ImageRepository +import com.olup.notable.db.KvProxy +import com.olup.notable.db.KvRepository +import com.olup.notable.db.Page +import com.olup.notable.db.PageRepository +import com.olup.notable.db.StrokeRepository +import java.util.Date +import java.util.UUID class AppRepository(context: Context) { @@ -46,8 +54,8 @@ class AppRepository(context: Context) { } fun duplicatePage(pageId: String) { - val pageWithStrokes = pageRepository.getWithStrokeById(pageId) ?: return - val pageWithImages = pageRepository.getWithImageById(pageId) ?: return + val pageWithStrokes = pageRepository.getWithStrokeById(pageId) + val pageWithImages = pageRepository.getWithImageById(pageId) val duplicatedPage = pageWithStrokes.page.copy( id = UUID.randomUUID().toString(), scroll = 0, @@ -71,20 +79,20 @@ class AppRepository(context: Context) { createdAt = Date() ) }) - if(pageWithStrokes.page.notebookId != null) { + if (pageWithStrokes.page.notebookId != null) { val book = bookRepository.getById(pageWithStrokes.page.notebookId) ?: return val pageIndex = book.pageIds.indexOf(pageWithStrokes.page.id) - if(pageIndex == -1) return + if (pageIndex == -1) return val pageIds = book.pageIds.toMutableList() - pageIds.add(pageIndex+1, duplicatedPage.id) + pageIds.add(pageIndex + 1, duplicatedPage.id) bookRepository.update(book.copy(pageIds = pageIds)) } - if(pageWithImages.page.notebookId != null) { + if (pageWithImages.page.notebookId != null) { val book = bookRepository.getById(pageWithImages.page.notebookId) ?: return val pageIndex = book.pageIds.indexOf(pageWithImages.page.id) - if(pageIndex == -1) return + if (pageIndex == -1) return val pageIds = book.pageIds.toMutableList() - pageIds.add(pageIndex+1, duplicatedPage.id) + pageIds.add(pageIndex + 1, duplicatedPage.id) bookRepository.update(book.copy(pageIds = pageIds)) } } diff --git a/app/src/main/java/com/olup/notable/classes/DrawCanvas.kt b/app/src/main/java/com/olup/notable/classes/DrawCanvas.kt index e647a4bd..069a5b4a 100644 --- a/app/src/main/java/com/olup/notable/classes/DrawCanvas.kt +++ b/app/src/main/java/com/olup/notable/classes/DrawCanvas.kt @@ -1,9 +1,11 @@ package com.olup.notable import android.content.Context -import android.graphics.* +import android.graphics.Bitmap +import android.graphics.Color +import android.graphics.Paint +import android.graphics.Rect import android.net.Uri -import io.shipbook.shipbooksdk.Log import android.view.SurfaceHolder import android.view.SurfaceView import androidx.compose.runtime.snapshotFlow @@ -20,6 +22,7 @@ import com.onyx.android.sdk.data.note.TouchPoint import com.onyx.android.sdk.pen.RawInputCallback import com.onyx.android.sdk.pen.TouchHelper import com.onyx.android.sdk.pen.data.TouchPointList +import io.shipbook.shipbooksdk.Log import kotlinx.coroutines.CompletableDeferred import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.Dispatchers @@ -40,7 +43,7 @@ var referencedSurfaceView: String = "" class DrawCanvas( - val _context: Context, + _context: Context, val coroutineScope: CoroutineScope, val state: EditorState, val page: PageView, @@ -172,7 +175,7 @@ class DrawCanvas( val surfaceCallback: SurfaceHolder.Callback = object : SurfaceHolder.Callback { override fun surfaceCreated(holder: SurfaceHolder) { - Log.i(TAG, "surface created ${holder}") + Log.i(TAG, "surface created $holder") // set up the drawing surface updateActiveSurface() // This is supposed to let the ui update while the old surface is being unmounted @@ -184,7 +187,7 @@ class DrawCanvas( override fun surfaceChanged( holder: SurfaceHolder, format: Int, width: Int, height: Int ) { - Log.i(TAG, "surface changed ${holder}") + Log.i(TAG, "surface changed $holder") drawCanvasToView() updatePenAndStroke() refreshUi() @@ -194,8 +197,8 @@ class DrawCanvas( Log.i( TAG, "surface destroyed ${ - this@DrawCanvas.hashCode().toString() - } - ref ${referencedSurfaceView}" + this@DrawCanvas.hashCode() + } - ref $referencedSurfaceView" ) holder.removeCallback(this) if (referencedSurfaceView == this@DrawCanvas.hashCode().toString()) { @@ -254,25 +257,33 @@ class DrawCanvas( } coroutineScope.launch { imageCoordinateToSelect.collect { point -> - if(point!=null) { - Log.i(TAG + "Observer", "position of image ${point}") + if (point != null) { + Log.i(TAG + "Observer", "position of image $point") // Query the database to find an image that coincides with the point val imageToSelect = withContext(Dispatchers.IO) { - ImageRepository(context).getImageAtPoint(point.first, point.second+page.scroll, page.id) + ImageRepository(context).getImageAtPoint( + point.first, + point.second + page.scroll, + page.id + ) } imageCoordinateToSelect.value = null if (imageToSelect != null) { - selectImage( coroutineScope, page,state, imageToSelect) - SnackState.globalSnackFlow.emit(SnackConf( - text = "Image selected!", - duration = 3000, - )) + selectImage(coroutineScope, page, state, imageToSelect) + SnackState.globalSnackFlow.emit( + SnackConf( + text = "Image selected!", + duration = 3000, + ) + ) } else { - SnackState.globalSnackFlow.emit(SnackConf( - text = "There is no image at this position", - duration = 3000, - )) + SnackState.globalSnackFlow.emit( + SnackConf( + text = "There is no image at this position", + duration = 3000, + ) + ) } } } @@ -373,7 +384,7 @@ class DrawCanvas( val softwareBitmap = imageBitmap?.asAndroidBitmap()?.copy(Bitmap.Config.ARGB_8888, true) if (softwareBitmap != null) { - DrawCanvas.addImageByUri.value = null + addImageByUri.value = null // Get the image dimensions val imageWidth = softwareBitmap.width @@ -396,7 +407,7 @@ class DrawCanvas( drawImage( context, page.windowedCanvas, imageToSave, IntOffset(0, -page.scroll) ) - selectImage(coroutineScope, page,state, imageToSave) + selectImage(coroutineScope, page, state, imageToSave) } else { // Handle cases where the bitmap could not be created Log.e("ImageProcessing", "Failed to create software bitmap from URI.") @@ -404,10 +415,9 @@ class DrawCanvas( } - fun drawCanvasToView() { val canvas = this.holder.lockCanvas() ?: return - canvas.drawBitmap(page.windowedBitmap, 0f, 0f, Paint()); + canvas.drawBitmap(page.windowedBitmap, 0f, 0f, Paint()) val timeToDraw = measureTimeMillis { if (getActualState().mode == Mode.Select) { // render selection @@ -427,7 +437,7 @@ class DrawCanvas( this.holder.unlockCanvasAndPost(canvas) } - fun updateIsDrawing() { + private fun updateIsDrawing() { Log.i(TAG, "Update is drawing : ${state.isDrawing}") if (state.isDrawing) { touchHelper.setRawDrawingEnabled(true) @@ -475,11 +485,11 @@ class DrawCanvas( touchHelper.setLimitRect( mutableListOf( - android.graphics.Rect( + Rect( 0, 0, this.width, this.height ) ) - ).setExcludeRect(listOf(android.graphics.Rect(0, 0, this.width, exclusionHeight))) + ).setExcludeRect(listOf(Rect(0, 0, this.width, exclusionHeight))) .openRawDrawing() touchHelper.setRawDrawingEnabled(true) diff --git a/app/src/main/java/com/olup/notable/classes/EditorControlTower.kt b/app/src/main/java/com/olup/notable/classes/EditorControlTower.kt index e305b4e0..7ffedb04 100644 --- a/app/src/main/java/com/olup/notable/classes/EditorControlTower.kt +++ b/app/src/main/java/com/olup/notable/classes/EditorControlTower.kt @@ -6,15 +6,16 @@ import android.graphics.Rect import android.util.Log import androidx.compose.ui.unit.IntOffset import androidx.compose.ui.unit.toOffset -import com.olup.notable.db.Image -import com.olup.notable.db.selectImage import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.launch import java.util.Date import java.util.UUID class EditorControlTower( - val scope: CoroutineScope, val page: PageView, val history: History, val state: EditorState + private val scope: CoroutineScope, + val page: PageView, + private val history: History, + val state: EditorState ) { fun onSingleFingerVerticalSwipe(startPosition: SimplePointF, delta: Int) { @@ -32,9 +33,9 @@ class EditorControlTower( } - fun onOpenPageCut(offset: Int) { + private fun onOpenPageCut(offset: Int) { if (offset < 0) return - var cutLine = state.selectionState.firstPageCut!! + val cutLine = state.selectionState.firstPageCut!! val (_, previousStrokes) = divideStrokesFromCut(page.strokes, cutLine) @@ -65,8 +66,8 @@ class EditorControlTower( ) } - fun onPageScroll(delta: Int) { - page!!.updateScroll(delta) + private fun onPageScroll(delta: Int) { + page.updateScroll(delta) } @@ -172,14 +173,15 @@ class EditorControlTower( state.selectionState.selectionRect = pageAreaToCanvasArea(pageBounds, page.scroll) state.selectionState.selectionDisplaceOffset = - state.selectionState.selectionDisplaceOffset?.let { it - sizeChange } ?: IntOffset.Zero + state.selectionState.selectionDisplaceOffset?.let { it - sizeChange } + ?: IntOffset.Zero val selectedBitmap = Bitmap.createBitmap( pageBounds.width(), pageBounds.height(), Bitmap.Config.ARGB_8888 ) val selectedCanvas = Canvas(selectedBitmap) - selectedImages.forEach() { + selectedImages.forEach { drawImage( page.context, selectedCanvas, diff --git a/app/src/main/java/com/olup/notable/classes/PageView.kt b/app/src/main/java/com/olup/notable/classes/PageView.kt index ca5ba604..1d767e36 100644 --- a/app/src/main/java/com/olup/notable/classes/PageView.kt +++ b/app/src/main/java/com/olup/notable/classes/PageView.kt @@ -1,16 +1,22 @@ package com.olup.notable + import android.content.Context -import android.graphics.* +import android.graphics.Bitmap +import android.graphics.BitmapFactory +import android.graphics.Canvas +import android.graphics.Color +import android.graphics.Paint +import android.graphics.Rect import androidx.compose.runtime.getValue -import androidx.compose.runtime.mutableStateOf +import androidx.compose.runtime.mutableIntStateOf import androidx.compose.runtime.setValue import androidx.compose.ui.unit.IntOffset import androidx.core.graphics.toRect import com.olup.notable.db.AppDatabase +import com.olup.notable.db.Image import com.olup.notable.db.Page import com.olup.notable.db.Stroke -import com.olup.notable.db.Image import io.shipbook.shipbooksdk.Log import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.flow.MutableSharedFlow @@ -21,14 +27,11 @@ import java.io.File import java.io.FileOutputStream import java.nio.file.Files import kotlin.io.path.Path +import kotlin.math.abs import kotlin.math.max import kotlin.system.measureTimeMillis -import android.graphics.Bitmap -import android.graphics.BitmapFactory - - class PageView( val context: Context, val coroutineScope: CoroutineScope, @@ -41,22 +44,24 @@ class PageView( var windowedBitmap = Bitmap.createBitmap(viewWidth, viewHeight, Bitmap.Config.ARGB_8888) var windowedCanvas = Canvas(windowedBitmap) var strokes = listOf() - var strokesById: HashMap = hashMapOf() + private var strokesById: HashMap = hashMapOf() var images = listOf() - var imagesById: HashMap = hashMapOf() - var scroll by mutableStateOf(0) // is observed by ui - val saveTopic = MutableSharedFlow() + private var imagesById: HashMap = hashMapOf() + var scroll by mutableIntStateOf(0) // is observed by ui + private val saveTopic = MutableSharedFlow() - var height by mutableStateOf(viewHeight) // is observed by ui + var height by mutableIntStateOf(viewHeight) // is observed by ui var pageFromDb = AppRepository(context).pageRepository.getById(id) - var dbStrokes = AppDatabase.getDatabase(context)?.strokeDao()!! - var dbImages = AppDatabase.getDatabase(context)?.ImageDao()!! + private var dbStrokes = AppDatabase.getDatabase(context).strokeDao() + private var dbImages = AppDatabase.getDatabase(context).ImageDao() init { coroutineScope.launch { + // TODO: + // Warning:(63, 23) This declaration is in a preview state and can be changed in a backwards-incompatible manner with a best-effort migration. Its usage should be marked with '@kotlinx.coroutines.FlowPreview' or '@OptIn(kotlinx.coroutines.FlowPreview::class)' if you accept the drawback of relying on preview API saveTopic.debounce(1000).collect { launch { persistBitmap() } launch { persistBitmapThumbnail() } @@ -70,13 +75,13 @@ class PageView( initFromPersistLayer(isCached) } - fun indexStrokes() { + private fun indexStrokes() { coroutineScope.launch { strokesById = hashMapOf(*strokes.map { s -> s.id to s }.toTypedArray()) } } - fun indexImages() { + private fun indexImages() { coroutineScope.launch { imagesById = hashMapOf(*images.map { img -> img.id to img }.toTypedArray()) } @@ -150,7 +155,7 @@ class PageView( fun addImage(imageToAdd: Image) { images += listOf(imageToAdd) val bottomPlusPadding = imageToAdd.x + imageToAdd.height + 50 - if (bottomPlusPadding > height) height = bottomPlusPadding.toInt() + if (bottomPlusPadding > height) height = bottomPlusPadding saveImagesToPersistLayer(listOf(imageToAdd)) indexImages() @@ -162,7 +167,7 @@ class PageView( images += imageToAdd imageToAdd.forEach { val bottomPlusPadding = it.x + it.height + 50 - if (bottomPlusPadding > height) height = bottomPlusPadding.toInt() + if (bottomPlusPadding > height) height = bottomPlusPadding } saveImagesToPersistLayer(imageToAdd) indexImages() @@ -184,12 +189,12 @@ class PageView( } - fun computeHeight() { + private fun computeHeight() { if (strokes.isEmpty()) { height = viewHeight return } - val maxStrokeBottom = strokes.maxOf { it.bottom }.plus(50) ?: 0 + val maxStrokeBottom = strokes.maxOf { it.bottom }.plus(50) height = max(maxStrokeBottom.toInt(), viewHeight) } @@ -197,7 +202,7 @@ class PageView( if (strokes.isEmpty()) { return viewWidth } - val maxStrokeRight = strokes.maxOf { it.right }.plus(50) ?: 0 + val maxStrokeRight = strokes.maxOf { it.right }.plus(50) return max(maxStrokeRight.toInt(), viewWidth) } @@ -211,11 +216,11 @@ class PageView( private fun loadBitmap(): Boolean { val imgFile = File(context.filesDir, "pages/previews/full/$id") - var imgBitmap: Bitmap? = null + val imgBitmap: Bitmap? if (imgFile.exists()) { imgBitmap = BitmapFactory.decodeFile(imgFile.absolutePath) if (imgBitmap != null) { - windowedCanvas.drawBitmap(imgBitmap, 0f, 0f, Paint()); + windowedCanvas.drawBitmap(imgBitmap, 0f, 0f, Paint()) Log.i(TAG, "Page rendered from cache") // let's control that the last preview fits the present orientation. Otherwise we'll ask for a redraw. if (imgBitmap.height == windowedCanvas.height && imgBitmap.width == windowedCanvas.width) { @@ -236,7 +241,7 @@ class PageView( val file = File(context.filesDir, "pages/previews/full/$id") Files.createDirectories(Path(file.absolutePath).parent) val os = BufferedOutputStream(FileOutputStream(file)) - windowedBitmap.compress(Bitmap.CompressFormat.PNG, 100, os); + windowedBitmap.compress(Bitmap.CompressFormat.PNG, 100, os) os.close() } @@ -246,7 +251,7 @@ class PageView( val os = BufferedOutputStream(FileOutputStream(file)) val ratio = windowedBitmap.height.toFloat() / windowedBitmap.width.toFloat() Bitmap.createScaledBitmap(windowedBitmap, 500, (500 * ratio).toInt(), false) - .compress(Bitmap.CompressFormat.JPEG, 80, os); + .compress(Bitmap.CompressFormat.JPEG, 80, os) os.close() } @@ -266,8 +271,8 @@ class PageView( area.bottom + scroll ) - activeCanvas.save(); - activeCanvas.clipRect(area); + activeCanvas.save() + activeCanvas.clipRect(area) activeCanvas.drawColor(Color.BLACK) @@ -308,7 +313,7 @@ class PageView( } Log.i(TAG, "Drew area in ${timeToDraw}ms") - activeCanvas.restore(); + activeCanvas.restore() } fun updateScroll(_delta: Int) { @@ -332,7 +337,7 @@ class PageView( 0, canvasOffset, windowedCanvas.width, - canvasOffset + Math.abs(delta) + canvasOffset + abs(delta) ), ) diff --git a/app/src/main/java/com/olup/notable/classes/SnackBar.kt b/app/src/main/java/com/olup/notable/classes/SnackBar.kt index a0251079..247c2f8c 100644 --- a/app/src/main/java/com/olup/notable/classes/SnackBar.kt +++ b/app/src/main/java/com/olup/notable/classes/SnackBar.kt @@ -1,10 +1,21 @@ package com.olup.notable -import io.shipbook.shipbooksdk.Log import androidx.compose.foundation.background -import androidx.compose.foundation.layout.* +import androidx.compose.foundation.layout.Arrangement +import androidx.compose.foundation.layout.Box +import androidx.compose.foundation.layout.Column +import androidx.compose.foundation.layout.Row +import androidx.compose.foundation.layout.Spacer +import androidx.compose.foundation.layout.fillMaxHeight +import androidx.compose.foundation.layout.fillMaxWidth +import androidx.compose.foundation.layout.padding +import androidx.compose.foundation.layout.width import androidx.compose.material.Text -import androidx.compose.runtime.* +import androidx.compose.runtime.Composable +import androidx.compose.runtime.LaunchedEffect +import androidx.compose.runtime.mutableStateListOf +import androidx.compose.runtime.remember +import androidx.compose.runtime.staticCompositionLocalOf import androidx.compose.ui.Alignment import androidx.compose.ui.Modifier import androidx.compose.ui.graphics.Color @@ -13,12 +24,10 @@ import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.delay import kotlinx.coroutines.flow.MutableSharedFlow -import kotlinx.coroutines.flow.collect -import kotlinx.coroutines.flow.onEach import kotlinx.coroutines.launch import java.util.UUID -val SnackContext = staticCompositionLocalOf { SnackState() } +val LocalSnackContext = staticCompositionLocalOf { SnackState() } data class SnackConf( val id: String = UUID.randomUUID().toString(), @@ -53,7 +62,7 @@ class SnackState { } } - suspend fun removeSnack(id: String) { + private suspend fun removeSnack(id: String) { cancelSnackFlow.emit(id) } } @@ -75,11 +84,11 @@ fun SnackBar(state: SnackState) { launch { state.snackFlow.collect { snack -> if (snack != null) { - getSnacks().add(snack!!) - if (snack!!.duration != null) { + getSnacks().add(snack) + if (snack.duration != null) { launch { - delay(snack!!.duration!!.toLong()) - getSnacks().removeIf { it.id == snack!!.id } + delay(snack.duration.toLong()) + getSnacks().removeIf { it.id == snack.id } } } } @@ -106,7 +115,7 @@ fun SnackBar(state: SnackState) { contentAlignment = Alignment.Center ) { if (it.text != null) { - Row() { + Row { Text(text = it.text, color = Color.White) if (it.actions != null && it.actions.isEmpty().not()) { it.actions.map { @@ -119,8 +128,8 @@ fun SnackBar(state: SnackState) { } } - } else if (it.content != null) { - it.content!!() + } else it.content?.let { content -> + content() } } } diff --git a/app/src/main/java/com/olup/notable/components/BreadCrumb.kt b/app/src/main/java/com/olup/notable/components/BreadCrumb.kt index 0a2db724..89a60f4d 100644 --- a/app/src/main/java/com/olup/notable/components/BreadCrumb.kt +++ b/app/src/main/java/com/olup/notable/components/BreadCrumb.kt @@ -1,6 +1,5 @@ package com.olup.notable -import androidx.compose.foundation.layout.Column import androidx.compose.foundation.layout.Row import androidx.compose.material.Icon import androidx.compose.material.Text @@ -14,26 +13,34 @@ import compose.icons.FeatherIcons import compose.icons.feathericons.ChevronRight @Composable -fun BreadCrumb(folderId : String? = null, onSelectFolderId:(String?)->Unit){ +fun BreadCrumb(folderId: String? = null, onSelectFolderId: (String?) -> Unit) { val context = LocalContext.current - fun getFolderList (folderId : String) : List{ + fun getFolderList(folderId: String): List { var folderList = listOf(FolderRepository(context).get(folderId)) val parentId = folderList.first().parentFolderId if (parentId != null) { + // '+=' on a read-only list creates a new list under the hood + // do we really want to copy that? folderList += getFolderList(parentId) } return folderList } - Row() { - Text(text = "Library", textDecoration = TextDecoration.Underline,modifier = Modifier.noRippleClickable { onSelectFolderId(null) }) + Row { + Text( + text = "Library", + textDecoration = TextDecoration.Underline, + modifier = Modifier.noRippleClickable { onSelectFolderId(null) }) if (folderId != null) { val folders = getFolderList(folderId).reversed() - folders.map{ f-> + folders.map { f -> Icon(imageVector = FeatherIcons.ChevronRight, contentDescription = "") - Text(text = f.title, textDecoration = TextDecoration.Underline, modifier = Modifier.noRippleClickable { onSelectFolderId(f.id) }) + Text( + text = f.title, + textDecoration = TextDecoration.Underline, + modifier = Modifier.noRippleClickable { onSelectFolderId(f.id) }) } } } diff --git a/app/src/main/java/com/olup/notable/components/EditorGestureReceiver.kt b/app/src/main/java/com/olup/notable/components/EditorGestureReceiver.kt index 9706fd0c..e2c0f998 100644 --- a/app/src/main/java/com/olup/notable/components/EditorGestureReceiver.kt +++ b/app/src/main/java/com/olup/notable/components/EditorGestureReceiver.kt @@ -1,6 +1,5 @@ package com.olup.notable -import io.shipbook.shipbooksdk.Log import androidx.compose.foundation.gestures.awaitEachGesture import androidx.compose.foundation.gestures.awaitFirstDown import androidx.compose.foundation.layout.Box @@ -17,6 +16,7 @@ import androidx.compose.ui.input.pointer.PointerId import androidx.compose.ui.input.pointer.PointerType import androidx.compose.ui.input.pointer.pointerInput import androidx.compose.ui.platform.LocalContext +import io.shipbook.shipbooksdk.Log import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.launch @@ -79,7 +79,7 @@ fun EditorGestureReceiver( val eventReference = fingerChange.find { it.id.value == inputId.value } ?: break inputsCount = fingerChange.size - if (fingerChange.any { !it.pressed }){ + if (fingerChange.any { !it.pressed }) { lastTimestamp = System.currentTimeMillis() break } @@ -88,7 +88,7 @@ fun EditorGestureReceiver( lastTimestamp = System.currentTimeMillis() val elapsedTime = lastTimestamp - initialTimestamp - if (elapsedTime >= 300 && inputsCount == 1 && !holdConsumed) { + if (elapsedTime >= 300 && inputsCount == 1 && !holdConsumed) { Log.i(TAG, "Held for ${elapsedTime}ms") if (calculateTotalDelta(initialPositions, lastPositions) < 15f) resolveGesture( @@ -110,7 +110,10 @@ fun EditorGestureReceiver( // Calculate the total delta (movement distance) for all pointers val totalDelta = calculateTotalDelta(initialPositions, lastPositions) val gestureDuration = lastTimestamp - initialTimestamp - Log.i(TAG, "Leaving gesture. totalDelta: ${totalDelta}, gestureDuration: ${gestureDuration} ") + Log.i( + TAG, + "Leaving gesture. totalDelta: ${totalDelta}, gestureDuration: $gestureDuration " + ) //Tolerance of 15 f for movement of fingers if (totalDelta < 15f && gestureDuration < 150) { // check how many fingers touched @@ -168,8 +171,10 @@ fun EditorGestureReceiver( val horizontalDrag = lastPosition.x - initialPosition.x // Determine if the movement is primarily vertical or horizontal - val isVerticalMove = kotlin.math.abs(verticalDrag) > kotlin.math.abs(horizontalDrag) - val isHorizontalMove = kotlin.math.abs(horizontalDrag) > kotlin.math.abs(verticalDrag) + val isVerticalMove = + kotlin.math.abs(verticalDrag) > kotlin.math.abs(horizontalDrag) + val isHorizontalMove = + kotlin.math.abs(horizontalDrag) > kotlin.math.abs(verticalDrag) // Handle vertical movements if (isVerticalMove && inputsCount == 1) { @@ -181,6 +186,7 @@ fun EditorGestureReceiver( verticalDrag.toInt() ) } + verticalDrag > 200 -> { controlTower.onSingleFingerVerticalSwipe( SimplePointF(initialPosition.x, initialPosition.y), @@ -205,6 +211,7 @@ fun EditorGestureReceiver( nextPage = goToNextPage, ) } + horizontalDrag > 200 -> { resolveGesture( settings = appSettings, @@ -268,7 +275,7 @@ private fun resolveGesture( AppSettings.GestureAction.Select -> { Log.i(TAG, "select") scope.launch { - DrawCanvas.imageCoordinateToSelect.emit(Pair(x.toInt(), y.toInt())) + DrawCanvas.imageCoordinateToSelect.emit(Pair(x.toInt(), y.toInt())) } } } diff --git a/app/src/main/java/com/olup/notable/components/EditorSurface.kt b/app/src/main/java/com/olup/notable/components/EditorSurface.kt index 3a7c2a4d..389518f0 100644 --- a/app/src/main/java/com/olup/notable/components/EditorSurface.kt +++ b/app/src/main/java/com/olup/notable/components/EditorSurface.kt @@ -1,20 +1,21 @@ package com.olup.notable -import io.shipbook.shipbooksdk.Log import androidx.compose.foundation.layout.Box import androidx.compose.foundation.layout.fillMaxHeight import androidx.compose.foundation.layout.fillMaxWidth -import androidx.compose.runtime.* +import androidx.compose.runtime.Composable +import androidx.compose.runtime.rememberCoroutineScope import androidx.compose.ui.ExperimentalComposeUiApi import androidx.compose.ui.Modifier import androidx.compose.ui.viewinterop.AndroidView +import io.shipbook.shipbooksdk.Log @Composable @ExperimentalComposeUiApi fun EditorSurface( - state: EditorState, page : PageView, history: History + state: EditorState, page: PageView, history: History ) { - val couroutineScope = rememberCoroutineScope() + val coroutineScope = rememberCoroutineScope() Log.i(TAG, "recompose surface") Box( @@ -24,7 +25,7 @@ fun EditorSurface( ) { AndroidView(factory = { ctx -> - DrawCanvas(ctx, couroutineScope, state, page, history ).apply { + DrawCanvas(ctx, coroutineScope, state, page, history).apply { init() registerObservers() } diff --git a/app/src/main/java/com/olup/notable/components/EraserToolbarButton.kt b/app/src/main/java/com/olup/notable/components/EraserToolbarButton.kt index 2f8555eb..d1dd128d 100644 --- a/app/src/main/java/com/olup/notable/components/EraserToolbarButton.kt +++ b/app/src/main/java/com/olup/notable/components/EraserToolbarButton.kt @@ -6,7 +6,12 @@ import androidx.compose.foundation.layout.Box import androidx.compose.foundation.layout.IntrinsicSize import androidx.compose.foundation.layout.Row import androidx.compose.foundation.layout.height -import androidx.compose.runtime.* +import androidx.compose.runtime.Composable +import androidx.compose.runtime.LaunchedEffect +import androidx.compose.runtime.getValue +import androidx.compose.runtime.mutableStateOf +import androidx.compose.runtime.remember +import androidx.compose.runtime.setValue import androidx.compose.ui.Alignment import androidx.compose.ui.Modifier import androidx.compose.ui.graphics.Color diff --git a/app/src/main/java/com/olup/notable/components/LineToolbarButton.kt b/app/src/main/java/com/olup/notable/components/LineToolbarButton.kt index 431ebf13..ff54d095 100644 --- a/app/src/main/java/com/olup/notable/components/LineToolbarButton.kt +++ b/app/src/main/java/com/olup/notable/components/LineToolbarButton.kt @@ -1,13 +1,13 @@ package com.olup.notable import androidx.compose.foundation.layout.Box -import androidx.compose.foundation.background -import androidx.compose.foundation.shape.CircleShape -import androidx.compose.foundation.clickable -import androidx.compose.runtime.* +import androidx.compose.runtime.Composable +import androidx.compose.runtime.LaunchedEffect +import androidx.compose.runtime.getValue +import androidx.compose.runtime.mutableStateOf +import androidx.compose.runtime.remember +import androidx.compose.runtime.setValue import androidx.compose.ui.graphics.Color -import androidx.compose.ui.Modifier -import androidx.compose.ui.graphics.RectangleShape @Composable fun LineToolbarButton( diff --git a/app/src/main/java/com/olup/notable/components/PageMenu.kt b/app/src/main/java/com/olup/notable/components/PageMenu.kt index 54b0808a..aafb7c98 100644 --- a/app/src/main/java/com/olup/notable/components/PageMenu.kt +++ b/app/src/main/java/com/olup/notable/components/PageMenu.kt @@ -2,7 +2,11 @@ package com.olup.notable import androidx.compose.foundation.background import androidx.compose.foundation.border -import androidx.compose.foundation.layout.* +import androidx.compose.foundation.layout.Box +import androidx.compose.foundation.layout.Column +import androidx.compose.foundation.layout.IntrinsicSize +import androidx.compose.foundation.layout.padding +import androidx.compose.foundation.layout.width import androidx.compose.material.Text import androidx.compose.runtime.Composable import androidx.compose.ui.Alignment @@ -13,7 +17,6 @@ import androidx.compose.ui.platform.LocalContext import androidx.compose.ui.unit.dp import androidx.compose.ui.window.Popup import androidx.compose.ui.window.PopupProperties -import com.olup.notable.AppRepository import com.olup.notable.db.Page @@ -43,7 +46,7 @@ fun PageMenu( Modifier .padding(10.dp) .noRippleClickable { - appRepository.bookRepository.changeePageIndex( + appRepository.bookRepository.changePageIndex( notebookId, pageId, index - 1 @@ -57,7 +60,7 @@ fun PageMenu( Modifier .padding(10.dp) .noRippleClickable { - appRepository.bookRepository.changeePageIndex( + appRepository.bookRepository.changePageIndex( notebookId, pageId, index + 1 @@ -69,8 +72,12 @@ fun PageMenu( Modifier .padding(10.dp) .noRippleClickable { - val book = appRepository.bookRepository.getById(notebookId) ?: return@noRippleClickable - val page = Page(notebookId = notebookId, nativeTemplate = book.defaultNativeTemplate) + val book = appRepository.bookRepository.getById(notebookId) + ?: return@noRippleClickable + val page = Page( + notebookId = notebookId, + nativeTemplate = book.defaultNativeTemplate + ) appRepository.pageRepository.create(page) appRepository.bookRepository.addPage(notebookId, page.id, index + 1) }) { diff --git a/app/src/main/java/com/olup/notable/components/PagePreview.kt b/app/src/main/java/com/olup/notable/components/PagePreview.kt index 30cfb2be..bdf7f681 100644 --- a/app/src/main/java/com/olup/notable/components/PagePreview.kt +++ b/app/src/main/java/com/olup/notable/components/PagePreview.kt @@ -4,23 +4,19 @@ import android.graphics.Bitmap import android.graphics.BitmapFactory import androidx.compose.foundation.Image import androidx.compose.foundation.background -import androidx.compose.foundation.layout.padding -import androidx.compose.material.Text import androidx.compose.runtime.Composable import androidx.compose.runtime.remember import androidx.compose.ui.Modifier -import androidx.compose.ui.composed import androidx.compose.ui.graphics.Color import androidx.compose.ui.layout.ContentScale import androidx.compose.ui.platform.LocalContext -import androidx.compose.ui.unit.dp import coil.compose.rememberAsyncImagePainter import java.io.File @Composable -fun PagePreview(modifier: Modifier, pageId: String){ +fun PagePreview(modifier: Modifier, pageId: String) { val context = LocalContext.current - val imgFile = remember() { + val imgFile = remember { File(context.filesDir, "pages/previews/thumbs/$pageId") } @@ -36,11 +32,11 @@ fun PagePreview(modifier: Modifier, pageId: String){ Modifier.padding(10.dp) )) }else {*/ - Image( - painter = rememberAsyncImagePainter(model = imgBitmap), - contentDescription = "Image", - contentScale = ContentScale.FillWidth, - modifier = modifier.then(Modifier.background(Color.LightGray)) - ) + Image( + painter = rememberAsyncImagePainter(model = imgBitmap), + contentDescription = "Image", + contentScale = ContentScale.FillWidth, + modifier = modifier.then(Modifier.background(Color.LightGray)) + ) //} } \ No newline at end of file diff --git a/app/src/main/java/com/olup/notable/components/PenToolbarButton.kt b/app/src/main/java/com/olup/notable/components/PenToolbarButton.kt index 6810dfa6..bb91a39d 100644 --- a/app/src/main/java/com/olup/notable/components/PenToolbarButton.kt +++ b/app/src/main/java/com/olup/notable/components/PenToolbarButton.kt @@ -1,13 +1,13 @@ package com.olup.notable import androidx.compose.foundation.layout.Box -import androidx.compose.foundation.background -import androidx.compose.foundation.shape.CircleShape -import androidx.compose.foundation.clickable -import androidx.compose.runtime.* +import androidx.compose.runtime.Composable +import androidx.compose.runtime.LaunchedEffect +import androidx.compose.runtime.getValue +import androidx.compose.runtime.mutableStateOf +import androidx.compose.runtime.remember +import androidx.compose.runtime.setValue import androidx.compose.ui.graphics.Color -import androidx.compose.ui.Modifier -import androidx.compose.ui.graphics.RectangleShape @Composable fun PenToolbarButton( diff --git a/app/src/main/java/com/olup/notable/components/QuickNav.kt b/app/src/main/java/com/olup/notable/components/QuickNav.kt index d330ed06..972a8647 100644 --- a/app/src/main/java/com/olup/notable/components/QuickNav.kt +++ b/app/src/main/java/com/olup/notable/components/QuickNav.kt @@ -6,7 +6,17 @@ import androidx.compose.foundation.border import androidx.compose.foundation.gestures.Orientation import androidx.compose.foundation.gestures.draggable import androidx.compose.foundation.gestures.rememberDraggableState -import androidx.compose.foundation.layout.* +import androidx.compose.foundation.layout.Arrangement +import androidx.compose.foundation.layout.Box +import androidx.compose.foundation.layout.Column +import androidx.compose.foundation.layout.Row +import androidx.compose.foundation.layout.Spacer +import androidx.compose.foundation.layout.aspectRatio +import androidx.compose.foundation.layout.fillMaxHeight +import androidx.compose.foundation.layout.fillMaxWidth +import androidx.compose.foundation.layout.height +import androidx.compose.foundation.layout.padding +import androidx.compose.foundation.layout.width import androidx.compose.foundation.lazy.grid.GridCells import androidx.compose.foundation.lazy.grid.LazyVerticalGrid import androidx.compose.foundation.lazy.grid.items @@ -19,7 +29,6 @@ import androidx.compose.ui.graphics.Color import androidx.compose.ui.platform.LocalContext import androidx.compose.ui.unit.dp import androidx.navigation.NavController -import com.olup.notable.AppRepository import compose.icons.FeatherIcons import compose.icons.feathericons.Home import compose.icons.feathericons.Plus @@ -74,9 +83,10 @@ fun QuickNav(navController: NavController, onClose: () -> Unit) { ToolbarButton( imageVector = FeatherIcons.Home, onSelect = { - val parentFolder = if(pageId != null) appRepository.pageRepository.getById(pageId)?.parentFolderId else null + val parentFolder = + if (pageId != null) appRepository.pageRepository.getById(pageId)?.parentFolderId else null navController.navigate( - route = if(parentFolder != null) "library?folderId=${parentFolder}" else "library" + route = if (parentFolder != null) "library?folderId=${parentFolder}" else "library" ) onClose() } @@ -97,10 +107,10 @@ fun QuickNav(navController: NavController, onClose: () -> Unit) { ) } } - - if (!pages.isEmpty()) Spacer(modifier = Modifier.height(10.dp)) - Row() { + if (pages.isNotEmpty()) Spacer(modifier = Modifier.height(10.dp)) + + Row { LazyVerticalGrid( modifier = Modifier.fillMaxWidth(), columns = GridCells.Adaptive(minSize = 80.dp), diff --git a/app/src/main/java/com/olup/notable/components/ScrollIndicator.kt b/app/src/main/java/com/olup/notable/components/ScrollIndicator.kt index e313ecb3..6b93d428 100644 --- a/app/src/main/java/com/olup/notable/components/ScrollIndicator.kt +++ b/app/src/main/java/com/olup/notable/components/ScrollIndicator.kt @@ -2,7 +2,13 @@ package com.olup.notable import android.content.Context import androidx.compose.foundation.background -import androidx.compose.foundation.layout.* +import androidx.compose.foundation.layout.Box +import androidx.compose.foundation.layout.BoxWithConstraints +import androidx.compose.foundation.layout.fillMaxHeight +import androidx.compose.foundation.layout.fillMaxWidth +import androidx.compose.foundation.layout.height +import androidx.compose.foundation.layout.offset +import androidx.compose.foundation.layout.width import androidx.compose.runtime.Composable import androidx.compose.ui.Modifier import androidx.compose.ui.graphics.Color @@ -12,7 +18,9 @@ import kotlin.math.max @Composable fun ScrollIndicator(context: Context, state: EditorState) { - BoxWithConstraints(modifier = Modifier.width(5.dp).fillMaxHeight()) { + BoxWithConstraints(modifier = Modifier + .width(5.dp) + .fillMaxHeight()) { val height = convertDpToPixel(this.maxHeight, LocalContext.current).toInt() val page = state.pageView println(page.scroll + height) @@ -29,7 +37,8 @@ fun ScrollIndicator(context: Context, state: EditorState) { Box( modifier = Modifier .fillMaxWidth() - .offset(y = indicatorPosition.dp + .offset( + y = indicatorPosition.dp ) .background(Color.Black) .height( diff --git a/app/src/main/java/com/olup/notable/components/Select.kt b/app/src/main/java/com/olup/notable/components/Select.kt index 698bca70..b5523dd4 100644 --- a/app/src/main/java/com/olup/notable/components/Select.kt +++ b/app/src/main/java/com/olup/notable/components/Select.kt @@ -2,12 +2,23 @@ package com.olup.notable.components import androidx.compose.foundation.background import androidx.compose.foundation.border -import androidx.compose.foundation.layout.* +import androidx.compose.foundation.layout.Box +import androidx.compose.foundation.layout.Column +import androidx.compose.foundation.layout.IntrinsicSize +import androidx.compose.foundation.layout.Row +import androidx.compose.foundation.layout.fillMaxWidth +import androidx.compose.foundation.layout.height +import androidx.compose.foundation.layout.padding +import androidx.compose.foundation.layout.width import androidx.compose.material.Icon import androidx.compose.material.Text import androidx.compose.material.icons.Icons import androidx.compose.material.icons.rounded.ArrowDropDown -import androidx.compose.runtime.* +import androidx.compose.runtime.Composable +import androidx.compose.runtime.getValue +import androidx.compose.runtime.mutableStateOf +import androidx.compose.runtime.remember +import androidx.compose.runtime.setValue import androidx.compose.ui.Modifier import androidx.compose.ui.graphics.Color import androidx.compose.ui.graphics.RectangleShape diff --git a/app/src/main/java/com/olup/notable/components/SelectorBitmap.kt b/app/src/main/java/com/olup/notable/components/SelectorBitmap.kt index 96c167bb..023a0375 100644 --- a/app/src/main/java/com/olup/notable/components/SelectorBitmap.kt +++ b/app/src/main/java/com/olup/notable/components/SelectorBitmap.kt @@ -1,19 +1,21 @@ package com.olup.notable -import android.util.Log import androidx.compose.foundation.ExperimentalFoundationApi import androidx.compose.foundation.Image import androidx.compose.foundation.background import androidx.compose.foundation.combinedClickable import androidx.compose.foundation.gestures.detectDragGestures import androidx.compose.foundation.interaction.MutableInteractionSource -import androidx.compose.foundation.layout.* -import androidx.compose.foundation.shape.RoundedCornerShape -import androidx.compose.material.Text -import androidx.compose.runtime.* +import androidx.compose.foundation.layout.Box +import androidx.compose.foundation.layout.Row +import androidx.compose.foundation.layout.fillMaxSize +import androidx.compose.foundation.layout.height +import androidx.compose.foundation.layout.offset +import androidx.compose.foundation.layout.padding +import androidx.compose.runtime.Composable +import androidx.compose.runtime.remember import androidx.compose.ui.ExperimentalComposeUiApi import androidx.compose.ui.Modifier -import androidx.compose.ui.draw.clip import androidx.compose.ui.draw.drawBehind import androidx.compose.ui.geometry.Offset import androidx.compose.ui.graphics.Color @@ -23,9 +25,6 @@ import androidx.compose.ui.input.pointer.pointerInput import androidx.compose.ui.unit.IntOffset import androidx.compose.ui.unit.dp import androidx.compose.ui.unit.round -import com.olup.notable.EditorControlTower -import java.util.Date -import java.util.UUID val strokeStyle = androidx.compose.ui.graphics.drawscope.Stroke( width = 2f, @@ -88,7 +87,7 @@ fun SelectedBitmap( selectionState.selectionStartOffset?.let { startOffset -> selectionState.selectionDisplaceOffset?.let { displaceOffset -> val xPos = selectionState.selectionRect?.let { rect -> - ((rect.left ?: 0) - (rect.right ?: 0)) / 2 + 35 * 3 + (rect.left - rect.right) / 2 + 35 * 3 } ?: 0 val offset = startOffset + displaceOffset + IntOffset(x = -xPos, y = -100) // Overlay buttons near the selection box diff --git a/app/src/main/java/com/olup/notable/components/StrokeMenu.kt b/app/src/main/java/com/olup/notable/components/StrokeMenu.kt index 87834b1b..ec182990 100644 --- a/app/src/main/java/com/olup/notable/components/StrokeMenu.kt +++ b/app/src/main/java/com/olup/notable/components/StrokeMenu.kt @@ -3,10 +3,13 @@ package com.olup.notable import androidx.compose.foundation.background import androidx.compose.foundation.border import androidx.compose.foundation.clickable -import androidx.compose.foundation.layout.* -import androidx.compose.material.Button -import androidx.compose.material.Text -import androidx.compose.runtime.* +import androidx.compose.foundation.layout.Box +import androidx.compose.foundation.layout.IntrinsicSize +import androidx.compose.foundation.layout.Row +import androidx.compose.foundation.layout.height +import androidx.compose.foundation.layout.padding +import androidx.compose.foundation.layout.size +import androidx.compose.runtime.Composable import androidx.compose.ui.Alignment import androidx.compose.ui.Modifier import androidx.compose.ui.graphics.Color @@ -40,7 +43,14 @@ fun StrokeMenu( ToolbarButton( text = it.first, isSelected = value.strokeSize == it.second, - onSelect = { onChange(PenSetting(strokeSize = it.second, color = value.color)) }, + onSelect = { + onChange( + PenSetting( + strokeSize = it.second, + color = value.color + ) + ) + }, modifier = Modifier ) } @@ -58,7 +68,7 @@ fun ColorSelectionDialog( val context = LocalContext.current Popup( - offset = IntOffset(0, convertDpToPixel(43.dp, context).toInt()), + offset = IntOffset(0, convertDpToPixel(43.dp, context).toInt()), onDismissRequest = { onClose() }, properties = PopupProperties(focusable = true), alignment = Alignment.TopCenter diff --git a/app/src/main/java/com/olup/notable/components/Toolbar.kt b/app/src/main/java/com/olup/notable/components/Toolbar.kt index b5113166..1d3b446f 100644 --- a/app/src/main/java/com/olup/notable/components/Toolbar.kt +++ b/app/src/main/java/com/olup/notable/components/Toolbar.kt @@ -1,36 +1,41 @@ package com.olup.notable + import android.content.Intent import androidx.activity.compose.rememberLauncherForActivityResult +import androidx.activity.result.PickVisualMediaRequest +import androidx.activity.result.contract.ActivityResultContracts.PickVisualMedia import androidx.compose.foundation.background -import androidx.compose.foundation.layout.* +import androidx.compose.foundation.layout.Box +import androidx.compose.foundation.layout.Column +import androidx.compose.foundation.layout.Row +import androidx.compose.foundation.layout.Spacer +import androidx.compose.foundation.layout.fillMaxHeight +import androidx.compose.foundation.layout.fillMaxWidth +import androidx.compose.foundation.layout.height +import androidx.compose.foundation.layout.padding +import androidx.compose.foundation.layout.width import androidx.compose.material.Text -import androidx.compose.runtime.* +import androidx.compose.runtime.Composable +import androidx.compose.runtime.LaunchedEffect +import androidx.compose.runtime.getValue +import androidx.compose.runtime.mutableStateOf +import androidx.compose.runtime.remember +import androidx.compose.runtime.rememberCoroutineScope +import androidx.compose.runtime.setValue import androidx.compose.ui.Alignment import androidx.compose.ui.ExperimentalComposeUiApi import androidx.compose.ui.Modifier import androidx.compose.ui.graphics.Color -import androidx.compose.ui.graphics.ImageBitmap import androidx.compose.ui.platform.LocalContext import androidx.compose.ui.text.font.FontWeight import androidx.compose.ui.text.style.TextAlign import androidx.compose.ui.unit.dp import androidx.navigation.NavController -import kotlinx.coroutines.launch - - -import androidx.activity.result.contract.ActivityResultContracts -import androidx.activity.result.PickVisualMediaRequest -import androidx.activity.result.contract.ActivityResultContracts.PickVisualMedia -import androidx.compose.runtime.Composable -import androidx.compose.runtime.LaunchedEffect -import androidx.compose.ui.graphics.asImageBitmap -import androidx.compose.foundation.layout.fillMaxWidth -import androidx.compose.foundation.layout.fillMaxHeight -import com.olup.notable.db.Image import io.shipbook.shipbooksdk.Log +import kotlinx.coroutines.launch -fun PresentlyUsedToolIcon(mode: Mode, pen: Pen): Int { +fun presentlyUsedToolIcon(mode: Mode, pen: Pen): Int { return when (mode) { Mode.Draw -> { when (pen) { @@ -98,7 +103,7 @@ fun Toolbar( context.contentResolver.takePersistableUriPermission(uri, flag) // Set isImageLoaded to true isImageLoaded = true - Log.i("InsertImage", "Hura! We have uri: ${uri}") + Log.i("InsertImage", "Hura! We have uri: $uri") DrawCanvas.addImageByUri.value = uri } @@ -124,6 +129,7 @@ fun Toolbar( fun handleSelection() { state.mode = Mode.Select } + fun handleLine() { state.mode = Mode.Line } @@ -350,7 +356,7 @@ fun Toolbar( onSelect = { // Call insertImage when the button is tapped Log.i("InsertImage", "Launching image picker...") - pickMedia.launch(PickVisualMediaRequest(ActivityResultContracts.PickVisualMedia.ImageOnly)) + pickMedia.launch(PickVisualMediaRequest(PickVisualMedia.ImageOnly)) } ) Box( @@ -403,7 +409,7 @@ fun Toolbar( // TODO maybe have generic utils for this ? val pageNumber = book!!.pageIds.indexOf(state.pageId) + 1 - val totalPageNumber = book!!.pageIds.size + val totalPageNumber = book.pageIds.size Box( contentAlignment = Alignment.Center, @@ -464,8 +470,12 @@ fun Toolbar( } else { ToolbarButton( onSelect = { state.isToolbarOpen = true }, - iconId = PresentlyUsedToolIcon(state.mode, state.pen), - penColor = if(state.mode != Mode.Erase) state.penSettings[state.pen.penName]?.color?.let { Color(it) } else null, + iconId = presentlyUsedToolIcon(state.mode, state.pen), + penColor = if (state.mode != Mode.Erase) state.penSettings[state.pen.penName]?.color?.let { + Color( + it + ) + } else null, contentDescription = "open toolbar", modifier = Modifier.height(37.dp) ) diff --git a/app/src/main/java/com/olup/notable/components/ToolbarButton.kt b/app/src/main/java/com/olup/notable/components/ToolbarButton.kt index 937e9b80..910f9a73 100644 --- a/app/src/main/java/com/olup/notable/components/ToolbarButton.kt +++ b/app/src/main/java/com/olup/notable/components/ToolbarButton.kt @@ -1,6 +1,5 @@ package com.olup.notable -import android.util.Log import androidx.compose.foundation.background import androidx.compose.foundation.layout.Box import androidx.compose.foundation.layout.padding @@ -17,14 +16,14 @@ import androidx.compose.ui.unit.dp @Composable fun ToolbarButton( + modifier: Modifier = Modifier, isSelected: Boolean = false, onSelect: () -> Unit = {}, iconId: Int? = null, imageVector: ImageVector? = null, text: String? = null, penColor: Color? = null, - contentDescription: String = "", - modifier: Modifier = Modifier + contentDescription: String = "" ) { Box( Modifier @@ -44,7 +43,7 @@ fun ToolbarButton( painter = painterResource(id = iconId), contentDescription, Modifier, - if(penColor==Color.Black || penColor==Color.DarkGray) Color.White else if (isSelected) Color.White else Color.Black + if (penColor == Color.Black || penColor == Color.DarkGray) Color.White else if (isSelected) Color.White else Color.Black ) } diff --git a/app/src/main/java/com/olup/notable/components/ToolbarMenu.kt b/app/src/main/java/com/olup/notable/components/ToolbarMenu.kt index 89726ced..98d1be8d 100644 --- a/app/src/main/java/com/olup/notable/components/ToolbarMenu.kt +++ b/app/src/main/java/com/olup/notable/components/ToolbarMenu.kt @@ -2,9 +2,16 @@ package com.olup.notable import androidx.compose.foundation.background import androidx.compose.foundation.border -import androidx.compose.foundation.layout.* +import androidx.compose.foundation.layout.Box +import androidx.compose.foundation.layout.Column +import androidx.compose.foundation.layout.IntrinsicSize +import androidx.compose.foundation.layout.fillMaxWidth +import androidx.compose.foundation.layout.height +import androidx.compose.foundation.layout.padding +import androidx.compose.foundation.layout.width import androidx.compose.material.Text -import androidx.compose.runtime.* +import androidx.compose.runtime.Composable +import androidx.compose.runtime.rememberCoroutineScope import androidx.compose.ui.Alignment import androidx.compose.ui.Modifier import androidx.compose.ui.graphics.Color @@ -27,11 +34,11 @@ fun ToolbarMenu( ) { val context = LocalContext.current val scope = rememberCoroutineScope() - val snackManager = SnackContext.current + val snackManager = LocalSnackContext.current val page = AppRepository(context).pageRepository.getById(state.pageId)!! val parentFolder = if (page.notebookId != null) - AppRepository(context).bookRepository.getById(page.notebookId!!)!! + AppRepository(context).bookRepository.getById(page.notebookId)!! .parentFolderId else page.parentFolderId @@ -40,7 +47,7 @@ fun ToolbarMenu( onDismissRequest = { onClose() }, offset = IntOffset( - convertDpToPixel(-10.dp, context).toInt(), + convertDpToPixel((-10).dp, context).toInt(), convertDpToPixel(50.dp, context).toInt() ), properties = PopupProperties(focusable = true) @@ -157,7 +164,7 @@ fun ToolbarMenu( ) delay(10L) // Why do I need this ? - val message = exportBook(context, state.bookId ?: return@launch) + val message = exportBook(context, state.bookId) removeSnack() snackManager.displaySnack( SnackConf(text = message, duration = 2000) @@ -184,7 +191,7 @@ fun ToolbarMenu( val message = - exportBookToPng(context, state.bookId ?: return@launch) + exportBookToPng(context, state.bookId) removeSnack() snackManager.displaySnack( diff --git a/app/src/main/java/com/olup/notable/components/Topbar.kt b/app/src/main/java/com/olup/notable/components/Topbar.kt index 457e3b3f..c9a52c9d 100644 --- a/app/src/main/java/com/olup/notable/components/Topbar.kt +++ b/app/src/main/java/com/olup/notable/components/Topbar.kt @@ -1,18 +1,21 @@ package com.olup.notable import androidx.compose.foundation.background -import androidx.compose.foundation.layout.* +import androidx.compose.foundation.layout.Box +import androidx.compose.foundation.layout.Column +import androidx.compose.foundation.layout.fillMaxWidth +import androidx.compose.foundation.layout.height import androidx.compose.runtime.Composable import androidx.compose.ui.Modifier import androidx.compose.ui.graphics.Color import androidx.compose.ui.unit.dp @Composable -fun Topbar(content: @Composable() () -> Unit){ +fun Topbar(content: @Composable () -> Unit) { Column( modifier = Modifier.fillMaxWidth() ) { - Box() { + Box { content() } Box( diff --git a/app/src/main/java/com/olup/notable/datastore/EditorSettingCacheManager.kt b/app/src/main/java/com/olup/notable/datastore/EditorSettingCacheManager.kt index 96b155b1..14a3ea76 100644 --- a/app/src/main/java/com/olup/notable/datastore/EditorSettingCacheManager.kt +++ b/app/src/main/java/com/olup/notable/datastore/EditorSettingCacheManager.kt @@ -1,13 +1,12 @@ package com.olup.notable import android.content.Context -import com.olup.notable.AppRepository import com.olup.notable.db.Kv import kotlinx.serialization.decodeFromString import kotlinx.serialization.encodeToString import kotlinx.serialization.json.Json -val persistVersion = 2 +const val persistVersion = 2 object EditorSettingCacheManager { @@ -21,15 +20,15 @@ object EditorSettingCacheManager { val mode: Mode ) - fun init(context: Context){ + fun init(context: Context) { val settingsJSon = AppRepository(context).kvRepository.get("EDITOR_SETTINGS") - if(settingsJSon != null) { + if (settingsJSon != null) { val settings = Json.decodeFromString(settingsJSon.value) - if(settings.version == persistVersion) setEditorSettings(context, settings, false) + if (settings.version == persistVersion) setEditorSettings(context, settings, false) } } - fun persist(context: Context, settings : EditorSettings){ + private fun persist(context: Context, settings: EditorSettings) { val settingsJson = Json.encodeToString(settings) AppRepository(context).kvRepository.set(Kv("EDITOR_SETTINGS", settingsJson)) } @@ -39,8 +38,12 @@ object EditorSettingCacheManager { return editorSettings } - fun setEditorSettings(context : Context, newEditorSettings: EditorSettings, shouldPersist : Boolean = true) { + fun setEditorSettings( + context: Context, + newEditorSettings: EditorSettings, + shouldPersist: Boolean = true + ) { editorSettings = newEditorSettings - if(shouldPersist) persist(context, newEditorSettings) + if (shouldPersist) persist(context, newEditorSettings) } } diff --git a/app/src/main/java/com/olup/notable/db/Db.kt b/app/src/main/java/com/olup/notable/db/Db.kt index 638887c2..f97c5406 100644 --- a/app/src/main/java/com/olup/notable/db/Db.kt +++ b/app/src/main/java/com/olup/notable/db/Db.kt @@ -1,28 +1,29 @@ package com.olup.notable.db import android.content.Context -import androidx.room.* -import androidx.room.migration.Migration -import androidx.sqlite.db.SupportSQLiteDatabase -import kotlinx.serialization.decodeFromString -import kotlinx.serialization.encodeToString -import kotlinx.serialization.json.Json -import java.util.Date import android.os.Environment import androidx.room.AutoMigration import androidx.room.Database import androidx.room.Room import androidx.room.RoomDatabase +import androidx.room.TypeConverter import androidx.room.TypeConverters +import kotlinx.serialization.decodeFromString +import kotlinx.serialization.encodeToString +import kotlinx.serialization.json.Json import java.io.File +import java.util.Date class Converters { @TypeConverter fun fromListString(value: List) = Json.encodeToString(value) + @TypeConverter fun toListString(value: String) = Json.decodeFromString>(value) + @TypeConverter fun fromListPoint(value: List) = Json.encodeToString(value) + @TypeConverter fun toListPoint(value: String) = Json.decodeFromString>(value) @@ -33,26 +34,25 @@ class Converters { @TypeConverter fun dateToTimestamp(date: Date?): Long? { - return date?.time?.toLong() + return date?.time } } - @Database( entities = [Folder::class, Notebook::class, Page::class, Stroke::class, Image::class, Kv::class], version = 30, autoMigrations = [ - AutoMigration(19,20), - AutoMigration(20,21), - AutoMigration(21,22), - AutoMigration(23,24), - AutoMigration(24,25), - AutoMigration(25,26), - AutoMigration(26,27), - AutoMigration(27,28), - AutoMigration(28,29), - AutoMigration(29,30), + AutoMigration(19, 20), + AutoMigration(20, 21), + AutoMigration(21, 22), + AutoMigration(23, 24), + AutoMigration(24, 25), + AutoMigration(25, 26), + AutoMigration(26, 27), + AutoMigration(27, 28), + AutoMigration(28, 29), + AutoMigration(29, 30), ], exportSchema = true ) @TypeConverters(Converters::class) @@ -71,22 +71,24 @@ abstract class AppDatabase : RoomDatabase() { fun getDatabase(context: Context): AppDatabase { if (INSTANCE == null) { synchronized(this) { - val documentsDir = Environment.getExternalStoragePublicDirectory(Environment.DIRECTORY_DOCUMENTS) + val documentsDir = + Environment.getExternalStoragePublicDirectory(Environment.DIRECTORY_DOCUMENTS) // val dbDir = File(documentsDir, "Obsidian/MinimalNotes/99-Meta/backups/notabledb") val dbDir = File(documentsDir, "notabledb") if (!dbDir.exists()) { dbDir.mkdirs() } val dbFile = File(dbDir, "app_database") - INSTANCE = Room.databaseBuilder(context, AppDatabase::class.java, dbFile.absolutePath) - .allowMainThreadQueries() - //.fallbackToDestructiveMigration() - .addMigrations( - MIGRATION_16_17, - MIGRATION_17_18, - MIGRATION_22_23 - ) - .build() + INSTANCE = + Room.databaseBuilder(context, AppDatabase::class.java, dbFile.absolutePath) + .allowMainThreadQueries() + //.fallbackToDestructiveMigration() + .addMigrations( + MIGRATION_16_17, + MIGRATION_17_18, + MIGRATION_22_23 + ) + .build() } } return INSTANCE!! diff --git a/app/src/main/java/com/olup/notable/db/Folder.kt b/app/src/main/java/com/olup/notable/db/Folder.kt index 56a25cde..99bddd20 100644 --- a/app/src/main/java/com/olup/notable/db/Folder.kt +++ b/app/src/main/java/com/olup/notable/db/Folder.kt @@ -2,7 +2,14 @@ package com.olup.notable.db import android.content.Context import androidx.lifecycle.LiveData -import androidx.room.* +import androidx.room.ColumnInfo +import androidx.room.Dao +import androidx.room.Entity +import androidx.room.ForeignKey +import androidx.room.Insert +import androidx.room.PrimaryKey +import androidx.room.Query +import androidx.room.Update import java.util.Date import java.util.UUID @@ -19,7 +26,7 @@ data class Folder( val id: String = UUID.randomUUID().toString(), val title: String = "New Folder", - @ColumnInfo(index = true) + @ColumnInfo(index = true) val parentFolderId: String? = null, val createdAt: Date = Date(), @@ -30,10 +37,10 @@ data class Folder( @Dao interface FolderDao { @Query("SELECT * FROM folder WHERE parentFolderId IS :folderId") - fun getChildrenFolders(folderId:String?): LiveData> + fun getChildrenFolders(folderId: String?): LiveData> @Query("SELECT * FROM folder WHERE id IS :folderId") - fun get(folderId:String): Folder + fun get(folderId: String): Folder @Insert @@ -47,13 +54,13 @@ interface FolderDao { } class FolderRepository(context: Context) { - var db = AppDatabase.getDatabase(context)?.folderDao()!! + var db = AppDatabase.getDatabase(context).folderDao() fun create(folder: Folder) { db.create(folder) } - fun update(folder : Folder) { + fun update(folder: Folder) { db.update(folder) } diff --git a/app/src/main/java/com/olup/notable/db/Image.kt b/app/src/main/java/com/olup/notable/db/Image.kt index 5c6e3e41..0386647b 100644 --- a/app/src/main/java/com/olup/notable/db/Image.kt +++ b/app/src/main/java/com/olup/notable/db/Image.kt @@ -1,14 +1,17 @@ package com.olup.notable.db import android.content.Context -import androidx.compose.ui.graphics.ImageBitmap -import androidx.room.* -import com.olup.notable.Pen -import java.util.* -import android.graphics.Bitmap -import android.graphics.BitmapFactory -import android.util.Log -import androidx.compose.ui.graphics.asAndroidBitmap // To convert ImageBitmap to Bitmap +import androidx.room.ColumnInfo +import androidx.room.Dao +import androidx.room.Entity +import androidx.room.ForeignKey +import androidx.room.Insert +import androidx.room.PrimaryKey +import androidx.room.Query +import androidx.room.Transaction +import androidx.room.Update +import java.util.Date +import java.util.UUID // Entity class for images @@ -73,7 +76,7 @@ interface ImageDao { // Repository for stroke operations class ImageRepository(context: Context) { - private val db = AppDatabase.getDatabase(context)?.ImageDao()!! + private val db = AppDatabase.getDatabase(context).ImageDao() fun create(image: Image): Long { return db.create(image) @@ -120,7 +123,7 @@ class ImageRepository(context: Context) { } fun getImageAtPoint(x: Int, y: Int, pageId: String): Image? { - return db.getImageAtPoint(x, y,pageId) + return db.getImageAtPoint(x, y, pageId) } } diff --git a/app/src/main/java/com/olup/notable/db/Kv.kt b/app/src/main/java/com/olup/notable/db/Kv.kt index 5a37b5f5..05e74808 100644 --- a/app/src/main/java/com/olup/notable/db/Kv.kt +++ b/app/src/main/java/com/olup/notable/db/Kv.kt @@ -1,28 +1,18 @@ package com.olup.notable.db import android.content.Context -import io.shipbook.shipbooksdk.Log import androidx.lifecycle.LiveData -import androidx.room.* -import java.util.Date -import java.util.UUID -import androidx.lifecycle.MutableLiveData import androidx.lifecycle.map -import androidx.room.Room -import androidx.room.RoomDatabase -import androidx.sqlite.db.SupportSQLiteDatabase -import com.olup.notable.AppSettings +import androidx.room.Dao +import androidx.room.Entity +import androidx.room.Insert +import androidx.room.OnConflictStrategy +import androidx.room.PrimaryKey +import androidx.room.Query import com.olup.notable.TAG -import com.olup.notable.persistVersion -import kotlinx.coroutines.CoroutineScope -import kotlinx.coroutines.Dispatchers -import kotlinx.coroutines.launch +import io.shipbook.shipbooksdk.Log import kotlinx.serialization.KSerializer -import kotlinx.serialization.decodeFromString -import kotlinx.serialization.encodeToString import kotlinx.serialization.json.Json -import kotlinx.serialization.serializer -import kotlin.reflect.KType @Entity @@ -35,24 +25,24 @@ data class Kv( // DAO @Dao interface KvDao { - @Query("SELECT * FROM kv WHERE key=:key") + @Query("SELECT * FROM kv WHERE `key`=:key") fun get(key: String): Kv - @Query("SELECT * FROM kv WHERE key=:key") + @Query("SELECT * FROM kv WHERE `key`=:key") fun getLive(key: String): LiveData @Insert(onConflict = OnConflictStrategy.REPLACE) fun set(kv: Kv) - @Query("DELETE FROM kv WHERE key=:key") + @Query("DELETE FROM kv WHERE `key`=:key") fun delete(key: String) } class KvRepository(context: Context) { - var db = AppDatabase.getDatabase(context)?.kvDao()!! + var db = AppDatabase.getDatabase(context).kvDao() - fun get(key: String): Kv? { + fun get(key: String): Kv { return db.get(key) } @@ -76,14 +66,14 @@ class KvProxy(context: Context) { fun observeKv(key: String, serializer: KSerializer, default: T): LiveData { return kvRepository.getLive(key).map { if (it == null) return@map default - val jsonValue = it!!.value + val jsonValue = it.value Json.decodeFromString(serializer, jsonValue) } } fun get(key: String, serializer: KSerializer): T? { - val kv = kvRepository.get(key) ?: return null - val jsonValue = kv!!.value + val kv = kvRepository.get(key) + val jsonValue = kv.value return Json.decodeFromString(serializer, jsonValue) } diff --git a/app/src/main/java/com/olup/notable/db/Migrations.kt b/app/src/main/java/com/olup/notable/db/Migrations.kt index ba678cb6..ed861f3c 100644 --- a/app/src/main/java/com/olup/notable/db/Migrations.kt +++ b/app/src/main/java/com/olup/notable/db/Migrations.kt @@ -1,6 +1,5 @@ package com.olup.notable.db -import androidx.room.AutoMigration import androidx.room.migration.Migration import androidx.sqlite.db.SupportSQLiteDatabase diff --git a/app/src/main/java/com/olup/notable/db/Notebook.kt b/app/src/main/java/com/olup/notable/db/Notebook.kt index d4db55e1..69c65966 100644 --- a/app/src/main/java/com/olup/notable/db/Notebook.kt +++ b/app/src/main/java/com/olup/notable/db/Notebook.kt @@ -1,10 +1,17 @@ package com.olup.notable.db import android.content.Context -import io.shipbook.shipbooksdk.Log import androidx.lifecycle.LiveData -import androidx.room.* +import androidx.room.ColumnInfo +import androidx.room.Dao +import androidx.room.Entity +import androidx.room.ForeignKey +import androidx.room.Insert +import androidx.room.PrimaryKey +import androidx.room.Query +import androidx.room.Update import com.olup.notable.TAG +import io.shipbook.shipbooksdk.Log import java.util.Date import java.util.UUID @@ -62,8 +69,8 @@ interface NotebookDao { } class BookRepository(context: Context) { - var db = AppDatabase.getDatabase(context)?.notebookDao()!! - var pageDb = AppDatabase.getDatabase(context)?.pageDao()!! + var db = AppDatabase.getDatabase(context).notebookDao() + private var pageDb = AppDatabase.getDatabase(context).pageDao() fun create(notebook: Notebook) { db.create(notebook) @@ -95,26 +102,26 @@ class BookRepository(context: Context) { } fun addPage(id: String, pageId: String, index: Int? = null) { - var pageIds = (db.getById(id) ?: return).pageIds.toMutableList() + val pageIds = (db.getById(id) ?: return).pageIds.toMutableList() if (index != null) pageIds.add(index, pageId) else pageIds.add(pageId) db.setPageIds(id, pageIds) } fun removePage(id: String, pageId: String) { - var notebook = db.getById(id) ?: return - var updatedNotebook = notebook.copy( + val notebook = db.getById(id) ?: return + val updatedNotebook = notebook.copy( // remove the page pageIds = notebook.pageIds.filterNot { it == pageId }, // remove the "open page" if it's the one openPageId = if (notebook.openPageId == pageId) null else notebook.openPageId ) db.update(updatedNotebook) - Log.i(TAG, "Cleaned ${id} ${pageId}") + Log.i(TAG, "Cleaned $id $pageId") } - fun changeePageIndex(id: String, pageId: String, index: Int) { - var pageIds = (db.getById(id) ?: return).pageIds.toMutableList() + fun changePageIndex(id: String, pageId: String, index: Int) { + val pageIds = (db.getById(id) ?: return).pageIds.toMutableList() var correctedIndex = index if (correctedIndex < 0) correctedIndex = 0 if (correctedIndex > pageIds.size - 1) correctedIndex = pageIds.size - 1 diff --git a/app/src/main/java/com/olup/notable/db/Page.kt b/app/src/main/java/com/olup/notable/db/Page.kt index 33441696..59f744d0 100644 --- a/app/src/main/java/com/olup/notable/db/Page.kt +++ b/app/src/main/java/com/olup/notable/db/Page.kt @@ -2,8 +2,19 @@ package com.olup.notable.db import android.content.Context import androidx.lifecycle.LiveData -import androidx.room.* -import java.util.* +import androidx.room.ColumnInfo +import androidx.room.Dao +import androidx.room.Embedded +import androidx.room.Entity +import androidx.room.ForeignKey +import androidx.room.Insert +import androidx.room.PrimaryKey +import androidx.room.Query +import androidx.room.Relation +import androidx.room.Transaction +import androidx.room.Update +import java.util.Date +import java.util.UUID @Entity( foreignKeys = [ForeignKey( @@ -31,6 +42,7 @@ data class PageWithStrokes( parentColumn = "id", entityColumn = "pageId", entity = Stroke::class ) val strokes: List ) + data class PageWithImages( @Embedded val page: Page, @Relation( parentColumn = "id", entityColumn = "pageId", entity = Image::class @@ -71,7 +83,7 @@ interface PageDao { } class PageRepository(context: Context) { - var db = AppDatabase.getDatabase(context)?.pageDao()!! + var db = AppDatabase.getDatabase(context).pageDao() fun create(page: Page): Long { return db.create(page) @@ -88,6 +100,7 @@ class PageRepository(context: Context) { fun getWithStrokeById(pageId: String): PageWithStrokes { return db.getPageWithStrokesById(pageId) } + fun getWithImageById(pageId: String): PageWithImages { return db.getPageWithImagesById(pageId) } diff --git a/app/src/main/java/com/olup/notable/db/Select.kt b/app/src/main/java/com/olup/notable/db/Select.kt index 62d68682..d15e003f 100644 --- a/app/src/main/java/com/olup/notable/db/Select.kt +++ b/app/src/main/java/com/olup/notable/db/Select.kt @@ -46,13 +46,13 @@ fun selectImage( IntOffset(-imageToSelect.x, -imageToSelect.y) ) // set state - editorState.selectionState.selectedImages = listOf(imageToSelect) + editorState.selectionState.selectedImages = listOf(imageToSelect) editorState.selectionState.selectedBitmap = selectedBitmap editorState.selectionState.selectionStartOffset = IntOffset(bounds.left, bounds.top) editorState.selectionState.selectionRect = bounds editorState.selectionState.selectionDisplaceOffset = IntOffset(0, 0) editorState.selectionState.placementMode = PlacementMode.Move - page.drawArea(bounds, ignoredImageIds = listOf(imageToSelect).map { it.id }) + page.drawArea(bounds, ignoredImageIds = listOf(imageToSelect).map { it.id }) scope.launch { DrawCanvas.refreshUi.emit(Unit) @@ -94,7 +94,7 @@ fun selectImage( * @param page The `PageView` object representing the current page, including its strokes and dimensions. * @param editorState The `EditorState` object storing the current state of the editor, such as selected strokes. * @param points A list of `SimplePointF` objects defining the user's selection path in page coordinates. - * points is in page coodinates + * points is in page coordinates */ fun handleSelect( scope: CoroutineScope, diff --git a/app/src/main/java/com/olup/notable/db/Stroke.kt b/app/src/main/java/com/olup/notable/db/Stroke.kt index 3d4f5bd5..d42d202d 100644 --- a/app/src/main/java/com/olup/notable/db/Stroke.kt +++ b/app/src/main/java/com/olup/notable/db/Stroke.kt @@ -1,10 +1,18 @@ package com.olup.notable.db import android.content.Context -import androidx.room.* +import androidx.room.ColumnInfo +import androidx.room.Dao +import androidx.room.Entity +import androidx.room.ForeignKey +import androidx.room.Insert +import androidx.room.PrimaryKey +import androidx.room.Query +import androidx.room.Transaction +import androidx.room.Update import com.olup.notable.Pen -import java.util.* -import android.graphics.* +import java.util.Date +import java.util.UUID @kotlinx.serialization.Serializable data class StrokePoint( @@ -30,7 +38,7 @@ data class Stroke( val id: String = UUID.randomUUID().toString(), val size: Float, val pen: Pen, - @ColumnInfo(defaultValue = "0xFF000000") + @ColumnInfo(defaultValue = "0xFF000000") val color: Int = 0xFF000000.toInt(), var top: Float, @@ -68,7 +76,7 @@ interface StrokeDao { } class StrokeRepository(context: Context) { - var db = AppDatabase.getDatabase(context)?.strokeDao()!! + var db = AppDatabase.getDatabase(context).strokeDao() fun create(stroke: Stroke): Long { return db.create(stroke) diff --git a/app/src/main/java/com/olup/notable/modals/AppSettings.kt b/app/src/main/java/com/olup/notable/modals/AppSettings.kt index 14bca30f..ffbaeb1a 100644 --- a/app/src/main/java/com/olup/notable/modals/AppSettings.kt +++ b/app/src/main/java/com/olup/notable/modals/AppSettings.kt @@ -4,10 +4,22 @@ import android.content.Intent import android.net.Uri import androidx.compose.foundation.background import androidx.compose.foundation.border -import androidx.compose.foundation.layout.* +import androidx.compose.foundation.layout.Box +import androidx.compose.foundation.layout.Column +import androidx.compose.foundation.layout.Row +import androidx.compose.foundation.layout.Spacer +import androidx.compose.foundation.layout.fillMaxWidth +import androidx.compose.foundation.layout.height +import androidx.compose.foundation.layout.padding +import androidx.compose.foundation.layout.width import androidx.compose.material.Text -import androidx.compose.runtime.* +import androidx.compose.runtime.Composable +import androidx.compose.runtime.LaunchedEffect +import androidx.compose.runtime.getValue import androidx.compose.runtime.livedata.observeAsState +import androidx.compose.runtime.mutableStateOf +import androidx.compose.runtime.remember +import androidx.compose.runtime.setValue import androidx.compose.ui.Modifier import androidx.compose.ui.graphics.Color import androidx.compose.ui.graphics.RectangleShape @@ -61,14 +73,14 @@ fun AppSettingsModal(onClose: () -> Unit) { LaunchedEffect(key1 = Unit, block = { thread { isLatestVersion = isLatestVersion(context) } }) val settings by - kv.observeKv("APP_SETTINGS", AppSettings.serializer(), AppSettings(version = 1)) - .observeAsState() + kv.observeKv("APP_SETTINGS", AppSettings.serializer(), AppSettings(version = 1)) + .observeAsState() if (settings == null) return Dialog( - onDismissRequest = { onClose() }, - properties = DialogProperties(usePlatformDefaultWidth = false), + onDismissRequest = { onClose() }, + properties = DialogProperties(usePlatformDefaultWidth = false), ) { Column( modifier = Modifier @@ -78,9 +90,13 @@ fun AppSettingsModal(onClose: () -> Unit) { .border(2.dp, Color.Black, RectangleShape) ) { Column(Modifier.padding(20.dp, 10.dp)) { - Text(text = "App setting - v${BuildConfig.VERSION_NAME}${if(isNext) " [NEXT]" else ""}") + Text(text = "App setting - v${BuildConfig.VERSION_NAME}${if (isNext) " [NEXT]" else ""}") } - Box(Modifier.height(0.5.dp).fillMaxWidth().background(Color.Black)) + Box( + Modifier + .height(0.5.dp) + .fillMaxWidth() + .background(Color.Black)) Column(Modifier.padding(20.dp, 10.dp)) { Row { diff --git a/app/src/main/java/com/olup/notable/modals/FolderConfig.kt b/app/src/main/java/com/olup/notable/modals/FolderConfig.kt index 5cc5304f..ae96ca8d 100644 --- a/app/src/main/java/com/olup/notable/modals/FolderConfig.kt +++ b/app/src/main/java/com/olup/notable/modals/FolderConfig.kt @@ -1,20 +1,29 @@ package com.olup.notable -import io.shipbook.shipbooksdk.Log import androidx.compose.foundation.background import androidx.compose.foundation.border -import androidx.compose.foundation.layout.* +import androidx.compose.foundation.layout.Box +import androidx.compose.foundation.layout.Column +import androidx.compose.foundation.layout.Row +import androidx.compose.foundation.layout.Spacer +import androidx.compose.foundation.layout.fillMaxWidth +import androidx.compose.foundation.layout.height +import androidx.compose.foundation.layout.padding +import androidx.compose.foundation.layout.width import androidx.compose.foundation.text.BasicTextField import androidx.compose.foundation.text.KeyboardActions import androidx.compose.foundation.text.KeyboardOptions import androidx.compose.material.Text -import androidx.compose.runtime.* +import androidx.compose.runtime.Composable +import androidx.compose.runtime.getValue +import androidx.compose.runtime.mutableStateOf +import androidx.compose.runtime.remember +import androidx.compose.runtime.setValue import androidx.compose.ui.ExperimentalComposeUiApi import androidx.compose.ui.Modifier import androidx.compose.ui.focus.onFocusChanged import androidx.compose.ui.graphics.Color import androidx.compose.ui.graphics.RectangleShape -import androidx.compose.ui.input.key.* import androidx.compose.ui.platform.LocalContext import androidx.compose.ui.platform.LocalFocusManager import androidx.compose.ui.text.TextStyle @@ -25,15 +34,15 @@ import androidx.compose.ui.text.style.TextAlign import androidx.compose.ui.unit.dp import androidx.compose.ui.unit.sp import androidx.compose.ui.window.Dialog -import androidx.navigation.NavController import com.olup.notable.db.FolderRepository +import io.shipbook.shipbooksdk.Log @ExperimentalComposeUiApi @Composable -fun FolderConfigDialog(folderId: String, onClose : ()->Unit) { +fun FolderConfigDialog(folderId: String, onClose: () -> Unit) { val folderRepository = FolderRepository(LocalContext.current) - val folder = folderRepository.get(folderId) ?: return + val folder = folderRepository.get(folderId) var folderTitle by remember { mutableStateOf(folder.title) @@ -70,7 +79,7 @@ fun FolderConfigDialog(folderId: String, onClose : ()->Unit) { Modifier.padding(20.dp, 10.dp) ) { - Row() { + Row { Text( text = "Folder Title", fontWeight = FontWeight.Bold @@ -92,12 +101,15 @@ fun FolderConfigDialog(folderId: String, onClose : ()->Unit) { keyboardActions = KeyboardActions(onDone = { focusManager.clearFocus() }), - modifier = Modifier.background(Color(230,230,230,255)).padding(10.dp, 0.dp).onFocusChanged { focusState -> - if (!focusState.isFocused) { - val updatedFolder = folder.copy(title = folderTitle) - folderRepository.update(updatedFolder) + modifier = Modifier + .background(Color(230, 230, 230, 255)) + .padding(10.dp, 0.dp) + .onFocusChanged { focusState -> + if (!focusState.isFocused) { + val updatedFolder = folder.copy(title = folderTitle) + folderRepository.update(updatedFolder) + } } - } ) diff --git a/app/src/main/java/com/olup/notable/modals/NotebookConfig.kt b/app/src/main/java/com/olup/notable/modals/NotebookConfig.kt index d121d7fc..135d74ce 100644 --- a/app/src/main/java/com/olup/notable/modals/NotebookConfig.kt +++ b/app/src/main/java/com/olup/notable/modals/NotebookConfig.kt @@ -1,21 +1,30 @@ package com.olup.notable -import io.shipbook.shipbooksdk.Log import androidx.compose.foundation.background import androidx.compose.foundation.border -import androidx.compose.foundation.layout.* +import androidx.compose.foundation.layout.Box +import androidx.compose.foundation.layout.Column +import androidx.compose.foundation.layout.Row +import androidx.compose.foundation.layout.Spacer +import androidx.compose.foundation.layout.fillMaxWidth +import androidx.compose.foundation.layout.height +import androidx.compose.foundation.layout.padding +import androidx.compose.foundation.layout.width import androidx.compose.foundation.text.BasicTextField import androidx.compose.foundation.text.KeyboardActions import androidx.compose.foundation.text.KeyboardOptions import androidx.compose.material.Text -import androidx.compose.runtime.* +import androidx.compose.runtime.Composable +import androidx.compose.runtime.getValue import androidx.compose.runtime.livedata.observeAsState +import androidx.compose.runtime.mutableStateOf +import androidx.compose.runtime.remember +import androidx.compose.runtime.setValue import androidx.compose.ui.ExperimentalComposeUiApi import androidx.compose.ui.Modifier import androidx.compose.ui.focus.onFocusChanged import androidx.compose.ui.graphics.Color import androidx.compose.ui.graphics.RectangleShape -import androidx.compose.ui.input.key.* import androidx.compose.ui.platform.LocalContext import androidx.compose.ui.platform.LocalFocusManager import androidx.compose.ui.text.TextStyle @@ -26,20 +35,19 @@ import androidx.compose.ui.text.style.TextAlign import androidx.compose.ui.unit.dp import androidx.compose.ui.unit.sp import androidx.compose.ui.window.Dialog -import androidx.navigation.NavController import com.olup.notable.components.SelectMenu import com.olup.notable.db.BookRepository -import kotlinx.coroutines.launch +import io.shipbook.shipbooksdk.Log @ExperimentalComposeUiApi @Composable -fun NotebookConfigDialog(bookId: String, onClose : ()->Unit) { +fun NotebookConfigDialog(bookId: String, onClose: () -> Unit) { val bookRepository = BookRepository(LocalContext.current) val book by bookRepository.getByIdLive(bookId).observeAsState() val context = LocalContext.current - if(book == null) return + if (book == null) return var bookTitle by remember { mutableStateOf(book!!.title) @@ -75,7 +83,7 @@ fun NotebookConfigDialog(bookId: String, onClose : ()->Unit) { Modifier.padding(20.dp, 10.dp) ) { - Row() { + Row { Text( text = "Notebook Title", fontWeight = FontWeight.Bold diff --git a/app/src/main/java/com/olup/notable/modals/PageSettings.kt b/app/src/main/java/com/olup/notable/modals/PageSettings.kt index 9cd5fe37..046c14d1 100644 --- a/app/src/main/java/com/olup/notable/modals/PageSettings.kt +++ b/app/src/main/java/com/olup/notable/modals/PageSettings.kt @@ -2,9 +2,21 @@ package com.olup.notable import androidx.compose.foundation.background import androidx.compose.foundation.border -import androidx.compose.foundation.layout.* +import androidx.compose.foundation.layout.Box +import androidx.compose.foundation.layout.Column +import androidx.compose.foundation.layout.Row +import androidx.compose.foundation.layout.Spacer +import androidx.compose.foundation.layout.fillMaxWidth +import androidx.compose.foundation.layout.height +import androidx.compose.foundation.layout.padding +import androidx.compose.foundation.layout.width import androidx.compose.material.Text -import androidx.compose.runtime.* +import androidx.compose.runtime.Composable +import androidx.compose.runtime.getValue +import androidx.compose.runtime.mutableStateOf +import androidx.compose.runtime.remember +import androidx.compose.runtime.rememberCoroutineScope +import androidx.compose.runtime.setValue import androidx.compose.ui.Modifier import androidx.compose.ui.graphics.Color import androidx.compose.ui.graphics.RectangleShape @@ -43,7 +55,7 @@ fun PageSettingsModal(pageView: PageView, onClose: () -> Unit) { Modifier.padding(20.dp, 10.dp) ) { - Row() { + Row { Text(text = "Background Template") Spacer(Modifier.width(10.dp)) SelectMenu( @@ -56,7 +68,7 @@ fun PageSettingsModal(pageView: PageView, onClose: () -> Unit) { onChange = { val updatedPage = pageView.pageFromDb!!.copy(nativeTemplate = it) pageView.updatePageSettings(updatedPage) - scope.launch { DrawCanvas.refreshUi.emit(Unit) } + scope.launch { DrawCanvas.refreshUi.emit(Unit) } pageTemplate = pageView.pageFromDb!!.nativeTemplate }, value = pageTemplate diff --git a/app/src/main/java/com/olup/notable/utils/EditorState.kt b/app/src/main/java/com/olup/notable/utils/EditorState.kt index 3583e02e..c720d1c4 100644 --- a/app/src/main/java/com/olup/notable/utils/EditorState.kt +++ b/app/src/main/java/com/olup/notable/utils/EditorState.kt @@ -5,7 +5,6 @@ import android.graphics.Color import android.graphics.Rect import androidx.compose.runtime.getValue import androidx.compose.runtime.mutableStateOf -import androidx.compose.runtime.remember import androidx.compose.runtime.setValue import androidx.compose.ui.unit.IntOffset import com.olup.notable.db.Image @@ -17,7 +16,7 @@ enum class Mode { class EditorState(val bookId: String? = null, val pageId: String, val pageView: PageView) { - val persistedEditorSettings = EditorSettingCacheManager.getEditorSettings() + private val persistedEditorSettings = EditorSettingCacheManager.getEditorSettings() var mode by mutableStateOf(persistedEditorSettings?.mode ?: Mode.Draw) // should save var pen by mutableStateOf(persistedEditorSettings?.pen ?: Pen.BALLPEN) // should save @@ -26,8 +25,8 @@ class EditorState(val bookId: String? = null, val pageId: String, val pageView: var isToolbarOpen by mutableStateOf( persistedEditorSettings?.isToolbarOpen ?: false ) // should save - var penSettings by mutableStateOf( - persistedEditorSettings?.penSettings ?: mapOf( + var penSettings by mutableStateOf( + persistedEditorSettings?.penSettings ?: mapOf( Pen.BALLPEN.penName to PenSetting(5f, Color.BLACK), Pen.REDBALLPEN.penName to PenSetting(5f, Color.RED), Pen.BLUEBALLPEN.penName to PenSetting(5f, Color.BLUE), diff --git a/app/src/main/java/com/olup/notable/utils/draw.kt b/app/src/main/java/com/olup/notable/utils/draw.kt index 45e57b42..701cbd7b 100644 --- a/app/src/main/java/com/olup/notable/utils/draw.kt +++ b/app/src/main/java/com/olup/notable/utils/draw.kt @@ -1,11 +1,18 @@ package com.olup.notable import android.content.Context -import android.graphics.* +import android.graphics.Bitmap +import android.graphics.Canvas +import android.graphics.Color +import android.graphics.DashPathEffect +import android.graphics.Matrix +import android.graphics.Paint +import android.graphics.Path +import android.graphics.PointF +import android.graphics.Rect import android.net.Uri import androidx.compose.ui.graphics.asAndroidBitmap import androidx.compose.ui.graphics.asImageBitmap -import io.shipbook.shipbooksdk.Log import androidx.compose.ui.unit.IntOffset import androidx.compose.ui.unit.toOffset import com.olup.notable.db.Image @@ -14,11 +21,9 @@ import com.onyx.android.sdk.data.note.ShapeCreateArgs import com.onyx.android.sdk.data.note.TouchPoint import com.onyx.android.sdk.pen.NeoBrushPen import com.onyx.android.sdk.pen.NeoCharcoalPen -import com.onyx.android.sdk.pen.NeoFountainPen -import com.onyx.android.sdk.pen.NeoMarkerPen +import io.shipbook.shipbooksdk.Log import kotlin.math.abs import kotlin.math.cos -import kotlin.math.sin fun drawBallPenStroke( diff --git a/app/src/main/java/com/olup/notable/utils/eraser.kt b/app/src/main/java/com/olup/notable/utils/eraser.kt index 4af6ff76..3bf5a410 100644 --- a/app/src/main/java/com/olup/notable/utils/eraser.kt +++ b/app/src/main/java/com/olup/notable/utils/eraser.kt @@ -1,6 +1,6 @@ package com.olup.notable -enum class Eraser (val _name : String) { +enum class Eraser(val _name: String) { PEN("PEN"), SELECT("SELECT"), } \ No newline at end of file diff --git a/app/src/main/java/com/olup/notable/utils/history.kt b/app/src/main/java/com/olup/notable/utils/history.kt index b87abe96..41d5fdc2 100644 --- a/app/src/main/java/com/olup/notable/utils/history.kt +++ b/app/src/main/java/com/olup/notable/utils/history.kt @@ -1,7 +1,6 @@ package com.olup.notable import android.graphics.Rect -import android.util.Log import com.olup.notable.db.Image import com.olup.notable.db.Stroke import kotlinx.coroutines.CompletableDeferred @@ -38,7 +37,7 @@ class History(coroutineScope: CoroutineScope, pageView: PageView) { private var undoList: OperationList = mutableListOf() private var redoList: OperationList = mutableListOf() - val pageModel = pageView + private val pageModel = pageView // TODO maybe not in a companion object ? companion object { @@ -68,10 +67,12 @@ class History(coroutineScope: CoroutineScope, pageView: PageView) { //moved to refresh after drawing DrawCanvas.refreshUi.emit(Unit) } else { - SnackState.globalSnackFlow.emit(SnackConf( - text = "Nothing to undo/redo", - duration = 3000, - )) + SnackState.globalSnackFlow.emit( + SnackConf( + text = "Nothing to undo/redo", + duration = 3000, + ) + ) } } diff --git a/app/src/main/java/com/olup/notable/utils/page.kt b/app/src/main/java/com/olup/notable/utils/page.kt index 36d44bb5..53f3f0dc 100644 --- a/app/src/main/java/com/olup/notable/utils/page.kt +++ b/app/src/main/java/com/olup/notable/utils/page.kt @@ -1,81 +1,32 @@ package com.olup.notable + import android.content.ClipData import android.content.ClipboardManager +import android.content.ContentResolver import android.content.ContentValues import android.content.Context -import android.graphics.pdf.PdfDocument +import android.content.Intent import android.graphics.Bitmap import android.graphics.Canvas +import android.graphics.ImageDecoder +import android.graphics.pdf.PdfDocument +import android.net.Uri import android.os.Environment import android.provider.MediaStore import androidx.compose.ui.unit.IntOffset -import androidx.compose.ui.graphics.asAndroidBitmap +import androidx.core.content.ContextCompat +import androidx.core.content.FileProvider import com.olup.notable.db.BookRepository import com.olup.notable.db.PageRepository import com.olup.notable.db.Stroke import io.shipbook.shipbooksdk.Log -import java.io.FileOutputStream -import java.io.File -import java.nio.file.Files -import kotlin.io.path.absolutePathString -import kotlin.io.path.div -import java.io.IOException import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.withContext -import android.app.Activity -import android.content.ContentResolver -import android.content.Intent -import android.graphics.BitmapFactory -import android.graphics.ImageDecoder -import android.net.Uri -import android.os.Build -import android.provider.OpenableColumns -import android.widget.Toast -import androidx.activity.compose.ManagedActivityResultLauncher -import androidx.activity.compose.rememberLauncherForActivityResult - - -import androidx.activity.result.contract.ActivityResultContracts -import androidx.activity.result.ActivityResultLauncher -import androidx.activity.result.PickVisualMediaRequest -import androidx.activity.result.contract.ActivityResultContracts.PickVisualMedia -import androidx.compose.foundation.Image -import androidx.compose.runtime.Composable -import androidx.compose.runtime.LaunchedEffect -import androidx.compose.ui.graphics.ImageBitmap -import androidx.compose.ui.graphics.asImageBitmap -import kotlinx.coroutines.launch -import androidx.compose.ui.Modifier -import androidx.compose.foundation.layout.fillMaxWidth -import androidx.compose.foundation.layout.fillMaxHeight -import androidx.compose.runtime.mutableStateOf -import androidx.compose.runtime.remember - - -import androidx.activity.compose.rememberLauncherForActivityResult -import androidx.compose.foundation.Image -import androidx.compose.foundation.layout.Arrangement -import androidx.compose.foundation.layout.Column -import androidx.compose.foundation.layout.Row -import androidx.compose.foundation.layout.Spacer -import androidx.compose.foundation.layout.fillMaxWidth -import androidx.compose.foundation.layout.height -import androidx.compose.foundation.layout.padding -import androidx.compose.foundation.layout.size -import androidx.compose.foundation.shape.RoundedCornerShape -import androidx.compose.runtime.getValue -import androidx.compose.runtime.mutableStateOf -import androidx.compose.runtime.remember -import androidx.compose.runtime.setValue -import androidx.compose.ui.Alignment -import androidx.compose.ui.draw.clip -import androidx.compose.ui.graphics.asImageBitmap -import androidx.compose.ui.layout.ContentScale -import androidx.compose.ui.platform.LocalContext -import androidx.core.content.ContentProviderCompat.requireContext -import androidx.core.content.ContextCompat -import androidx.core.content.FileProvider +import java.io.File +import java.io.FileOutputStream +import java.io.IOException +import kotlin.io.path.div suspend fun exportBook(context: Context, bookId: String): String { val book = BookRepository(context).getById(bookId) ?: return "Book ID not found" @@ -152,21 +103,21 @@ fun exportPageToPng(context: Context, pageId: String): String { context, "com.olup.notable.provider", //(use your app signature + ".provider" ) bitmapFile - ); + ) val sendIntent = Intent().apply { if (contentUri != null) { action = Intent.ACTION_SEND addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION) // temp permission for receiving app to read this file - putExtra(Intent.EXTRA_STREAM, contentUri); - type = "image/png"; + putExtra(Intent.EXTRA_STREAM, contentUri) + type = "image/png" } context.grantUriPermission( "android", contentUri, Intent.FLAG_GRANT_READ_URI_PERMISSION - ); + ) } ContextCompat.startActivity( @@ -399,20 +350,12 @@ private fun PdfDocument.writePage(context: Context, number: Int, repo: PageRepos * @return The Bitmap representation of the image, or null if conversion fails. * https://medium.com/@munbonecci/how-to-display-an-image-loaded-from-the-gallery-using-pick-visual-media-in-jetpack-compose-df83c78a66bf */ -fun uriToBitmap(context: Context, uri: Uri): Bitmap? { +fun uriToBitmap(context: Context, uri: Uri): Bitmap { // Obtain the content resolver from the context val contentResolver: ContentResolver = context.contentResolver - // Check the API level to use the appropriate method for decoding the Bitmap - return if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.P) { - // For Android P (API level 28) and higher, use ImageDecoder to decode the Bitmap - val source = ImageDecoder.createSource(contentResolver, uri) - ImageDecoder.decodeBitmap(source) - } else { - // For versions prior to Android P, use BitmapFactory to decode the Bitmap - val bitmap = context.contentResolver.openInputStream(uri)?.use { stream -> - Bitmap.createBitmap(BitmapFactory.decodeStream(stream)) - } - bitmap - } + // Since the minimum SDK is 29, we can directly use ImageDecoder to decode the Bitmap + val source = ImageDecoder.createSource(contentResolver, uri) + return ImageDecoder.decodeBitmap(source) + } diff --git a/app/src/main/java/com/olup/notable/utils/pen.kt b/app/src/main/java/com/olup/notable/utils/pen.kt index dc8c51b8..f70b99df 100644 --- a/app/src/main/java/com/olup/notable/utils/pen.kt +++ b/app/src/main/java/com/olup/notable/utils/pen.kt @@ -2,7 +2,7 @@ package com.olup.notable import com.onyx.android.sdk.pen.style.StrokeStyle -enum class Pen (val penName : String) { +enum class Pen(val penName: String) { BALLPEN("BALLPEN"), REDBALLPEN("REDBALLPEN"), GREENBALLPEN("GREENBALLPEN"), diff --git a/app/src/main/java/com/olup/notable/utils/utils.kt b/app/src/main/java/com/olup/notable/utils/utils.kt index 0d79e708..1bd00632 100644 --- a/app/src/main/java/com/olup/notable/utils/utils.kt +++ b/app/src/main/java/com/olup/notable/utils/utils.kt @@ -2,10 +2,16 @@ package com.olup.notable import android.content.Context import android.content.Intent -import android.graphics.* -import android.util.DisplayMetrics +import android.graphics.Bitmap +import android.graphics.Canvas +import android.graphics.Color +import android.graphics.Paint +import android.graphics.Path +import android.graphics.PointF +import android.graphics.Rect +import android.graphics.RectF +import android.graphics.Region import android.util.TypedValue -import io.shipbook.shipbooksdk.Log import androidx.compose.foundation.clickable import androidx.compose.foundation.interaction.MutableInteractionSource import androidx.compose.runtime.remember @@ -13,14 +19,15 @@ import androidx.compose.ui.Modifier import androidx.compose.ui.composed import androidx.compose.ui.geometry.Offset import androidx.compose.ui.unit.Dp -import androidx.compose.ui.unit.IntOffset import androidx.core.content.ContextCompat import androidx.core.content.FileProvider import androidx.core.graphics.toRect import androidx.core.graphics.toRegion -import com.olup.notable.db.* +import com.olup.notable.db.Image +import com.olup.notable.db.Stroke +import com.olup.notable.db.StrokePoint import com.onyx.android.sdk.data.note.TouchPoint -import kotlinx.coroutines.CoroutineScope +import io.shipbook.shipbooksdk.Log import kotlinx.coroutines.flow.Flow import kotlinx.coroutines.flow.flow import kotlinx.coroutines.launch @@ -33,15 +40,16 @@ import java.io.IOException fun Modifier.noRippleClickable( onClick: () -> Unit ): Modifier = composed { - clickable(indication = null, interactionSource = remember { MutableInteractionSource() }) { - onClick() - } + this.then( + clickable(indication = null, interactionSource = remember { MutableInteractionSource() }) { + onClick() + }) } fun convertDpToPixel(dp: Dp, context: Context): Float { val resources = context.resources - val metrics: DisplayMetrics = resources.getDisplayMetrics() +// val metrics: DisplayMetrics = resources.displayMetrics return TypedValue.applyDimension( TypedValue.COMPLEX_UNIT_DIP, dp.value, @@ -159,7 +167,7 @@ enum class SelectPointPosition { CENTER } -// touchpoints is in wiew coordinates +// touchpoints is in view coordinates fun handleDraw( page: PageView, historyBucket: MutableList, @@ -240,7 +248,7 @@ fun handleLine( fun lerp(start: Float, end: Float, fraction: Float) = start + (end - start) * fraction val numberOfPoints = 100 // Define how many points should line have - val points2 = List(numberOfPoints) { i -> + val points2 = List(numberOfPoints) { i -> val fraction = i.toFloat() / (numberOfPoints - 1) val x = lerp(startPoint.x, endPoint.x, fraction) val y = lerp(startPoint.y, endPoint.y, fraction) @@ -297,7 +305,7 @@ fun imageBounds(image: Image): RectF { fun strokeBounds(strokes: List): Rect { - if (strokes.size == 0) return Rect() + if (strokes.isEmpty()) return Rect() val stroke = strokes[0] val rect = Rect( stroke.left.toInt(), stroke.top.toInt(), stroke.right.toInt(), stroke.bottom.toInt() @@ -392,7 +400,7 @@ fun selectStrokesFromPath(strokes: List, path: Path): List { return strokes.filter { strokeBounds(it).intersect(bounds) - }.filter() { it.points.any { region.contains(it.x.toInt(), it.y.toInt()) } } + }.filter { it.points.any { region.contains(it.x.toInt(), it.y.toInt()) } } } fun offsetStroke(stroke: Stroke, offset: Offset): Stroke { @@ -417,8 +425,7 @@ fun offsetImage(image: Image, offset: Offset): Image { } // Why it is needed? I try to removed it, and sharing bimap seems to work. -public class Provider : FileProvider(R.xml.file_paths) { -} +class Provider : FileProvider(R.xml.file_paths) fun shareBitmap(context: Context, bitmap: Bitmap) { val bmpWithBackground = @@ -448,17 +455,17 @@ fun shareBitmap(context: Context, bitmap: Bitmap) { context, "com.olup.notable.provider", //(use your app signature + ".provider" ) bitmapFile - ); + ) val sendIntent = Intent().apply { if (contentUri != null) { action = Intent.ACTION_SEND addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION) // temp permission for receiving app to read this file - putExtra(Intent.EXTRA_STREAM, contentUri); - type = "image/png"; + putExtra(Intent.EXTRA_STREAM, contentUri) + type = "image/png" } - context.grantUriPermission("android", contentUri, Intent.FLAG_GRANT_READ_URI_PERMISSION); + context.grantUriPermission("android", contentUri, Intent.FLAG_GRANT_READ_URI_PERMISSION) } ContextCompat.startActivity(context, Intent.createChooser(sendIntent, "Choose an app"), null) diff --git a/app/src/main/java/com/olup/notable/utils/versionChecker.kt b/app/src/main/java/com/olup/notable/utils/versionChecker.kt index 7ddf61d6..5c4931b5 100644 --- a/app/src/main/java/com/olup/notable/utils/versionChecker.kt +++ b/app/src/main/java/com/olup/notable/utils/versionChecker.kt @@ -6,9 +6,9 @@ import io.shipbook.shipbooksdk.Log import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.launch -import java.net.URL import kotlinx.serialization.decodeFromString import kotlinx.serialization.json.Json +import java.net.URL @kotlinx.serialization.Serializable data class ghVersion(val name: String, val prerelease: Boolean, val html_url: String) @@ -73,7 +73,7 @@ fun isLatestVersion(context: Context, force: Boolean = false): Boolean { try { val version = getCurrentVersionName(context) val latestVersion = getLatestReleaseVersion("olup", "notable") - Log.i(TAG, "Version is ${version} and latest on repo is ${latestVersion}") + Log.i(TAG, "Version is $version and latest on repo is $latestVersion") CoroutineScope(Dispatchers.Default).launch { SnackState.globalSnackFlow.emit( SnackConf( @@ -88,13 +88,13 @@ fun isLatestVersion(context: Context, force: Boolean = false): Boolean { throw Exception("One of the version is null - comparison is impossible") } - val versionVersion = Version.fromString(version!!) - val latestVersionVersion = Version.fromString(latestVersion!!) + val versionVersion = Version.fromString(version) + val latestVersionVersion = Version.fromString(latestVersion) // If either version does not fit simple semantic version don't compare if (latestVersionVersion == null || versionVersion == null) { throw Exception( - "One of the version doesn't match simple semantic - comparison is impossible" + "One of the version doesn't match simple semantic - comparison is impossible" ) } @@ -107,4 +107,4 @@ fun isLatestVersion(context: Context, force: Boolean = false): Boolean { } } -val isNext = BuildConfig.IS_NEXT +const val isNext = BuildConfig.IS_NEXT diff --git a/app/src/main/java/com/olup/notable/views/EditorView.kt b/app/src/main/java/com/olup/notable/views/EditorView.kt index c26063d5..5f323636 100644 --- a/app/src/main/java/com/olup/notable/views/EditorView.kt +++ b/app/src/main/java/com/olup/notable/views/EditorView.kt @@ -1,19 +1,21 @@ package com.olup.notable -import io.shipbook.shipbooksdk.Log import androidx.compose.foundation.ExperimentalFoundationApi -import androidx.compose.foundation.gestures.* -import androidx.compose.foundation.layout.* -import androidx.compose.material.* -import androidx.compose.runtime.* +import androidx.compose.foundation.layout.BoxWithConstraints +import androidx.compose.foundation.layout.Row +import androidx.compose.foundation.layout.Spacer +import androidx.compose.foundation.layout.fillMaxHeight +import androidx.compose.foundation.layout.fillMaxWidth +import androidx.compose.runtime.Composable +import androidx.compose.runtime.LaunchedEffect +import androidx.compose.runtime.remember +import androidx.compose.runtime.rememberCoroutineScope import androidx.compose.ui.ExperimentalComposeUiApi import androidx.compose.ui.Modifier -import androidx.compose.ui.input.pointer.* import androidx.compose.ui.platform.LocalContext import androidx.navigation.NavController -import com.olup.notable.db.* import com.olup.notable.ui.theme.InkaTheme -import com.onyx.android.sdk.pen.* +import io.shipbook.shipbooksdk.Log @OptIn(ExperimentalComposeUiApi::class) @@ -37,9 +39,9 @@ fun EditorView( return } - BoxWithConstraints() { - var height = convertDpToPixel(this.maxHeight, context).toInt() - var width = convertDpToPixel(this.maxWidth, context).toInt() + BoxWithConstraints { + val height = convertDpToPixel(this.maxHeight, context).toInt() + val width = convertDpToPixel(this.maxWidth, context).toInt() val page = remember { @@ -55,7 +57,7 @@ fun EditorView( // Dynamically update the page width when the Box constraints change LaunchedEffect(width, height) { if (page.width != width || page.viewHeight != height) { - page.updateDimensions(width,height) + page.updateDimensions(width, height) DrawCanvas.refreshUi.emit(Unit) } } @@ -104,7 +106,7 @@ fun EditorView( fun goToNextPage() { if (_bookId != null) { val newPageId = appRepository.getNextPageIdFromBookAndPage( - pageId = _pageId, notebookId = _bookId!! + pageId = _pageId, notebookId = _bookId ) navController.navigate("books/${_bookId}/pages/${newPageId}") { popUpTo(lastRoute!!.destination.id) { @@ -117,7 +119,7 @@ fun EditorView( fun goToPreviousPage() { if (_bookId != null) { val newPageId = appRepository.getPreviousPageIdFromBookAndPage( - pageId = _pageId, notebookId = _bookId!! + pageId = _pageId, notebookId = _bookId ) if (newPageId != null) navController.navigate("books/${_bookId}/pages/${newPageId}") } @@ -136,7 +138,9 @@ fun EditorView( state = editorState ) SelectedBitmap(editorState = editorState, controlTower = editorControlTower) - Row(modifier = Modifier.fillMaxWidth().fillMaxHeight()){ + Row(modifier = Modifier + .fillMaxWidth() + .fillMaxHeight()) { Spacer(modifier = Modifier.weight(1f)) ScrollIndicator(context = context, state = editorState) } diff --git a/app/src/main/java/com/olup/notable/views/FloatingEditorView.kt b/app/src/main/java/com/olup/notable/views/FloatingEditorView.kt index 7a55d09b..0fedb8cc 100644 --- a/app/src/main/java/com/olup/notable/views/FloatingEditorView.kt +++ b/app/src/main/java/com/olup/notable/views/FloatingEditorView.kt @@ -2,22 +2,25 @@ package com.olup.notable.views import androidx.compose.foundation.ExperimentalFoundationApi import androidx.compose.foundation.background -import androidx.compose.foundation.layout.* -import androidx.compose.material.* -import androidx.compose.runtime.* -import androidx.compose.ui.Alignment +import androidx.compose.foundation.layout.Box +import androidx.compose.foundation.layout.Column +import androidx.compose.foundation.layout.fillMaxHeight +import androidx.compose.foundation.layout.fillMaxSize +import androidx.compose.foundation.layout.fillMaxWidth +import androidx.compose.runtime.Composable +import androidx.compose.runtime.getValue +import androidx.compose.runtime.mutableStateOf +import androidx.compose.runtime.remember +import androidx.compose.runtime.setValue import androidx.compose.ui.Modifier import androidx.compose.ui.graphics.Color import androidx.compose.ui.platform.LocalContext -import androidx.compose.ui.res.painterResource -import androidx.compose.ui.unit.dp import androidx.compose.ui.window.Dialog import androidx.compose.ui.window.DialogProperties import androidx.navigation.NavController import com.olup.notable.AppRepository import com.olup.notable.AppSettings import com.olup.notable.EditorView -import com.olup.notable.R import com.olup.notable.db.Page import com.olup.notable.ui.theme.InkaTheme @@ -29,6 +32,7 @@ fun FloatingEditorView( pageId: String? = null, onDismissRequest: () -> Unit ) { + // TODO: var isFullScreen by remember { mutableStateOf(false) } // State for full-screen mode Dialog( @@ -58,7 +62,8 @@ fun FloatingEditorView( } else if (bookId != null) { // get first page of notebook and use it as pageId val appRepository = AppRepository(LocalContext.current) - val firstPageId = appRepository.bookRepository.getById(bookId)?.pageIds?.firstOrNull() + val firstPageId = + appRepository.bookRepository.getById(bookId)?.pageIds?.firstOrNull() if (firstPageId == null) { // new page uuid val page = Page( @@ -81,7 +86,7 @@ fun FloatingEditorView( ) } - + } } } diff --git a/app/src/main/java/com/olup/notable/views/HomeView.kt b/app/src/main/java/com/olup/notable/views/HomeView.kt index 0f8e4577..f45d23eb 100644 --- a/app/src/main/java/com/olup/notable/views/HomeView.kt +++ b/app/src/main/java/com/olup/notable/views/HomeView.kt @@ -1,8 +1,21 @@ package com.olup.notable -import io.shipbook.shipbooksdk.Log -import androidx.compose.foundation.* -import androidx.compose.foundation.layout.* +import androidx.compose.foundation.ExperimentalFoundationApi +import androidx.compose.foundation.background +import androidx.compose.foundation.border +import androidx.compose.foundation.combinedClickable +import androidx.compose.foundation.layout.Arrangement +import androidx.compose.foundation.layout.Box +import androidx.compose.foundation.layout.Column +import androidx.compose.foundation.layout.Row +import androidx.compose.foundation.layout.Spacer +import androidx.compose.foundation.layout.aspectRatio +import androidx.compose.foundation.layout.fillMaxSize +import androidx.compose.foundation.layout.fillMaxWidth +import androidx.compose.foundation.layout.height +import androidx.compose.foundation.layout.offset +import androidx.compose.foundation.layout.padding +import androidx.compose.foundation.layout.width import androidx.compose.foundation.lazy.LazyRow import androidx.compose.foundation.lazy.grid.GridCells import androidx.compose.foundation.lazy.grid.LazyVerticalGrid @@ -13,9 +26,14 @@ import androidx.compose.material.Badge import androidx.compose.material.BadgedBox import androidx.compose.material.Icon import androidx.compose.material.Text -import androidx.compose.runtime.* +import androidx.compose.runtime.Composable +import androidx.compose.runtime.LaunchedEffect +import androidx.compose.runtime.getValue import androidx.compose.runtime.livedata.observeAsState -import androidx.compose.ui.Alignment.Companion.CenterVertically +import androidx.compose.runtime.mutableStateOf +import androidx.compose.runtime.remember +import androidx.compose.runtime.setValue +import androidx.compose.ui.Alignment import androidx.compose.ui.ExperimentalComposeUiApi import androidx.compose.ui.Modifier import androidx.compose.ui.draw.clip @@ -25,19 +43,15 @@ import androidx.compose.ui.platform.LocalContext import androidx.compose.ui.text.style.TextAlign import androidx.compose.ui.unit.dp import androidx.navigation.NavController -import com.olup.notable.AppRepository import com.olup.notable.db.Folder import com.olup.notable.db.Notebook import com.olup.notable.db.Page +import com.olup.notable.views.FloatingEditorView import compose.icons.FeatherIcons import compose.icons.feathericons.Folder import compose.icons.feathericons.Settings -import java.net.URL +import io.shipbook.shipbooksdk.Log import kotlin.concurrent.thread -import androidx.compose.material.Button -import androidx.compose.ui.Alignment -import com.olup.notable.views.FloatingEditorView -import com.olup.notable.AppSettings @ExperimentalFoundationApi @ExperimentalComposeUiApi @@ -70,8 +84,7 @@ fun Library(navController: NavController, folderId: String? = null) { Column( Modifier.fillMaxSize() ) { - Topbar( - ) { + Topbar { Row(Modifier.fillMaxWidth()) { Spacer(modifier = Modifier.weight(1f)) BadgedBox( diff --git a/app/src/main/java/com/olup/notable/views/PagesView.kt b/app/src/main/java/com/olup/notable/views/PagesView.kt index 2c39613f..67b8886f 100644 --- a/app/src/main/java/com/olup/notable/views/PagesView.kt +++ b/app/src/main/java/com/olup/notable/views/PagesView.kt @@ -3,26 +3,35 @@ package com.olup.notable import androidx.compose.foundation.ExperimentalFoundationApi import androidx.compose.foundation.border import androidx.compose.foundation.combinedClickable -import androidx.compose.foundation.layout.* +import androidx.compose.foundation.layout.Arrangement +import androidx.compose.foundation.layout.Box +import androidx.compose.foundation.layout.Column +import androidx.compose.foundation.layout.aspectRatio +import androidx.compose.foundation.layout.fillMaxSize +import androidx.compose.foundation.layout.fillMaxWidth +import androidx.compose.foundation.layout.padding import androidx.compose.foundation.lazy.grid.GridCells import androidx.compose.foundation.lazy.grid.LazyVerticalGrid import androidx.compose.material.Text -import androidx.compose.runtime.* +import androidx.compose.runtime.Composable +import androidx.compose.runtime.getValue import androidx.compose.runtime.livedata.observeAsState +import androidx.compose.runtime.mutableStateOf +import androidx.compose.runtime.remember +import androidx.compose.runtime.setValue import androidx.compose.ui.Modifier import androidx.compose.ui.graphics.Color import androidx.compose.ui.graphics.RectangleShape import androidx.compose.ui.platform.LocalContext import androidx.compose.ui.unit.dp import androidx.navigation.NavController -import com.olup.notable.AppRepository @ExperimentalFoundationApi @Composable fun PagesView(navController: NavController, bookId: String) { val appRepository = AppRepository(LocalContext.current) val book by appRepository.bookRepository.getByIdLive(bookId).observeAsState() - if(book == null) return + if (book == null) return val pageIds = book!!.pageIds val openPageId = book?.openPageId @@ -70,7 +79,12 @@ fun PagesView(navController: NavController, bookId: String) { ), pageId ) - if (selectedPageId == pageId) PageMenu(bookId, pageId, pageIndex, canDelete = pageIds.size > 1) { + if (selectedPageId == pageId) PageMenu( + bookId, + pageId, + pageIndex, + canDelete = pageIds.size > 1 + ) { selectedPageId = null } diff --git a/app/src/main/java/com/olup/notable/views/Router.kt b/app/src/main/java/com/olup/notable/views/Router.kt index c6f0960a..140377d1 100644 --- a/app/src/main/java/com/olup/notable/views/Router.kt +++ b/app/src/main/java/com/olup/notable/views/Router.kt @@ -5,8 +5,18 @@ import androidx.compose.animation.ExitTransition import androidx.compose.animation.ExperimentalAnimationApi import androidx.compose.foundation.ExperimentalFoundationApi import androidx.compose.foundation.gestures.detectTapGestures -import androidx.compose.foundation.layout.* -import androidx.compose.runtime.* +import androidx.compose.foundation.layout.Box +import androidx.compose.foundation.layout.Column +import androidx.compose.foundation.layout.Spacer +import androidx.compose.foundation.layout.fillMaxHeight +import androidx.compose.foundation.layout.fillMaxWidth +import androidx.compose.foundation.layout.height +import androidx.compose.runtime.Composable +import androidx.compose.runtime.LaunchedEffect +import androidx.compose.runtime.getValue +import androidx.compose.runtime.mutableStateOf +import androidx.compose.runtime.remember +import androidx.compose.runtime.setValue import androidx.compose.ui.ExperimentalComposeUiApi import androidx.compose.ui.Modifier import androidx.compose.ui.input.pointer.pointerInput @@ -108,7 +118,7 @@ fun Router() { .fillMaxWidth() .height(50.dp) .pointerInteropFilter { - if(it.size == 0f) return@pointerInteropFilter true + if (it.size == 0f) return@pointerInteropFilter true false } .pointerInput(Unit) { diff --git a/app/src/main/res/mipmap-anydpi-v26/ic_launcher.xml b/app/src/main/res/mipmap-anydpi/ic_launcher.xml similarity index 100% rename from app/src/main/res/mipmap-anydpi-v26/ic_launcher.xml rename to app/src/main/res/mipmap-anydpi/ic_launcher.xml diff --git a/app/src/main/res/mipmap-anydpi-v26/ic_launcher_round.xml b/app/src/main/res/mipmap-anydpi/ic_launcher_round.xml similarity index 100% rename from app/src/main/res/mipmap-anydpi-v26/ic_launcher_round.xml rename to app/src/main/res/mipmap-anydpi/ic_launcher_round.xml diff --git a/readme.md b/readme.md index 34a2c4d5..7fcdea54 100644 --- a/readme.md +++ b/readme.md @@ -36,10 +36,10 @@ Notable is a **custom note-taking app designed specifically for BOOX e-ink devices.** It offers a clean, minimalist design, with a range of special features and optimizations to enhance the note-taking experience. -*⚠️ This is alpha software with a couple of part time individuals pushing it further. We try to make it as stable as possible and to support a smooth update experience, but be prepared for the occasionnal bug and possible breaking changes.* +*⚠️ This is alpha software with a couple of part time individuals pushing it further. We try to make it as stable as possible and to support a smooth update experience, but be prepared for the occasional bug and possible breaking changes.* ## Features -* ⚡ **Fast Page Turn with Caching:** Notable leverages caching techniques to ensure smooth and swift page transitions, allowing you to navigate through your notes seamlessly. (next and previus pages are cached) +* ⚡ **Fast Page Turn with Caching:** Notable leverages caching techniques to ensure smooth and swift page transitions, allowing you to navigate through your notes seamlessly. (next and previous pages are cached) * ↕️ **Infinite Vertical Scroll:** Enjoy a virtually endless canvas for your notes. Scroll vertically without limitations. * 📝 **Quick Pages:** Quickly create a new page using the Quick Pages feature. * 📒 **Notebooks:** Keep related notes together and easily switch between different noteboo︂︂ks based on your needs. @@ -60,7 +60,7 @@ Select the projects ❓ What is a 'next' release?
-The 'next' release is a pre-release, and will contain features imlemented but not yet released as part of a version - and sometimes experiments that could very well not be part a release. +The 'next' release is a pre-release, and will contain features implemented but not yet released as part of a version - and sometimes experiments that could very well not be part a release. ## Gestures @@ -84,11 +84,11 @@ Notable features intuitive gestures controls within Editor's Mode, to optimize t The following table lists devices confirmed by users to be compatible with specific versions of Notable. This does not imply any commitment from the developers. Feel free to add your device to the list if tested successfully. -| Device Name | v0.0.10 | v0.0.11dev | | | | -|------------------------------------------------|---------|-----------|---------|---------|---------| -| [ONYX BOOX Go 10.3](https://onyxboox.com/boox_go103) | ✔ | ? | | | | -| [Onyx Boox Note Air 4 C](https://onyxboox.pl/en/ebook-readers/onyx-boox-note-air-4-c) | ✘ | ✔ | | | | -| [Onyx Boox Note Air 3 C](https://onyxboox.pl/en/ebook-readers/onyx-boox-note-air-3-c) | ✘ | ✔ | | | | +| Device Name | v0.0.10 | v0.0.11dev | | | | +|---------------------------------------------------------------------------------------|---------|------------|--------|--------|--------| +| [ONYX BOOX Go 10.3](https://onyxboox.com/boox_go103) | ✔ | ? | | | | +| [Onyx Boox Note Air 4 C](https://onyxboox.pl/en/ebook-readers/onyx-boox-note-air-4-c) | ✘ | ✔ | | | | +| [Onyx Boox Note Air 3 C](https://onyxboox.pl/en/ebook-readers/onyx-boox-note-air-3-c) | ✘ | ✔ | | | | From ea8b91c6f62508bd4351d40a223549688439b36a Mon Sep 17 00:00:00 2001 From: Wiktor Wichrowski <77555700+Ethran@users.noreply.github.com> Date: Wed, 15 Jan 2025 17:24:40 +0100 Subject: [PATCH 084/246] Update preview.yml --- .github/workflows/preview.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/preview.yml b/.github/workflows/preview.yml index 98493af0..f82d9e4c 100644 --- a/.github/workflows/preview.yml +++ b/.github/workflows/preview.yml @@ -2,7 +2,7 @@ name: Build and Release Preview on: push: - branches: ["main"] + branches: ["dev"] env: MAVEN_OPTS: >- From 872641108cf8790926e2fd16955f9d56e08924fd Mon Sep 17 00:00:00 2001 From: Wiktor Wichrowski <77555700+Ethran@users.noreply.github.com> Date: Wed, 15 Jan 2025 17:27:48 +0100 Subject: [PATCH 085/246] Update readme.md --- readme.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/readme.md b/readme.md index 7fcdea54..088ced08 100644 --- a/readme.md +++ b/readme.md @@ -69,7 +69,7 @@ Notable features intuitive gestures controls within Editor's Mode, to optimize t * **Swipe up or down**: Scroll the page. * **Swipe left or right:** Change to the previous/next page (only available in notebooks). * **Double tap:** Show or hide the toolbar. -* **Double tap bottom part of the screen:** Show quick navigation. +* ~**Double tap bottom part of the screen:** Show quick navigation.~ * **Hold** select image #### ✌️ 2 Fingers * **Swipe left or right:** Undo/redo your changes. From 3bb737292142da7b034f1153c0cd1aaaee870e2b Mon Sep 17 00:00:00 2001 From: Wiktor Wichrowski Date: Wed, 15 Jan 2025 18:46:13 +0100 Subject: [PATCH 086/246] when deleting selected strokes, the strokes ware not added to history, now they are --- .../com/olup/notable/classes/EditorControlTower.kt | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/app/src/main/java/com/olup/notable/classes/EditorControlTower.kt b/app/src/main/java/com/olup/notable/classes/EditorControlTower.kt index 7ffedb04..7275d0eb 100644 --- a/app/src/main/java/com/olup/notable/classes/EditorControlTower.kt +++ b/app/src/main/java/com/olup/notable/classes/EditorControlTower.kt @@ -138,10 +138,16 @@ class EditorControlTower( } val selectedStrokes = state.selectionState.selectedStrokes if (!selectedStrokes.isNullOrEmpty()) { - val imageIds: List = selectedStrokes.map { it.id } + val strokeIds: List = selectedStrokes.map { it.id } Log.i(TAG, "removing strokes") - page.removeStrokes(imageIds) + page.removeStrokes(strokeIds) + history.addOperationsToHistory( + operations = listOf( + Operation.AddStroke(selectedStrokes) + ) + ) } + state.selectionState.reset() state.isDrawing = true scope.launch { From 7cc26e2b9fbe6c76f99f9c79086372414ecf32a4 Mon Sep 17 00:00:00 2001 From: Wiktor Wichrowski <77555700+Ethran@users.noreply.github.com> Date: Wed, 15 Jan 2025 18:52:38 +0100 Subject: [PATCH 087/246] Update preview.yml --- .github/workflows/preview.yml | 1 + 1 file changed, 1 insertion(+) diff --git a/.github/workflows/preview.yml b/.github/workflows/preview.yml index f82d9e4c..984ab955 100644 --- a/.github/workflows/preview.yml +++ b/.github/workflows/preview.yml @@ -3,6 +3,7 @@ name: Build and Release Preview on: push: branches: ["dev"] + workflow_dispatch: env: MAVEN_OPTS: >- From 882fad83017d8d7e61bc51207c73b360475453bb Mon Sep 17 00:00:00 2001 From: Wiktor Wichrowski <77555700+Ethran@users.noreply.github.com> Date: Wed, 15 Jan 2025 19:04:31 +0100 Subject: [PATCH 088/246] Update preview.yml --- .github/workflows/preview.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/preview.yml b/.github/workflows/preview.yml index 984ab955..2f75cf94 100644 --- a/.github/workflows/preview.yml +++ b/.github/workflows/preview.yml @@ -24,7 +24,7 @@ jobs: - uses: gradle/gradle-build-action@v2 with: - gradle-version: 7.5 + gradle-version: 8.5 - name: Decode Keystore id: decode_keystore @@ -60,5 +60,5 @@ jobs: tag_name: next name: next prerelease: true - body: "Preview version corresponding to the latest build on main" + body: "Preview version built from branch: ${{ github.ref_name }}" token: ${{ secrets.TOKEN }} From de516373c0e0a8a560bc41e7f81cf70d82215b19 Mon Sep 17 00:00:00 2001 From: Wiktor Wichrowski <77555700+Ethran@users.noreply.github.com> Date: Thu, 16 Jan 2025 21:20:47 +0100 Subject: [PATCH 089/246] Update preview.yml --- .github/workflows/preview.yml | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/.github/workflows/preview.yml b/.github/workflows/preview.yml index 2f75cf94..9888abc6 100644 --- a/.github/workflows/preview.yml +++ b/.github/workflows/preview.yml @@ -38,9 +38,9 @@ jobs: run: | ./gradlew \ -PDEBUG_STORE_FILE="../${{ steps.decode_keystore.outputs.filePath }}" \ - -PDEBUG_STORE_PASSWORD=${{ secrets.KEYSTORE_PASSWORD }} \ - -PDEBUG_KEY_ALIAS=${{ secrets.KEY_ALIAS }} \ - -PDEBUG_KEY_PASSWORD=${{ secrets.KEY_PASSWORD }} \ + -PDEBUG_STORE_PASSWORD="${{ secrets.KEYSTORE_PASSWORD }}" \ + -PDEBUG_KEY_ALIAS="${{ secrets.KEY_ALIAS }}" \ + -PDEBUG_KEY_PASSWORD="${{ secrets.KEY_PASSWORD }}" \ -PIS_NEXT=true \ assembleDebug From 667a37de06a512388c7ff49375dce8867d29f5ae Mon Sep 17 00:00:00 2001 From: Wiktor Wichrowski <77555700+Ethran@users.noreply.github.com> Date: Thu, 16 Jan 2025 21:24:51 +0100 Subject: [PATCH 090/246] Update preview.yml --- .github/workflows/preview.yml | 17 +++++++++++------ 1 file changed, 11 insertions(+), 6 deletions(-) diff --git a/.github/workflows/preview.yml b/.github/workflows/preview.yml index 9888abc6..dcb1d8cc 100644 --- a/.github/workflows/preview.yml +++ b/.github/workflows/preview.yml @@ -36,13 +36,18 @@ jobs: - name: Execute Gradle build run: | + export DEBUG_STORE_FILE="../${{ steps.decode_keystore.outputs.filePath }}" + export DEBUG_STORE_PASSWORD="${{ secrets.KEYSTORE_PASSWORD }}" + export DEBUG_KEY_ALIAS="${{ secrets.KEY_ALIAS }}" + export DEBUG_KEY_PASSWORD="${{ secrets.KEY_PASSWORD }}" + ./gradlew \ - -PDEBUG_STORE_FILE="../${{ steps.decode_keystore.outputs.filePath }}" \ - -PDEBUG_STORE_PASSWORD="${{ secrets.KEYSTORE_PASSWORD }}" \ - -PDEBUG_KEY_ALIAS="${{ secrets.KEY_ALIAS }}" \ - -PDEBUG_KEY_PASSWORD="${{ secrets.KEY_PASSWORD }}" \ - -PIS_NEXT=true \ - assembleDebug + -PDEBUG_STORE_FILE="$DEBUG_STORE_FILE" \ + -PDEBUG_STORE_PASSWORD="$DEBUG_STORE_PASSWORD" \ + -PDEBUG_KEY_ALIAS="$DEBUG_KEY_ALIAS" \ + -PDEBUG_KEY_PASSWORD="$DEBUG_KEY_PASSWORD" \ + -PIS_NEXT=true \ + assembleDebug # - name: Cache Gradle packages # uses: actions/cache@v1 From bfb66e1d46e20e14dadf9200b7ad9f46f5e47f71 Mon Sep 17 00:00:00 2001 From: Wiktor Wichrowski <77555700+Ethran@users.noreply.github.com> Date: Thu, 16 Jan 2025 21:44:18 +0100 Subject: [PATCH 091/246] Update preview.yml --- .github/workflows/preview.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/preview.yml b/.github/workflows/preview.yml index dcb1d8cc..08fb6b41 100644 --- a/.github/workflows/preview.yml +++ b/.github/workflows/preview.yml @@ -66,4 +66,4 @@ jobs: name: next prerelease: true body: "Preview version built from branch: ${{ github.ref_name }}" - token: ${{ secrets.TOKEN }} + token: ${{ secrets.GITHUB_TOKEN }} From 7e4defbf8beea6b71a672f1654042c1faacbc363 Mon Sep 17 00:00:00 2001 From: Wiktor Wichrowski <77555700+Ethran@users.noreply.github.com> Date: Thu, 16 Jan 2025 22:22:34 +0100 Subject: [PATCH 092/246] Update preview.yml --- .github/workflows/preview.yml | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/.github/workflows/preview.yml b/.github/workflows/preview.yml index 08fb6b41..9138a17e 100644 --- a/.github/workflows/preview.yml +++ b/.github/workflows/preview.yml @@ -1,5 +1,4 @@ name: Build and Release Preview - on: push: branches: ["dev"] @@ -66,4 +65,4 @@ jobs: name: next prerelease: true body: "Preview version built from branch: ${{ github.ref_name }}" - token: ${{ secrets.GITHUB_TOKEN }} + token: ${{ secrets.TOKEN }} From 13617586e3a3725de7d184695ee85b52c003d088 Mon Sep 17 00:00:00 2001 From: Wiktor Wichrowski Date: Fri, 17 Jan 2025 00:50:22 +0100 Subject: [PATCH 093/246] Modify the readme me to include information's about this fork --- readme.md | 43 ++++++++++++++++++++++++++----------------- 1 file changed, 26 insertions(+), 17 deletions(-) diff --git a/readme.md b/readme.md index 088ced08..b4ffd45c 100644 --- a/readme.md +++ b/readme.md @@ -9,34 +9,42 @@
-Notable is a **custom note-taking app designed specifically for BOOX e-ink devices.** It offers a clean, minimalist design, with a range of special features and optimizations to enhance the note-taking experience. +## About This Fork + +This repository, maintained by **Ethran**, is a personal project based on the original [olup/notable](https://github.com/olup/notable) repository. +The base repository has been inactive for over a year, and this fork introduces updates and features tailored to my specific needs and preferences. + +### What's New in This Fork? +- Semi-active development with regular updates. +- Personal features and optimizations that make the app more functional for my use. +- Pre-release builds with experimental features and enhancements. + +> ⚠️ Please note: Since this is a personal project, the features and updates reflect what I find useful. However, feedback and suggestions are welcome! -*⚠️ This is alpha software with a couple of part time individuals pushing it further. We try to make it as stable as possible and to support a smooth update experience, but be prepared for the occasional bug and possible breaking changes.* +--- ## Features * ⚡ **Fast Page Turn with Caching:** Notable leverages caching techniques to ensure smooth and swift page transitions, allowing you to navigate through your notes seamlessly. (next and previous pages are cached) @@ -46,17 +54,17 @@ Notable is a **custom note-taking app designed specifically for BOOX e-ink devic * 📁 **Folders:** Create folders to organize your notes. * 🤏 **Editors' Mode Gestures:** [Intuitive gesture controls](#gestures) to enhance the editing experience. * 🌅 **Images:** Add, move, scale, and remove images -* ︂︂᠋︁ **slection export** share selected text +* ︂︂᠋︁ **selection export** share selected text ## Download -**Download the latest stable version of the [Notable app here.](https://github.com/olup/notable/releases/latest)** +**Download the latest stable version of the [Notable app here.](https://github.com/Ethran/notable/releases/latest)** -Alternatively, get the latest build from main from the ["next" release](https://github.com/olup/notable/releases/next) +Alternatively, get the latest build from main from the ["next" release](https://github.com/Ethran/notable/releases/next) Open up the '**Assets**' from the release, and select the `.apk` file.
❓ Where can I see alternative/older releases?
-Select the projects 'Releases' and download alternative versions of the Notable app. +You can go to original olup 'Releases' and download alternative versions of the Notable app.
❓ What is a 'next' release?
@@ -90,6 +98,7 @@ This does not imply any commitment from the developers. Feel free to add your de | [Onyx Boox Note Air 4 C](https://onyxboox.pl/en/ebook-readers/onyx-boox-note-air-4-c) | ✘ | ✔ | | | | | [Onyx Boox Note Air 3 C](https://onyxboox.pl/en/ebook-readers/onyx-boox-note-air-3-c) | ✘ | ✔ | | | | +Feel free to add your device if tested successfully! ## Screenshots From 08e698b08e0019951e7634d2ae1bad0f19137b20 Mon Sep 17 00:00:00 2001 From: Wiktor Wichrowski Date: Fri, 17 Jan 2025 00:53:51 +0100 Subject: [PATCH 094/246] marged readme.md from dev branch --- readme.md | 85 +++++++++++++++++++++++++++++++++++++------------------ 1 file changed, 57 insertions(+), 28 deletions(-) diff --git a/readme.md b/readme.md index 7b3e0175..b4ffd45c 100644 --- a/readme.md +++ b/readme.md @@ -9,56 +9,66 @@
-![Notable App](https://github.com/olup/notable/blob/main/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png?raw=true "Notable Logo") -# Notable - - - +![Notable App](https://github.com/Ethran/notable/blob/main/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png?raw=true "Notable Logo") +# Notable (Ethran Fork) + + Download here
- - - - Downloads + + + Downloads Discord - + [Features](#features) • [Download](#download) • [Gestures](#gestures) • [Supported Devices](#supported-devices) • [Contribute](#contribute) - +
-Notable is a **custom note-taking app designed specifically for BOOX e-ink devices.** It offers a clean, minimalist design, with a range of special features and optimizations to enhance the note-taking experience. +## About This Fork + +This repository, maintained by **Ethran**, is a personal project based on the original [olup/notable](https://github.com/olup/notable) repository. +The base repository has been inactive for over a year, and this fork introduces updates and features tailored to my specific needs and preferences. -*⚠️ This is alpha software with a couple of part time individuals pushing it further. We try to make it as stable as possible and to support a smooth update experience, but be prepared for the occasionnal bug and possible breaking changes.* +### What's New in This Fork? +- Semi-active development with regular updates. +- Personal features and optimizations that make the app more functional for my use. +- Pre-release builds with experimental features and enhancements. + +> ⚠️ Please note: Since this is a personal project, the features and updates reflect what I find useful. However, feedback and suggestions are welcome! + +--- ## Features -* ⚡ **Fast Page Turn with Caching:** Notable leverages caching techniques to ensure smooth and swift page transitions, allowing you to navigate through your notes seamlessly. +* ⚡ **Fast Page Turn with Caching:** Notable leverages caching techniques to ensure smooth and swift page transitions, allowing you to navigate through your notes seamlessly. (next and previous pages are cached) * ↕️ **Infinite Vertical Scroll:** Enjoy a virtually endless canvas for your notes. Scroll vertically without limitations. * 📝 **Quick Pages:** Quickly create a new page using the Quick Pages feature. -* 📒 **Notebooks:** Keep related notes together and easily switch between different notebooks based on your needs. +* 📒 **Notebooks:** Keep related notes together and easily switch between different noteboo︂︂ks based on your needs. * 📁 **Folders:** Create folders to organize your notes. * 🤏 **Editors' Mode Gestures:** [Intuitive gesture controls](#gestures) to enhance the editing experience. +* 🌅 **Images:** Add, move, scale, and remove images +* ︂︂᠋︁ **selection export** share selected text ## Download -**Download the latest stable version of the [Notable app here.](https://github.com/olup/notable/releases/latest)** +**Download the latest stable version of the [Notable app here.](https://github.com/Ethran/notable/releases/latest)** -Alternatively, get the latest build from main from the ["next" release](https://github.com/olup/notable/releases/next) +Alternatively, get the latest build from main from the ["next" release](https://github.com/Ethran/notable/releases/next) Open up the '**Assets**' from the release, and select the `.apk` file.
❓ Where can I see alternative/older releases?
-Select the projects 'Releases' and download alternative versions of the Notable app. +You can go to original olup 'Releases' and download alternative versions of the Notable app.
❓ What is a 'next' release?
-The 'next' release is a pre-release, and will contain features imlemented but not yet released as part of a version - and sometimes experiments that could very well not be part a release. +The 'next' release is a pre-release, and will contain features implemented but not yet released as part of a version - and sometimes experiments that could very well not be part a release.
## Gestures @@ -67,8 +77,8 @@ Notable features intuitive gestures controls within Editor's Mode, to optimize t * **Swipe up or down**: Scroll the page. * **Swipe left or right:** Change to the previous/next page (only available in notebooks). * **Double tap:** Show or hide the toolbar. -* **Double tap bottom part of the screen:** Show quick navigation. - +* ~**Double tap bottom part of the screen:** Show quick navigation.~ +* **Hold** select image #### ✌️ 2 Fingers * **Swipe left or right:** Undo/redo your changes. * **Single tap:** Switch between writing modes and eraser modes. @@ -79,17 +89,36 @@ Notable features intuitive gestures controls within Editor's Mode, to optimize t ## Supported Devices -The list below lists devices which users confirm to be supported by Notable. It does not imply any -commitment from the developers. Feel free to add your device to the list. +The following table lists devices confirmed by users to be compatible with specific versions of Notable. +This does not imply any commitment from the developers. Feel free to add your device to the list if tested successfully. + +| Device Name | v0.0.10 | v0.0.11dev | | | | +|---------------------------------------------------------------------------------------|---------|------------|--------|--------|--------| +| [ONYX BOOX Go 10.3](https://onyxboox.com/boox_go103) | ✔ | ? | | | | +| [Onyx Boox Note Air 4 C](https://onyxboox.pl/en/ebook-readers/onyx-boox-note-air-4-c) | ✘ | ✔ | | | | +| [Onyx Boox Note Air 3 C](https://onyxboox.pl/en/ebook-readers/onyx-boox-note-air-3-c) | ✘ | ✔ | | | | + +Feel free to add your device if tested successfully! -* [ONYX BOOX Go 10.3](https://onyxboox.com/boox_go103) -* [Onyx Boox Note Air 4 C](https://onyxboox.pl/en/ebook-readers/onyx-boox-note-air-4-c) ## Screenshots -drawing -drawing -drawing +
+ +screenshot-1 + +screenshot-2 + +screenshot-3 + + +screenshot-5 + +screenshot-6 + +screenshot-7 + +
From 047310253f22e44e852e4edddd7a77884811fa0a Mon Sep 17 00:00:00 2001 From: Wiktor Wichrowski Date: Fri, 17 Jan 2025 20:49:30 +0100 Subject: [PATCH 095/246] the return null is necessary --- app/src/main/java/com/olup/notable/db/Kv.kt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/src/main/java/com/olup/notable/db/Kv.kt b/app/src/main/java/com/olup/notable/db/Kv.kt index 05e74808..03acce8f 100644 --- a/app/src/main/java/com/olup/notable/db/Kv.kt +++ b/app/src/main/java/com/olup/notable/db/Kv.kt @@ -72,7 +72,7 @@ class KvProxy(context: Context) { } fun get(key: String, serializer: KSerializer): T? { - val kv = kvRepository.get(key) + val kv = kvRepository.get(key) ?: return null val jsonValue = kv.value return Json.decodeFromString(serializer, jsonValue) } From 832456b5b03df4843818562687bb5960c2776507 Mon Sep 17 00:00:00 2001 From: Wiktor Wichrowski Date: Fri, 17 Jan 2025 20:49:30 +0100 Subject: [PATCH 096/246] the return null is necessary --- app/src/main/java/com/olup/notable/db/Kv.kt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/src/main/java/com/olup/notable/db/Kv.kt b/app/src/main/java/com/olup/notable/db/Kv.kt index 05e74808..72dac192 100644 --- a/app/src/main/java/com/olup/notable/db/Kv.kt +++ b/app/src/main/java/com/olup/notable/db/Kv.kt @@ -72,7 +72,7 @@ class KvProxy(context: Context) { } fun get(key: String, serializer: KSerializer): T? { - val kv = kvRepository.get(key) + val kv = kvRepository.get(key) ?: return null //returns null when there is no database val jsonValue = kv.value return Json.decodeFromString(serializer, jsonValue) } From dce1ee263f53894e729ca8ce6b1e44133363218b Mon Sep 17 00:00:00 2001 From: Wiktor Wichrowski Date: Sat, 18 Jan 2025 19:35:30 +0100 Subject: [PATCH 097/246] Undo no longer waits 500ms to complete. --- .idea/other.xml | 22 +++++ .../com/olup/notable/classes/DrawCanvas.kt | 91 +++++++++++-------- .../java/com/olup/notable/utils/history.kt | 8 +- 3 files changed, 79 insertions(+), 42 deletions(-) diff --git a/.idea/other.xml b/.idea/other.xml index b45a6e02..e3e6e5de 100644 --- a/.idea/other.xml +++ b/.idea/other.xml @@ -146,6 +146,28 @@