Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 3 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,8 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/),
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).

## [Unreleased]
### Added
- Option to make a note read-only ([#69])

## [1.7.0] - 2026-01-30
### Added
Expand Down Expand Up @@ -104,6 +106,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
- Initial release

[#59]: https://github.com/FossifyOrg/Notes/issues/59
[#69]: https://github.com/FossifyOrg/Notes/issues/69
[#81]: https://github.com/FossifyOrg/Notes/issues/81
[#83]: https://github.com/FossifyOrg/Notes/issues/83
[#96]: https://github.com/FossifyOrg/Notes/issues/96
Expand Down
142 changes: 142 additions & 0 deletions app/schemas/org.fossify.notes.databases.NotesDatabase/5.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,142 @@
{
"formatVersion": 1,
"database": {
"version": 5,
"identityHash": "6d2c2b2e1c69d9d296b9c6c621f66033",
"entities": [
{
"tableName": "notes",
"createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`id` INTEGER PRIMARY KEY AUTOINCREMENT, `title` TEXT NOT NULL, `value` TEXT NOT NULL, `type` INTEGER NOT NULL, `path` TEXT NOT NULL, `protection_type` INTEGER NOT NULL, `protection_hash` TEXT NOT NULL, `is_read_only` INTEGER NOT NULL DEFAULT 0)",
"fields": [
{
"fieldPath": "id",
"columnName": "id",
"affinity": "INTEGER"
},
{
"fieldPath": "title",
"columnName": "title",
"affinity": "TEXT",
"notNull": true
},
{
"fieldPath": "value",
"columnName": "value",
"affinity": "TEXT",
"notNull": true
},
{
"fieldPath": "type",
"columnName": "type",
"affinity": "INTEGER",
"notNull": true
},
{
"fieldPath": "path",
"columnName": "path",
"affinity": "TEXT",
"notNull": true
},
{
"fieldPath": "protectionType",
"columnName": "protection_type",
"affinity": "INTEGER",
"notNull": true
},
{
"fieldPath": "protectionHash",
"columnName": "protection_hash",
"affinity": "TEXT",
"notNull": true
},
{
"fieldPath": "isReadOnly",
"columnName": "is_read_only",
"affinity": "INTEGER",
"notNull": true,
"defaultValue": "0"
}
],
"primaryKey": {
"autoGenerate": true,
"columnNames": [
"id"
]
},
"indices": [
{
"name": "index_notes_id",
"unique": true,
"columnNames": [
"id"
],
"orders": [],
"createSql": "CREATE UNIQUE INDEX IF NOT EXISTS `index_notes_id` ON `${TABLE_NAME}` (`id`)"
}
]
},
{
"tableName": "widgets",
"createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`id` INTEGER PRIMARY KEY AUTOINCREMENT, `widget_id` INTEGER NOT NULL, `note_id` INTEGER NOT NULL, `widget_bg_color` INTEGER NOT NULL, `widget_text_color` INTEGER NOT NULL, `widget_show_title` INTEGER NOT NULL)",
"fields": [
{
"fieldPath": "id",
"columnName": "id",
"affinity": "INTEGER"
},
{
"fieldPath": "widgetId",
"columnName": "widget_id",
"affinity": "INTEGER",
"notNull": true
},
{
"fieldPath": "noteId",
"columnName": "note_id",
"affinity": "INTEGER",
"notNull": true
},
{
"fieldPath": "widgetBgColor",
"columnName": "widget_bg_color",
"affinity": "INTEGER",
"notNull": true
},
{
"fieldPath": "widgetTextColor",
"columnName": "widget_text_color",
"affinity": "INTEGER",
"notNull": true
},
{
"fieldPath": "widgetShowTitle",
"columnName": "widget_show_title",
"affinity": "INTEGER",
"notNull": true
}
],
"primaryKey": {
"autoGenerate": true,
"columnNames": [
"id"
]
},
"indices": [
{
"name": "index_widgets_widget_id",
"unique": true,
"columnNames": [
"widget_id"
],
"orders": [],
"createSql": "CREATE UNIQUE INDEX IF NOT EXISTS `index_widgets_widget_id` ON `${TABLE_NAME}` (`widget_id`)"
}
]
}
],
"setupQueries": [
"CREATE TABLE IF NOT EXISTS room_master_table (id INTEGER PRIMARY KEY,identity_hash TEXT)",
"INSERT OR REPLACE INTO room_master_table (id,identity_hash) VALUES(42, '6d2c2b2e1c69d9d296b9c6c621f66033')"
]
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -254,33 +254,37 @@ class MainActivity : SimpleActivity() {

binding.mainToolbar.menu.apply {
findItem(R.id.undo).apply {
isVisible = showUndoButton && mCurrentNote.type == NoteType.TYPE_TEXT
isVisible = showUndoButton && ::mCurrentNote.isInitialized && !mCurrentNote.isReadOnly && mCurrentNote.type == NoteType.TYPE_TEXT
icon?.alpha = if (isEnabled) 255 else 127
}

findItem(R.id.redo).apply {
isVisible = showRedoButton && mCurrentNote.type == NoteType.TYPE_TEXT
isVisible = showRedoButton && ::mCurrentNote.isInitialized && !mCurrentNote.isReadOnly && mCurrentNote.type == NoteType.TYPE_TEXT
icon?.alpha = if (isEnabled) 255 else 127
}

findItem(R.id.rename_note).isVisible = multipleNotesExist
findItem(R.id.open_note).isVisible = multipleNotesExist
findItem(R.id.open_search).isVisible = !isCurrentItemChecklist
findItem(R.id.remove_done_items).isVisible = isCurrentItemChecklist
findItem(R.id.uncheck_all_items).isVisible = isCurrentItemChecklist
findItem(R.id.sort_checklist).isVisible = isCurrentItemChecklist
findItem(R.id.remove_done_items).isVisible = isCurrentItemChecklist && ::mCurrentNote.isInitialized && !mCurrentNote.isReadOnly
findItem(R.id.uncheck_all_items).isVisible = isCurrentItemChecklist && ::mCurrentNote.isInitialized && !mCurrentNote.isReadOnly
findItem(R.id.sort_checklist).isVisible = isCurrentItemChecklist && ::mCurrentNote.isInitialized && !mCurrentNote.isReadOnly
findItem(R.id.import_folder).isVisible = !isQPlus()
findItem(R.id.lock_note).isVisible =
mNotes.isNotEmpty() && (::mCurrentNote.isInitialized && !mCurrentNote.isLocked())
findItem(R.id.unlock_note).isVisible =
mNotes.isNotEmpty() && (::mCurrentNote.isInitialized && mCurrentNote.isLocked())
findItem(R.id.make_read_only).isVisible =
mNotes.isNotEmpty() && (::mCurrentNote.isInitialized && !mCurrentNote.isReadOnly)
findItem(R.id.allow_editing).isVisible =
mNotes.isNotEmpty() && (::mCurrentNote.isInitialized && mCurrentNote.isReadOnly)
findItem(R.id.more_apps_from_us).isVisible =
!resources.getBoolean(org.fossify.commons.R.bool.hide_google_relations)
findItem(R.id.delete_note).isVisible = !isDefaultEmptyNote || mNotes.size > 1

saveNoteButton = findItem(R.id.save_note)
saveNoteButton!!.isVisible =
!config.autosaveNotes && showSaveButton && (::mCurrentNote.isInitialized && mCurrentNote.type == NoteType.TYPE_TEXT)
!config.autosaveNotes && showSaveButton && (::mCurrentNote.isInitialized && mCurrentNote.type == NoteType.TYPE_TEXT && !mCurrentNote.isReadOnly)
}

binding.pagerTabStrip.beVisibleIf(multipleNotesExist)
Expand All @@ -307,6 +311,7 @@ class MainActivity : SimpleActivity() {
R.id.cab_create_shortcut -> createShortcut()
R.id.lock_note -> lockNote()
R.id.unlock_note -> unlockNote()
R.id.make_read_only, R.id.allow_editing -> fragment?.handleUnlocking { toggleReadOnly() }
R.id.open_file -> tryOpenFile()
R.id.import_folder -> openFolder()
R.id.export_as_file -> fragment?.handleUnlocking { tryExportAsFile() }
Expand Down Expand Up @@ -582,13 +587,15 @@ class MainActivity : SimpleActivity() {
onPageChangeListener {
mCurrentNote = mNotes[it]
config.currentNoteId = mCurrentNote.id!!
applyReadOnlyStateToCurrentNote()
refreshMenuItems()
}
}

if (!config.showKeyboard || mCurrentNote.type == NoteType.TYPE_CHECKLIST) {
if (!config.showKeyboard || mCurrentNote.type == NoteType.TYPE_CHECKLIST || mCurrentNote.isReadOnly) {
hideKeyboard()
}
applyReadOnlyStateToCurrentNote()
refreshMenuItems()
}
}
Expand Down Expand Up @@ -744,6 +751,7 @@ class MainActivity : SimpleActivity() {
val index = getNoteIndexWithId(id)
binding.viewPager.currentItem = index
mCurrentNote = mNotes[index]
applyReadOnlyStateToCurrentNote()
}
}

Expand Down Expand Up @@ -1334,6 +1342,10 @@ class MainActivity : SimpleActivity() {
}

private fun addTextToCurrentNote(text: String) {
if (::mCurrentNote.isInitialized && mCurrentNote.isReadOnly) {
toast(R.string.note_is_read_only)
return
}
getPagerAdapter().appendText(binding.viewPager.currentItem, text)
}

Expand Down Expand Up @@ -1599,4 +1611,29 @@ class MainActivity : SimpleActivity() {
updateWidgets()
}
}

private fun toggleReadOnly() {
if (!::mCurrentNote.isInitialized) {
return
}

mCurrentNote.isReadOnly = !mCurrentNote.isReadOnly
if (mCurrentNote.isReadOnly) {
showUndoButton = false
showRedoButton = false
showSaveButton = false
}
NotesHelper(this).insertOrUpdateNote(mCurrentNote) {
mNotes.firstOrNull { it.id == mCurrentNote.id }?.isReadOnly = mCurrentNote.isReadOnly
applyReadOnlyStateToCurrentNote()
refreshMenuItems()
}
}

private fun applyReadOnlyStateToCurrentNote() {
if (!::mCurrentNote.isInitialized) {
return
}
getCurrentFragment()?.updateReadOnlyState(mCurrentNote.isReadOnly)
}
}
20 changes: 17 additions & 3 deletions app/src/main/kotlin/org/fossify/notes/adapters/TasksAdapter.kt
Original file line number Diff line number Diff line change
Expand Up @@ -45,6 +45,7 @@ class TasksAdapter(

private var touchHelper: ItemTouchHelper? = null
private var startReorderDragListener: StartReorderDragListener
private var readOnly = false

init {
setupDragListener(true)
Expand All @@ -63,7 +64,7 @@ class TasksAdapter(
override fun getActionMenuId() = R.menu.cab_checklist

override fun actionItemPressed(id: Int) {
if (selectedKeys.isEmpty()) {
if (readOnly || selectedKeys.isEmpty()) {
return
}

Expand Down Expand Up @@ -123,7 +124,7 @@ class TasksAdapter(

override fun onBindViewHolder(holder: ViewHolder, position: Int) {
val item = getItem(position)
holder.bindView(item, allowSingleClick = true, allowLongClick = true) { itemView, _ ->
holder.bindView(item, allowSingleClick = true, allowLongClick = !readOnly) { itemView, _ ->
when (item) {
is Task -> setupView(itemView, item, holder)
is CompletedTasks -> setupCompletedTasks(itemView, item)
Expand Down Expand Up @@ -174,9 +175,11 @@ class TasksAdapter(
}

checklistCheckbox.isChecked = task.isDone
checklistCheckbox.isClickable = !readOnly
checklistCheckbox.isFocusable = !readOnly
checklistHolder.isSelected = isSelected

val canMoveTask = !task.isDone || !activity.config.moveDoneChecklistItems
val canMoveTask = !readOnly && (!task.isDone || !activity.config.moveDoneChecklistItems)
checklistDragHandle.beVisibleIf(beVisible = canMoveTask && selectedKeys.isNotEmpty())
checklistDragHandle.applyColorFilter(textColor)
checklistDragHandle.setOnTouchListener { _, event ->
Expand Down Expand Up @@ -207,7 +210,18 @@ class TasksAdapter(
}
}

fun setReadOnly(enabled: Boolean) {
readOnly = enabled
setupDragListener(!enabled)
if (enabled) {
finishActMode()
}
}

override fun onRowMoved(fromPosition: Int, toPosition: Int) {
if (readOnly) {
return
}
listener?.moveTask(fromPosition, toPosition)
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@ import org.fossify.notes.models.NoteType
import org.fossify.notes.models.Widget
import java.util.concurrent.Executors

@Database(entities = [Note::class, Widget::class], version = 4)
@Database(entities = [Note::class, Widget::class], version = 5)
abstract class NotesDatabase : RoomDatabase() {

abstract fun NotesDao(): NotesDao
Expand All @@ -42,6 +42,7 @@ abstract class NotesDatabase : RoomDatabase() {
.addMigrations(MIGRATION_1_2)
.addMigrations(MIGRATION_2_3)
.addMigrations(MIGRATION_3_4)
.addMigrations(MIGRATION_4_5)
.build()
db!!.openHelper.setWriteAheadLoggingEnabled(true)
}
Expand Down Expand Up @@ -85,5 +86,11 @@ abstract class NotesDatabase : RoomDatabase() {
database.execSQL("ALTER TABLE widgets ADD COLUMN widget_show_title INTEGER NOT NULL DEFAULT 0")
}
}

private val MIGRATION_4_5 = object : Migration(4, 5) {
override fun migrate(database: SupportSQLiteDatabase) {
database.execSQL("ALTER TABLE notes ADD COLUMN is_read_only INTEGER NOT NULL DEFAULT 0")
}
}
}
}
Loading
Loading