Skip to content

Commit c50b893

Browse files
Fix canvas notes layout
1 parent 05d1827 commit c50b893

9 files changed

Lines changed: 303 additions & 8 deletions

File tree

‎app/build.gradle‎

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -23,7 +23,8 @@ android {
2323
buildTypes {
2424
release {
2525
minifyEnabled true
26-
proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro'
26+
// TODO: check if proguard-android-optimize.txt still causes trouble
27+
proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
2728
}
2829
debug {
2930
applicationIdSuffix ".debug"

‎app/proguard-rules.pro‎

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -19,3 +19,9 @@
1919
-keepattributes SourceFile,LineNumberTable
2020
# Preserve class names.
2121
-dontobfuscate
22+
# NOTE: these only apply to classes matched by keep rules.
23+
-keep,allowshrinking,allowoptimization class * {
24+
*;
25+
}
26+
27+
-printconfiguration "build/outputs/mapping/configuration.txt"

‎app/src/main/assets/excalidraw_loader.js‎

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -79,7 +79,7 @@ const App = () => {
7979
export: false,
8080
loadScene: false,
8181
saveToActiveFile: false,
82-
saveAsImage: false2
82+
saveAsImage: false
8383
}
8484
}
8585
}),

‎app/src/main/assets/noteEditable.js‎

Lines changed: 30 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,37 @@
11
"use strict";
22

