mirror of
https://github.com/SimpleMobileTools/Simple-Notes.git
synced 2025-06-05 17:00:23 +02:00
appending package name with .pro
This commit is contained in:
11
app/src/main/kotlin/com/simplemobiletools/notes/pro/App.kt
Normal file
11
app/src/main/kotlin/com/simplemobiletools/notes/pro/App.kt
Normal file
@ -0,0 +1,11 @@
|
||||
package com.simplemobiletools.notes.pro
|
||||
|
||||
import android.app.Application
|
||||
import com.simplemobiletools.commons.extensions.checkUseEnglish
|
||||
|
||||
class App : Application() {
|
||||
override fun onCreate() {
|
||||
super.onCreate()
|
||||
checkUseEnglish()
|
||||
}
|
||||
}
|
@ -0,0 +1,670 @@
|
||||
package com.simplemobiletools.notes.pro.activities
|
||||
|
||||
import android.content.Intent
|
||||
import android.net.Uri
|
||||
import android.os.Bundle
|
||||
import android.text.method.ArrowKeyMovementMethod
|
||||
import android.text.method.LinkMovementMethod
|
||||
import android.util.TypedValue
|
||||
import android.view.ActionMode
|
||||
import android.view.Gravity
|
||||
import android.view.Menu
|
||||
import android.view.MenuItem
|
||||
import com.simplemobiletools.commons.dialogs.ConfirmationAdvancedDialog
|
||||
import com.simplemobiletools.commons.dialogs.FilePickerDialog
|
||||
import com.simplemobiletools.commons.dialogs.RadioGroupDialog
|
||||
import com.simplemobiletools.commons.extensions.*
|
||||
import com.simplemobiletools.commons.helpers.LICENSE_RTL
|
||||
import com.simplemobiletools.commons.helpers.PERMISSION_READ_STORAGE
|
||||
import com.simplemobiletools.commons.helpers.PERMISSION_WRITE_STORAGE
|
||||
import com.simplemobiletools.commons.models.FAQItem
|
||||
import com.simplemobiletools.commons.models.FileDirItem
|
||||
import com.simplemobiletools.commons.models.RadioItem
|
||||
import com.simplemobiletools.commons.models.Release
|
||||
import com.simplemobiletools.commons.views.MyEditText
|
||||
import com.simplemobiletools.notes.pro.BuildConfig
|
||||
import com.simplemobiletools.notes.pro.R
|
||||
import com.simplemobiletools.notes.pro.adapters.NotesPagerAdapter
|
||||
import com.simplemobiletools.notes.pro.dialogs.*
|
||||
import com.simplemobiletools.notes.pro.extensions.config
|
||||
import com.simplemobiletools.notes.pro.extensions.dbHelper
|
||||
import com.simplemobiletools.notes.pro.extensions.getTextSize
|
||||
import com.simplemobiletools.notes.pro.extensions.updateWidgets
|
||||
import com.simplemobiletools.notes.pro.helpers.MIME_TEXT_PLAIN
|
||||
import com.simplemobiletools.notes.pro.helpers.OPEN_NOTE_ID
|
||||
import com.simplemobiletools.notes.pro.helpers.TYPE_NOTE
|
||||
import com.simplemobiletools.notes.pro.models.Note
|
||||
import kotlinx.android.synthetic.main.activity_main.*
|
||||
import java.io.File
|
||||
import java.nio.charset.Charset
|
||||
|
||||
class MainActivity : SimpleActivity() {
|
||||
private val EXPORT_FILE_SYNC = 1
|
||||
private val EXPORT_FILE_NO_SYNC = 2
|
||||
|
||||
private lateinit var mCurrentNote: Note
|
||||
private var mNotes = ArrayList<Note>()
|
||||
private var mAdapter: NotesPagerAdapter? = null
|
||||
private var noteViewWithTextSelected: MyEditText? = null
|
||||
private var saveNoteButton: MenuItem? = null
|
||||
|
||||
private var wasInit = false
|
||||
private var storedEnableLineWrap = true
|
||||
private var showSaveButton = false
|
||||
private var showUndoButton = false
|
||||
private var showRedoButton = false
|
||||
|
||||
override fun onCreate(savedInstanceState: Bundle?) {
|
||||
super.onCreate(savedInstanceState)
|
||||
setContentView(R.layout.activity_main)
|
||||
appLaunched(BuildConfig.APPLICATION_ID)
|
||||
|
||||
initViewPager()
|
||||
|
||||
pager_title_strip.setTextSize(TypedValue.COMPLEX_UNIT_PX, getTextSize())
|
||||
pager_title_strip.layoutParams.height = (pager_title_strip.height + resources.getDimension(R.dimen.activity_margin) * 2).toInt()
|
||||
checkWhatsNewDialog()
|
||||
|
||||
intent.apply {
|
||||
if (action == Intent.ACTION_SEND && type == MIME_TEXT_PLAIN) {
|
||||
getStringExtra(Intent.EXTRA_TEXT)?.let {
|
||||
handleText(it)
|
||||
intent.removeExtra(Intent.EXTRA_TEXT)
|
||||
}
|
||||
}
|
||||
|
||||
if (action == Intent.ACTION_VIEW) {
|
||||
handleUri(data)
|
||||
intent.removeCategory(Intent.CATEGORY_DEFAULT)
|
||||
intent.action = null
|
||||
}
|
||||
}
|
||||
|
||||
storeStateVariables()
|
||||
if (config.showNotePicker && savedInstanceState == null) {
|
||||
displayOpenNoteDialog()
|
||||
}
|
||||
|
||||
wasInit = true
|
||||
checkAppOnSDCard()
|
||||
}
|
||||
|
||||
override fun onResume() {
|
||||
super.onResume()
|
||||
if (storedEnableLineWrap != config.enableLineWrap) {
|
||||
initViewPager()
|
||||
}
|
||||
|
||||
invalidateOptionsMenu()
|
||||
pager_title_strip.apply {
|
||||
setTextSize(TypedValue.COMPLEX_UNIT_PX, getTextSize())
|
||||
setGravity(Gravity.CENTER_VERTICAL)
|
||||
setNonPrimaryAlpha(0.4f)
|
||||
setTextColor(config.textColor)
|
||||
}
|
||||
updateTextColors(view_pager)
|
||||
}
|
||||
|
||||
override fun onPause() {
|
||||
super.onPause()
|
||||
storeStateVariables()
|
||||
}
|
||||
|
||||
override fun onCreateOptionsMenu(menu: Menu): Boolean {
|
||||
menuInflater.inflate(R.menu.menu, menu)
|
||||
menu.apply {
|
||||
findItem(R.id.undo).isVisible = showUndoButton
|
||||
findItem(R.id.redo).isVisible = showRedoButton
|
||||
}
|
||||
|
||||
return true
|
||||
}
|
||||
|
||||
override fun onPrepareOptionsMenu(menu: Menu): Boolean {
|
||||
val shouldBeVisible = mNotes.size > 1
|
||||
menu.apply {
|
||||
findItem(R.id.rename_note).isVisible = shouldBeVisible
|
||||
findItem(R.id.open_note).isVisible = shouldBeVisible
|
||||
findItem(R.id.delete_note).isVisible = shouldBeVisible
|
||||
findItem(R.id.export_all_notes).isVisible = shouldBeVisible
|
||||
|
||||
saveNoteButton = findItem(R.id.save_note)
|
||||
saveNoteButton!!.isVisible = !config.autosaveNotes && showSaveButton
|
||||
}
|
||||
|
||||
pager_title_strip.beVisibleIf(shouldBeVisible)
|
||||
return super.onPrepareOptionsMenu(menu)
|
||||
}
|
||||
|
||||
override fun onOptionsItemSelected(item: MenuItem): Boolean {
|
||||
if (config.autosaveNotes) {
|
||||
saveCurrentNote(false)
|
||||
}
|
||||
|
||||
when (item.itemId) {
|
||||
R.id.open_note -> displayOpenNoteDialog()
|
||||
R.id.save_note -> saveNote()
|
||||
R.id.undo -> undo()
|
||||
R.id.redo -> redo()
|
||||
R.id.new_note -> displayNewNoteDialog()
|
||||
R.id.rename_note -> displayRenameDialog()
|
||||
R.id.share -> shareText()
|
||||
R.id.open_file -> tryOpenFile()
|
||||
R.id.import_folder -> tryOpenFolder()
|
||||
R.id.export_as_file -> tryExportAsFile()
|
||||
R.id.export_all_notes -> tryExportAllNotes()
|
||||
R.id.delete_note -> displayDeleteNotePrompt()
|
||||
R.id.settings -> startActivity(Intent(applicationContext, SettingsActivity::class.java))
|
||||
R.id.about -> launchAbout()
|
||||
else -> return super.onOptionsItemSelected(item)
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
||||
// https://code.google.com/p/android/issues/detail?id=191430 quickfix
|
||||
override fun onActionModeStarted(mode: ActionMode?) {
|
||||
super.onActionModeStarted(mode)
|
||||
if (wasInit) {
|
||||
currentNotesView()?.apply {
|
||||
if (config.clickableLinks || movementMethod is LinkMovementMethod) {
|
||||
movementMethod = ArrowKeyMovementMethod.getInstance()
|
||||
noteViewWithTextSelected = this
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
override fun onActionModeFinished(mode: ActionMode?) {
|
||||
super.onActionModeFinished(mode)
|
||||
if (config.clickableLinks) {
|
||||
noteViewWithTextSelected?.movementMethod = LinkMovementMethod.getInstance()
|
||||
}
|
||||
}
|
||||
|
||||
override fun onBackPressed() {
|
||||
if (!config.autosaveNotes && mAdapter?.anyHasUnsavedChanges() == true) {
|
||||
ConfirmationAdvancedDialog(this, "", R.string.unsaved_changes_warning, R.string.save, R.string.discard) {
|
||||
if (it) {
|
||||
mAdapter?.saveAllFragmentTexts()
|
||||
}
|
||||
super.onBackPressed()
|
||||
}
|
||||
} else {
|
||||
super.onBackPressed()
|
||||
}
|
||||
}
|
||||
|
||||
override fun onNewIntent(intent: Intent) {
|
||||
super.onNewIntent(intent)
|
||||
view_pager.currentItem = getWantedNoteIndex()
|
||||
}
|
||||
|
||||
private fun storeStateVariables() {
|
||||
config.apply {
|
||||
storedEnableLineWrap = enableLineWrap
|
||||
}
|
||||
}
|
||||
|
||||
private fun handleText(text: String) {
|
||||
val notes = dbHelper.getNotes()
|
||||
val list = arrayListOf<RadioItem>().apply {
|
||||
add(RadioItem(0, getString(R.string.create_new_note)))
|
||||
notes.forEachIndexed { index, note ->
|
||||
add(RadioItem(index + 1, note.title))
|
||||
}
|
||||
}
|
||||
|
||||
RadioGroupDialog(this, list, -1, R.string.add_to_note) {
|
||||
if (it as Int == 0) {
|
||||
displayNewNoteDialog(text)
|
||||
} else {
|
||||
updateSelectedNote(notes[it - 1].id)
|
||||
addTextToCurrentNote(if (mCurrentNote.value.isEmpty()) text else "\n$text")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private fun handleUri(uri: Uri) {
|
||||
val id = dbHelper.getNoteId(uri.path)
|
||||
|
||||
if (dbHelper.isValidId(id)) {
|
||||
updateSelectedNote(id)
|
||||
return
|
||||
}
|
||||
|
||||
handlePermission(PERMISSION_WRITE_STORAGE) {
|
||||
if (it) {
|
||||
importFileWithSync(uri)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private fun initViewPager() {
|
||||
mNotes = dbHelper.getNotes()
|
||||
mCurrentNote = mNotes[0]
|
||||
mAdapter = NotesPagerAdapter(supportFragmentManager, mNotes, this)
|
||||
view_pager.apply {
|
||||
adapter = mAdapter
|
||||
currentItem = getWantedNoteIndex()
|
||||
|
||||
onPageChangeListener {
|
||||
mCurrentNote = mNotes[it]
|
||||
config.currentNoteId = mCurrentNote.id
|
||||
}
|
||||
}
|
||||
|
||||
if (!config.showKeyboard) {
|
||||
hideKeyboard()
|
||||
}
|
||||
}
|
||||
|
||||
private fun getWantedNoteIndex(): Int {
|
||||
var wantedNoteId = intent.getIntExtra(OPEN_NOTE_ID, -1)
|
||||
if (wantedNoteId == -1) {
|
||||
wantedNoteId = config.currentNoteId
|
||||
}
|
||||
return getNoteIndexWithId(wantedNoteId)
|
||||
}
|
||||
|
||||
private fun currentNotesView() = if (view_pager == null) {
|
||||
null
|
||||
} else {
|
||||
mAdapter?.getCurrentNotesView(view_pager.currentItem)
|
||||
}
|
||||
|
||||
private fun displayRenameDialog() {
|
||||
RenameNoteDialog(this, mCurrentNote) {
|
||||
mCurrentNote = it
|
||||
initViewPager()
|
||||
}
|
||||
}
|
||||
|
||||
private fun updateSelectedNote(id: Int) {
|
||||
config.currentNoteId = id
|
||||
val index = getNoteIndexWithId(id)
|
||||
view_pager.currentItem = index
|
||||
mCurrentNote = mNotes[index]
|
||||
}
|
||||
|
||||
private fun displayNewNoteDialog(value: String = "") {
|
||||
NewNoteDialog(this, dbHelper) {
|
||||
val newNote = Note(0, it, value, TYPE_NOTE)
|
||||
addNewNote(newNote)
|
||||
}
|
||||
}
|
||||
|
||||
private fun addNewNote(note: Note) {
|
||||
val id = dbHelper.insertNote(note)
|
||||
mNotes = dbHelper.getNotes()
|
||||
showSaveButton = false
|
||||
invalidateOptionsMenu()
|
||||
initViewPager()
|
||||
updateSelectedNote(id)
|
||||
view_pager.onGlobalLayout {
|
||||
mAdapter?.focusEditText(getNoteIndexWithId(id))
|
||||
}
|
||||
}
|
||||
|
||||
private fun launchAbout() {
|
||||
val licenses = LICENSE_RTL
|
||||
|
||||
val faqItems = arrayListOf(
|
||||
FAQItem(R.string.faq_1_title_commons, R.string.faq_1_text_commons),
|
||||
FAQItem(R.string.faq_4_title_commons, R.string.faq_4_text_commons),
|
||||
FAQItem(R.string.faq_2_title_commons, R.string.faq_2_text_commons)
|
||||
)
|
||||
|
||||
startAboutActivity(R.string.app_name, licenses, BuildConfig.VERSION_NAME, faqItems, true)
|
||||
}
|
||||
|
||||
private fun tryOpenFile() {
|
||||
handlePermission(PERMISSION_WRITE_STORAGE) {
|
||||
if (it) {
|
||||
openFile()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private fun openFile() {
|
||||
FilePickerDialog(this, canAddShowHiddenButton = true) {
|
||||
openFile(it, true) {
|
||||
OpenFileDialog(this, it.path) {
|
||||
addNewNote(it)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private fun openFile(path: String, checkTitle: Boolean, onChecksPassed: (file: File) -> Unit) {
|
||||
val file = File(path)
|
||||
if (path.isMediaFile()) {
|
||||
toast(R.string.invalid_file_format)
|
||||
} else if (file.length() > 10 * 1000 * 1000) {
|
||||
toast(R.string.file_too_large)
|
||||
} else if (checkTitle && dbHelper.doesNoteTitleExist(path.getFilenameFromPath())) {
|
||||
toast(R.string.title_taken)
|
||||
} else {
|
||||
onChecksPassed(file)
|
||||
}
|
||||
}
|
||||
|
||||
private fun openFolder(path: String, onChecksPassed: (file: File) -> Unit) {
|
||||
val file = File(path)
|
||||
if (file.isDirectory) {
|
||||
onChecksPassed(file)
|
||||
}
|
||||
}
|
||||
|
||||
private fun importFileWithSync(uri: Uri) {
|
||||
when (uri.scheme) {
|
||||
"file" -> openPath(uri.path)
|
||||
"content" -> {
|
||||
val realPath = getRealPathFromURI(uri)
|
||||
if (realPath != null) {
|
||||
openPath(realPath)
|
||||
} else {
|
||||
R.string.unknown_error_occurred
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private fun openPath(path: String) {
|
||||
openFile(path, false) {
|
||||
var title = path.getFilenameFromPath()
|
||||
if (dbHelper.doesNoteTitleExist(title))
|
||||
title += " (file)"
|
||||
|
||||
val note = Note(0, title, "", TYPE_NOTE, path)
|
||||
addNewNote(note)
|
||||
}
|
||||
}
|
||||
|
||||
private fun tryOpenFolder() {
|
||||
handlePermission(PERMISSION_READ_STORAGE) {
|
||||
if (it) {
|
||||
openFolder()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private fun openFolder() {
|
||||
FilePickerDialog(this, pickFile = false, canAddShowHiddenButton = true) {
|
||||
openFolder(it) {
|
||||
ImportFolderDialog(this, it.path) {
|
||||
mNotes = dbHelper.getNotes()
|
||||
showSaveButton = false
|
||||
invalidateOptionsMenu()
|
||||
initViewPager()
|
||||
updateSelectedNote(it)
|
||||
view_pager.onGlobalLayout {
|
||||
mAdapter?.focusEditText(getNoteIndexWithId(it))
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private fun tryExportAsFile() {
|
||||
handlePermission(PERMISSION_WRITE_STORAGE) {
|
||||
if (it) {
|
||||
exportAsFile()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private fun exportAsFile() {
|
||||
ExportFileDialog(this, mCurrentNote) {
|
||||
if (getCurrentNoteText()?.isNotEmpty() == true) {
|
||||
showExportFilePickUpdateDialog(it)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private fun showExportFilePickUpdateDialog(exportPath: String) {
|
||||
val items = arrayListOf(
|
||||
RadioItem(EXPORT_FILE_SYNC, getString(R.string.update_file_at_note)),
|
||||
RadioItem(EXPORT_FILE_NO_SYNC, getString(R.string.only_export_file_content)))
|
||||
|
||||
RadioGroupDialog(this, items) {
|
||||
val syncFile = it as Int == EXPORT_FILE_SYNC
|
||||
val currentNoteText = getCurrentNoteText()
|
||||
if (currentNoteText == null) {
|
||||
toast(R.string.unknown_error_occurred)
|
||||
return@RadioGroupDialog
|
||||
}
|
||||
|
||||
exportNoteValueToFile(exportPath, currentNoteText, true) {
|
||||
if (syncFile) {
|
||||
mCurrentNote.path = exportPath
|
||||
dbHelper.updateNotePath(mCurrentNote)
|
||||
|
||||
if (mCurrentNote.getNoteStoredValue() == currentNoteText) {
|
||||
mCurrentNote.value = ""
|
||||
dbHelper.updateNoteValue(mCurrentNote)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private fun tryExportAllNotes() {
|
||||
handlePermission(PERMISSION_WRITE_STORAGE) {
|
||||
if (it) {
|
||||
exportAllNotes()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private fun exportAllNotes() {
|
||||
ExportFilesDialog(this, mNotes) { parent, extension ->
|
||||
var failCount = 0
|
||||
mNotes = dbHelper.getNotes()
|
||||
mNotes.forEachIndexed { index, note ->
|
||||
val filename = if (extension.isEmpty()) note.title else "${note.title}.$extension"
|
||||
val file = File(parent, filename)
|
||||
if (!filename.isAValidFilename()) {
|
||||
toast(String.format(getString(R.string.filename_invalid_characters_placeholder, filename)))
|
||||
} else {
|
||||
exportNoteValueToFile(file.absolutePath, note.value, false) {
|
||||
if (!it) {
|
||||
failCount++
|
||||
}
|
||||
|
||||
if (index == mNotes.size - 1) {
|
||||
toast(if (failCount == 0) R.string.exporting_successful else R.string.exporting_some_entries_failed)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fun exportNoteValueToFile(path: String, content: String, showSuccessToasts: Boolean, callback: ((success: Boolean) -> Unit)? = null) {
|
||||
try {
|
||||
if (getIsPathDirectory(path)) {
|
||||
toast(R.string.name_taken)
|
||||
return
|
||||
}
|
||||
|
||||
if (needsStupidWritePermissions(path)) {
|
||||
handleSAFDialog(path) {
|
||||
var document = getDocumentFile(path) ?: return@handleSAFDialog
|
||||
if (!getDoesFilePathExist(path)) {
|
||||
document = document.createFile("", path.getFilenameFromPath())!!
|
||||
}
|
||||
|
||||
contentResolver.openOutputStream(document.uri).apply {
|
||||
write(content.toByteArray(Charset.forName("UTF-8")), 0, content.length)
|
||||
flush()
|
||||
close()
|
||||
}
|
||||
if (showSuccessToasts) {
|
||||
noteExportedSuccessfully(path.getFilenameFromPath())
|
||||
}
|
||||
callback?.invoke(true)
|
||||
}
|
||||
} else {
|
||||
val file = File(path)
|
||||
file.printWriter().use { out ->
|
||||
out.write(content)
|
||||
}
|
||||
if (showSuccessToasts) {
|
||||
noteExportedSuccessfully(path.getFilenameFromPath())
|
||||
}
|
||||
callback?.invoke(true)
|
||||
}
|
||||
} catch (e: Exception) {
|
||||
showErrorToast(e)
|
||||
callback?.invoke(false)
|
||||
}
|
||||
}
|
||||
|
||||
private fun noteExportedSuccessfully(title: String) {
|
||||
val message = String.format(getString(R.string.note_exported_successfully), title)
|
||||
toast(message)
|
||||
}
|
||||
|
||||
fun noteSavedSuccessfully(title: String) {
|
||||
if (config.displaySuccess) {
|
||||
val message = String.format(getString(R.string.note_saved_successfully), title)
|
||||
toast(message)
|
||||
}
|
||||
}
|
||||
|
||||
private fun getPagerAdapter() = view_pager.adapter as NotesPagerAdapter
|
||||
|
||||
private fun getCurrentNoteText() = getPagerAdapter().getCurrentNoteViewText(view_pager.currentItem)
|
||||
|
||||
private fun addTextToCurrentNote(text: String) = getPagerAdapter().appendText(view_pager.currentItem, text)
|
||||
|
||||
private fun saveCurrentNote(force: Boolean) = getPagerAdapter().saveCurrentNote(view_pager.currentItem, force)
|
||||
|
||||
private fun displayDeleteNotePrompt() {
|
||||
DeleteNoteDialog(this, mCurrentNote) {
|
||||
deleteNote(it)
|
||||
}
|
||||
}
|
||||
|
||||
fun deleteNote(deleteFile: Boolean) {
|
||||
if (mNotes.size <= 1) {
|
||||
return
|
||||
}
|
||||
|
||||
if (!deleteFile) {
|
||||
doDeleteNote(mCurrentNote, deleteFile)
|
||||
} else {
|
||||
handleSAFDialog(mCurrentNote.path) {
|
||||
doDeleteNote(mCurrentNote, deleteFile)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private fun doDeleteNote(note: Note, deleteFile: Boolean) {
|
||||
dbHelper.deleteNote(mCurrentNote.id)
|
||||
mNotes = dbHelper.getNotes()
|
||||
|
||||
val firstNoteId = mNotes[0].id
|
||||
updateSelectedNote(firstNoteId)
|
||||
if (config.widgetNoteId == note.id) {
|
||||
config.widgetNoteId = mCurrentNote.id
|
||||
updateWidgets()
|
||||
}
|
||||
|
||||
invalidateOptionsMenu()
|
||||
initViewPager()
|
||||
|
||||
if (deleteFile) {
|
||||
deleteFile(FileDirItem(note.path, note.title)) {
|
||||
if (!it) {
|
||||
toast(R.string.unknown_error_occurred)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private fun displayOpenNoteDialog() {
|
||||
OpenNoteDialog(this) {
|
||||
updateSelectedNote(it)
|
||||
}
|
||||
}
|
||||
|
||||
private fun saveNote() {
|
||||
saveCurrentNote(true)
|
||||
showSaveButton = false
|
||||
invalidateOptionsMenu()
|
||||
}
|
||||
|
||||
private fun undo() {
|
||||
mAdapter?.undo(view_pager.currentItem)
|
||||
}
|
||||
|
||||
private fun redo() {
|
||||
mAdapter?.redo(view_pager.currentItem)
|
||||
}
|
||||
|
||||
private fun getNoteIndexWithId(id: Int): Int {
|
||||
for (i in 0 until mNotes.count()) {
|
||||
if (mNotes[i].id == id) {
|
||||
mCurrentNote = mNotes[i]
|
||||
return i
|
||||
}
|
||||
}
|
||||
return 0
|
||||
}
|
||||
|
||||
private fun shareText() {
|
||||
val text = getCurrentNoteText()
|
||||
if (text == null || text.isEmpty()) {
|
||||
toast(R.string.cannot_share_empty_text)
|
||||
return
|
||||
}
|
||||
|
||||
val res = resources
|
||||
val shareTitle = res.getString(R.string.share_via)
|
||||
Intent().apply {
|
||||
action = Intent.ACTION_SEND
|
||||
putExtra(Intent.EXTRA_SUBJECT, mCurrentNote.title)
|
||||
putExtra(Intent.EXTRA_TEXT, text)
|
||||
type = "text/plain"
|
||||
startActivity(Intent.createChooser(this, shareTitle))
|
||||
}
|
||||
}
|
||||
|
||||
fun currentNoteTextChanged(newText: String, showUndo: Boolean, showRedo: Boolean) {
|
||||
var shouldRecreateMenu = false
|
||||
if (showUndo != showUndoButton) {
|
||||
showUndoButton = showUndo
|
||||
shouldRecreateMenu = true
|
||||
}
|
||||
|
||||
if (showRedo != showRedoButton) {
|
||||
showRedoButton = showRedo
|
||||
shouldRecreateMenu = true
|
||||
}
|
||||
|
||||
if (!config.autosaveNotes) {
|
||||
showSaveButton = newText != mCurrentNote.value
|
||||
if (showSaveButton != saveNoteButton?.isVisible) {
|
||||
shouldRecreateMenu = true
|
||||
}
|
||||
}
|
||||
|
||||
if (shouldRecreateMenu) {
|
||||
invalidateOptionsMenu()
|
||||
}
|
||||
}
|
||||
|
||||
private fun checkWhatsNewDialog() {
|
||||
arrayListOf<Release>().apply {
|
||||
add(Release(25, R.string.release_25))
|
||||
add(Release(28, R.string.release_28))
|
||||
add(Release(29, R.string.release_29))
|
||||
add(Release(39, R.string.release_39))
|
||||
add(Release(45, R.string.release_45))
|
||||
add(Release(49, R.string.release_49))
|
||||
add(Release(51, R.string.release_51))
|
||||
checkWhatsNew(this, BuildConfig.VERSION_CODE)
|
||||
}
|
||||
}
|
||||
}
|
@ -0,0 +1,207 @@
|
||||
package com.simplemobiletools.notes.pro.activities
|
||||
|
||||
import android.content.Intent
|
||||
import android.os.Bundle
|
||||
import com.simplemobiletools.commons.dialogs.RadioGroupDialog
|
||||
import com.simplemobiletools.commons.extensions.beVisibleIf
|
||||
import com.simplemobiletools.commons.extensions.getAdjustedPrimaryColor
|
||||
import com.simplemobiletools.commons.extensions.updateTextColors
|
||||
import com.simplemobiletools.commons.helpers.IS_CUSTOMIZING_COLORS
|
||||
import com.simplemobiletools.commons.helpers.isOreoPlus
|
||||
import com.simplemobiletools.commons.models.RadioItem
|
||||
import com.simplemobiletools.notes.pro.R
|
||||
import com.simplemobiletools.notes.pro.extensions.config
|
||||
import com.simplemobiletools.notes.pro.extensions.dbHelper
|
||||
import com.simplemobiletools.notes.pro.extensions.updateWidgets
|
||||
import com.simplemobiletools.notes.pro.helpers.*
|
||||
import kotlinx.android.synthetic.main.activity_settings.*
|
||||
import java.util.*
|
||||
|
||||
class SettingsActivity : SimpleActivity() {
|
||||
override fun onCreate(savedInstanceState: Bundle?) {
|
||||
super.onCreate(savedInstanceState)
|
||||
setContentView(R.layout.activity_settings)
|
||||
}
|
||||
|
||||
override fun onResume() {
|
||||
super.onResume()
|
||||
|
||||
setupCustomizeColors()
|
||||
setupUseEnglish()
|
||||
setupAutosaveNotes()
|
||||
setupDisplaySuccess()
|
||||
setupClickableLinks()
|
||||
setupMonospacedFont()
|
||||
setupShowKeyboard()
|
||||
setupShowNotePicker()
|
||||
setupShowWordCount()
|
||||
setupEnableLineWrap()
|
||||
setupFontSize()
|
||||
setupGravity()
|
||||
setupCursorPlacement()
|
||||
setupIncognitoMode()
|
||||
setupCustomizeWidgetColors()
|
||||
updateTextColors(settings_scrollview)
|
||||
setupSectionColors()
|
||||
}
|
||||
|
||||
private fun setupSectionColors() {
|
||||
val adjustedPrimaryColor = getAdjustedPrimaryColor()
|
||||
arrayListOf(text_label, startup_label, saving_label, widgets_label).forEach {
|
||||
it.setTextColor(adjustedPrimaryColor)
|
||||
}
|
||||
}
|
||||
|
||||
private fun setupCustomizeColors() {
|
||||
settings_customize_colors_holder.setOnClickListener {
|
||||
startCustomizationActivity()
|
||||
}
|
||||
}
|
||||
|
||||
private fun setupUseEnglish() {
|
||||
settings_use_english_holder.beVisibleIf(config.wasUseEnglishToggled || Locale.getDefault().language != "en")
|
||||
settings_use_english.isChecked = config.useEnglish
|
||||
settings_use_english_holder.setOnClickListener {
|
||||
settings_use_english.toggle()
|
||||
config.useEnglish = settings_use_english.isChecked
|
||||
System.exit(0)
|
||||
}
|
||||
}
|
||||
|
||||
private fun setupAutosaveNotes() {
|
||||
settings_autosave_notes.isChecked = config.autosaveNotes
|
||||
settings_autosave_notes_holder.setOnClickListener {
|
||||
settings_autosave_notes.toggle()
|
||||
config.autosaveNotes = settings_autosave_notes.isChecked
|
||||
}
|
||||
}
|
||||
|
||||
private fun setupDisplaySuccess() {
|
||||
settings_display_success.isChecked = config.displaySuccess
|
||||
settings_display_success_holder.setOnClickListener {
|
||||
settings_display_success.toggle()
|
||||
config.displaySuccess = settings_display_success.isChecked
|
||||
}
|
||||
}
|
||||
|
||||
private fun setupClickableLinks() {
|
||||
settings_clickable_links.isChecked = config.clickableLinks
|
||||
settings_clickable_links_holder.setOnClickListener {
|
||||
settings_clickable_links.toggle()
|
||||
config.clickableLinks = settings_clickable_links.isChecked
|
||||
}
|
||||
}
|
||||
|
||||
private fun setupMonospacedFont() {
|
||||
settings_monospaced_font.isChecked = config.monospacedFont
|
||||
settings_monospaced_font_holder.setOnClickListener {
|
||||
settings_monospaced_font.toggle()
|
||||
config.monospacedFont = settings_monospaced_font.isChecked
|
||||
}
|
||||
}
|
||||
|
||||
private fun setupShowKeyboard() {
|
||||
settings_show_keyboard.isChecked = config.showKeyboard
|
||||
settings_show_keyboard_holder.setOnClickListener {
|
||||
settings_show_keyboard.toggle()
|
||||
config.showKeyboard = settings_show_keyboard.isChecked
|
||||
}
|
||||
}
|
||||
|
||||
private fun setupShowNotePicker() {
|
||||
settings_show_note_picker_holder.beVisibleIf(dbHelper.getNotes().size > 1)
|
||||
settings_show_note_picker.isChecked = config.showNotePicker
|
||||
settings_show_note_picker_holder.setOnClickListener {
|
||||
settings_show_note_picker.toggle()
|
||||
config.showNotePicker = settings_show_note_picker.isChecked
|
||||
}
|
||||
}
|
||||
|
||||
private fun setupShowWordCount() {
|
||||
settings_show_word_count.isChecked = config.showWordCount
|
||||
settings_show_word_count_holder.setOnClickListener {
|
||||
settings_show_word_count.toggle()
|
||||
config.showWordCount = settings_show_word_count.isChecked
|
||||
}
|
||||
}
|
||||
|
||||
private fun setupEnableLineWrap() {
|
||||
settings_enable_line_wrap.isChecked = config.enableLineWrap
|
||||
settings_enable_line_wrap_holder.setOnClickListener {
|
||||
settings_enable_line_wrap.toggle()
|
||||
config.enableLineWrap = settings_enable_line_wrap.isChecked
|
||||
}
|
||||
}
|
||||
|
||||
private fun setupFontSize() {
|
||||
settings_font_size.text = getFontSizeText()
|
||||
settings_font_size_holder.setOnClickListener {
|
||||
val items = arrayListOf(
|
||||
RadioItem(FONT_SIZE_SMALL, getString(R.string.small)),
|
||||
RadioItem(FONT_SIZE_MEDIUM, getString(R.string.medium)),
|
||||
RadioItem(FONT_SIZE_LARGE, getString(R.string.large)),
|
||||
RadioItem(FONT_SIZE_EXTRA_LARGE, getString(R.string.extra_large)))
|
||||
|
||||
RadioGroupDialog(this@SettingsActivity, items, config.fontSize) {
|
||||
config.fontSize = it as Int
|
||||
settings_font_size.text = getFontSizeText()
|
||||
updateWidgets()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private fun getFontSizeText() = getString(when (config.fontSize) {
|
||||
FONT_SIZE_SMALL -> R.string.small
|
||||
FONT_SIZE_MEDIUM -> R.string.medium
|
||||
FONT_SIZE_LARGE -> R.string.large
|
||||
else -> R.string.extra_large
|
||||
})
|
||||
|
||||
private fun setupGravity() {
|
||||
settings_gravity.text = getGravityText()
|
||||
settings_gravity_holder.setOnClickListener {
|
||||
val items = arrayListOf(
|
||||
RadioItem(GRAVITY_LEFT, getString(R.string.left)),
|
||||
RadioItem(GRAVITY_CENTER, getString(R.string.center)),
|
||||
RadioItem(GRAVITY_RIGHT, getString(R.string.right)))
|
||||
|
||||
RadioGroupDialog(this@SettingsActivity, items, config.gravity) {
|
||||
config.gravity = it as Int
|
||||
settings_gravity.text = getGravityText()
|
||||
updateWidgets()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private fun getGravityText() = getString(when (config.gravity) {
|
||||
GRAVITY_LEFT -> R.string.left
|
||||
GRAVITY_CENTER -> R.string.center
|
||||
else -> R.string.right
|
||||
})
|
||||
|
||||
private fun setupCursorPlacement() {
|
||||
settings_cursor_placement.isChecked = config.placeCursorToEnd
|
||||
settings_cursor_placement_holder.setOnClickListener {
|
||||
settings_cursor_placement.toggle()
|
||||
config.placeCursorToEnd = settings_cursor_placement.isChecked
|
||||
}
|
||||
}
|
||||
|
||||
private fun setupCustomizeWidgetColors() {
|
||||
settings_customize_widget_colors_holder.setOnClickListener {
|
||||
Intent(this, WidgetConfigureActivity::class.java).apply {
|
||||
putExtra(IS_CUSTOMIZING_COLORS, true)
|
||||
startActivity(this)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private fun setupIncognitoMode() {
|
||||
settings_use_incognito_mode_holder.beVisibleIf(isOreoPlus())
|
||||
settings_use_incognito_mode.isChecked = config.useIncognitoMode
|
||||
settings_use_incognito_mode_holder.setOnClickListener {
|
||||
settings_use_incognito_mode.toggle()
|
||||
config.useIncognitoMode = settings_use_incognito_mode.isChecked
|
||||
}
|
||||
}
|
||||
}
|
@ -0,0 +1,30 @@
|
||||
package com.simplemobiletools.notes.pro.activities
|
||||
|
||||
import com.simplemobiletools.commons.activities.BaseSimpleActivity
|
||||
import com.simplemobiletools.notes.pro.R
|
||||
|
||||
open class SimpleActivity : BaseSimpleActivity() {
|
||||
override fun getAppIconIDs() = arrayListOf(
|
||||
R.mipmap.ic_launcher_red,
|
||||
R.mipmap.ic_launcher_pink,
|
||||
R.mipmap.ic_launcher_purple,
|
||||
R.mipmap.ic_launcher_deep_purple,
|
||||
R.mipmap.ic_launcher_indigo,
|
||||
R.mipmap.ic_launcher_blue,
|
||||
R.mipmap.ic_launcher_light_blue,
|
||||
R.mipmap.ic_launcher_cyan,
|
||||
R.mipmap.ic_launcher_teal,
|
||||
R.mipmap.ic_launcher_green,
|
||||
R.mipmap.ic_launcher_light_green,
|
||||
R.mipmap.ic_launcher_lime,
|
||||
R.mipmap.ic_launcher_yellow,
|
||||
R.mipmap.ic_launcher_amber,
|
||||
R.mipmap.ic_launcher,
|
||||
R.mipmap.ic_launcher_deep_orange,
|
||||
R.mipmap.ic_launcher_brown,
|
||||
R.mipmap.ic_launcher_blue_grey,
|
||||
R.mipmap.ic_launcher_grey_black
|
||||
)
|
||||
|
||||
override fun getAppLauncherName() = getString(R.string.app_launcher_name)
|
||||
}
|
@ -0,0 +1,19 @@
|
||||
package com.simplemobiletools.notes.pro.activities
|
||||
|
||||
import android.content.Intent
|
||||
import com.simplemobiletools.commons.activities.BaseSplashActivity
|
||||
import com.simplemobiletools.notes.pro.helpers.OPEN_NOTE_ID
|
||||
|
||||
class SplashActivity : BaseSplashActivity() {
|
||||
override fun initActivity() {
|
||||
if (intent.extras?.containsKey(OPEN_NOTE_ID) == true) {
|
||||
Intent(this, MainActivity::class.java).apply {
|
||||
putExtra(OPEN_NOTE_ID, intent.getIntExtra(OPEN_NOTE_ID, -1))
|
||||
startActivity(this)
|
||||
}
|
||||
} else {
|
||||
startActivity(Intent(this, MainActivity::class.java))
|
||||
}
|
||||
finish()
|
||||
}
|
||||
}
|
@ -0,0 +1,169 @@
|
||||
package com.simplemobiletools.notes.pro.activities
|
||||
|
||||
import android.app.Activity
|
||||
import android.appwidget.AppWidgetManager
|
||||
import android.content.Intent
|
||||
import android.graphics.Color
|
||||
import android.graphics.drawable.ColorDrawable
|
||||
import android.os.Bundle
|
||||
import android.util.TypedValue
|
||||
import android.widget.RemoteViews
|
||||
import com.simplemobiletools.commons.dialogs.ColorPickerDialog
|
||||
import com.simplemobiletools.commons.dialogs.RadioGroupDialog
|
||||
import com.simplemobiletools.commons.extensions.*
|
||||
import com.simplemobiletools.commons.helpers.IS_CUSTOMIZING_COLORS
|
||||
import com.simplemobiletools.commons.models.RadioItem
|
||||
import com.simplemobiletools.notes.pro.R
|
||||
import com.simplemobiletools.notes.pro.extensions.config
|
||||
import com.simplemobiletools.notes.pro.extensions.dbHelper
|
||||
import com.simplemobiletools.notes.pro.extensions.getTextSize
|
||||
import com.simplemobiletools.notes.pro.helpers.MyWidgetProvider
|
||||
import com.simplemobiletools.notes.pro.models.Note
|
||||
import com.simplemobiletools.notes.pro.models.Widget
|
||||
import kotlinx.android.synthetic.main.widget_config.*
|
||||
|
||||
class WidgetConfigureActivity : SimpleActivity() {
|
||||
private var mBgAlpha = 0f
|
||||
private var mWidgetId = 0
|
||||
private var mBgColor = 0
|
||||
private var mBgColorWithoutTransparency = 0
|
||||
private var mTextColor = 0
|
||||
private var mCurrentNoteId = 0
|
||||
private var mIsCustomizingColors = false
|
||||
private var mNotes = ArrayList<Note>()
|
||||
|
||||
public override fun onCreate(savedInstanceState: Bundle?) {
|
||||
useDynamicTheme = false
|
||||
super.onCreate(savedInstanceState)
|
||||
setResult(RESULT_CANCELED)
|
||||
setContentView(R.layout.widget_config)
|
||||
initVariables()
|
||||
|
||||
mWidgetId = intent.extras?.getInt(AppWidgetManager.EXTRA_APPWIDGET_ID) ?: AppWidgetManager.INVALID_APPWIDGET_ID
|
||||
|
||||
if (mWidgetId == AppWidgetManager.INVALID_APPWIDGET_ID && !mIsCustomizingColors) {
|
||||
finish()
|
||||
}
|
||||
|
||||
updateTextColors(notes_picker_holder)
|
||||
config_save.setOnClickListener { saveConfig() }
|
||||
config_bg_color.setOnClickListener { pickBackgroundColor() }
|
||||
config_text_color.setOnClickListener { pickTextColor() }
|
||||
notes_picker_value.setOnClickListener { showNoteSelector() }
|
||||
notes_picker_holder.background = ColorDrawable(config.backgroundColor)
|
||||
}
|
||||
|
||||
override fun onResume() {
|
||||
super.onResume()
|
||||
notes_view.setTextSize(TypedValue.COMPLEX_UNIT_PX, applicationContext.getTextSize())
|
||||
}
|
||||
|
||||
private fun initVariables() {
|
||||
mBgColor = config.widgetBgColor
|
||||
if (mBgColor == 1) {
|
||||
mBgColor = Color.BLACK
|
||||
mBgAlpha = .2f
|
||||
} else {
|
||||
mBgAlpha = Color.alpha(mBgColor) / 255f
|
||||
}
|
||||
|
||||
mBgColorWithoutTransparency = Color.rgb(Color.red(mBgColor), Color.green(mBgColor), Color.blue(mBgColor))
|
||||
config_bg_seekbar.apply {
|
||||
progress = (mBgAlpha * 100).toInt()
|
||||
|
||||
onSeekBarChangeListener {
|
||||
mBgAlpha = it / 100f
|
||||
updateBackgroundColor()
|
||||
}
|
||||
}
|
||||
updateBackgroundColor()
|
||||
|
||||
mTextColor = config.widgetTextColor
|
||||
updateTextColor()
|
||||
mNotes = dbHelper.getNotes()
|
||||
mIsCustomizingColors = intent.extras?.getBoolean(IS_CUSTOMIZING_COLORS) ?: false
|
||||
notes_picker_holder.beVisibleIf(mNotes.size > 1 && !mIsCustomizingColors)
|
||||
updateCurrentNote(mNotes.first())
|
||||
}
|
||||
|
||||
private fun showNoteSelector() {
|
||||
val items = ArrayList<RadioItem>()
|
||||
mNotes.forEach {
|
||||
items.add(RadioItem(it.id, it.title))
|
||||
}
|
||||
|
||||
RadioGroupDialog(this, items, mCurrentNoteId) {
|
||||
val selectedId = it as Int
|
||||
updateCurrentNote(mNotes.first { it.id == selectedId })
|
||||
}
|
||||
}
|
||||
|
||||
private fun updateCurrentNote(note: Note) {
|
||||
mCurrentNoteId = note.id
|
||||
notes_picker_value.text = note.title
|
||||
val sampleValue = if (note.value.isEmpty() || mIsCustomizingColors) getString(R.string.widget_config) else note.value
|
||||
notes_view.text = sampleValue
|
||||
}
|
||||
|
||||
private fun saveConfig() {
|
||||
val views = RemoteViews(packageName, R.layout.activity_main)
|
||||
views.setBackgroundColor(R.id.notes_view, mBgColor)
|
||||
AppWidgetManager.getInstance(this).updateAppWidget(mWidgetId, views)
|
||||
val widget = Widget(mWidgetId, mCurrentNoteId)
|
||||
dbHelper.insertWidget(widget)
|
||||
|
||||
storeWidgetBackground()
|
||||
requestWidgetUpdate()
|
||||
|
||||
Intent().apply {
|
||||
putExtra(AppWidgetManager.EXTRA_APPWIDGET_ID, mWidgetId)
|
||||
setResult(Activity.RESULT_OK, this)
|
||||
}
|
||||
finish()
|
||||
}
|
||||
|
||||
private fun storeWidgetBackground() {
|
||||
config.apply {
|
||||
widgetBgColor = mBgColor
|
||||
widgetTextColor = mTextColor
|
||||
}
|
||||
}
|
||||
|
||||
private fun requestWidgetUpdate() {
|
||||
Intent(AppWidgetManager.ACTION_APPWIDGET_UPDATE, null, this, MyWidgetProvider::class.java).apply {
|
||||
putExtra(AppWidgetManager.EXTRA_APPWIDGET_IDS, intArrayOf(mWidgetId))
|
||||
sendBroadcast(this)
|
||||
}
|
||||
}
|
||||
|
||||
private fun updateBackgroundColor() {
|
||||
mBgColor = mBgColorWithoutTransparency.adjustAlpha(mBgAlpha)
|
||||
notes_view.setBackgroundColor(mBgColor)
|
||||
config_save.setBackgroundColor(mBgColor)
|
||||
config_bg_color.setFillWithStroke(mBgColor, Color.BLACK)
|
||||
}
|
||||
|
||||
private fun updateTextColor() {
|
||||
config_save.setTextColor(mTextColor)
|
||||
notes_view.setTextColor(mTextColor)
|
||||
config_text_color.setFillWithStroke(mTextColor, Color.BLACK)
|
||||
}
|
||||
|
||||
private fun pickBackgroundColor() {
|
||||
ColorPickerDialog(this, mBgColorWithoutTransparency) { wasPositivePressed, color ->
|
||||
if (wasPositivePressed) {
|
||||
mBgColorWithoutTransparency = color
|
||||
updateBackgroundColor()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private fun pickTextColor() {
|
||||
ColorPickerDialog(this, mTextColor) { wasPositivePressed, color ->
|
||||
if (wasPositivePressed) {
|
||||
mTextColor = color
|
||||
updateTextColor()
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
@ -0,0 +1,71 @@
|
||||
package com.simplemobiletools.notes.pro.adapters
|
||||
|
||||
import android.app.Activity
|
||||
import android.os.Bundle
|
||||
import android.view.ViewGroup
|
||||
import androidx.fragment.app.FragmentManager
|
||||
import androidx.fragment.app.FragmentStatePagerAdapter
|
||||
import com.simplemobiletools.commons.extensions.showErrorToast
|
||||
import com.simplemobiletools.notes.pro.fragments.NoteFragment
|
||||
import com.simplemobiletools.notes.pro.helpers.NOTE_ID
|
||||
import com.simplemobiletools.notes.pro.models.Note
|
||||
|
||||
class NotesPagerAdapter(fm: FragmentManager, val notes: List<Note>, val activity: Activity) : FragmentStatePagerAdapter(fm) {
|
||||
private var fragments: HashMap<Int, NoteFragment> = LinkedHashMap()
|
||||
|
||||
override fun getCount() = notes.size
|
||||
|
||||
override fun getItem(position: Int): NoteFragment {
|
||||
val bundle = Bundle()
|
||||
val id = notes[position].id
|
||||
bundle.putInt(NOTE_ID, id)
|
||||
|
||||
if (fragments.containsKey(position)) {
|
||||
return fragments[position]!!
|
||||
}
|
||||
|
||||
val fragment = NoteFragment()
|
||||
fragment.arguments = bundle
|
||||
fragments[position] = fragment
|
||||
return fragment
|
||||
}
|
||||
|
||||
override fun getPageTitle(position: Int) = notes[position].title
|
||||
|
||||
fun getCurrentNotesView(position: Int) = fragments[position]?.getNotesView()
|
||||
|
||||
fun getCurrentNoteViewText(position: Int) = fragments[position]?.getCurrentNoteViewText()
|
||||
|
||||
fun appendText(position: Int, text: String) = fragments[position]?.getNotesView()?.append(text)
|
||||
|
||||
fun saveCurrentNote(position: Int, force: Boolean) = fragments[position]?.saveText(force)
|
||||
|
||||
fun focusEditText(position: Int) = fragments[position]?.focusEditText()
|
||||
|
||||
fun anyHasUnsavedChanges() = fragments.values.any { it.hasUnsavedChanges() }
|
||||
|
||||
fun saveAllFragmentTexts() = fragments.values.forEach { it.saveText(false) }
|
||||
|
||||
fun undo(position: Int) = fragments[position]?.undo()
|
||||
|
||||
fun redo(position: Int) = fragments[position]?.redo()
|
||||
|
||||
override fun finishUpdate(container: ViewGroup) {
|
||||
try {
|
||||
super.finishUpdate(container)
|
||||
} catch (e: Exception) {
|
||||
activity.showErrorToast(e)
|
||||
}
|
||||
}
|
||||
|
||||
override fun destroyItem(container: ViewGroup, position: Int, `object`: Any) {
|
||||
super.destroyItem(container, position, `object`)
|
||||
fragments.remove(position)
|
||||
}
|
||||
|
||||
override fun instantiateItem(container: ViewGroup, position: Int): Any {
|
||||
val fragment = super.instantiateItem(container, position) as NoteFragment
|
||||
fragments[position] = fragment
|
||||
return fragment
|
||||
}
|
||||
}
|
@ -0,0 +1,72 @@
|
||||
package com.simplemobiletools.notes.pro.adapters
|
||||
|
||||
import android.content.Context
|
||||
import android.content.Intent
|
||||
import android.view.View
|
||||
import android.widget.RemoteViews
|
||||
import android.widget.RemoteViewsService
|
||||
import com.simplemobiletools.commons.extensions.setText
|
||||
import com.simplemobiletools.commons.extensions.setTextSize
|
||||
import com.simplemobiletools.notes.pro.R
|
||||
import com.simplemobiletools.notes.pro.R.id.widget_text_holder
|
||||
import com.simplemobiletools.notes.pro.extensions.config
|
||||
import com.simplemobiletools.notes.pro.extensions.dbHelper
|
||||
import com.simplemobiletools.notes.pro.extensions.getTextSize
|
||||
import com.simplemobiletools.notes.pro.helpers.GRAVITY_CENTER
|
||||
import com.simplemobiletools.notes.pro.helpers.GRAVITY_RIGHT
|
||||
import com.simplemobiletools.notes.pro.helpers.NOTE_ID
|
||||
import com.simplemobiletools.notes.pro.helpers.OPEN_NOTE_ID
|
||||
|
||||
class WidgetAdapter(val context: Context, val intent: Intent) : RemoteViewsService.RemoteViewsFactory {
|
||||
private val textIds = arrayOf(R.id.widget_text_left, R.id.widget_text_center, R.id.widget_text_right)
|
||||
private var widgetTextColor = context.config.widgetTextColor
|
||||
|
||||
override fun getViewAt(position: Int): RemoteViews {
|
||||
val noteId = intent.getIntExtra(NOTE_ID, 1)
|
||||
val views = RemoteViews(context.packageName, R.layout.widget_text_layout).apply {
|
||||
val note = context.dbHelper.getNoteWithId(noteId)
|
||||
if (note != null) {
|
||||
val noteText = note.getNoteStoredValue() ?: ""
|
||||
val textSize = context.getTextSize() / context.resources.displayMetrics.density
|
||||
for (id in textIds) {
|
||||
setText(id, noteText)
|
||||
setTextColor(id, widgetTextColor)
|
||||
setTextSize(id, textSize)
|
||||
setViewVisibility(id, View.GONE)
|
||||
}
|
||||
}
|
||||
|
||||
Intent().apply {
|
||||
putExtra(OPEN_NOTE_ID, noteId)
|
||||
setOnClickFillInIntent(widget_text_holder, this)
|
||||
}
|
||||
|
||||
setViewVisibility(getProperTextView(context), View.VISIBLE)
|
||||
}
|
||||
return views
|
||||
}
|
||||
|
||||
private fun getProperTextView(context: Context) = when (context.config.gravity) {
|
||||
GRAVITY_CENTER -> R.id.widget_text_center
|
||||
GRAVITY_RIGHT -> R.id.widget_text_right
|
||||
else -> R.id.widget_text_left
|
||||
}
|
||||
|
||||
override fun onCreate() {}
|
||||
|
||||
override fun getLoadingView() = null
|
||||
|
||||
override fun getItemId(position: Int) = position.toLong()
|
||||
|
||||
override fun onDataSetChanged() {
|
||||
widgetTextColor = context.config.widgetTextColor
|
||||
}
|
||||
|
||||
override fun hasStableIds() = true
|
||||
|
||||
override fun getCount() = 1
|
||||
|
||||
override fun getViewTypeCount() = 1
|
||||
|
||||
override fun onDestroy() {}
|
||||
}
|
@ -0,0 +1,36 @@
|
||||
package com.simplemobiletools.notes.pro.dialogs
|
||||
|
||||
import androidx.appcompat.app.AlertDialog
|
||||
import com.simplemobiletools.commons.extensions.beVisible
|
||||
import com.simplemobiletools.commons.extensions.setupDialogStuff
|
||||
import com.simplemobiletools.notes.pro.R
|
||||
import com.simplemobiletools.notes.pro.activities.SimpleActivity
|
||||
import com.simplemobiletools.notes.pro.models.Note
|
||||
import kotlinx.android.synthetic.main.dialog_delete_note.view.*
|
||||
|
||||
class DeleteNoteDialog(val activity: SimpleActivity, val note: Note, val callback: (deleteFile: Boolean) -> Unit) {
|
||||
var dialog: AlertDialog? = null
|
||||
|
||||
init {
|
||||
val message = String.format(activity.getString(R.string.delete_note_prompt_message), note.title)
|
||||
val view = activity.layoutInflater.inflate(R.layout.dialog_delete_note, null).apply {
|
||||
if (note.path.isNotEmpty()) {
|
||||
delete_note_checkbox.text = String.format(activity.getString(R.string.delete_file_itself), note.path)
|
||||
delete_note_checkbox.beVisible()
|
||||
}
|
||||
delete_note_description.text = message
|
||||
}
|
||||
|
||||
AlertDialog.Builder(activity)
|
||||
.setPositiveButton(R.string.ok) { dialog, which -> dialogConfirmed(view.delete_note_checkbox.isChecked) }
|
||||
.setNegativeButton(R.string.cancel, null)
|
||||
.create().apply {
|
||||
activity.setupDialogStuff(view, this)
|
||||
}
|
||||
}
|
||||
|
||||
private fun dialogConfirmed(deleteFile: Boolean) {
|
||||
callback(deleteFile && note.path.isNotEmpty())
|
||||
dialog?.dismiss()
|
||||
}
|
||||
}
|
@ -0,0 +1,59 @@
|
||||
package com.simplemobiletools.notes.pro.dialogs
|
||||
|
||||
import androidx.appcompat.app.AlertDialog
|
||||
import com.simplemobiletools.commons.dialogs.FilePickerDialog
|
||||
import com.simplemobiletools.commons.extensions.*
|
||||
import com.simplemobiletools.notes.pro.R
|
||||
import com.simplemobiletools.notes.pro.activities.SimpleActivity
|
||||
import com.simplemobiletools.notes.pro.extensions.config
|
||||
import com.simplemobiletools.notes.pro.models.Note
|
||||
import kotlinx.android.synthetic.main.dialog_export_file.view.*
|
||||
import java.io.File
|
||||
|
||||
class ExportFileDialog(val activity: SimpleActivity, val note: Note, val callback: (exportPath: String) -> Unit) {
|
||||
|
||||
init {
|
||||
var realPath = File(note.path).parent ?: activity.config.lastUsedSavePath
|
||||
val view = activity.layoutInflater.inflate(R.layout.dialog_export_file, null).apply {
|
||||
file_path.text = activity.humanizePath(realPath)
|
||||
|
||||
file_name.setText(note.title)
|
||||
file_extension.setText(activity.config.lastUsedExtension)
|
||||
file_path.setOnClickListener {
|
||||
FilePickerDialog(activity, realPath, false, false, true, true) {
|
||||
file_path.text = activity.humanizePath(it)
|
||||
realPath = it
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
AlertDialog.Builder(activity)
|
||||
.setPositiveButton(R.string.ok, null)
|
||||
.setNegativeButton(R.string.cancel, null)
|
||||
.create().apply {
|
||||
activity.setupDialogStuff(view, this, R.string.export_as_file) {
|
||||
showKeyboard(view.file_name)
|
||||
getButton(AlertDialog.BUTTON_POSITIVE).setOnClickListener {
|
||||
val filename = view.file_name.value
|
||||
val extension = view.file_extension.value
|
||||
|
||||
if (filename.isEmpty()) {
|
||||
activity.toast(R.string.filename_cannot_be_empty)
|
||||
return@setOnClickListener
|
||||
}
|
||||
|
||||
val fullFilename = if (extension.isEmpty()) filename else "$filename.$extension"
|
||||
if (!fullFilename.isAValidFilename()) {
|
||||
activity.toast(String.format(activity.getString(R.string.filename_invalid_characters_placeholder, fullFilename)))
|
||||
return@setOnClickListener
|
||||
}
|
||||
|
||||
activity.config.lastUsedExtension = extension
|
||||
activity.config.lastUsedSavePath = realPath
|
||||
callback("$realPath/$fullFilename")
|
||||
dismiss()
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
@ -0,0 +1,48 @@
|
||||
package com.simplemobiletools.notes.pro.dialogs
|
||||
|
||||
import androidx.appcompat.app.AlertDialog
|
||||
import com.simplemobiletools.commons.dialogs.FilePickerDialog
|
||||
import com.simplemobiletools.commons.extensions.humanizePath
|
||||
import com.simplemobiletools.commons.extensions.setupDialogStuff
|
||||
import com.simplemobiletools.commons.extensions.showKeyboard
|
||||
import com.simplemobiletools.commons.extensions.value
|
||||
import com.simplemobiletools.notes.pro.R
|
||||
import com.simplemobiletools.notes.pro.activities.SimpleActivity
|
||||
import com.simplemobiletools.notes.pro.extensions.config
|
||||
import com.simplemobiletools.notes.pro.models.Note
|
||||
import kotlinx.android.synthetic.main.dialog_export_files.view.*
|
||||
|
||||
class ExportFilesDialog(val activity: SimpleActivity, val notes: ArrayList<Note>, val callback: (parent: String, extension: String) -> Unit) {
|
||||
init {
|
||||
var realPath = activity.config.lastUsedSavePath
|
||||
val view = activity.layoutInflater.inflate(R.layout.dialog_export_files, null).apply {
|
||||
folder_path.text = activity.humanizePath(realPath)
|
||||
|
||||
file_extension.setText(activity.config.lastUsedExtension)
|
||||
folder_path.setOnClickListener {
|
||||
FilePickerDialog(activity, realPath, false, false, true, true) {
|
||||
folder_path.text = activity.humanizePath(it)
|
||||
realPath = it
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
AlertDialog.Builder(activity)
|
||||
.setPositiveButton(R.string.ok, null)
|
||||
.setNegativeButton(R.string.cancel, null)
|
||||
.create().apply {
|
||||
activity.setupDialogStuff(view, this, R.string.export_as_file) {
|
||||
showKeyboard(view.file_extension)
|
||||
getButton(AlertDialog.BUTTON_POSITIVE).setOnClickListener {
|
||||
activity.handleSAFDialog(realPath) {
|
||||
val extension = view.file_extension.value
|
||||
activity.config.lastUsedExtension = extension
|
||||
activity.config.lastUsedSavePath = realPath
|
||||
callback(realPath, extension)
|
||||
dismiss()
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
@ -0,0 +1,75 @@
|
||||
package com.simplemobiletools.notes.pro.dialogs
|
||||
|
||||
import android.view.ViewGroup
|
||||
import androidx.appcompat.app.AlertDialog
|
||||
import com.simplemobiletools.commons.extensions.getFilenameFromPath
|
||||
import com.simplemobiletools.commons.extensions.humanizePath
|
||||
import com.simplemobiletools.commons.extensions.isMediaFile
|
||||
import com.simplemobiletools.commons.extensions.setupDialogStuff
|
||||
import com.simplemobiletools.notes.pro.R
|
||||
import com.simplemobiletools.notes.pro.activities.SimpleActivity
|
||||
import com.simplemobiletools.notes.pro.extensions.dbHelper
|
||||
import com.simplemobiletools.notes.pro.helpers.TYPE_NOTE
|
||||
import com.simplemobiletools.notes.pro.models.Note
|
||||
import kotlinx.android.synthetic.main.dialog_import_folder.view.*
|
||||
import java.io.File
|
||||
|
||||
class ImportFolderDialog(val activity: SimpleActivity, val path: String, val callback: (id: Int) -> Unit) : AlertDialog.Builder(activity) {
|
||||
private var dialog: AlertDialog
|
||||
|
||||
init {
|
||||
val view = (activity.layoutInflater.inflate(R.layout.dialog_import_folder, null) as ViewGroup).apply {
|
||||
open_file_filename.text = activity.humanizePath(path)
|
||||
}
|
||||
|
||||
dialog = AlertDialog.Builder(activity)
|
||||
.setPositiveButton(R.string.ok, null)
|
||||
.setNegativeButton(R.string.cancel, null)
|
||||
.create().apply {
|
||||
activity.setupDialogStuff(view, this, R.string.import_folder) {
|
||||
getButton(AlertDialog.BUTTON_POSITIVE).setOnClickListener {
|
||||
val updateFilesOnEdit = view.open_file_type.checkedRadioButtonId == R.id.open_file_update_file
|
||||
saveFolder(updateFilesOnEdit)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private fun saveFolder(updateFilesOnEdit: Boolean) {
|
||||
val folder = File(path)
|
||||
var lastSavedNoteId = -1
|
||||
folder.listFiles { file ->
|
||||
val filename = file.path.getFilenameFromPath()
|
||||
when {
|
||||
file.isDirectory -> false
|
||||
filename.isMediaFile() -> false
|
||||
file.length() > 10 * 1000 * 1000 -> false
|
||||
activity.dbHelper.doesNoteTitleExist(filename) -> false
|
||||
else -> true
|
||||
}
|
||||
}.forEach {
|
||||
val storePath = if (updateFilesOnEdit) it.absolutePath else ""
|
||||
val title = it.absolutePath.getFilenameFromPath()
|
||||
val value = if (updateFilesOnEdit) "" else it.readText()
|
||||
|
||||
if (updateFilesOnEdit) {
|
||||
activity.handleSAFDialog(path) {
|
||||
lastSavedNoteId = saveNote(title, value, storePath)
|
||||
}
|
||||
} else {
|
||||
lastSavedNoteId = saveNote(title, value, storePath)
|
||||
}
|
||||
}
|
||||
|
||||
if (lastSavedNoteId != -1) {
|
||||
callback(lastSavedNoteId)
|
||||
}
|
||||
|
||||
dialog.dismiss()
|
||||
}
|
||||
|
||||
private fun saveNote(title: String, value: String, path: String): Int {
|
||||
val note = Note(0, title, value, TYPE_NOTE, path)
|
||||
return activity.dbHelper.insertNote(note)
|
||||
}
|
||||
}
|
@ -0,0 +1,38 @@
|
||||
package com.simplemobiletools.notes.pro.dialogs
|
||||
|
||||
import android.app.Activity
|
||||
import android.content.DialogInterface.BUTTON_POSITIVE
|
||||
import androidx.appcompat.app.AlertDialog
|
||||
import com.simplemobiletools.commons.extensions.setupDialogStuff
|
||||
import com.simplemobiletools.commons.extensions.showKeyboard
|
||||
import com.simplemobiletools.commons.extensions.toast
|
||||
import com.simplemobiletools.commons.extensions.value
|
||||
import com.simplemobiletools.notes.pro.R
|
||||
import com.simplemobiletools.notes.pro.helpers.DBHelper
|
||||
import kotlinx.android.synthetic.main.dialog_new_note.view.*
|
||||
|
||||
class NewNoteDialog(val activity: Activity, val db: DBHelper, callback: (title: String) -> Unit) {
|
||||
init {
|
||||
val view = activity.layoutInflater.inflate(R.layout.dialog_new_note, null)
|
||||
|
||||
AlertDialog.Builder(activity)
|
||||
.setPositiveButton(R.string.ok, null)
|
||||
.setNegativeButton(R.string.cancel, null)
|
||||
.create().apply {
|
||||
activity.setupDialogStuff(view, this, R.string.new_note) {
|
||||
showKeyboard(view.note_name)
|
||||
getButton(BUTTON_POSITIVE).setOnClickListener {
|
||||
val title = view.note_name.value
|
||||
when {
|
||||
title.isEmpty() -> activity.toast(R.string.no_title)
|
||||
db.doesNoteTitleExist(title) -> activity.toast(R.string.title_taken)
|
||||
else -> {
|
||||
callback(title)
|
||||
dismiss()
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
@ -0,0 +1,52 @@
|
||||
package com.simplemobiletools.notes.pro.dialogs
|
||||
|
||||
import android.view.ViewGroup
|
||||
import androidx.appcompat.app.AlertDialog
|
||||
import com.simplemobiletools.commons.extensions.getFilenameFromPath
|
||||
import com.simplemobiletools.commons.extensions.humanizePath
|
||||
import com.simplemobiletools.commons.extensions.setupDialogStuff
|
||||
import com.simplemobiletools.notes.pro.R
|
||||
import com.simplemobiletools.notes.pro.activities.SimpleActivity
|
||||
import com.simplemobiletools.notes.pro.helpers.TYPE_NOTE
|
||||
import com.simplemobiletools.notes.pro.models.Note
|
||||
import kotlinx.android.synthetic.main.dialog_open_file.*
|
||||
import kotlinx.android.synthetic.main.dialog_open_file.view.*
|
||||
import java.io.File
|
||||
|
||||
class OpenFileDialog(val activity: SimpleActivity, val path: String, val callback: (note: Note) -> Unit) : AlertDialog.Builder(activity) {
|
||||
private var dialog: AlertDialog
|
||||
|
||||
init {
|
||||
val view = (activity.layoutInflater.inflate(R.layout.dialog_open_file, null) as ViewGroup).apply {
|
||||
open_file_filename.text = activity.humanizePath(path)
|
||||
}
|
||||
|
||||
dialog = AlertDialog.Builder(activity)
|
||||
.setPositiveButton(R.string.ok, null)
|
||||
.setNegativeButton(R.string.cancel, null)
|
||||
.create().apply {
|
||||
activity.setupDialogStuff(view, this, R.string.open_file) {
|
||||
getButton(AlertDialog.BUTTON_POSITIVE).setOnClickListener {
|
||||
val updateFileOnEdit = view.open_file_type.checkedRadioButtonId == open_file_update_file.id
|
||||
val storePath = if (updateFileOnEdit) path else ""
|
||||
val storeContent = if (updateFileOnEdit) "" else File(path).readText()
|
||||
|
||||
if (updateFileOnEdit) {
|
||||
activity.handleSAFDialog(path) {
|
||||
saveNote(storeContent, storePath)
|
||||
}
|
||||
} else {
|
||||
saveNote(storeContent, storePath)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private fun saveNote(storeContent: String, storePath: String) {
|
||||
val filename = path.getFilenameFromPath()
|
||||
val note = Note(0, filename, storeContent, TYPE_NOTE, storePath)
|
||||
callback(note)
|
||||
dialog.dismiss()
|
||||
}
|
||||
}
|
@ -0,0 +1,53 @@
|
||||
package com.simplemobiletools.notes.pro.dialogs
|
||||
|
||||
import android.app.Activity
|
||||
import android.view.ViewGroup
|
||||
import android.widget.RadioGroup
|
||||
import androidx.appcompat.app.AlertDialog
|
||||
import com.simplemobiletools.commons.extensions.applyColorFilter
|
||||
import com.simplemobiletools.commons.extensions.beVisibleIf
|
||||
import com.simplemobiletools.commons.extensions.setupDialogStuff
|
||||
import com.simplemobiletools.commons.extensions.toast
|
||||
import com.simplemobiletools.notes.pro.R
|
||||
import com.simplemobiletools.notes.pro.extensions.config
|
||||
import com.simplemobiletools.notes.pro.extensions.dbHelper
|
||||
import kotlinx.android.synthetic.main.dialog_open_note.view.*
|
||||
import kotlinx.android.synthetic.main.open_note_item.view.*
|
||||
|
||||
class OpenNoteDialog(val activity: Activity, val callback: (checkedId: Int) -> Unit) {
|
||||
lateinit var dialog: AlertDialog
|
||||
|
||||
init {
|
||||
val view = activity.layoutInflater.inflate(R.layout.dialog_open_note, null)
|
||||
val textColor = activity.config.textColor
|
||||
val notes = activity.dbHelper.getNotes()
|
||||
notes.forEach {
|
||||
activity.layoutInflater.inflate(R.layout.open_note_item, null).apply {
|
||||
val note = it
|
||||
open_note_item_radio_button.apply {
|
||||
text = note.title
|
||||
isChecked = note.id == activity.config.currentNoteId
|
||||
id = note.id
|
||||
|
||||
setOnClickListener {
|
||||
callback(id)
|
||||
dialog.dismiss()
|
||||
}
|
||||
}
|
||||
open_note_item_icon.apply {
|
||||
beVisibleIf(note.path.isNotEmpty())
|
||||
applyColorFilter(textColor)
|
||||
setOnClickListener {
|
||||
activity.toast(note.path)
|
||||
}
|
||||
}
|
||||
view.dialog_open_note_linear.addView(this, RadioGroup.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.WRAP_CONTENT))
|
||||
}
|
||||
}
|
||||
|
||||
dialog = AlertDialog.Builder(activity)
|
||||
.create().apply {
|
||||
activity.setupDialogStuff(view, this, R.string.pick_a_note)
|
||||
}
|
||||
}
|
||||
}
|
@ -0,0 +1,65 @@
|
||||
package com.simplemobiletools.notes.pro.dialogs
|
||||
|
||||
import android.content.DialogInterface.BUTTON_POSITIVE
|
||||
import androidx.appcompat.app.AlertDialog
|
||||
import com.simplemobiletools.commons.extensions.*
|
||||
import com.simplemobiletools.notes.pro.R
|
||||
import com.simplemobiletools.notes.pro.activities.SimpleActivity
|
||||
import com.simplemobiletools.notes.pro.extensions.dbHelper
|
||||
import com.simplemobiletools.notes.pro.models.Note
|
||||
import kotlinx.android.synthetic.main.dialog_new_note.view.*
|
||||
import java.io.File
|
||||
|
||||
class RenameNoteDialog(val activity: SimpleActivity, val note: Note, callback: (note: Note) -> Unit) {
|
||||
|
||||
init {
|
||||
val view = activity.layoutInflater.inflate(R.layout.dialog_rename_note, null)
|
||||
view.note_name.setText(note.title)
|
||||
|
||||
AlertDialog.Builder(activity)
|
||||
.setPositiveButton(R.string.ok, null)
|
||||
.setNegativeButton(R.string.cancel, null)
|
||||
.create().apply {
|
||||
activity.setupDialogStuff(view, this, R.string.rename_note) {
|
||||
showKeyboard(view.note_name)
|
||||
getButton(BUTTON_POSITIVE).setOnClickListener {
|
||||
val title = view.note_name.value
|
||||
when {
|
||||
title.isEmpty() -> activity.toast(R.string.no_title)
|
||||
activity.dbHelper.doesNoteTitleExist(title) -> activity.toast(R.string.title_taken)
|
||||
else -> {
|
||||
note.title = title
|
||||
val path = note.path
|
||||
if (path.isNotEmpty()) {
|
||||
if (title.isEmpty()) {
|
||||
activity.toast(R.string.filename_cannot_be_empty)
|
||||
return@setOnClickListener
|
||||
}
|
||||
|
||||
val file = File(path)
|
||||
val newFile = File(file.parent, title)
|
||||
if (!newFile.name.isAValidFilename()) {
|
||||
activity.toast(R.string.invalid_name)
|
||||
return@setOnClickListener
|
||||
}
|
||||
|
||||
activity.renameFile(file.absolutePath, newFile.absolutePath) {
|
||||
if (it) {
|
||||
note.path = newFile.absolutePath
|
||||
activity.dbHelper.updateNotePath(note)
|
||||
} else {
|
||||
activity.toast(R.string.rename_file_error)
|
||||
return@renameFile
|
||||
}
|
||||
}
|
||||
}
|
||||
activity.dbHelper.updateNoteTitle(note)
|
||||
dismiss()
|
||||
callback(note)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
@ -0,0 +1,30 @@
|
||||
package com.simplemobiletools.notes.pro.extensions
|
||||
|
||||
import android.appwidget.AppWidgetManager
|
||||
import android.content.ComponentName
|
||||
import android.content.Context
|
||||
import android.content.Intent
|
||||
import com.simplemobiletools.notes.pro.R
|
||||
import com.simplemobiletools.notes.pro.helpers.*
|
||||
|
||||
val Context.config: Config get() = Config.newInstance(applicationContext)
|
||||
|
||||
val Context.dbHelper: DBHelper get() = DBHelper.newInstance(applicationContext)
|
||||
|
||||
fun Context.getTextSize() = when (config.fontSize) {
|
||||
FONT_SIZE_SMALL -> resources.getDimension(R.dimen.smaller_text_size)
|
||||
FONT_SIZE_LARGE -> resources.getDimension(R.dimen.big_text_size)
|
||||
FONT_SIZE_EXTRA_LARGE -> resources.getDimension(R.dimen.extra_big_text_size)
|
||||
else -> resources.getDimension(R.dimen.bigger_text_size)
|
||||
}
|
||||
|
||||
fun Context.updateWidgets() {
|
||||
val widgetIDs = AppWidgetManager.getInstance(applicationContext).getAppWidgetIds(ComponentName(applicationContext, MyWidgetProvider::class.java))
|
||||
if (widgetIDs.isNotEmpty()) {
|
||||
Intent(applicationContext, MyWidgetProvider::class.java).apply {
|
||||
action = AppWidgetManager.ACTION_APPWIDGET_UPDATE
|
||||
putExtra(AppWidgetManager.EXTRA_APPWIDGET_IDS, widgetIDs)
|
||||
sendBroadcast(this)
|
||||
}
|
||||
}
|
||||
}
|
@ -0,0 +1,6 @@
|
||||
package com.simplemobiletools.notes.pro.extensions
|
||||
|
||||
import androidx.fragment.app.Fragment
|
||||
import com.simplemobiletools.notes.pro.helpers.Config
|
||||
|
||||
val Fragment.config: Config? get() = if (context != null) Config.newInstance(context!!) else null
|
@ -0,0 +1,283 @@
|
||||
package com.simplemobiletools.notes.pro.fragments
|
||||
|
||||
import android.annotation.SuppressLint
|
||||
import android.graphics.Typeface
|
||||
import android.os.Build
|
||||
import android.os.Bundle
|
||||
import android.text.Editable
|
||||
import android.text.Selection
|
||||
import android.text.TextWatcher
|
||||
import android.text.style.UnderlineSpan
|
||||
import android.text.util.Linkify
|
||||
import android.util.TypedValue
|
||||
import android.view.Gravity
|
||||
import android.view.LayoutInflater
|
||||
import android.view.View
|
||||
import android.view.ViewGroup
|
||||
import android.view.inputmethod.EditorInfo
|
||||
import androidx.annotation.RequiresApi
|
||||
import com.simplemobiletools.commons.extensions.*
|
||||
import com.simplemobiletools.notes.pro.R
|
||||
import com.simplemobiletools.notes.pro.activities.MainActivity
|
||||
import com.simplemobiletools.notes.pro.extensions.config
|
||||
import com.simplemobiletools.notes.pro.extensions.dbHelper
|
||||
import com.simplemobiletools.notes.pro.extensions.getTextSize
|
||||
import com.simplemobiletools.notes.pro.extensions.updateWidgets
|
||||
import com.simplemobiletools.notes.pro.helpers.*
|
||||
import com.simplemobiletools.notes.pro.models.Note
|
||||
import com.simplemobiletools.notes.pro.models.TextHistory
|
||||
import com.simplemobiletools.notes.pro.models.TextHistoryItem
|
||||
import kotlinx.android.synthetic.main.fragment_note.*
|
||||
import kotlinx.android.synthetic.main.fragment_note.view.*
|
||||
import kotlinx.android.synthetic.main.note_view_horiz_scrollable.view.*
|
||||
import java.io.File
|
||||
|
||||
// text history handling taken from https://gist.github.com/zeleven/0cfa738c1e8b65b23ff7df1fc30c9f7e
|
||||
class NoteFragment : androidx.fragment.app.Fragment() {
|
||||
private val TEXT = "text"
|
||||
|
||||
private var textHistory = TextHistory()
|
||||
private var isUndoOrRedo = false
|
||||
private var skipTextUpdating = false
|
||||
private var noteId = 0
|
||||
lateinit var note: Note
|
||||
lateinit var view: ViewGroup
|
||||
private lateinit var db: DBHelper
|
||||
|
||||
override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View? {
|
||||
view = inflater.inflate(R.layout.fragment_note, container, false) as ViewGroup
|
||||
noteId = arguments!!.getInt(NOTE_ID)
|
||||
db = context!!.dbHelper
|
||||
note = db.getNoteWithId(noteId) ?: return view
|
||||
retainInstance = true
|
||||
|
||||
val layoutToInflate = if (config!!.enableLineWrap) R.layout.note_view_static else R.layout.note_view_horiz_scrollable
|
||||
inflater.inflate(layoutToInflate, view.notes_relative_layout, true)
|
||||
if (config!!.clickableLinks) {
|
||||
view.notes_view.apply {
|
||||
linksClickable = true
|
||||
autoLinkMask = Linkify.WEB_URLS or Linkify.EMAIL_ADDRESSES
|
||||
movementMethod = MyMovementMethod.getInstance()
|
||||
}
|
||||
}
|
||||
|
||||
view.notes_horizontal_scrollview?.onGlobalLayout {
|
||||
view.notes_view.minWidth = view.notes_horizontal_scrollview.width
|
||||
}
|
||||
|
||||
return view
|
||||
}
|
||||
|
||||
@RequiresApi(Build.VERSION_CODES.O)
|
||||
override fun onResume() {
|
||||
super.onResume()
|
||||
|
||||
val config = config!!
|
||||
view.notes_view.apply {
|
||||
typeface = if (config.monospacedFont) Typeface.MONOSPACE else Typeface.DEFAULT
|
||||
|
||||
val fileContents = note.getNoteStoredValue()
|
||||
if (fileContents == null) {
|
||||
(activity as MainActivity).deleteNote(false)
|
||||
return
|
||||
}
|
||||
|
||||
setColors(config.textColor, config.primaryColor, config.backgroundColor)
|
||||
setTextSize(TypedValue.COMPLEX_UNIT_PX, context.getTextSize())
|
||||
gravity = getTextGravity()
|
||||
if (text.toString() != fileContents) {
|
||||
if (!skipTextUpdating) {
|
||||
setText(fileContents)
|
||||
}
|
||||
skipTextUpdating = false
|
||||
setSelection(if (config.placeCursorToEnd) text.length else 0)
|
||||
}
|
||||
|
||||
if (config.showKeyboard) {
|
||||
requestFocus()
|
||||
}
|
||||
|
||||
imeOptions = if (config.useIncognitoMode) {
|
||||
imeOptions or EditorInfo.IME_FLAG_NO_PERSONALIZED_LEARNING
|
||||
} else {
|
||||
imeOptions.removeBit(EditorInfo.IME_FLAG_NO_PERSONALIZED_LEARNING)
|
||||
}
|
||||
}
|
||||
|
||||
if (config.showWordCount) {
|
||||
view.notes_counter.beVisible()
|
||||
view.notes_counter.setTextColor(config.textColor)
|
||||
setWordCounter(view.notes_view.text.toString())
|
||||
} else {
|
||||
view.notes_counter.beGone()
|
||||
}
|
||||
|
||||
view.notes_view.addTextChangedListener(textWatcher)
|
||||
}
|
||||
|
||||
override fun onPause() {
|
||||
super.onPause()
|
||||
if (config!!.autosaveNotes) {
|
||||
saveText(false)
|
||||
}
|
||||
view.notes_view.removeTextChangedListener(textWatcher)
|
||||
}
|
||||
|
||||
override fun setMenuVisibility(menuVisible: Boolean) {
|
||||
super.setMenuVisibility(menuVisible)
|
||||
if (!menuVisible && noteId != 0 && config?.autosaveNotes == true) {
|
||||
saveText(false)
|
||||
}
|
||||
|
||||
if (menuVisible && noteId != 0) {
|
||||
val currentText = getCurrentNoteViewText()
|
||||
if (currentText != null) {
|
||||
(activity as MainActivity).currentNoteTextChanged(currentText, isUndoAvailable(), isRedoAvailable())
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
override fun onSaveInstanceState(outState: Bundle) {
|
||||
super.onSaveInstanceState(outState)
|
||||
outState.putString(TEXT, getCurrentNoteViewText())
|
||||
}
|
||||
|
||||
override fun onViewStateRestored(savedInstanceState: Bundle?) {
|
||||
super.onViewStateRestored(savedInstanceState)
|
||||
if (savedInstanceState != null) {
|
||||
skipTextUpdating = true
|
||||
val newText = savedInstanceState.getString(TEXT) ?: ""
|
||||
view.notes_view.setText(newText)
|
||||
}
|
||||
}
|
||||
|
||||
fun getNotesView() = view.notes_view
|
||||
|
||||
fun saveText(force: Boolean) {
|
||||
if (note.path.isNotEmpty() && !File(note.path).exists()) {
|
||||
return
|
||||
}
|
||||
|
||||
if (context == null || activity == null) {
|
||||
return
|
||||
}
|
||||
|
||||
val newText = getCurrentNoteViewText()
|
||||
val oldText = note.getNoteStoredValue()
|
||||
if (newText != null && (newText != oldText || force)) {
|
||||
note.value = newText
|
||||
saveNoteValue(note)
|
||||
context!!.updateWidgets()
|
||||
}
|
||||
}
|
||||
|
||||
fun hasUnsavedChanges() = getCurrentNoteViewText() != note.getNoteStoredValue()
|
||||
|
||||
fun focusEditText() {
|
||||
view.notes_view.requestFocus()
|
||||
}
|
||||
|
||||
private fun saveNoteValue(note: Note) {
|
||||
if (note.path.isEmpty()) {
|
||||
db.updateNoteValue(note)
|
||||
(activity as MainActivity).noteSavedSuccessfully(note.title)
|
||||
} else {
|
||||
val currentText = getCurrentNoteViewText()
|
||||
if (currentText != null) {
|
||||
(activity as MainActivity).exportNoteValueToFile(note.path, currentText, true)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fun getCurrentNoteViewText() = view.notes_view?.text?.toString()
|
||||
|
||||
@SuppressLint("RtlHardcoded")
|
||||
private fun getTextGravity() = when (config!!.gravity) {
|
||||
GRAVITY_CENTER -> Gravity.CENTER_HORIZONTAL
|
||||
GRAVITY_RIGHT -> Gravity.RIGHT
|
||||
else -> Gravity.LEFT
|
||||
}
|
||||
|
||||
private fun setWordCounter(text: String) {
|
||||
val words = text.replace("\n", " ").split(" ")
|
||||
notes_counter.text = words.count { it.isNotEmpty() }.toString()
|
||||
}
|
||||
|
||||
fun undo() {
|
||||
val edit = textHistory.getPrevious() ?: return
|
||||
|
||||
val text = view.notes_view.editableText
|
||||
val start = edit.start
|
||||
val end = start + if (edit.after != null) edit.after.length else 0
|
||||
|
||||
isUndoOrRedo = true
|
||||
try {
|
||||
text.replace(start, end, edit.before)
|
||||
} catch (e: Exception) {
|
||||
activity?.showErrorToast(e)
|
||||
return
|
||||
}
|
||||
|
||||
isUndoOrRedo = false
|
||||
|
||||
for (span in text.getSpans(0, text.length, UnderlineSpan::class.java)) {
|
||||
text.removeSpan(span)
|
||||
}
|
||||
|
||||
Selection.setSelection(text, if (edit.before == null) {
|
||||
start
|
||||
} else {
|
||||
start + edit.before.length
|
||||
})
|
||||
}
|
||||
|
||||
fun redo() {
|
||||
val edit = textHistory.getNext() ?: return
|
||||
|
||||
val text = view.notes_view.editableText
|
||||
val start = edit.start
|
||||
val end = start + if (edit.before != null) edit.before.length else 0
|
||||
|
||||
isUndoOrRedo = true
|
||||
text.replace(start, end, edit.after)
|
||||
isUndoOrRedo = false
|
||||
|
||||
for (o in text.getSpans(0, text.length, UnderlineSpan::class.java)) {
|
||||
text.removeSpan(o)
|
||||
}
|
||||
|
||||
Selection.setSelection(text, if (edit.after == null) {
|
||||
start
|
||||
} else {
|
||||
start + edit.after.length
|
||||
})
|
||||
}
|
||||
|
||||
fun isUndoAvailable() = textHistory.position > 0
|
||||
|
||||
fun isRedoAvailable() = textHistory.position < textHistory.history.size
|
||||
|
||||
private var textWatcher: TextWatcher = object : TextWatcher {
|
||||
private var beforeChange: CharSequence? = null
|
||||
private var afterChange: CharSequence? = null
|
||||
|
||||
override fun beforeTextChanged(s: CharSequence, start: Int, count: Int, after: Int) {
|
||||
if (!isUndoOrRedo) {
|
||||
beforeChange = s.subSequence(start, start + count)
|
||||
}
|
||||
}
|
||||
|
||||
override fun onTextChanged(s: CharSequence, start: Int, before: Int, count: Int) {
|
||||
if (!isUndoOrRedo) {
|
||||
afterChange = s.subSequence(start, start + count)
|
||||
textHistory.add(TextHistoryItem(start, beforeChange!!, afterChange!!))
|
||||
}
|
||||
}
|
||||
|
||||
override fun afterTextChanged(editable: Editable) {
|
||||
val text = editable.toString()
|
||||
setWordCounter(text)
|
||||
(activity as MainActivity).currentNoteTextChanged(text, isUndoAvailable(), isRedoAvailable())
|
||||
}
|
||||
}
|
||||
}
|
@ -0,0 +1,75 @@
|
||||
package com.simplemobiletools.notes.pro.helpers
|
||||
|
||||
import android.content.Context
|
||||
import android.os.Environment
|
||||
import com.simplemobiletools.commons.helpers.BaseConfig
|
||||
|
||||
class Config(context: Context) : BaseConfig(context) {
|
||||
companion object {
|
||||
fun newInstance(context: Context) = Config(context)
|
||||
}
|
||||
|
||||
var autosaveNotes: Boolean
|
||||
get() = prefs.getBoolean(AUTOSAVE_NOTES, true)
|
||||
set(autosaveNotes) = prefs.edit().putBoolean(AUTOSAVE_NOTES, autosaveNotes).apply()
|
||||
|
||||
var displaySuccess: Boolean
|
||||
get() = prefs.getBoolean(DISPLAY_SUCCESS, false)
|
||||
set(displaySuccess) = prefs.edit().putBoolean(DISPLAY_SUCCESS, displaySuccess).apply()
|
||||
|
||||
var clickableLinks: Boolean
|
||||
get() = prefs.getBoolean(CLICKABLE_LINKS, false)
|
||||
set(clickableLinks) = prefs.edit().putBoolean(CLICKABLE_LINKS, clickableLinks).apply()
|
||||
|
||||
var monospacedFont: Boolean
|
||||
get() = prefs.getBoolean(MONOSPACED_FONT, false)
|
||||
set(monospacedFont) = prefs.edit().putBoolean(MONOSPACED_FONT, monospacedFont).apply()
|
||||
|
||||
var showKeyboard: Boolean
|
||||
get() = prefs.getBoolean(SHOW_KEYBOARD, true)
|
||||
set(showKeyboard) = prefs.edit().putBoolean(SHOW_KEYBOARD, showKeyboard).apply()
|
||||
|
||||
var showNotePicker: Boolean
|
||||
get() = prefs.getBoolean(SHOW_NOTE_PICKER, false)
|
||||
set(showNotePicker) = prefs.edit().putBoolean(SHOW_NOTE_PICKER, showNotePicker).apply()
|
||||
|
||||
var showWordCount: Boolean
|
||||
get() = prefs.getBoolean(SHOW_WORD_COUNT, false)
|
||||
set(showWordCount) = prefs.edit().putBoolean(SHOW_WORD_COUNT, showWordCount).apply()
|
||||
|
||||
var fontSize: Int
|
||||
get() = prefs.getInt(FONT_SIZE, FONT_SIZE_MEDIUM)
|
||||
set(size) = prefs.edit().putInt(FONT_SIZE, size).apply()
|
||||
|
||||
var gravity: Int
|
||||
get() = prefs.getInt(GRAVITY, GRAVITY_LEFT)
|
||||
set(size) = prefs.edit().putInt(GRAVITY, size).apply()
|
||||
|
||||
var currentNoteId: Int
|
||||
get() = prefs.getInt(CURRENT_NOTE_ID, 1)
|
||||
set(id) = prefs.edit().putInt(CURRENT_NOTE_ID, id).apply()
|
||||
|
||||
var widgetNoteId: Int
|
||||
get() = prefs.getInt(WIDGET_NOTE_ID, 1)
|
||||
set(id) = prefs.edit().putInt(WIDGET_NOTE_ID, id).apply()
|
||||
|
||||
var placeCursorToEnd: Boolean
|
||||
get() = prefs.getBoolean(CURSOR_PLACEMENT, true)
|
||||
set(placement) = prefs.edit().putBoolean(CURSOR_PLACEMENT, placement).apply()
|
||||
|
||||
var enableLineWrap: Boolean
|
||||
get() = prefs.getBoolean(ENABLE_LINE_WRAP, true)
|
||||
set(enableLineWrap) = prefs.edit().putBoolean(ENABLE_LINE_WRAP, enableLineWrap).apply()
|
||||
|
||||
var lastUsedExtension: String
|
||||
get() = prefs.getString(LAST_USED_EXTENSION, "txt")
|
||||
set(lastUsedExtension) = prefs.edit().putString(LAST_USED_EXTENSION, lastUsedExtension).apply()
|
||||
|
||||
var lastUsedSavePath: String
|
||||
get() = prefs.getString(LAST_USED_SAVE_PATH, Environment.getExternalStorageDirectory().toString())
|
||||
set(lastUsedSavePath) = prefs.edit().putString(LAST_USED_SAVE_PATH, lastUsedSavePath).apply()
|
||||
|
||||
var useIncognitoMode: Boolean
|
||||
get() = prefs.getBoolean(USE_INCOGNITO_MODE, false)
|
||||
set(useIncognitoMode) = prefs.edit().putBoolean(USE_INCOGNITO_MODE, useIncognitoMode).apply()
|
||||
}
|
@ -0,0 +1,40 @@
|
||||
package com.simplemobiletools.notes.pro.helpers
|
||||
|
||||
const val NOTE_ID = "note_id"
|
||||
const val OPEN_NOTE_ID = "open_note_id"
|
||||
|
||||
// shared preferences
|
||||
const val CURRENT_NOTE_ID = "current_note_id"
|
||||
const val AUTOSAVE_NOTES = "autosave_notes"
|
||||
const val DISPLAY_SUCCESS = "display_success"
|
||||
const val CLICKABLE_LINKS = "clickable_links"
|
||||
const val WIDGET_NOTE_ID = "widget_note_id"
|
||||
const val MONOSPACED_FONT = "monospaced_font"
|
||||
const val SHOW_KEYBOARD = "show_keyboard"
|
||||
const val SHOW_NOTE_PICKER = "show_note_picker"
|
||||
const val SHOW_WORD_COUNT = "show_word_count"
|
||||
const val FONT_SIZE = "font_size"
|
||||
const val GRAVITY = "gravity"
|
||||
const val CURSOR_PLACEMENT = "cursor_placement"
|
||||
const val LAST_USED_EXTENSION = "last_used_extension"
|
||||
const val LAST_USED_SAVE_PATH = "last_used_save_path"
|
||||
const val ENABLE_LINE_WRAP = "enable_line_wrap"
|
||||
const val USE_INCOGNITO_MODE = "use_incognito_mode"
|
||||
|
||||
// gravity
|
||||
const val GRAVITY_LEFT = 0
|
||||
const val GRAVITY_CENTER = 1
|
||||
const val GRAVITY_RIGHT = 2
|
||||
|
||||
// font sizes
|
||||
const val FONT_SIZE_SMALL = 0
|
||||
const val FONT_SIZE_MEDIUM = 1
|
||||
const val FONT_SIZE_LARGE = 2
|
||||
const val FONT_SIZE_EXTRA_LARGE = 3
|
||||
|
||||
// note types
|
||||
const val TYPE_NOTE = 0
|
||||
const val TYPE_CHECKLIST = 1
|
||||
|
||||
// mime types
|
||||
const val MIME_TEXT_PLAIN = "text/plain"
|
@ -0,0 +1,244 @@
|
||||
package com.simplemobiletools.notes.pro.helpers
|
||||
|
||||
import android.appwidget.AppWidgetManager
|
||||
import android.content.ComponentName
|
||||
import android.content.ContentValues
|
||||
import android.content.Context
|
||||
import android.database.Cursor
|
||||
import android.database.sqlite.SQLiteDatabase
|
||||
import android.database.sqlite.SQLiteDatabase.CONFLICT_IGNORE
|
||||
import android.database.sqlite.SQLiteOpenHelper
|
||||
import com.simplemobiletools.commons.extensions.getIntValue
|
||||
import com.simplemobiletools.commons.extensions.getStringValue
|
||||
import com.simplemobiletools.notes.pro.R
|
||||
import com.simplemobiletools.notes.pro.extensions.config
|
||||
import com.simplemobiletools.notes.pro.models.Note
|
||||
import com.simplemobiletools.notes.pro.models.Widget
|
||||
import java.io.File
|
||||
import java.util.*
|
||||
|
||||
class DBHelper private constructor(private val mContext: Context) : SQLiteOpenHelper(mContext, DB_NAME, null, DB_VERSION) {
|
||||
private val mDb = writableDatabase
|
||||
|
||||
companion object {
|
||||
private const val DB_NAME = "notes.db"
|
||||
private const val DB_VERSION = 4
|
||||
private const val NOTES_TABLE_NAME = "notes"
|
||||
private const val WIDGETS_TABLE_NAME = "widgets"
|
||||
|
||||
private const val COL_ID = "id"
|
||||
private const val COL_TITLE = "title"
|
||||
private const val COL_VALUE = "value"
|
||||
private const val COL_TYPE = "type"
|
||||
private const val COL_PATH = "path"
|
||||
|
||||
private const val COL_WIDGET_ID = "widget_id"
|
||||
private const val COL_NOTE_ID = "note_id"
|
||||
|
||||
fun newInstance(context: Context) = DBHelper(context)
|
||||
}
|
||||
|
||||
override fun onCreate(db: SQLiteDatabase) {
|
||||
db.execSQL("CREATE TABLE $NOTES_TABLE_NAME ($COL_ID INTEGER PRIMARY KEY AUTOINCREMENT, $COL_TITLE TEXT UNIQUE, $COL_VALUE TEXT, $COL_TYPE INTEGER DEFAULT 0, $COL_PATH TEXT)")
|
||||
db.execSQL("CREATE TABLE $WIDGETS_TABLE_NAME ($COL_ID INTEGER PRIMARY KEY AUTOINCREMENT, $COL_WIDGET_ID INTEGER DEFAULT 0, $COL_NOTE_ID INTEGER DEFAULT 0)")
|
||||
insertFirstNote(db)
|
||||
}
|
||||
|
||||
override fun onUpgrade(db: SQLiteDatabase, oldVersion: Int, newVersion: Int) {
|
||||
if (oldVersion < 2) {
|
||||
db.execSQL("ALTER TABLE $NOTES_TABLE_NAME ADD COLUMN $COL_TYPE INTEGER DEFAULT 0")
|
||||
}
|
||||
|
||||
if (oldVersion < 3) {
|
||||
db.execSQL("ALTER TABLE $NOTES_TABLE_NAME ADD COLUMN $COL_PATH TEXT DEFAULT ''")
|
||||
}
|
||||
|
||||
if (oldVersion < 4) {
|
||||
db.execSQL("CREATE TABLE $WIDGETS_TABLE_NAME ($COL_ID INTEGER PRIMARY KEY AUTOINCREMENT, $COL_WIDGET_ID INTEGER DEFAULT 0, $COL_NOTE_ID INTEGER DEFAULT 0)")
|
||||
insertFirstWidget(db)
|
||||
}
|
||||
}
|
||||
|
||||
private fun insertFirstNote(db: SQLiteDatabase) {
|
||||
val generalNote = mContext.resources.getString(R.string.general_note)
|
||||
val note = Note(1, generalNote, "", TYPE_NOTE)
|
||||
insertNote(note, db)
|
||||
}
|
||||
|
||||
// if a user has exactly 1 widget active, prefill it. Can happen only at upgrading from older app versions
|
||||
private fun insertFirstWidget(db: SQLiteDatabase) {
|
||||
val widgetIDs = AppWidgetManager.getInstance(mContext).getAppWidgetIds(ComponentName(mContext, MyWidgetProvider::class.java))
|
||||
if (widgetIDs.size == 1) {
|
||||
val widget = Widget(widgetIDs.first(), mContext.config.widgetNoteId)
|
||||
insertWidget(widget, db)
|
||||
}
|
||||
}
|
||||
|
||||
private fun insertNote(note: Note, db: SQLiteDatabase) {
|
||||
val values = fillNoteContentValues(note)
|
||||
db.insert(NOTES_TABLE_NAME, null, values)
|
||||
}
|
||||
|
||||
private fun insertWidget(widget: Widget, db: SQLiteDatabase) {
|
||||
val values = fillWidgetContentValues(widget)
|
||||
db.insert(WIDGETS_TABLE_NAME, null, values)
|
||||
}
|
||||
|
||||
fun insertNote(note: Note): Int {
|
||||
val values = fillNoteContentValues(note)
|
||||
return mDb.insertWithOnConflict(NOTES_TABLE_NAME, null, values, CONFLICT_IGNORE).toInt()
|
||||
}
|
||||
|
||||
fun insertWidget(widget: Widget): Int {
|
||||
val values = fillWidgetContentValues(widget)
|
||||
return mDb.insertWithOnConflict(WIDGETS_TABLE_NAME, null, values, CONFLICT_IGNORE).toInt()
|
||||
}
|
||||
|
||||
private fun fillNoteContentValues(note: Note): ContentValues {
|
||||
return ContentValues().apply {
|
||||
put(COL_TITLE, note.title)
|
||||
put(COL_VALUE, note.value)
|
||||
put(COL_PATH, note.path)
|
||||
put(COL_TYPE, 0)
|
||||
}
|
||||
}
|
||||
|
||||
private fun fillWidgetContentValues(widget: Widget): ContentValues {
|
||||
return ContentValues().apply {
|
||||
put(COL_WIDGET_ID, widget.widgetId)
|
||||
put(COL_NOTE_ID, widget.noteId)
|
||||
}
|
||||
}
|
||||
|
||||
fun deleteNote(id: Int) {
|
||||
mDb.delete(NOTES_TABLE_NAME, "$COL_ID = $id", null)
|
||||
mDb.delete(WIDGETS_TABLE_NAME, "$COL_NOTE_ID = $id", null)
|
||||
}
|
||||
|
||||
fun doesNoteTitleExist(title: String): Boolean {
|
||||
val cols = arrayOf(COL_ID)
|
||||
val selection = "$COL_TITLE = ? COLLATE NOCASE"
|
||||
val selectionArgs = arrayOf(title)
|
||||
var cursor: Cursor? = null
|
||||
try {
|
||||
cursor = mDb.query(NOTES_TABLE_NAME, cols, selection, selectionArgs, null, null, null)
|
||||
return cursor.count == 1
|
||||
} finally {
|
||||
cursor?.close()
|
||||
}
|
||||
}
|
||||
|
||||
fun getNotes(): ArrayList<Note> {
|
||||
val notes = ArrayList<Note>()
|
||||
val cols = arrayOf(COL_ID, COL_TITLE, COL_VALUE, COL_TYPE, COL_PATH)
|
||||
var cursor: Cursor? = null
|
||||
try {
|
||||
cursor = mDb.query(NOTES_TABLE_NAME, cols, null, null, null, null, "$COL_TITLE COLLATE NOCASE ASC")
|
||||
if (cursor?.moveToFirst() == true) {
|
||||
do {
|
||||
try {
|
||||
val id = cursor.getIntValue(COL_ID)
|
||||
val title = cursor.getStringValue(COL_TITLE)
|
||||
val value = cursor.getStringValue(COL_VALUE)
|
||||
val type = cursor.getIntValue(COL_TYPE)
|
||||
val path = cursor.getStringValue(COL_PATH) ?: ""
|
||||
if (path.isNotEmpty() && !File(path).exists()) {
|
||||
deleteNote(id)
|
||||
continue
|
||||
}
|
||||
|
||||
val note = Note(id, title, value, type, path)
|
||||
notes.add(note)
|
||||
} catch (e: Exception) {
|
||||
continue
|
||||
}
|
||||
} while (cursor.moveToNext())
|
||||
}
|
||||
} finally {
|
||||
cursor?.close()
|
||||
}
|
||||
|
||||
return notes
|
||||
}
|
||||
|
||||
fun getNoteWithId(id: Int): Note? {
|
||||
val cols = arrayOf(COL_TITLE, COL_VALUE, COL_TYPE, COL_PATH)
|
||||
val selection = "$COL_ID = ?"
|
||||
val selectionArgs = arrayOf(id.toString())
|
||||
var note: Note? = null
|
||||
var cursor: Cursor? = null
|
||||
try {
|
||||
cursor = mDb.query(NOTES_TABLE_NAME, cols, selection, selectionArgs, null, null, null)
|
||||
if (cursor?.moveToFirst() == true) {
|
||||
val title = cursor.getStringValue(COL_TITLE)
|
||||
val value = cursor.getStringValue(COL_VALUE)
|
||||
val type = cursor.getIntValue(COL_TYPE)
|
||||
val path = cursor.getStringValue(COL_PATH) ?: ""
|
||||
note = Note(id, title, value, type, path)
|
||||
}
|
||||
} finally {
|
||||
cursor?.close()
|
||||
}
|
||||
return note
|
||||
}
|
||||
|
||||
fun getNoteId(path: String): Int {
|
||||
val cols = arrayOf(COL_ID)
|
||||
val selection = "$COL_PATH = ?"
|
||||
val selectionArgs = arrayOf(path)
|
||||
var cursor: Cursor? = null
|
||||
try {
|
||||
cursor = mDb.query(NOTES_TABLE_NAME, cols, selection, selectionArgs, null, null, null)
|
||||
if (cursor?.moveToFirst() == true) {
|
||||
return cursor.getIntValue(COL_ID)
|
||||
}
|
||||
} finally {
|
||||
cursor?.close()
|
||||
}
|
||||
return 0
|
||||
}
|
||||
|
||||
fun updateNoteValue(note: Note) {
|
||||
val values = ContentValues().apply { put(COL_VALUE, note.value) }
|
||||
updateNote(note.id, values)
|
||||
}
|
||||
|
||||
fun updateNoteTitle(note: Note) {
|
||||
val values = ContentValues().apply { put(COL_TITLE, note.title) }
|
||||
updateNote(note.id, values)
|
||||
}
|
||||
|
||||
fun updateNotePath(note: Note) {
|
||||
val values = ContentValues().apply { put(COL_PATH, note.path) }
|
||||
updateNote(note.id, values)
|
||||
}
|
||||
|
||||
private fun updateNote(id: Int, values: ContentValues) {
|
||||
val selection = "$COL_ID = ?"
|
||||
val selectionArgs = arrayOf(id.toString())
|
||||
mDb.update(NOTES_TABLE_NAME, values, selection, selectionArgs)
|
||||
}
|
||||
|
||||
fun isValidId(id: Int) = id > 0
|
||||
|
||||
fun getWidgets(): ArrayList<Widget> {
|
||||
val widgets = ArrayList<Widget>()
|
||||
val cols = arrayOf(COL_WIDGET_ID, COL_NOTE_ID)
|
||||
var cursor: Cursor? = null
|
||||
try {
|
||||
cursor = mDb.query(WIDGETS_TABLE_NAME, cols, null, null, null, null, null)
|
||||
if (cursor?.moveToFirst() == true) {
|
||||
do {
|
||||
val widgetId = cursor.getIntValue(COL_WIDGET_ID)
|
||||
val noteId = cursor.getIntValue(COL_NOTE_ID)
|
||||
val widget = Widget(widgetId, noteId)
|
||||
widgets.add(widget)
|
||||
} while (cursor.moveToNext())
|
||||
}
|
||||
} finally {
|
||||
cursor?.close()
|
||||
}
|
||||
|
||||
return widgets
|
||||
}
|
||||
}
|
@ -0,0 +1,54 @@
|
||||
package com.simplemobiletools.notes.pro.helpers
|
||||
|
||||
import android.text.Selection
|
||||
import android.text.Spannable
|
||||
import android.text.method.ArrowKeyMovementMethod
|
||||
import android.text.style.ClickableSpan
|
||||
import android.view.MotionEvent
|
||||
import android.widget.TextView
|
||||
|
||||
class MyMovementMethod : ArrowKeyMovementMethod() {
|
||||
companion object {
|
||||
private var sInstance: MyMovementMethod? = null
|
||||
|
||||
fun getInstance(): MyMovementMethod {
|
||||
if (sInstance == null) {
|
||||
sInstance = MyMovementMethod()
|
||||
}
|
||||
return sInstance!!
|
||||
}
|
||||
}
|
||||
|
||||
override fun onTouchEvent(widget: TextView, buffer: Spannable, event: MotionEvent): Boolean {
|
||||
val action = event.action
|
||||
|
||||
if (action == MotionEvent.ACTION_UP || action == MotionEvent.ACTION_DOWN) {
|
||||
var x = event.x.toInt()
|
||||
var y = event.y.toInt()
|
||||
|
||||
x -= widget.totalPaddingLeft
|
||||
y -= widget.totalPaddingTop
|
||||
|
||||
x += widget.scrollX
|
||||
y += widget.scrollY
|
||||
|
||||
val layout = widget.layout
|
||||
val line = layout.getLineForVertical(y)
|
||||
val off = layout.getOffsetForHorizontal(line, x.toFloat())
|
||||
|
||||
val links = buffer.getSpans(off, off, ClickableSpan::class.java)
|
||||
if (links.isNotEmpty()) {
|
||||
if (action == MotionEvent.ACTION_UP) {
|
||||
links[0].onClick(widget)
|
||||
} else if (action == MotionEvent.ACTION_DOWN) {
|
||||
Selection.setSelection(buffer,
|
||||
buffer.getSpanStart(links[0]),
|
||||
buffer.getSpanEnd(links[0]))
|
||||
}
|
||||
return true
|
||||
}
|
||||
}
|
||||
|
||||
return super.onTouchEvent(widget, buffer, event)
|
||||
}
|
||||
}
|
@ -0,0 +1,50 @@
|
||||
package com.simplemobiletools.notes.pro.helpers
|
||||
|
||||
import android.app.PendingIntent
|
||||
import android.appwidget.AppWidgetManager
|
||||
import android.appwidget.AppWidgetProvider
|
||||
import android.content.Context
|
||||
import android.content.Intent
|
||||
import android.net.Uri
|
||||
import android.widget.RemoteViews
|
||||
import com.simplemobiletools.commons.extensions.getLaunchIntent
|
||||
import com.simplemobiletools.commons.extensions.setBackgroundColor
|
||||
import com.simplemobiletools.notes.pro.R
|
||||
import com.simplemobiletools.notes.pro.activities.SplashActivity
|
||||
import com.simplemobiletools.notes.pro.extensions.config
|
||||
import com.simplemobiletools.notes.pro.extensions.dbHelper
|
||||
import com.simplemobiletools.notes.pro.models.Widget
|
||||
import com.simplemobiletools.notes.pro.services.WidgetService
|
||||
|
||||
class MyWidgetProvider : AppWidgetProvider() {
|
||||
private fun setupAppOpenIntent(context: Context, views: RemoteViews, id: Int, widget: Widget) {
|
||||
val intent = context.getLaunchIntent() ?: Intent(context, SplashActivity::class.java)
|
||||
intent.putExtra(OPEN_NOTE_ID, widget.noteId)
|
||||
val pendingIntent = PendingIntent.getActivity(context, widget.widgetId, intent, PendingIntent.FLAG_UPDATE_CURRENT)
|
||||
views.setOnClickPendingIntent(id, pendingIntent)
|
||||
}
|
||||
|
||||
override fun onUpdate(context: Context, appWidgetManager: AppWidgetManager, appWidgetIds: IntArray) {
|
||||
super.onUpdate(context, appWidgetManager, appWidgetIds)
|
||||
val widgets = context.dbHelper.getWidgets()
|
||||
widgets.forEach {
|
||||
val views = RemoteViews(context.packageName, R.layout.widget)
|
||||
views.setBackgroundColor(R.id.notes_widget_holder, context.config.widgetBgColor)
|
||||
setupAppOpenIntent(context, views, R.id.notes_widget_holder, it)
|
||||
|
||||
Intent(context, WidgetService::class.java).apply {
|
||||
putExtra(NOTE_ID, it.noteId)
|
||||
data = Uri.parse(this.toUri(Intent.URI_INTENT_SCHEME))
|
||||
views.setRemoteAdapter(R.id.notes_widget_listview, this)
|
||||
}
|
||||
|
||||
val startActivityIntent = context.getLaunchIntent() ?: Intent(context, SplashActivity::class.java)
|
||||
startActivityIntent.putExtra(OPEN_NOTE_ID, it.noteId)
|
||||
val startActivityPendingIntent = PendingIntent.getActivity(context, it.widgetId, startActivityIntent, PendingIntent.FLAG_UPDATE_CURRENT)
|
||||
views.setPendingIntentTemplate(R.id.notes_widget_listview, startActivityPendingIntent)
|
||||
|
||||
appWidgetManager.updateAppWidget(it.widgetId, views)
|
||||
appWidgetManager.notifyAppWidgetViewDataChanged(it.widgetId, R.id.notes_widget_listview)
|
||||
}
|
||||
}
|
||||
}
|
@ -0,0 +1,18 @@
|
||||
package com.simplemobiletools.notes.pro.models
|
||||
|
||||
import java.io.File
|
||||
import java.io.FileNotFoundException
|
||||
|
||||
data class Note(var id: Int, var title: String, var value: String, val type: Int, var path: String = "") {
|
||||
fun getNoteStoredValue(): String? {
|
||||
return if (path.isNotEmpty()) {
|
||||
return try {
|
||||
File(path).readText()
|
||||
} catch (e: FileNotFoundException) {
|
||||
null
|
||||
}
|
||||
} else {
|
||||
value
|
||||
}
|
||||
}
|
||||
}
|
@ -0,0 +1,35 @@
|
||||
package com.simplemobiletools.notes.pro.models
|
||||
|
||||
import java.util.*
|
||||
|
||||
class TextHistory {
|
||||
var position = 0
|
||||
val history = LinkedList<TextHistoryItem>()
|
||||
|
||||
fun getPrevious(): TextHistoryItem? {
|
||||
if (position == 0) {
|
||||
return null
|
||||
}
|
||||
position--
|
||||
return history[position]
|
||||
}
|
||||
|
||||
fun getNext(): TextHistoryItem? {
|
||||
if (position >= history.size) {
|
||||
return null
|
||||
}
|
||||
|
||||
val item = history[position]
|
||||
position++
|
||||
return item
|
||||
}
|
||||
|
||||
fun add(item: TextHistoryItem) {
|
||||
while (history.size > position) {
|
||||
history.removeLast()
|
||||
}
|
||||
|
||||
history.add(item)
|
||||
position++
|
||||
}
|
||||
}
|
@ -0,0 +1,3 @@
|
||||
package com.simplemobiletools.notes.pro.models
|
||||
|
||||
data class TextHistoryItem(val start: Int, val before: CharSequence?, val after: CharSequence?)
|
@ -0,0 +1,3 @@
|
||||
package com.simplemobiletools.notes.pro.models
|
||||
|
||||
data class Widget(var widgetId: Int, var noteId: Int)
|
@ -0,0 +1,9 @@
|
||||
package com.simplemobiletools.notes.pro.services
|
||||
|
||||
import android.content.Intent
|
||||
import android.widget.RemoteViewsService
|
||||
import com.simplemobiletools.notes.pro.adapters.WidgetAdapter
|
||||
|
||||
class WidgetService : RemoteViewsService() {
|
||||
override fun onGetViewFactory(intent: Intent) = WidgetAdapter(applicationContext, intent)
|
||||
}
|
@ -0,0 +1,17 @@
|
||||
package com.simplemobiletools.notes.pro.views
|
||||
|
||||
import android.content.Context
|
||||
import android.util.AttributeSet
|
||||
import android.view.MotionEvent
|
||||
import android.widget.HorizontalScrollView
|
||||
|
||||
class MyHorizontalScrollView : HorizontalScrollView {
|
||||
constructor(context: Context) : super(context)
|
||||
|
||||
constructor(context: Context, attrs: AttributeSet) : super(context, attrs)
|
||||
|
||||
override fun onTouchEvent(ev: MotionEvent): Boolean {
|
||||
parent.requestDisallowInterceptTouchEvent(false)
|
||||
return super.onTouchEvent(ev)
|
||||
}
|
||||
}
|
Reference in New Issue
Block a user