diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..aa724b7 --- /dev/null +++ b/.gitignore @@ -0,0 +1,15 @@ +*.iml +.gradle +/local.properties +/.idea/caches +/.idea/libraries +/.idea/modules.xml +/.idea/workspace.xml +/.idea/navEditor.xml +/.idea/assetWizardSettings.xml +.DS_Store +/build +/captures +.externalNativeBuild +.cxx +local.properties diff --git a/.idea/.gitignore b/.idea/.gitignore new file mode 100644 index 0000000..26d3352 --- /dev/null +++ b/.idea/.gitignore @@ -0,0 +1,3 @@ +# Default ignored files +/shelf/ +/workspace.xml diff --git a/.idea/compiler.xml b/.idea/compiler.xml new file mode 100644 index 0000000..fb7f4a8 --- /dev/null +++ b/.idea/compiler.xml @@ -0,0 +1,6 @@ + + + + + + \ No newline at end of file diff --git a/.idea/gradle.xml b/.idea/gradle.xml new file mode 100644 index 0000000..5cd135a --- /dev/null +++ b/.idea/gradle.xml @@ -0,0 +1,20 @@ + + + + + + + \ No newline at end of file diff --git a/.idea/jarRepositories.xml b/.idea/jarRepositories.xml new file mode 100644 index 0000000..0380d8d --- /dev/null +++ b/.idea/jarRepositories.xml @@ -0,0 +1,30 @@ + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/.idea/misc.xml b/.idea/misc.xml new file mode 100644 index 0000000..4700283 --- /dev/null +++ b/.idea/misc.xml @@ -0,0 +1,9 @@ + + + + + + + + \ No newline at end of file diff --git a/.idea/runConfigurations.xml b/.idea/runConfigurations.xml new file mode 100644 index 0000000..797acea --- /dev/null +++ b/.idea/runConfigurations.xml @@ -0,0 +1,10 @@ + + + + + + \ No newline at end of file diff --git a/app/.gitignore b/app/.gitignore new file mode 100644 index 0000000..42afabf --- /dev/null +++ b/app/.gitignore @@ -0,0 +1 @@ +/build \ No newline at end of file diff --git a/app/build.gradle b/app/build.gradle new file mode 100644 index 0000000..a50f1a6 --- /dev/null +++ b/app/build.gradle @@ -0,0 +1,65 @@ +plugins { + id 'com.android.application' + id 'kotlin-android' +} + +android { + compileSdkVersion 30 + buildToolsVersion "30.0.3" + + defaultConfig { + applicationId "io.github.chronosx88.noteful" + minSdkVersion 21 + targetSdkVersion 30 + versionCode 1 + versionName "1.0" + + testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner" + } + + buildTypes { + release { + minifyEnabled false + proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro' + } + } + compileOptions { + sourceCompatibility JavaVersion.VERSION_1_8 + targetCompatibility JavaVersion.VERSION_1_8 + } + kotlinOptions { + jvmTarget = '1.8' + } +} + +dependencies { + + implementation "org.jetbrains.kotlin:kotlin-stdlib:$kotlin_version" + implementation 'androidx.core:core-ktx:1.5.0' + implementation 'androidx.appcompat:appcompat:1.3.0' + implementation 'com.google.android.material:material:1.3.0' + implementation 'androidx.constraintlayout:constraintlayout:2.0.4' + + implementation 'androidx.navigation:navigation-fragment-ktx:2.3.5' + implementation 'androidx.navigation:navigation-ui-ktx:2.3.5' + implementation 'androidx.lifecycle:lifecycle-livedata-ktx:2.3.1' + implementation 'androidx.lifecycle:lifecycle-viewmodel-ktx:2.3.1' + implementation 'androidx.navigation:navigation-fragment-ktx:2.3.5' + implementation 'androidx.navigation:navigation-ui-ktx:2.3.5' + implementation 'androidx.lifecycle:lifecycle-extensions:2.2.0' + + final def markwon_version = '4.6.2' + implementation "io.noties.markwon:core:$markwon_version" + implementation "io.noties.markwon:editor:$markwon_version" + implementation "io.noties.markwon:ext-strikethrough:$markwon_version" + implementation "io.noties.markwon:ext-tables:$markwon_version" + implementation "io.noties.markwon:ext-tasklist:$markwon_version" + implementation "io.noties.markwon:html:$markwon_version" +// implementation "io.noties.markwon:syntax-highlight:$markwon_version" + implementation 'me.saket:better-link-movement-method:2.2.0' + + + testImplementation 'junit:junit:4.+' + androidTestImplementation 'androidx.test.ext:junit:1.1.2' + androidTestImplementation 'androidx.test.espresso:espresso-core:3.3.0' +} \ No newline at end of file diff --git a/app/proguard-rules.pro b/app/proguard-rules.pro new file mode 100644 index 0000000..481bb43 --- /dev/null +++ b/app/proguard-rules.pro @@ -0,0 +1,21 @@ +# Add project specific ProGuard rules here. +# You can control the set of applied configuration files using the +# proguardFiles setting in build.gradle. +# +# For more details, see +# http://developer.android.com/guide/developing/tools/proguard.html + +# If your project uses WebView with JS, uncomment the following +# and specify the fully qualified class name to the JavaScript interface +# class: +#-keepclassmembers class fqcn.of.javascript.interface.for.webview { +# public *; +#} + +# Uncomment this to preserve the line number information for +# debugging stack traces. +#-keepattributes SourceFile,LineNumberTable + +# If you keep the line number information, uncomment this to +# hide the original source file name. +#-renamesourcefileattribute SourceFile \ No newline at end of file diff --git a/app/src/androidTest/java/io/github/chronosx88/noteful/ExampleInstrumentedTest.kt b/app/src/androidTest/java/io/github/chronosx88/noteful/ExampleInstrumentedTest.kt new file mode 100644 index 0000000..c53f325 --- /dev/null +++ b/app/src/androidTest/java/io/github/chronosx88/noteful/ExampleInstrumentedTest.kt @@ -0,0 +1,24 @@ +package io.github.chronosx88.noteful + +import androidx.test.platform.app.InstrumentationRegistry +import androidx.test.ext.junit.runners.AndroidJUnit4 + +import org.junit.Test +import org.junit.runner.RunWith + +import org.junit.Assert.* + +/** + * Instrumented test, which will execute on an Android device. + * + * See [testing documentation](http://d.android.com/tools/testing). + */ +@RunWith(AndroidJUnit4::class) +class ExampleInstrumentedTest { + @Test + fun useAppContext() { + // Context of the app under test. + val appContext = InstrumentationRegistry.getInstrumentation().targetContext + assertEquals("io.github.chronosx88.noteful", appContext.packageName) + } +} \ No newline at end of file diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml new file mode 100644 index 0000000..1c09e7c --- /dev/null +++ b/app/src/main/AndroidManifest.xml @@ -0,0 +1,23 @@ + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/app/src/main/java/io/github/chronosx88/noteful/NoteEditActivity.kt b/app/src/main/java/io/github/chronosx88/noteful/NoteEditActivity.kt new file mode 100644 index 0000000..8e9ec37 --- /dev/null +++ b/app/src/main/java/io/github/chronosx88/noteful/NoteEditActivity.kt @@ -0,0 +1,75 @@ +package io.github.chronosx88.noteful + +import android.os.Bundle +import android.view.MenuItem +import android.widget.EditText +import androidx.activity.viewModels +import androidx.appcompat.app.ActionBar +import androidx.appcompat.app.AppCompatActivity +import io.github.chronosx88.noteful.editHandlers.* +import io.github.chronosx88.noteful.models.Note +import io.noties.markwon.Markwon +import io.noties.markwon.editor.MarkwonEditor +import io.noties.markwon.editor.MarkwonEditorTextWatcher +import io.noties.markwon.editor.handler.EmphasisEditHandler +import io.noties.markwon.editor.handler.StrongEmphasisEditHandler +import io.noties.markwon.ext.strikethrough.StrikethroughPlugin +import io.noties.markwon.ext.tables.TablePlugin +import io.noties.markwon.ext.tasklist.TaskListPlugin +import io.noties.markwon.html.HtmlPlugin +import java.util.concurrent.Executors + + +class NoteEditActivity : AppCompatActivity() { + private val viewModel: NoteEditViewModel by viewModels() + + override fun onCreate(savedInstanceState: Bundle?) { + super.onCreate(savedInstanceState) + setContentView(R.layout.activity_note_edit) + + initActivity() + } + + private fun initActivity() { + // showing the back button in action bar + val actionBar: ActionBar? = supportActionBar + actionBar?.setDisplayHomeAsUpEnabled(true) + + + val note = intent.extras?.get("note") as Note + actionBar?.title = note.title + + val markwon : Markwon = Markwon.builder(this) + .usePlugin(TablePlugin.create(this)) + .usePlugin(StrikethroughPlugin.create()) + .usePlugin(HtmlPlugin.create()) + .usePlugin(TaskListPlugin.create(this)) + .build(); + val editor = MarkwonEditor.builder(markwon) + .useEditHandler(EmphasisEditHandler()) + .useEditHandler(StrongEmphasisEditHandler()) + .useEditHandler(StrikethroughEditHandler()) + .useEditHandler(CodeEditHandler()) + .useEditHandler(BlockQuoteEditHandler()) + .useEditHandler(LinkEditHandler { _, _ -> + // FIXME need to fix movement method as it doesn't support correctly edittext + }) + .useEditHandler(HeadingEditHandler()) + .build() + val editText: EditText = findViewById(R.id.note_edit_text) + +// editText.setMovementMethod(BetterLinkMovementMethod.getInstance()); // FIXME + editText.addTextChangedListener(MarkwonEditorTextWatcher.withPreRender(editor, Executors.newCachedThreadPool(), editText)); + editText.setText("**Hello World!**") + } + + override fun onOptionsItemSelected(item: MenuItem): Boolean { + when (item.itemId) { + android.R.id.home -> { + finish() + return true + } + } + return super.onOptionsItemSelected(item) + } +} \ No newline at end of file diff --git a/app/src/main/java/io/github/chronosx88/noteful/NoteEditViewModel.kt b/app/src/main/java/io/github/chronosx88/noteful/NoteEditViewModel.kt new file mode 100644 index 0000000..32d4058 --- /dev/null +++ b/app/src/main/java/io/github/chronosx88/noteful/NoteEditViewModel.kt @@ -0,0 +1,7 @@ +package io.github.chronosx88.noteful + +import androidx.lifecycle.ViewModel + +class NoteEditViewModel: ViewModel() { + +} \ No newline at end of file diff --git a/app/src/main/java/io/github/chronosx88/noteful/NoteListActivity.kt b/app/src/main/java/io/github/chronosx88/noteful/NoteListActivity.kt new file mode 100644 index 0000000..50ff045 --- /dev/null +++ b/app/src/main/java/io/github/chronosx88/noteful/NoteListActivity.kt @@ -0,0 +1,121 @@ +package io.github.chronosx88.noteful + +import android.content.Intent +import android.os.Bundle +import android.view.MenuItem +import android.widget.ArrayAdapter +import android.widget.AutoCompleteTextView +import androidx.activity.viewModels +import androidx.appcompat.app.ActionBarDrawerToggle +import androidx.appcompat.app.AppCompatActivity +import androidx.appcompat.content.res.AppCompatResources +import androidx.appcompat.widget.Toolbar +import androidx.core.view.GravityCompat +import androidx.drawerlayout.widget.DrawerLayout +import androidx.lifecycle.Observer +import androidx.recyclerview.widget.LinearLayoutManager +import androidx.recyclerview.widget.RecyclerView +import com.google.android.material.button.MaterialButton +import com.google.android.material.floatingactionbutton.FloatingActionButton +import com.google.android.material.snackbar.Snackbar +import com.google.android.material.textfield.TextInputLayout +import io.github.chronosx88.noteful.models.Note +import java.util.* + + +class NoteListActivity : AppCompatActivity() { + private lateinit var drawer: DrawerLayout + private val viewModel: NoteListViewModel by viewModels() + + override fun onCreate(savedInstanceState: Bundle?) { + super.onCreate(savedInstanceState) + setContentView(R.layout.activity_main) + + initActivity() + initSorting() + initRecyclerView() + } + + private fun initActivity() { + val toolbar: Toolbar = findViewById(R.id.toolbar) + setSupportActionBar(toolbar) + + val fab: FloatingActionButton = findViewById(R.id.fab) + fab.setOnClickListener { view -> + Snackbar.make(view, "Replace with your own action", Snackbar.LENGTH_LONG) + .setAction("Action", null).show() + } + + drawer = findViewById(R.id.drawer_layout) + + val drawerToggle = ActionBarDrawerToggle(this, drawer, R.string.navigation_drawer_open, R.string.navigation_drawer_close) + drawer.addDrawerListener(drawerToggle) + drawerToggle.syncState() + + supportActionBar?.setDisplayHomeAsUpEnabled(true) + } + + private fun initSorting() { + val items = listOf("Sort By Title", "Sort by Date Created", "Sort By Date Modified") + val adapter = ArrayAdapter(this, R.layout.simple_dropdown_item, items) + val dropDownMenu: TextInputLayout = findViewById(R.id.sort_by_menu) + val autoCompleteTV: AutoCompleteTextView = dropDownMenu.editText as AutoCompleteTextView + autoCompleteTV.setAdapter(adapter) + autoCompleteTV.setOnItemClickListener { _, _, position, _ -> + viewModel.changeSortType(position) + } + autoCompleteTV.setText(items[viewModel.sortType], false) + + val sortDirectionButton: MaterialButton = findViewById(R.id.sort_direction_button) + val changeSortDirectionIconButton: (Boolean) -> Unit = { + if (it) sortDirectionButton.icon = + AppCompatResources.getDrawable(this, R.drawable.ic_arrow_downward_black_24) + else sortDirectionButton.icon = + AppCompatResources.getDrawable(this, R.drawable.ic_arrow_upward_black_24) + } + changeSortDirectionIconButton(viewModel.sortDirection) + sortDirectionButton.setOnClickListener { + val dir = viewModel.changeSortDirection() + changeSortDirectionIconButton(dir) + } + } + + fun initRecyclerView() { + val noteList: RecyclerView = findViewById(R.id.note_list_rv) + + val adapter = NoteListAdapter { note -> + val intent = Intent(this, NoteEditActivity::class.java) + intent.putExtra("note", note) + + startActivity(intent) + } + noteList.adapter = adapter + noteList.layoutManager = LinearLayoutManager(this) + + viewModel.notes.observe(this, Observer { notes -> + adapter.data = notes.toList() as ArrayList + adapter.notifyDataSetChanged() + }) + + + } + + override fun onBackPressed() { + if (drawer.isDrawerOpen(GravityCompat.START)) { + drawer.closeDrawer(GravityCompat.START) + } else { + super.onBackPressed() + } + } + + override fun onOptionsItemSelected(item: MenuItem): Boolean { + super.onOptionsItemSelected(item) + return when (item.itemId) { + android.R.id.home -> { + drawer.openDrawer(GravityCompat.START) + true + } + else -> super.onOptionsItemSelected(item) + } + } +} \ No newline at end of file diff --git a/app/src/main/java/io/github/chronosx88/noteful/NoteListAdapter.kt b/app/src/main/java/io/github/chronosx88/noteful/NoteListAdapter.kt new file mode 100644 index 0000000..284808c --- /dev/null +++ b/app/src/main/java/io/github/chronosx88/noteful/NoteListAdapter.kt @@ -0,0 +1,32 @@ +package io.github.chronosx88.noteful + +import android.view.LayoutInflater +import android.view.View +import android.view.ViewGroup +import android.widget.TextView +import androidx.recyclerview.widget.RecyclerView +import io.github.chronosx88.noteful.models.Note +import java.util.* + +class NoteListAdapter(val listener: (Note)->Unit): RecyclerView.Adapter() { + var data = ArrayList() + + class ViewHolder(view: View) : RecyclerView.ViewHolder(view) { + val noteName: TextView = view.findViewById(R.id.note_list_item_name) + } + + override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): ViewHolder { + val view = LayoutInflater.from(parent.context) + .inflate(R.layout.note_list_item, parent, false) + return ViewHolder(view) + } + + override fun onBindViewHolder(holder: ViewHolder, position: Int) { + holder.noteName.text = data[position].title + holder.itemView.setOnClickListener { + listener(data[position]) + } + } + + override fun getItemCount(): Int = data.size +} \ No newline at end of file diff --git a/app/src/main/java/io/github/chronosx88/noteful/NoteListViewModel.kt b/app/src/main/java/io/github/chronosx88/noteful/NoteListViewModel.kt new file mode 100644 index 0000000..bf7cf62 --- /dev/null +++ b/app/src/main/java/io/github/chronosx88/noteful/NoteListViewModel.kt @@ -0,0 +1,97 @@ +package io.github.chronosx88.noteful + +import androidx.lifecycle.LiveData +import androidx.lifecycle.MutableLiveData +import androidx.lifecycle.ViewModel +import androidx.lifecycle.viewModelScope +import io.github.chronosx88.noteful.models.Note +import kotlinx.coroutines.launch + +class NoteListViewModel: ViewModel() { + private val _notes = MutableLiveData>() + val notes : LiveData> = _notes + val notesArray = arrayOf( + Note( + title = "Simple Note", + createdAt = 0, + modifiedAt = 0, + tags = listOf(), + attachments = listOf(), + favorite = false, + pinned = false, + additionalData = mapOf() + ), + Note( + title = "Yet Another Simple Note", + createdAt = 3, + modifiedAt = 4, + tags = listOf(), + attachments = listOf(), + favorite = false, + pinned = false, + additionalData = mapOf() + ), + Note( + title = "Another Simple Note", + createdAt = 2, + modifiedAt = 2, + tags = listOf(), + attachments = listOf(), + favorite = false, + pinned = false, + additionalData = mapOf() + ) + ) + var sortDirection = true + private set + var sortType = 0 + private set + + init { + sortNotes() + viewModelScope.launch { + _notes.value = notesArray + } + } + + fun changeSortType(sortTypeItem: Int) { + sortType = sortTypeItem + } + + fun changeSortDirection(): Boolean { + sortDirection = !sortDirection + sortNotes() + viewModelScope.launch { + _notes.value = notesArray + } + return sortDirection + } + + private fun sortNotes() { + if (sortDirection) { + when(sortType) { + 0 -> { + notesArray.sortBy { note -> note.title } + } + 1 -> { + notesArray.sortBy { note -> note.createdAt } + } + 2 -> { + notesArray.sortBy { note -> note.modifiedAt } + } + } + } else { + when(sortType) { + 0 -> { + notesArray.sortByDescending { note -> note.title } + } + 1 -> { + notesArray.sortByDescending { note -> note.createdAt } + } + 2 -> { + notesArray.sortByDescending { note -> note.modifiedAt } + } + } + } + } +} \ No newline at end of file diff --git a/app/src/main/java/io/github/chronosx88/noteful/editHandlers/BlockQuoteEditHandler.kt b/app/src/main/java/io/github/chronosx88/noteful/editHandlers/BlockQuoteEditHandler.kt new file mode 100644 index 0000000..7734797 --- /dev/null +++ b/app/src/main/java/io/github/chronosx88/noteful/editHandlers/BlockQuoteEditHandler.kt @@ -0,0 +1,44 @@ +package io.github.chronosx88.noteful.editHandlers + +import android.text.Editable +import android.text.Spanned +import io.noties.markwon.Markwon +import io.noties.markwon.core.MarkwonTheme +import io.noties.markwon.core.spans.BlockQuoteSpan +import io.noties.markwon.editor.EditHandler +import io.noties.markwon.editor.PersistedSpans + + +class BlockQuoteEditHandler : EditHandler { + private var theme: MarkwonTheme? = null + override fun init(markwon: Markwon) { + theme = markwon.configuration().theme() + } + + override fun configurePersistedSpans(builder: PersistedSpans.Builder) { + builder.persistSpan( + BlockQuoteSpan::class.java + ) { BlockQuoteSpan(theme!!) } + } + + override fun handleMarkdownSpan( + persistedSpans: PersistedSpans, + editable: Editable, + input: String, + span: BlockQuoteSpan, + spanStart: Int, + spanTextLength: Int + ) { + // todo: here we should actually find a proper ending of a block quote... + editable.setSpan( + persistedSpans.get(BlockQuoteSpan::class.java), + spanStart, + spanStart + spanTextLength, + Spanned.SPAN_EXCLUSIVE_EXCLUSIVE + ) + } + + override fun markdownSpanType(): Class { + return BlockQuoteSpan::class.java + } +} \ No newline at end of file diff --git a/app/src/main/java/io/github/chronosx88/noteful/editHandlers/CodeEditHandler.kt b/app/src/main/java/io/github/chronosx88/noteful/editHandlers/CodeEditHandler.kt new file mode 100644 index 0000000..72795dd --- /dev/null +++ b/app/src/main/java/io/github/chronosx88/noteful/editHandlers/CodeEditHandler.kt @@ -0,0 +1,49 @@ +package io.github.chronosx88.noteful.editHandlers + +import android.text.Editable +import android.text.Spanned +import io.noties.markwon.Markwon +import io.noties.markwon.core.MarkwonTheme +import io.noties.markwon.core.spans.CodeSpan +import io.noties.markwon.editor.EditHandler +import io.noties.markwon.editor.MarkwonEditorUtils +import io.noties.markwon.editor.PersistedSpans + + +class CodeEditHandler : EditHandler { + private var theme: MarkwonTheme? = null + override fun init(markwon: Markwon) { + theme = markwon.configuration().theme() + } + + override fun configurePersistedSpans(builder: PersistedSpans.Builder) { + builder.persistSpan(CodeSpan::class.java) { + CodeSpan( + theme!! + ) + } + } + + override fun handleMarkdownSpan( + persistedSpans: PersistedSpans, + editable: Editable, + input: String, + span: CodeSpan, + spanStart: Int, + spanTextLength: Int + ) { + val match = MarkwonEditorUtils.findDelimited(input, spanStart, "`") + if (match != null) { + editable.setSpan( + persistedSpans.get(CodeSpan::class.java), + match.start(), + match.end(), + Spanned.SPAN_EXCLUSIVE_EXCLUSIVE + ) + } + } + + override fun markdownSpanType(): Class { + return CodeSpan::class.java + } +} \ No newline at end of file diff --git a/app/src/main/java/io/github/chronosx88/noteful/editHandlers/HeadingEditHandler.kt b/app/src/main/java/io/github/chronosx88/noteful/editHandlers/HeadingEditHandler.kt new file mode 100644 index 0000000..87274ab --- /dev/null +++ b/app/src/main/java/io/github/chronosx88/noteful/editHandlers/HeadingEditHandler.kt @@ -0,0 +1,60 @@ +package io.github.chronosx88.noteful.editHandlers + +import android.text.Editable +import android.text.Spanned +import io.noties.markwon.Markwon +import io.noties.markwon.core.MarkwonTheme +import io.noties.markwon.core.spans.HeadingSpan +import io.noties.markwon.editor.EditHandler +import io.noties.markwon.editor.PersistedSpans + + +class HeadingEditHandler : EditHandler { + private var theme: MarkwonTheme? = null + override fun init(markwon: Markwon) { + theme = markwon.configuration().theme() + } + + override fun configurePersistedSpans(builder: PersistedSpans.Builder) { + builder + .persistSpan( + Head1::class.java + ) { Head1(theme!!) } + .persistSpan( + Head2::class.java + ) { Head2(theme!!) } + } + + override fun handleMarkdownSpan( + persistedSpans: PersistedSpans, + editable: Editable, + input: String, + span: HeadingSpan, + spanStart: Int, + spanTextLength: Int + ) { + val type: Class<*>? + type = when (span.level) { + 1 -> Head1::class.java + 2 -> Head2::class.java + else -> null + } + if (type != null) { + val index = input.indexOf('\n', spanStart + spanTextLength) + val end = if (index < 0) input.length else index + editable.setSpan( + persistedSpans[type], + spanStart, + end, + Spanned.SPAN_EXCLUSIVE_EXCLUSIVE + ) + } + } + + override fun markdownSpanType(): Class { + return HeadingSpan::class.java + } + + private class Head1 internal constructor(theme: MarkwonTheme) : HeadingSpan(theme, 1) + private class Head2 internal constructor(theme: MarkwonTheme) : HeadingSpan(theme, 2) +} \ No newline at end of file diff --git a/app/src/main/java/io/github/chronosx88/noteful/editHandlers/LinkEditHandler.kt b/app/src/main/java/io/github/chronosx88/noteful/editHandlers/LinkEditHandler.kt new file mode 100644 index 0000000..eed04f9 --- /dev/null +++ b/app/src/main/java/io/github/chronosx88/noteful/editHandlers/LinkEditHandler.kt @@ -0,0 +1,74 @@ +package io.github.chronosx88.noteful.editHandlers + +import android.text.Editable +import android.text.Spanned +import android.text.style.ClickableSpan +import android.view.View +import io.noties.markwon.core.spans.LinkSpan +import io.noties.markwon.editor.AbstractEditHandler +import io.noties.markwon.editor.PersistedSpans + + +class LinkEditHandler(private val onClick: (widget: View, link: String)->Unit) : + AbstractEditHandler() { + + override fun configurePersistedSpans(builder: PersistedSpans.Builder) { + builder.persistSpan( + EditLinkSpan::class.java + ) { + EditLinkSpan( + onClick + ) + } + } + + override fun handleMarkdownSpan( + persistedSpans: PersistedSpans, + editable: Editable, + input: String, + span: LinkSpan, + spanStart: Int, + spanTextLength: Int + ) { + val editLinkSpan = persistedSpans[EditLinkSpan::class.java] + editLinkSpan.link = span.link + + // First first __letter__ to find link content (scheme start in URL, receiver in email address) + // NB! do not use phone number auto-link (via LinkifyPlugin) as we cannot guarantee proper link + // display. For example, we _could_ also look for a digit, but: + // * if phone number start with special symbol, we won't have it (`+`, `(`) + // * it might interfere with an ordered-list + var start = -1 + var i = spanStart + val length = input.length + while (i < length) { + if (Character.isLetter(input[i])) { + start = i + break + } + i++ + } + if (start > -1) { + editable.setSpan( + editLinkSpan, + start, + start + spanTextLength, + Spanned.SPAN_EXCLUSIVE_EXCLUSIVE + ) + } + } + + override fun markdownSpanType(): Class { + return LinkSpan::class.java + } + + internal class EditLinkSpan(private val onClick: (widget: View, link: String)->Unit) : + ClickableSpan() { + var link: String? = null + override fun onClick(widget: View) { + if (link != null) { + onClick(widget, link!!) + } + } + } +} \ No newline at end of file diff --git a/app/src/main/java/io/github/chronosx88/noteful/editHandlers/StrikethroughEditHandler.kt b/app/src/main/java/io/github/chronosx88/noteful/editHandlers/StrikethroughEditHandler.kt new file mode 100644 index 0000000..6d7e204 --- /dev/null +++ b/app/src/main/java/io/github/chronosx88/noteful/editHandlers/StrikethroughEditHandler.kt @@ -0,0 +1,39 @@ +package io.github.chronosx88.noteful.editHandlers + +import android.text.Editable +import android.text.Spanned +import android.text.style.StrikethroughSpan +import io.noties.markwon.editor.AbstractEditHandler +import io.noties.markwon.editor.MarkwonEditorUtils +import io.noties.markwon.editor.PersistedSpans + +class StrikethroughEditHandler : AbstractEditHandler() { + override fun configurePersistedSpans(builder: PersistedSpans.Builder) { + builder.persistSpan( + StrikethroughSpan::class.java + ) { StrikethroughSpan() } + } + + override fun handleMarkdownSpan( + persistedSpans: PersistedSpans, + editable: Editable, + input: String, + span: StrikethroughSpan, + spanStart: Int, + spanTextLength: Int + ) { + val match = MarkwonEditorUtils.findDelimited(input, spanStart, "~~") + if (match != null) { + editable.setSpan( + persistedSpans.get(StrikethroughSpan::class.java), + match.start(), + match.end(), + Spanned.SPAN_EXCLUSIVE_EXCLUSIVE + ) + } + } + + override fun markdownSpanType(): Class { + return StrikethroughSpan::class.java + } +} \ No newline at end of file diff --git a/app/src/main/java/io/github/chronosx88/noteful/models/Note.kt b/app/src/main/java/io/github/chronosx88/noteful/models/Note.kt new file mode 100644 index 0000000..64c5e23 --- /dev/null +++ b/app/src/main/java/io/github/chronosx88/noteful/models/Note.kt @@ -0,0 +1,14 @@ +package io.github.chronosx88.noteful.models + +import java.io.Serializable + +data class Note( + var title: String, + var createdAt: Long, + var modifiedAt: Long, + val tags: List = listOf(), + val attachments: List = listOf(), + var favorite: Boolean, + var pinned: Boolean, + val additionalData: Map = mapOf() +): Serializable diff --git a/app/src/main/res/drawable-v24/ic_launcher_foreground.xml b/app/src/main/res/drawable-v24/ic_launcher_foreground.xml new file mode 100644 index 0000000..2b068d1 --- /dev/null +++ b/app/src/main/res/drawable-v24/ic_launcher_foreground.xml @@ -0,0 +1,30 @@ + + + + + + + + + + + \ No newline at end of file diff --git a/app/src/main/res/drawable/ic_add_white_24.xml b/app/src/main/res/drawable/ic_add_white_24.xml new file mode 100644 index 0000000..70046c4 --- /dev/null +++ b/app/src/main/res/drawable/ic_add_white_24.xml @@ -0,0 +1,5 @@ + + + diff --git a/app/src/main/res/drawable/ic_arrow_downward_black_24.xml b/app/src/main/res/drawable/ic_arrow_downward_black_24.xml new file mode 100644 index 0000000..8f9be44 --- /dev/null +++ b/app/src/main/res/drawable/ic_arrow_downward_black_24.xml @@ -0,0 +1,10 @@ + + + diff --git a/app/src/main/res/drawable/ic_arrow_upward_black_24.xml b/app/src/main/res/drawable/ic_arrow_upward_black_24.xml new file mode 100644 index 0000000..07b7a83 --- /dev/null +++ b/app/src/main/res/drawable/ic_arrow_upward_black_24.xml @@ -0,0 +1,10 @@ + + + diff --git a/app/src/main/res/drawable/ic_launcher_background.xml b/app/src/main/res/drawable/ic_launcher_background.xml new file mode 100644 index 0000000..07d5da9 --- /dev/null +++ b/app/src/main/res/drawable/ic_launcher_background.xml @@ -0,0 +1,170 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/app/src/main/res/layout/activity_main.xml b/app/src/main/res/layout/activity_main.xml new file mode 100644 index 0000000..549382a --- /dev/null +++ b/app/src/main/res/layout/activity_main.xml @@ -0,0 +1,25 @@ + + + + + + + + \ No newline at end of file diff --git a/app/src/main/res/layout/activity_note_edit.xml b/app/src/main/res/layout/activity_note_edit.xml new file mode 100644 index 0000000..6fcb48f --- /dev/null +++ b/app/src/main/res/layout/activity_note_edit.xml @@ -0,0 +1,25 @@ + + + + + \ No newline at end of file diff --git a/app/src/main/res/layout/app_bar_main.xml b/app/src/main/res/layout/app_bar_main.xml new file mode 100644 index 0000000..7ff6223 --- /dev/null +++ b/app/src/main/res/layout/app_bar_main.xml @@ -0,0 +1,35 @@ + + + + + + + + + + + + + + \ No newline at end of file diff --git a/app/src/main/res/layout/content_main.xml b/app/src/main/res/layout/content_main.xml new file mode 100644 index 0000000..c81ec18 --- /dev/null +++ b/app/src/main/res/layout/content_main.xml @@ -0,0 +1,68 @@ + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/app/src/main/res/layout/nav_header_main.xml b/app/src/main/res/layout/nav_header_main.xml new file mode 100644 index 0000000..da6c0ac --- /dev/null +++ b/app/src/main/res/layout/nav_header_main.xml @@ -0,0 +1,30 @@ + + + + + + + + \ No newline at end of file diff --git a/app/src/main/res/layout/note_list_item.xml b/app/src/main/res/layout/note_list_item.xml new file mode 100644 index 0000000..0171e54 --- /dev/null +++ b/app/src/main/res/layout/note_list_item.xml @@ -0,0 +1,22 @@ + + + + + + \ No newline at end of file diff --git a/app/src/main/res/layout/simple_dropdown_item.xml b/app/src/main/res/layout/simple_dropdown_item.xml new file mode 100644 index 0000000..6d338ca --- /dev/null +++ b/app/src/main/res/layout/simple_dropdown_item.xml @@ -0,0 +1,8 @@ + \ No newline at end of file diff --git a/app/src/main/res/mipmap-anydpi-v26/ic_launcher.xml b/app/src/main/res/mipmap-anydpi-v26/ic_launcher.xml new file mode 100644 index 0000000..eca70cf --- /dev/null +++ b/app/src/main/res/mipmap-anydpi-v26/ic_launcher.xml @@ -0,0 +1,5 @@ + + + + + \ No newline at end of file diff --git a/app/src/main/res/mipmap-anydpi-v26/ic_launcher_round.xml b/app/src/main/res/mipmap-anydpi-v26/ic_launcher_round.xml new file mode 100644 index 0000000..eca70cf --- /dev/null +++ b/app/src/main/res/mipmap-anydpi-v26/ic_launcher_round.xml @@ -0,0 +1,5 @@ + + + + + \ No newline at end of file diff --git a/app/src/main/res/mipmap-hdpi/ic_launcher.png b/app/src/main/res/mipmap-hdpi/ic_launcher.png new file mode 100644 index 0000000..a571e60 Binary files /dev/null and b/app/src/main/res/mipmap-hdpi/ic_launcher.png differ diff --git a/app/src/main/res/mipmap-hdpi/ic_launcher_round.png b/app/src/main/res/mipmap-hdpi/ic_launcher_round.png new file mode 100644 index 0000000..61da551 Binary files /dev/null and b/app/src/main/res/mipmap-hdpi/ic_launcher_round.png differ diff --git a/app/src/main/res/mipmap-mdpi/ic_launcher.png b/app/src/main/res/mipmap-mdpi/ic_launcher.png new file mode 100644 index 0000000..c41dd28 Binary files /dev/null and b/app/src/main/res/mipmap-mdpi/ic_launcher.png differ diff --git a/app/src/main/res/mipmap-mdpi/ic_launcher_round.png b/app/src/main/res/mipmap-mdpi/ic_launcher_round.png new file mode 100644 index 0000000..db5080a Binary files /dev/null and b/app/src/main/res/mipmap-mdpi/ic_launcher_round.png differ diff --git a/app/src/main/res/mipmap-xhdpi/ic_launcher.png b/app/src/main/res/mipmap-xhdpi/ic_launcher.png new file mode 100644 index 0000000..6dba46d Binary files /dev/null and b/app/src/main/res/mipmap-xhdpi/ic_launcher.png differ diff --git a/app/src/main/res/mipmap-xhdpi/ic_launcher_round.png b/app/src/main/res/mipmap-xhdpi/ic_launcher_round.png new file mode 100644 index 0000000..da31a87 Binary files /dev/null and b/app/src/main/res/mipmap-xhdpi/ic_launcher_round.png differ diff --git a/app/src/main/res/mipmap-xxhdpi/ic_launcher.png b/app/src/main/res/mipmap-xxhdpi/ic_launcher.png new file mode 100644 index 0000000..15ac681 Binary files /dev/null and b/app/src/main/res/mipmap-xxhdpi/ic_launcher.png differ diff --git a/app/src/main/res/mipmap-xxhdpi/ic_launcher_round.png b/app/src/main/res/mipmap-xxhdpi/ic_launcher_round.png new file mode 100644 index 0000000..b216f2d Binary files /dev/null and b/app/src/main/res/mipmap-xxhdpi/ic_launcher_round.png differ diff --git a/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png b/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png new file mode 100644 index 0000000..f25a419 Binary files /dev/null and b/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png differ diff --git a/app/src/main/res/mipmap-xxxhdpi/ic_launcher_round.png b/app/src/main/res/mipmap-xxxhdpi/ic_launcher_round.png new file mode 100644 index 0000000..e96783c Binary files /dev/null and b/app/src/main/res/mipmap-xxxhdpi/ic_launcher_round.png differ diff --git a/app/src/main/res/values-night/themes.xml b/app/src/main/res/values-night/themes.xml new file mode 100644 index 0000000..517742c --- /dev/null +++ b/app/src/main/res/values-night/themes.xml @@ -0,0 +1,16 @@ + + + + \ No newline at end of file diff --git a/app/src/main/res/values/colors.xml b/app/src/main/res/values/colors.xml new file mode 100644 index 0000000..f8c6127 --- /dev/null +++ b/app/src/main/res/values/colors.xml @@ -0,0 +1,10 @@ + + + #FFBB86FC + #FF6200EE + #FF3700B3 + #FF03DAC5 + #FF018786 + #FF000000 + #FFFFFFFF + \ No newline at end of file diff --git a/app/src/main/res/values/dimen.xml b/app/src/main/res/values/dimen.xml new file mode 100644 index 0000000..0ebcaa8 --- /dev/null +++ b/app/src/main/res/values/dimen.xml @@ -0,0 +1,9 @@ + + + + 16dp + 16dp + 8dp + 100dp + 16dp + \ No newline at end of file diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml new file mode 100644 index 0000000..78195e4 --- /dev/null +++ b/app/src/main/res/values/strings.xml @@ -0,0 +1,5 @@ + + Noteful + Open navigation drawer + Close navigation drawer + \ No newline at end of file diff --git a/app/src/main/res/values/themes.xml b/app/src/main/res/values/themes.xml new file mode 100644 index 0000000..f274aeb --- /dev/null +++ b/app/src/main/res/values/themes.xml @@ -0,0 +1,37 @@ + + + + + + +