Skip to content
This repository has been archived by the owner on Sep 2, 2024. It is now read-only.

Commit

Permalink
Merge branch 'develop'
Browse files Browse the repository at this point in the history
  • Loading branch information
msoultanidis committed Jul 5, 2021
2 parents 222a9fb + da8f6cd commit 25e84e8
Show file tree
Hide file tree
Showing 67 changed files with 2,533 additions and 147 deletions.
5 changes: 5 additions & 0 deletions .github/CONTRIBUTING.md
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,11 @@
|--------------|---------------|----------|
| English | [@msoultanidis](https://github.com/msoultanidis) | Complete |
| Greek (`el`) | [@msoultanidis](https://github.com/msoultanidis) | Complete |
| Polish (`pl`) | [@TheDidek](https://github.com/TheDidek) | Complete |
| Brazilian Portuguese (`pt-rBR`) | [@RodolfoCandido](https://github.com/RodolfoCandido) | Complete |
| Italian (`it`) | [@danigarau](https://github.com/danigarau) | Complete |
| French (`fr`) | [@locness3](https://github.com/locness3) | Complete |
| Spanish (`es`) | [@urizev](https://github.com/urizev) | Complete |

You can help Quillnote grow by translating it in languages it does not support yet or by improving existing translations.

Expand Down
22 changes: 17 additions & 5 deletions app/build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -16,8 +16,8 @@ android {
applicationId "org.qosp.notes"
minSdkVersion 21
targetSdkVersion 30
versionCode 3
versionName "1.2.0"
versionCode 4
versionName "1.3.0"

testInstrumentationRunner "org.qosp.notes.TestRunner"
}
Expand All @@ -30,6 +30,18 @@ android {
}
}

flavorDimensions "versions"
productFlavors {
googleFlavor {
dimension "versions"
buildConfigField "boolean", "IS_GOOGLE", "true"
}
defaultFlavor {
dimension "versions"
buildConfigField "boolean", "IS_GOOGLE", "false"
}
}

kotlinOptions {
jvmTarget = "1.8"
}
Expand Down Expand Up @@ -104,7 +116,7 @@ dependencies {
implementation "io.noties.markwon:linkify:$markwon_version"
implementation "io.noties.markwon:ext-strikethrough:$markwon_version"
implementation "io.noties.markwon:ext-tables:$markwon_version"
implementation "io.noties.markwon:image-coil:$markwon_version"
implementation "io.noties.markwon:ext-tasklist:$markwon_version"
implementation "me.saket:better-link-movement-method:2.2.0"

// Work Manager
Expand Down Expand Up @@ -135,6 +147,6 @@ dependencies {
implementation "com.squareup.retrofit2:retrofit:$retrofit_version"
implementation "com.jakewharton.retrofit:retrofit2-kotlinx-serialization-converter:0.8.0"

// LeakCanary
debugImplementation "com.squareup.leakcanary:leakcanary-android:$leakcanary_version"
// LeakCanary
debugImplementation "com.squareup.leakcanary:leakcanary-android:$leakcanary_version"
}
4 changes: 4 additions & 0 deletions app/src/googleFlavor/res/values/no_translate_strings.xml
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
<?xml version="1.0" encoding="utf-8"?>
<resources>
<string name="app_support_page" translatable="false"></string>
</resources>
4 changes: 4 additions & 0 deletions app/src/googleFlavor/res/values/strings.xml
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
<resources>
<string name="about_support" translatable="false"></string>
<string name="about_support_subtext" translatable="false"></string>
</resources>
12 changes: 12 additions & 0 deletions app/src/main/java/org/qosp/notes/data/dao/NoteDao.kt
Original file line number Diff line number Diff line change
Expand Up @@ -148,4 +148,16 @@ interface NoteDao {
}
return Pair(column, order)
}

fun getNotesWithoutNotebook(sortMethod: SortMethod): Flow<List<Note>> {
val (column, order) = getOrderByMethod(sortMethod)
return rawGetQuery(
SimpleSQLiteQuery(
"""
SELECT * FROM notes WHERE isArchived = 0 AND isDeleted = 0 AND notebookId IS NULL
ORDER BY isPinned DESC, $column $order
"""
)
)
}
}
4 changes: 4 additions & 0 deletions app/src/main/java/org/qosp/notes/data/repo/NoteRepository.kt
Original file line number Diff line number Diff line change
Expand Up @@ -182,6 +182,10 @@ class NoteRepository(
return noteDao.getNonRemoteNotes(sortMethod, provider)
}

fun getNotesWithoutNotebook(sortMethod: SortMethod = defaultOf()): Flow<List<Note>> {
return noteDao.getNotesWithoutNotebook(sortMethod)
}

suspend fun moveRemotelyDeletedNotesToBin(idsInUse: List<Long>, provider: CloudService) {
noteDao.moveRemotelyDeletedNotesToBin(idsInUse, provider)
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,4 +6,5 @@ interface ProviderConfig {
val remoteAddress: String
val username: String
val provider: CloudService
val authenticationHeaders: Map<String, String>
}
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,7 @@ class SyncManager(
}

val config = prefs.map { prefs -> prefs.config }
.stateIn(syncingScope, SharingStarted.WhileSubscribed(5000), null)

@OptIn(ObsoleteCoroutinesApi::class)
private val actor = syncingScope.actor<Message> {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,10 @@ data class NextcloudConfig(
) : ProviderConfig {

val credentials = ("Basic " + Base64.encodeToString("$username:$password".toByteArray(), Base64.NO_WRAP)).trim()

override val provider: CloudService = CloudService.NEXTCLOUD
override val authenticationHeaders: Map<String, String>
get() = mapOf("Authorization" to credentials)

companion object {
@OptIn(ExperimentalCoroutinesApi::class)
Expand Down
35 changes: 13 additions & 22 deletions app/src/main/java/org/qosp/notes/di/MarkwonModule.kt
Original file line number Diff line number Diff line change
Expand Up @@ -6,29 +6,31 @@ import dagger.Module
import dagger.Provides
import dagger.hilt.InstallIn
import dagger.hilt.android.components.FragmentComponent
import dagger.hilt.android.qualifiers.ApplicationContext
import dagger.hilt.android.qualifiers.ActivityContext
import dagger.hilt.android.scopes.FragmentScoped
import io.noties.markwon.*
import io.noties.markwon.editor.MarkwonEditor
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.image.coil.CoilImagesPlugin
import io.noties.markwon.ext.tasklist.TaskListPlugin
import io.noties.markwon.linkify.LinkifyPlugin
import io.noties.markwon.movement.MovementMethodPlugin
import me.saket.bettermovementmethod.BetterLinkMovementMethod
import org.qosp.notes.R
import org.qosp.notes.data.sync.core.SyncManager
import org.qosp.notes.ui.editor.markdown.*
import javax.inject.Named
import org.qosp.notes.ui.utils.coil.CoilImagesPlugin
import org.qosp.notes.ui.utils.resolveAttribute

@Module
@InstallIn(FragmentComponent::class)
object MarkwonModule {
const val SUPPORTS_IMAGES = "SUPPORTS_IMAGES"

@Provides
@FragmentScoped
fun provideBaseMarkwonBuilder(@ApplicationContext context: Context): Markwon.Builder {
fun provideMarkwon(@ActivityContext context: Context, syncManager: SyncManager): Markwon {
return Markwon.builder(context)
.usePlugin(LinkifyPlugin.create(Linkify.EMAIL_ADDRESSES or Linkify.WEB_URLS))
.usePlugin(SoftBreakAddsNewLinePlugin.create())
Expand All @@ -40,26 +42,15 @@ object MarkwonModule {
builder.linkResolver(LinkResolverDef())
}
})
}

@Provides
@Named(SUPPORTS_IMAGES)
@FragmentScoped
fun provideMarkwonInstanceWithImageSupport(
@ApplicationContext context: Context,
builder: Markwon.Builder
): Markwon {
return builder
.usePlugin(CoilImagesPlugin.create(context))
.usePlugin(CoilImagesPlugin.create(context, syncManager))
.apply {
val mainColor = context.resolveAttribute(R.attr.colorMarkdownTask) ?: return@apply
val backgroundColor = context.resolveAttribute(R.attr.colorBackground) ?: return@apply
usePlugin(TaskListPlugin.create(mainColor, mainColor, backgroundColor))
}
.build()
}

@Provides
@FragmentScoped
fun provideBaseMarkwonInstance(builder: Markwon.Builder): Markwon {
return builder.build()
}

@Provides
@FragmentScoped
fun provideMarkwonEditor(markwon: Markwon): MarkwonEditor {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,8 @@ data class AppPreferences(
val dateFormat: DateFormat = defaultOf(),
val timeFormat: TimeFormat = defaultOf(),
val openMediaIn: OpenMediaIn = defaultOf(),
val showDate: ShowDate = defaultOf(),
val groupNotesWithoutNotebook: GroupNotesWithoutNotebook = defaultOf(),
val cloudService: CloudService = defaultOf(),
val syncMode: SyncMode = defaultOf(),
val backgroundSync: BackgroundSync = defaultOf(),
Expand Down
12 changes: 12 additions & 0 deletions app/src/main/java/org/qosp/notes/preferences/PreferenceEnums.kt
Original file line number Diff line number Diff line change
Expand Up @@ -72,6 +72,18 @@ enum class OpenMediaIn(override val nameResource: Int) : HasNameResource, EnumPr
EXTERNAL(R.string.preferences_open_media_in_external),
}

enum class ShowDate(override val nameResource: Int) : HasNameResource, EnumPreference by key("show_date") {
YES(R.string.yes) { override val isDefault = true },
NO(R.string.no),
}

enum class GroupNotesWithoutNotebook(
override val nameResource: Int,
) : HasNameResource, EnumPreference by key("group_notes_without_notebook") {
YES(R.string.yes),
NO(R.string.no) { override val isDefault = true },
}

enum class CloudService(override val nameResource: Int) : HasNameResource, EnumPreference by key("cloud_service") {
DISABLED(R.string.preferences_cloud_service_disabled) { override val isDefault = true },
NEXTCLOUD(R.string.preferences_cloud_service_nextcloud),
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,8 @@ class PreferenceRepository(
dateFormat = prefs.getEnum(),
timeFormat = prefs.getEnum(),
openMediaIn = prefs.getEnum(),
showDate = prefs.getEnum(),
groupNotesWithoutNotebook = prefs.getEnum(),
cloudService = prefs.getEnum(),
syncMode = prefs.getEnum(),
backgroundSync = prefs.getEnum(),
Expand Down
31 changes: 29 additions & 2 deletions app/src/main/java/org/qosp/notes/ui/ActivityViewModel.kt
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,10 @@ import kotlinx.coroutines.*
import kotlinx.coroutines.flow.SharingStarted
import kotlinx.coroutines.flow.StateFlow
import kotlinx.coroutines.flow.first
import kotlinx.coroutines.flow.flatMapLatest
import kotlinx.coroutines.flow.map
import kotlinx.coroutines.flow.stateIn
import me.msoul.datastore.defaultOf
import org.qosp.notes.components.MediaStorageManager
import org.qosp.notes.data.model.Note
import org.qosp.notes.data.model.Notebook
Expand All @@ -35,8 +38,18 @@ class ActivityViewModel @Inject constructor(
private val syncManager: SyncManager,
) : ViewModel() {

val notebooks: StateFlow<List<Notebook>> = notebookRepository.getAll()
.stateIn(viewModelScope, SharingStarted.WhileSubscribed(5000), listOf())
@OptIn(ExperimentalCoroutinesApi::class)
val notebooks: StateFlow<Pair<Boolean, List<Notebook>>> =
preferenceRepository.get<GroupNotesWithoutNotebook>().flatMapLatest { groupNotesWithoutNotebook ->
notebookRepository.getAll().map { notebooks ->
(groupNotesWithoutNotebook == GroupNotesWithoutNotebook.YES) to notebooks
}
}
.stateIn(
scope = viewModelScope,
started = SharingStarted.WhileSubscribed(5000),
initialValue = (defaultOf<GroupNotesWithoutNotebook>() == GroupNotesWithoutNotebook.YES) to listOf(),
)

var showHiddenNotes: Boolean = false
var notesToBackup: Set<Note>? = null
Expand Down Expand Up @@ -134,6 +147,20 @@ class ActivityViewModel @Inject constructor(
)
}

fun disableMarkdown(vararg notes: Note) = update(*notes) { note ->
note.copy(
isMarkdownEnabled = false,
modifiedDate = Instant.now().epochSecond,
)
}

fun enableMarkdown(vararg notes: Note) = update(*notes) { note ->
note.copy(
isMarkdownEnabled = true,
modifiedDate = Instant.now().epochSecond,
)
}

fun duplicateNotes(vararg notes: Note) = notes.forEachAsync { note ->
val oldId = note.id
val cloned = note.copy(
Expand Down
45 changes: 33 additions & 12 deletions app/src/main/java/org/qosp/notes/ui/MainActivity.kt
Original file line number Diff line number Diff line change
Expand Up @@ -125,7 +125,7 @@ class MainActivity : BaseActivity() {
val textViewUsername = header.findViewById<AppCompatTextView>(R.id.text_view_username)
val textViewProvider = header.findViewById<AppCompatTextView>(R.id.text_view_provider)

// Fixes bug that causes the header to have large padding when the keybord is open
// Fixes bug that causes the header to have large padding when the keyboard is open
ViewCompat.setOnApplyWindowInsetsListener(header) { view, insets ->
header.setPadding(0, insets.getInsets(WindowInsetsCompat.Type.systemBars()).top, 0, 0)
WindowInsetsCompat.CONSUMED
Expand Down Expand Up @@ -166,20 +166,35 @@ class MainActivity : BaseActivity() {
}
}

private fun setupDrawerMenuItemClickListeners() {
private fun setupDrawerMenuItems() {
// Alternative of setupWithNavController(), NavigationUI.java
// Sets up click listeners for all drawer menu items except from notebooks.
// Those are handled in createNotebookMenuItems()
(topLevelMenu.children + notebooksMenu.children).forEach { item ->
if (item.itemId !in primaryDestinations + secondaryDestinations) return@forEach
(topLevelMenu.children + listOfNotNull(notebooksMenu.findItem(R.id.fragment_manage_notebooks)))
.forEach { item ->
if (item.itemId !in primaryDestinations + secondaryDestinations) return@forEach

item.setOnMenuItemClickListener {
binding.drawer.closeAndThen {
navController.navigateSafely(item.itemId)
item.setOnMenuItemClickListener {
binding.drawer.closeAndThen {
navController.navigateSafely(item.itemId)
}
false // Returning true would cause the menu item to become checked.
// We check the menu items only when the destination changes.
}
false // Returning true would cause the menu item to become checked.
// We check the menu items only when the destination changes.
}

notebooksMenu.findItem(R.id.nav_default_notebook)?.setOnMenuItemClickListener {
binding.drawer.closeAndThen {
navController.navigateSafely(
R.id.fragment_notebook,
bundleOf(
"notebookId" to R.id.nav_default_notebook.toLong(),
"notebookName" to getString(R.string.default_notebook),
)
)
}
false // Returning true would cause the menu item to become checked.
// We check the menu items only when the destination changes.
}
}

Expand Down Expand Up @@ -222,7 +237,7 @@ class MainActivity : BaseActivity() {
navController =
(supportFragmentManager.findFragmentById(R.id.nav_host_fragment) as NavHostFragment).navController

setupDrawerMenuItemClickListeners()
setupDrawerMenuItems()

navController.addOnDestinationChangedListener { controller, destination, arguments ->
currentFocus?.hideKeyboard()
Expand All @@ -231,8 +246,8 @@ class MainActivity : BaseActivity() {
setDrawerEnabled(destination.id != R.id.fragment_editor)
}

activityModel.notebooks.collect(this) { notebooks ->
val notebookIds = notebooks.map { it.id.toInt() }.toSet()
activityModel.notebooks.collect(this) { (showDefaultNotebook, notebooks) ->
val notebookIds = (notebooks.map { it.id.toInt() } + R.id.nav_default_notebook).toSet()

// Remove deleted notebooks from the menu
(primaryDestinations + secondaryDestinations + notebookIds).let { dests ->
Expand All @@ -244,6 +259,12 @@ class MainActivity : BaseActivity() {
}

createNotebookMenuItems(notebooks)

val defaultTitle = getString(R.string.default_notebook)
notebooksMenu.findItem(R.id.nav_default_notebook)?.apply {
isVisible = showDefaultNotebook
title = defaultTitle + " (${getString(R.string.default_string)})".takeIf { notebooks.any { it.name == defaultTitle } }.orEmpty()
}
}
}

Expand Down
Loading

0 comments on commit 25e84e8

Please sign in to comment.