3+
var $ = (x) => x;
34
var glob = {
45
getComponentByEl: (x) => {
56
console.log("getComponentByEl?", x["tagName"], x["className"]);
6-
return null;
7+
return {
8+
loadReferenceLinkTitle: (el, href) => {
9+
console.log("loadReferenceLinkTitle", el, href);
10+
const a = href.lastIndexOf("/");
11+
let title;
12+
if (a === -1) {
13+
title = "root";
14+
} else {
15+
title = JSON.parse(api.getNoteInternal(href.substring(a+1))).title
16+
}
17+
if (el) {
18+
el.innerText = title;
19+
}
20+
},
21+
getData: (...x) => {
22+
console.log(x);
23+
return "FIXME: put data here";
24+
}
25+
};
26+
},
27+
getReferenceLinkTitleSync: (href) => {
28+
console.log("getReferencLinkTitleSync", href);
29+
const a = href.lastIndexOf("/");
30+
if (a === -1) {
31+
return "root";
32+
} else {
33+
return JSON.parse(api.getNoteInternal(href.substring(a+1))).title
34+
}
735
}
836
};
937
var noteId = window.location.href.substring("https://trilium-notes.invalid/note-editable#".length);
@@ -317,7 +345,7 @@ watchdog.setCreator(async (elementOrData, editorConfig) => {
317345
async function initialize() {
318346
console.log("in initialize");
319347
await watchdog.create(editor, {
320-
placeholder: "editable_text.placeholder", // TODO
348+
placeholder: "Tap to start editing...", // "editable_text.placeholder", // TODO
321349
/*
322350
mention: {}, // TODO: mentionSetup,
323351
codeBlock: {

‎app/src/main/kotlin/eu/fliegendewurst/triliumdroid/FrontendBackendApi.kt‎

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -270,16 +270,17 @@ class FrontendBackendApi(
270270
* Get a note by its ID.
271271
*/
272272
fun getNote(noteId: String): Note? {
273-
return runBlocking { Notes.getNoteWithContent(noteId) }
273+
return runBlocking { Notes.getNote(noteId) }
274274
}
275275

276276
/**
277277
* @suppress
278278
*/
279279
@JavascriptInterface
280-
fun getNoteInternal(noteId: String): JSONObject? {
280+
fun getNoteInternal(noteId: String): String? {
281281
val note = getNote(noteId) ?: return null
282-
return encodeNote(note)
282+
val json = encodeNote(note)?.toString()
283+
return json
283284
}
284285

285286
@JavascriptInterface

‎app/src/main/kotlin/eu/fliegendewurst/triliumdroid/activity/SetupActivity.kt‎

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -188,6 +188,9 @@ class SetupActivity : AppCompatActivity() {
188188

189189
runBlocking {
190190
val newInterval = binding.revisionInterval.text.toString().toIntOrNull()
191+
if (newInterval == 528491) {
192+
throw IllegalStateException("don't set the interval to 528491, that value triggers a crash")
193+
}
191194
if (newInterval != null && newInterval != Option.revisionInterval()) {
192195
Option.revisionIntervalUpdate(newInterval)
193196
}

‎app/src/main/kotlin/eu/fliegendewurst/triliumdroid/activity/main/MainActivity.kt‎

Lines changed: 15 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -69,6 +69,7 @@ import eu.fliegendewurst.triliumdroid.fragment.NoteMapFragment
6969
import eu.fliegendewurst.triliumdroid.fragment.NoteRelatedFragment
7070
import eu.fliegendewurst.triliumdroid.fragment.NoteTreeFragment
7171
import eu.fliegendewurst.triliumdroid.fragment.SyncErrorFragment
72+
import eu.fliegendewurst.triliumdroid.fragment.note.CanvasNoteFragment
7273
import eu.fliegendewurst.triliumdroid.fragment.note.NoteFragment
7374
import eu.fliegendewurst.triliumdroid.service.Icon
7475
import eu.fliegendewurst.triliumdroid.service.ProtectedSession
@@ -770,6 +771,8 @@ class MainActivity : AppCompatActivity() {
770771
}
771772
if (noteContent.isProtected && !ProtectedSession.isActive()) {
772773
showFragment(EncryptedNoteFragment(), true)
774+
} else if (noteContent.type == "canvas") {
775+
getCanvasNoteFragment().load(noteContent, content)
773776
} else {
774777
getNoteFragment().load(noteContent, content)
775778
}
@@ -795,7 +798,7 @@ class MainActivity : AppCompatActivity() {
795798
val hostFragment =
796799
supportFragmentManager.findFragmentById(R.id.fragment_container)
797800
return when (hostFragment) {
798-
is NoteFragment, is NoteEditFragment, is EmptyFragment, is NoteMapFragment, is NavigationFragment, is SyncErrorFragment, is EncryptedNoteFragment -> {
801+
is NoteFragment, is CanvasNoteFragment, is NoteEditFragment, is EmptyFragment, is NoteMapFragment, is NavigationFragment, is SyncErrorFragment, is EncryptedNoteFragment -> {
799802
hostFragment
800803
}
801804

@@ -810,6 +813,17 @@ class MainActivity : AppCompatActivity() {
810813
}
811814
}
812815

816+
fun getCanvasNoteFragment(): CanvasNoteFragment {
817+
var frag = getFragment()
818+
if (frag is CanvasNoteFragment) {
819+
return frag
820+
}
821+
// replace fragment
822+
frag = CanvasNoteFragment()
823+
showFragment(frag, false)
824+
return frag
825+
}
826+
813827
fun getNoteFragment(): NoteFragment {
814828
var frag = getFragment()
815829
if (frag is NoteFragment) {
Lines changed: 193 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,193 @@
1+
package eu.fliegendewurst.triliumdroid.fragment.note
2+
3+
import android.annotation.SuppressLint
4+
import android.os.Bundle
5+
import android.os.Handler
6+
import android.util.Log
7+
import android.view.LayoutInflater
8+
import android.view.View
9+
import android.view.ViewGroup
10+
import android.webkit.ConsoleMessage
11+
import android.widget.TextView
12+
import androidx.constraintlayout.widget.ConstraintLayout
13+
import androidx.core.view.iterator
14+
import androidx.fragment.app.Fragment
15+
import androidx.lifecycle.lifecycleScope
16+
import eu.fliegendewurst.triliumdroid.FrontendBackendApi
17+
import eu.fliegendewurst.triliumdroid.R
18+
import eu.fliegendewurst.triliumdroid.activity.main.MainActivity
19+
import eu.fliegendewurst.triliumdroid.data.Blob
20+
import eu.fliegendewurst.triliumdroid.data.Note
21+
import eu.fliegendewurst.triliumdroid.database.Attributes
22+
import eu.fliegendewurst.triliumdroid.database.Cache
23+
import eu.fliegendewurst.triliumdroid.database.Notes
24+
import eu.fliegendewurst.triliumdroid.databinding.FragmentCanvasNoteBinding
25+
import eu.fliegendewurst.triliumdroid.fragment.NoteRelatedFragment
26+
import eu.fliegendewurst.triliumdroid.util.MyWebChromeClient
27+
import kotlinx.coroutines.launch
28+
import kotlinx.coroutines.runBlocking
29+
30+
31+
class CanvasNoteFragment : Fragment(R.layout.fragment_canvas_note), NoteRelatedFragment {
32+
companion object {
33+
private const val TAG: String = "CanvasNoteFragment"
34+
const val WEBVIEW_DOMAIN: String = "https://trilium-notes.invalid/"
35+
}
36+
37+
private lateinit var binding: FragmentCanvasNoteBinding
38+
private var handler: Handler? = null
39+
private var note: Note? = null
40+
private var blob: Blob? = null
41+
private var load: Boolean = false
42+
private var subCodeNotes: List<Note>? = null
43+
var console: MutableList<ConsoleMessage> = mutableListOf()
44+
45+
override fun getNoteId(): String? {
46+
return note?.id
47+
}
48+
49+
@SuppressLint("SetJavaScriptEnabled")
50+
override fun onCreateView(
51+
inflater: LayoutInflater,
52+
container: ViewGroup?,
53+
savedInstanceState: Bundle?
54+
): View {
55+
handler = Handler(requireContext().mainLooper)
56+
57+
binding = FragmentCanvasNoteBinding.inflate(inflater, container, false)
58+
binding.webview.settings.javaScriptEnabled = true
59+
binding.webview.addJavascriptInterface(
60+
FrontendBackendApi(this, this.requireContext(), handler!!),
61+
"api"
62+
)
63+
binding.webview.webChromeClient = MyWebChromeClient(
64+
{ (this@CanvasNoteFragment.activity as MainActivity?) },
65+
{ this@CanvasNoteFragment.console.add(it) })
66+
val wvc = NoteWebViewClient(
67+
{ return@NoteWebViewClient note },
68+
{ return@NoteWebViewClient blob },
69+
{ return@NoteWebViewClient subCodeNotes },
70+
{ return@NoteWebViewClient activity as MainActivity? },
71+
{ startActivity(it) })
72+
73+
binding.webview.webViewClient = wvc
74+
75+
if (load) {
76+
viewLifecycleOwner.lifecycleScope.launch {
77+
load(note, blob)
78+
}
79+
}
80+
81+
return binding.root
82+
}
83+
84+
fun loadLater(note: Note?) {
85+
load = true
86+
this.note = note
87+
this.blob = null
88+
}
89+
90+
suspend fun load(noteToLoad: Note?, blobToDisplay: Blob? = null) {
91+
var note = noteToLoad
92+
// if called before proper creation
93+
if (this.activity == null) {
94+
loadLater(note)
95+
return
96+
}
97+
console.clear()
98+
subCodeNotes = emptyList()
99+
100+
this.note = note
101+
this.blob = blobToDisplay
102+
this.load = true
103+
Log.i(TAG, "loading ${note?.id}")
104+
if (note == null) {
105+
return
106+
}
107+
binding.textId.text = note.id
108+
if (note.content() == null && blob == null) {
109+
Cache.initializeDatabase(requireContext())
110+
note = Notes.getNoteWithContent(note.id)
111+
}
112+
if (note == null) {
113+
(this@CanvasNoteFragment.activity as MainActivity).handleEmptyNote()
114+
return
115+
}
116+
val consoleLog = false
117+
var execute = false
118+
var share = false
119+
120+
refreshHeader(note)
121+
122+
binding.webview.loadUrl(WEBVIEW_DOMAIN + note.id)
123+
124+
val main = (this@CanvasNoteFragment.activity ?: return) as MainActivity
125+
// FABs obscure excalidraw menu items
126+
main.fixVisibilityFABs(true)
127+
128+
main.setupActions(
129+
consoleLog,
130+
execute,
131+
share,
132+
note.id == "root"
133+
)
134+
}
135+
136+
suspend fun refreshHeader(note: Note) {
137+
if (!this.load || context == null) {
138+
return
139+
}
140+
if (blob != null) {
141+
// previous revision: hide labels
142+
binding.labelNoteRevisionInfo.text = blob!!.dateModified
143+
binding.noteHeaderAttributes.visibility = View.GONE
144+
return
145+
}
146+
binding.labelNoteRevisionInfo.text = ""
147+
val constraintLayout = binding.noteHeader
148+
val flow = binding.noteHeaderAttributes
149+
val attributeContentDesc = getString(R.string.attribute)
150+
// remove previously shown attributes
151+
constraintLayout.iterator().also { iterator ->
152+
iterator.forEach { view ->
153+
if (view.contentDescription == attributeContentDesc) {
154+
iterator.remove()
155+
}
156+
}
157+
}
158+
for (attribute in note.getLabels()) {
159+
if (!attribute.promoted) {
160+
continue
161+
}
162+
val view =
163+
LayoutInflater.from(context)
164+
.inflate(R.layout.item_attribute, constraintLayout, false)
165+
view.findViewById<TextView>(R.id.label_attribute_name).text = attribute.name
166+
val textInput = view.findViewById<TextView>(R.id.label_attribute_value)
167+
textInput.text = attribute.value()
168+
textInput.onFocusChangeListener = View.OnFocusChangeListener { _, hasFocus ->
169+
if (hasFocus) {
170+
return@OnFocusChangeListener
171+
}
172+
val newValue = textInput.text
173+
if (newValue != attribute.value) {
174+
runBlocking {
175+
Attributes.updateLabel(
176+
note,
177+
attribute.name,
178+
newValue.toString(),
179+
attribute.inheritable
180+
)
181+
}
182+
}
183+
}
184+
view.layoutParams = ConstraintLayout.LayoutParams(
185+
ConstraintLayout.LayoutParams.WRAP_CONTENT,
186+
ConstraintLayout.LayoutParams.WRAP_CONTENT
187+
)
188+
view.id = View.generateViewId()
189+
constraintLayout.addView(view)
190+
flow.addView(view)
191+
}
192+
}
193+
}
Lines changed: 49 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,49 @@
1+
<?xml version="1.0" encoding="utf-8"?>
2+
<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
3+
xmlns:app="http://schemas.android.com/apk/res-auto"
4+
xmlns:tools="http://schemas.android.com/tools"
5+
android:id="@+id/note_header"
6+
android:layout_width="match_parent"
7+
android:layout_height="match_parent"
8+
android:background="@color/background"
9+
tools:context=".fragment.note.CanvasNoteFragment">
10+
11+
<androidx.constraintlayout.helper.widget.Flow
12+
android:id="@+id/note_header_attributes"
13+
android:layout_width="wrap_content"
14+
android:layout_height="wrap_content"
15+
app:flow_wrapMode="chain"
16+
app:layout_constrainedWidth="true"
17+
app:layout_constraintEnd_toStartOf="@id/text_id"
18+
app:layout_constraintStart_toStartOf="parent"
19+
app:layout_constraintTop_toTopOf="parent" />
20+
21+
<TextView
22+
android:id="@+id/label_note_revision_info"
23+
android:layout_width="wrap_content"
24+
android:layout_height="wrap_content"
25+
android:textColor="@color/foreground"
26+
android:typeface="monospace"
27+
app:layout_constraintStart_toStartOf="parent"
28+
app:layout_constraintTop_toTopOf="parent" />
29+
30+
<TextView
31+
android:id="@+id/text_id"
32+
android:layout_width="wrap_content"
33+
android:layout_height="wrap_content"
34+
android:textColor="@color/foreground"
35+
android:typeface="monospace"
36+
app:layout_constraintEnd_toStartOf="parent"
37+
app:layout_constraintTop_toTopOf="parent" />
38+
39+
<WebView
40+
android:id="@+id/webview"
41+
android:layout_width="match_parent"
42+
android:layout_height="match_parent"
43+
app:layout_constrainedHeight="true"
44+
app:layout_constraintBottom_toBottomOf="parent"
45+
app:layout_constraintEnd_toEndOf="parent"
46+
app:layout_constraintStart_toStartOf="parent"
47+
app:layout_constraintTop_toBottomOf="@id/text_id" />
48+
49+
</androidx.constraintlayout.widget.ConstraintLayout>

0 commit comments

Comments
 (0)