diff --git a/app/build.gradle b/app/build.gradle
index f038574..c42034f 100644
--- a/app/build.gradle
+++ b/app/build.gradle
@@ -20,23 +20,28 @@ android {
applicationId "ua.leonidius.rtlnotepad"
minSdkVersion 17
targetSdkVersion 29
- versionCode 2
+ versionCode 3
versionName getVersionName()
}
buildTypes {
release {
+ resValue "bool", "DEBUG", "false"
minifyEnabled true
shrinkResources true
proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro'
}
+ debug {
+ resValue "bool", "DEBUG", "true"
+ }
}
}
dependencies {
implementation fileTree(dir: 'libs', include: ['*.jar'])
- implementation "androidx.lifecycle:lifecycle-extensions:2.2.0-beta01"
+ implementation "androidx.lifecycle:lifecycle-extensions:2.2.0"
implementation project(":navdialogs")
implementation "org.jetbrains.kotlin:kotlin-stdlib-jdk7:$kotlin_version"
+ implementation 'com.chibatching.kotpref:kotpref:2.10.0'
}
repositories {
mavenCentral()
diff --git a/app/proguard-rules.pro b/app/proguard-rules.pro
index f1b4245..21c39e6 100644
--- a/app/proguard-rules.pro
+++ b/app/proguard-rules.pro
@@ -19,3 +19,7 @@
# If you keep the line number information, uncomment this to
# hide the original source file name.
#-renamesourcefileattribute SourceFile
+
+-assumenosideeffects class kotlin.jvm.internal.Intrinsics {
+ static void checkParameterIsNotNull(java.lang.Object, java.lang.String);
+}
\ No newline at end of file
diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml
index 884a936..7b54acd 100644
--- a/app/src/main/AndroidManifest.xml
+++ b/app/src/main/AndroidManifest.xml
@@ -1,41 +1,35 @@
+ package="ua.leonidius.rtlnotepad"
+ android:installLocation="auto">
-
+
+ android:name=".MyApplication"
+ android:supportsRtl="true"
+ android:allowBackup="true"
+ android:icon="@drawable/ic_launcher"
+ android:label="@string/app_name"
+ android:theme="@style/Leonidius.Light">
+ android:name=".MainActivity"
+ android:windowSoftInputMode="stateHidden"
+ android:label="@string/app_name">
-
-
+
+
-
-
+
+
-
-
+
+
-
-
+
\ No newline at end of file
diff --git a/app/src/main/java/ua/leonidius/rtlnotepad/EditorFragment.kt b/app/src/main/java/ua/leonidius/rtlnotepad/EditorFragment.kt
index 672c0f4..2683a68 100644
--- a/app/src/main/java/ua/leonidius/rtlnotepad/EditorFragment.kt
+++ b/app/src/main/java/ua/leonidius/rtlnotepad/EditorFragment.kt
@@ -1,30 +1,29 @@
package ua.leonidius.rtlnotepad
-import android.Manifest
+import android.app.Activity
import android.content.Context
-import android.content.pm.PackageManager
+import android.content.Intent
+import android.net.Uri
+import android.os.Build
import android.os.Bundle
import android.text.Editable
import android.text.TextWatcher
-import android.util.Log
import android.view.*
import android.widget.EditText
import android.widget.Toast
-import androidx.core.content.ContextCompat
import androidx.fragment.app.Fragment
-import ua.leonidius.navdialogs.SaveDialog
-import ua.leonidius.rtlnotepad.dialogs.*
-import ua.leonidius.rtlnotepad.utils.LastFilesMaster
-import ua.leonidius.rtlnotepad.utils.ReadTask
-import ua.leonidius.rtlnotepad.utils.WriteTask
-
-import java.io.File
+import ua.leonidius.navdialogs.LegacySaveDialog
+import ua.leonidius.rtlnotepad.dialogs.CloseTabDialog
+import ua.leonidius.rtlnotepad.dialogs.ConfirmEncodingChangeDialog
+import ua.leonidius.rtlnotepad.dialogs.EncodingDialog
+import ua.leonidius.rtlnotepad.utils.*
class EditorFragment : Fragment() {
internal var mTag: String = System.currentTimeMillis().toString()
- var file: File? = null
+ var uri: Uri? = null
+ private lateinit var tabTitle: String
private var currentEncoding = "UTF-8"
internal var hasUnsavedChanges = false
@@ -49,42 +48,31 @@ class EditorFragment : Fragment() {
override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedState: Bundle?): View? {
val scrollView = inflater.inflate(R.layout.main, container, false)
- editor = scrollView.findViewById(R.id.editor)
- editor.textSize = mActivity.pref.getInt(mActivity.PREF_TEXT_SIZE, mActivity.SIZE_MEDIUM).toFloat()
- editor.addTextChangedListener(object : TextWatcher {
- override fun beforeTextChanged(p1: CharSequence, p2: Int, p3: Int, p4: Int) {}
- override fun onTextChanged(p1: CharSequence, p2: Int, p3: Int, p4: Int) {}
- override fun afterTextChanged(p1: Editable) {
- if (!ignoreNextTextChange) setTextChanged(true)
- else ignoreNextTextChange = false
- }
- })
+
+ editor = scrollView.findViewById(R.id.editor).apply {
+ textSize = Settings.textSize.toFloat()
+ addTextChangedListener(getTextWatcher())
+ }
return scrollView
}
override fun onActivityCreated(savedInstanceState: Bundle?) {
super.onActivityCreated(savedInstanceState)
- if (!initialized) { // Cold start
-
- val arguments = arguments
- if (arguments != null) {
- val filePath = arguments.getString(ARGUMENT_FILE_PATH, null)
- if (filePath != null) file = File(filePath)
-
- if (file != null) {
- readFile(file!!, currentEncoding) { text ->
- if (text == null)
- close() // Close if failed to read requested file
- else
- setTextWithProgressDialog(text)
- setTextChanged(false)
- }
+ if (initialized) return
+ arguments?.getParcelable(ARGUMENT_URI)?.also {
+ uri = it
+ tabTitle = getFileName(mActivity, it) ?: getString(R.string.new_document)
+ ReadTask(mActivity.contentResolver, it, currentEncoding) { text ->
+ if (text == null) close()
+ else {
+ setTextWithProgressDialog(text)
+ setTextChanged(false)
}
-
- }
- initialized = true
+ }.execute()
}
+ initialized = true
+ if (uri == null) tabTitle = getString(R.string.new_document)
}
override fun onCreateOptionsMenu(menu: Menu, inflater: MenuInflater) {
@@ -92,13 +80,28 @@ class EditorFragment : Fragment() {
super.onCreateOptionsMenu(menu, inflater)
}
+ override fun onPrepareOptionsMenu(menu: Menu) {
+ super.onPrepareOptionsMenu(menu)
+ uri?.let {
+ if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT && !canWriteFile(mActivity, it)) {
+ menu.findItem(R.id.options_save).isEnabled = false
+ menu.findItem(R.id.options_save_as).isEnabled = false
+ }
+ }
+ }
+
override fun onOptionsItemSelected(item: MenuItem): Boolean {
when (item.itemId) {
R.id.options_save -> {
- if (file == null)
- openSaveDialog()
- else
- saveChanges()
+ if (uri == null) openSaveDialog()
+ else {
+ // Saving changes
+ if (!canWriteFile(mActivity, uri!!)) {
+ Toast.makeText(mActivity, R.string.file_read_only, Toast.LENGTH_SHORT).show()
+ return true
+ }
+ writeFile(uri!!, currentEncoding)
+ }
return true
}
R.id.options_save_as -> {
@@ -122,73 +125,45 @@ class EditorFragment : Fragment() {
* Shows a SaveDialog and writes the text to the selected file.
*/
private fun openSaveDialog() {
- SaveDialog.create { file, encoding ->
- writeFile(file, editor.text.toString(), encoding) { success ->
- if (success) {
- this.file = file
- this.currentEncoding = encoding
- setTextChanged(false)
- val successMessage = resources.getString(R.string.file_save_success, file.name)
- Toast.makeText(context, successMessage, Toast.LENGTH_SHORT).show()
- LastFilesMaster.add(file)
- } else {
- Toast.makeText(context, R.string.file_save_error, Toast.LENGTH_SHORT).show()
- }
+ if (Build.VERSION.SDK_INT < Build.VERSION_CODES.KITKAT || Settings.useLegacyDialogs) {
+ LegacySaveDialog.create(defaultEncoding = currentEncoding, callback = writeFile)
+ .show(childFragmentManager, "saveDialogLegacy")
+ } else {
+ val intent = Intent(Intent.ACTION_CREATE_DOCUMENT).apply {
+ addCategory(Intent.CATEGORY_OPENABLE)
+ type = "text/*"
+ // TODO: add a dialog to choose file's mime type
+ putExtra(Intent.EXTRA_TITLE, ".txt")
}
- }.show(childFragmentManager, "saveDialog")
- }
-
- /**
- * Saves changes in the current file. Doesn't check if the file equals null.
- */
- private fun saveChanges() {
- writeFile(file!!, editor.text.toString(), currentEncoding) { success ->
- if (success) {
- setTextChanged(false)
- val successMessage = resources.getString(R.string.file_save_success, file!!.name)
- Toast.makeText(activity, successMessage, Toast.LENGTH_SHORT).show()
- } else
- Toast.makeText(activity, R.string.file_save_error, Toast.LENGTH_SHORT).show()
+ startActivityForResult(intent, SAVE_FILE)
}
}
private fun setTextChanged(changed: Boolean) {
hasUnsavedChanges = changed
- val selectedTab = mActivity.actionBar!!.selectedTab
- val name: String = if (file == null) getString(R.string.new_document) else file!!.name
- selectedTab.text = if (changed) "$name*" else name
+ mActivity.actionBar!!.selectedTab.text = if (changed) "$tabTitle*" else tabTitle
}
private fun setEncoding(newEncoding: String) {
- if (file == null) {
+ if (uri == null) {
currentEncoding = newEncoding
return
}
- if (!hasUnsavedChanges) {
- readFile(file!!, newEncoding) { result ->
- if (result != null) {
- editor.setText(result)
+ val readFileAgain = {
+ ReadTask(mActivity.contentResolver, uri!!, newEncoding) {
+ if (it != null) {
+ setTextWithProgressDialog(it)
currentEncoding = newEncoding
- } else {
- Toast.makeText(activity, R.string.reading_error, Toast.LENGTH_SHORT).show()
- }
- }
- return
+ } else Toast.makeText(activity, R.string.reading_error, Toast.LENGTH_SHORT).show()
+ }.execute()
}
- ConfirmEncodingChangeDialog.create { change ->
- if (change) {
- readFile(file!!, newEncoding) { result ->
- if (result != null) {
- editor.setText(result)
- currentEncoding = newEncoding
- } else {
- Toast.makeText(activity, R.string.reading_error, Toast.LENGTH_SHORT).show()
- }
- }
- }
- }.show(childFragmentManager, "confirmEncodingChangeDialog")
+ if (hasUnsavedChanges) {
+ ConfirmEncodingChangeDialog.create {
+ if (it) readFileAgain()
+ }.show(childFragmentManager, "confirmEncodingChangeDialog")
+ } else readFileAgain()
}
/**
@@ -209,146 +184,109 @@ class EditorFragment : Fragment() {
return@create
}
- if (file != null) {
- saveChanges()
+ if (uri != null) {
+ writeFile(uri!!, currentEncoding) // Saving changes
mActivity.closeTab(selectedTab)
return@create
}
- SaveDialog.create { file, encoding ->
- writeFile(file, editor.text.toString(), encoding) { success ->
- if (success) {
- val successMessage = resources.getString(R.string.file_save_success, file.name)
- Toast.makeText(activity, successMessage, Toast.LENGTH_SHORT).show()
- LastFilesMaster.add(file)
- mActivity.closeTab(selectedTab)
- } else {
- Toast.makeText(activity, R.string.file_save_error, Toast.LENGTH_SHORT).show()
- }
+ if (Build.VERSION.SDK_INT < Build.VERSION_CODES.KITKAT || Settings.useLegacyDialogs) {
+ LegacySaveDialog.create(defaultEncoding = currentEncoding, callback = writeFileAndCloseTab)
+ .show(childFragmentManager, "saveDialogLegacy")
+ } else {
+ val intent = Intent(Intent.ACTION_CREATE_DOCUMENT).apply {
+ addCategory(Intent.CATEGORY_OPENABLE)
+ type = "text/*"
+ putExtra(Intent.EXTRA_TITLE, ".txt")
}
- }.show(childFragmentManager, "saveDialog")
+ startActivityForResult(intent, SAVE_FILE_AND_CLOSE)
+ }
}.show(childFragmentManager, "closeTabDialog")
}
- fun setEditorTextSize(size: Float) {
- editor.textSize = size
+ override fun onActivityResult(requestCode: Int, resultCode: Int, resultData: Intent?) {
+ if (resultCode != Activity.RESULT_OK) return
+ resultData?.data?.let {
+ if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT) {
+ takePersistablePermissions(mActivity, it)
+ }
+ when (requestCode) {
+ SAVE_FILE -> writeFile(it, currentEncoding)
+ SAVE_FILE_AND_CLOSE -> writeFileAndCloseTab(it, currentEncoding)
+ else -> return@let
+ }
+ }
}
- /**
- * Sets a specified text to editor and shows a progress dialog while it is being set.
- * @param text Text to set
- */
- private fun setTextWithProgressDialog(text: CharSequence?) {
- val dialog = LoadingDialog()
- dialog.show(childFragmentManager, "loadingDialog")
- editor.setText(text)
- dialog.dismiss()
+ private val writeFile: (Uri, String) -> Unit = { uri, encoding ->
+ WriteTask(mActivity.contentResolver, uri, editor.text.toString(), encoding) {
+ onFileWritten(uri, encoding, it)
+ }.execute()
}
- private lateinit var fileToRead: File
- private lateinit var encodingForReading: String
- private lateinit var readCallback: (String) -> Unit
-
- /**
- * Asynchronously reads a specified file into a string and returns it via a callback.
- * Requests reading permission. Shows a LoadingDialog in the process.
- *
- * @param file File to read
- * @param encoding Encoding to use for decoding of the file
- * @param callback Defines what to do with the results of the operation
- */
- private fun readFile(file: File, encoding: String, callback: (String) -> Unit) {
- if (ContextCompat.checkSelfPermission(activity!!, Manifest.permission.READ_EXTERNAL_STORAGE) != PackageManager.PERMISSION_GRANTED) {
- Log.d("RTLnotepad", "No read permission, requesting...")
- // saving data to use in onRequestPermissionsResult()
- fileToRead = file
- encodingForReading = encoding
- readCallback = callback
- requestPermissions(arrayOf(Manifest.permission.READ_EXTERNAL_STORAGE), READ_PERMISSION_CODE)
- return
+ private val writeFileAndCloseTab: (Uri, String) -> Unit = { uri, encoding ->
+ WriteTask(mActivity.contentResolver, uri, editor.text.toString(), encoding) {
+ onFileWritten(uri, encoding, it)
+ if (it) with (mActivity) {
+ closeTab(actionBar!!.selectedTab)
+ }
}
- val dialog = LoadingDialog()
- dialog.show(childFragmentManager, "loadingDialog")
- val task = ReadTask(file, encoding) { result ->
- dialog.dismiss()
- callback.invoke(result)
+ }
+
+ private val onFileWritten: (Uri, String, Boolean) -> Unit = { uri, encoding, successfully ->
+ if (successfully) {
+ this.uri = uri
+ this.tabTitle = getFileName(mActivity, uri)!!
+ this.currentEncoding = encoding
+ setTextChanged(false)
+ val successMessage = resources.getString(R.string.file_save_success, tabTitle)
+ Toast.makeText(context, successMessage, Toast.LENGTH_SHORT).show()
+ addToLastFiles(uri)
+ } else {
+ Toast.makeText(context, R.string.file_save_error, Toast.LENGTH_SHORT).show()
}
- task.execute()
}
- private lateinit var fileToWrite: File
- private lateinit var textToWrite: String
- private lateinit var encodingForWriting: String
- private lateinit var writeCallback: (Boolean) -> Unit
+ fun setEditorTextSize(size: Float) {
+ editor.textSize = size
+ }
/**
- * Asynchronously writes a file to the disk. Returns the status (success/failure) via
- * a callback. Requests writing permission. Shows a LoadingDialog in the process.
- *
- * @param file File to write into
- * @param text Text to write into the file
- * @param encoding Encoding to use
- * @param callback Defines what to do after the operation
+ * Sets a specified text to editor and shows a progress dialog while it is being set.
+ * @param text Text to set
*/
- private fun writeFile(file: File, text: String, encoding: String, callback: (Boolean) -> Unit) {
- if (ContextCompat.checkSelfPermission(activity!!, Manifest.permission.WRITE_EXTERNAL_STORAGE) != PackageManager.PERMISSION_GRANTED) {
- Log.d("RTLnotepad", "No write permission, requesting...")
- // saving data to use in onRequestPermissionsResult()
- fileToWrite = file
- textToWrite = text
- encodingForWriting = encoding
- writeCallback = callback
- requestPermissions(arrayOf(Manifest.permission.WRITE_EXTERNAL_STORAGE), WRITE_PERMISSION_CODE)
- return
- }
- val dialog = LoadingDialog()
- dialog.show(childFragmentManager, "loadingDialog")
- val task = WriteTask(file, text, encoding) { success ->
- dialog.dismiss()
- callback.invoke(success)
- }
- task.execute()
+ private fun setTextWithProgressDialog(text: CharSequence) {
+ /*Log.d("EditorFragment", "Inside setTextWithProgressDialog")
+ SetTextTask(mActivity, childFragmentManager, editor, text) {
+ Log.d("EditorFragment", "inside callback")
+ editor = it.apply {
+ textSize = Settings.textSize.toFloat()
+ addTextChangedListener(getTextWatcher())
+ }
+ }.execute()
+ Log.d("EditorFragment", "New Thread must've been started")*/
+ editor.setText(text) // TODO
}
- override fun onRequestPermissionsResult(requestCode: Int, permissions: Array, grantResults: IntArray) {
- if (grantResults.isNotEmpty() && grantResults[0] == PackageManager.PERMISSION_GRANTED) {
- when (requestCode) {
- READ_PERMISSION_CODE -> tryReadingFileAgain()
- WRITE_PERMISSION_CODE -> tryWritingFileAgain()
+ private fun getTextWatcher(): TextWatcher {
+ return object: TextWatcher {
+ override fun beforeTextChanged(p1: CharSequence, p2: Int, p3: Int, p4: Int) {}
+ override fun onTextChanged(p1: CharSequence, p2: Int, p3: Int, p4: Int) {}
+ override fun afterTextChanged(p1: Editable) {
+ if (!ignoreNextTextChange) setTextChanged(true)
+ else ignoreNextTextChange = false
}
- } else {
- val dialog = PermissionRequestDialog()
- val args = Bundle()
- args.putInt(PermissionRequestDialog.TYPE, requestCode)
- dialog.arguments = args
- dialog.show(childFragmentManager, "permissionRequestDialog")
}
}
- fun tryReadingFileAgain() {
- readFile(fileToRead, encodingForReading, readCallback)
- }
-
- fun tryWritingFileAgain() {
- writeFile(fileToWrite, textToWrite, encodingForWriting, writeCallback)
- }
-
companion object {
- internal const val ARGUMENT_FILE_PATH = "filePath" // will be removed once ViewModel is separated from the fragment
- //private val BUNDLE_FILE = "file"
- //private val BUNDLE_TAG = "tag"
- //private val BUNDLE_CURRENT_ENCODING = "currentEncoding"
- //private val BUNDLE_HAS_UNSAVED_CHANGES = "hasUnsavedChanges"
+ internal const val ARGUMENT_URI = "URI"
- const val READ_PERMISSION_CODE = 0
- const val WRITE_PERMISSION_CODE = 1
+ // Request codes
+ const val SAVE_FILE = 1
+ const val SAVE_FILE_AND_CLOSE = 2
}
-}
-
-/* TODO: if the process was killed while a dialogFragment was open, dismiss the dialog
-* we might pass null to super.onCreate(Bundle savedState) and handle everything ourselves.
-* in such a case the dialog fragments will not be retained
- */
\ No newline at end of file
+}
\ No newline at end of file
diff --git a/app/src/main/java/ua/leonidius/rtlnotepad/MainActivity.kt b/app/src/main/java/ua/leonidius/rtlnotepad/MainActivity.kt
index a60ea49..ff55eb4 100644
--- a/app/src/main/java/ua/leonidius/rtlnotepad/MainActivity.kt
+++ b/app/src/main/java/ua/leonidius/rtlnotepad/MainActivity.kt
@@ -1,136 +1,115 @@
package ua.leonidius.rtlnotepad
import android.app.ActionBar
-import android.content.Context
-import android.content.SharedPreferences
+import android.content.Intent
import android.net.Uri
+import android.os.Build
import android.os.Bundle
import android.view.Menu
import android.view.MenuItem
import android.webkit.MimeTypeMap
import android.widget.LinearLayout
import androidx.fragment.app.FragmentActivity
-import ua.leonidius.navdialogs.OpenDialog
+import ua.leonidius.navdialogs.LegacyOpenDialog
import ua.leonidius.rtlnotepad.dialogs.ExitDialog
import ua.leonidius.rtlnotepad.dialogs.LastFilesDialog
+import ua.leonidius.rtlnotepad.dialogs.LoadingDialog
import ua.leonidius.rtlnotepad.dialogs.WrongFileTypeDialog
-import ua.leonidius.rtlnotepad.utils.LastFilesMaster
-import java.io.File
+import ua.leonidius.rtlnotepad.utils.addToLastFiles
+import ua.leonidius.rtlnotepad.utils.getFileName
+import ua.leonidius.rtlnotepad.utils.takePersistablePermissions
import java.util.*
class MainActivity : FragmentActivity() {
- //private PlaceholderFragment placeholderFragment;
- lateinit var pref: SharedPreferences
-
- internal val SIZE_SMALL = 14
- internal val SIZE_MEDIUM = 18
- internal val SIZE_LARGE = 22
- internal val PREF_TEXT_SIZE = "textSize"
- private val PREF_THEME = "theme"
- private val PREF_THEME_LIGHT = "light"
- private val PREF_THEME_DARK = "dark"
-
- private val BUNDLE_SELECTED_TAB_INDEX = "selectedTabIndex"
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
instance = this
- pref = getPreferences(Context.MODE_PRIVATE)
// Applying dark theme if chosen
- if (pref.getString(PREF_THEME, PREF_THEME_LIGHT) == PREF_THEME_DARK) {
+ if (Settings.theme == Settings.PREF_THEME_DARK) {
setTheme(R.style.Leonidius_Dark)
}
setContentView(LinearLayout(this))
// Setting up tab navigation
- val actionBar = actionBar
actionBar!!.navigationMode = ActionBar.NAVIGATION_MODE_TABS
- actionBar.setDisplayShowTitleEnabled(false)
-
- LastFilesMaster.initSlots(this)
+ actionBar!!.setDisplayShowTitleEnabled(false)
if (savedInstanceState == null) { // Cold start
+ intent.data?.let { addTab(it) }
addPlaceholderFragmentIfNeeded()
- // Opening a file from intent
- if (intent.data != null) {
- val path = intent.data!!.schemeSpecificPart
- if (path != null) addTab(File(path))
- }
- } else { // Restoring tabs after activity recreation
- val tabs = lastCustomNonConfigurationInstance as LinkedHashMap?
- if (tabs!!.size != 0) {
- val selectedTabIndex = savedInstanceState.getInt(BUNDLE_SELECTED_TAB_INDEX, -1)
- for (fragmentTag in tabs.keys) {
- val tab = tabs[fragmentTag]
- //tab.setTag(getFragmentManager().findFragmentByTag(fragmentTag));
- // we don't change the fragment held in 'tag', because it is not destroyed
- getActionBar()!!.addTab(tab)
- if (tab!!.position == selectedTabIndex) getActionBar()!!.selectTab(tab)
- // they reset fragment tags on recreate?
+ return
+ }
+
+ // Restoring tabs after activity recreation
+ val tabs = lastCustomNonConfigurationInstance as LinkedHashMap
+ if (tabs.size != 0) {
+ val selectedTabIndex = savedInstanceState.getInt(BUNDLE_SELECTED_TAB_INDEX, -1)
+ for (tab in tabs.values) {
+ with (actionBar!!) {
+ addTab(tab)
+ if (tab.position == selectedTabIndex) selectTab(tab)
}
}
}
}
/**
- * Opens a file in a new tab.
- *
- * @param file File to open
+ * Opens the file with the given Uri in a new tab
*/
- private fun addTab(file: File) {
- removePlaceholderFragmentIfNeeded()
-
- // If the file is already opened, switching to the file's tab
- // TODO: INSTEAD OF SWITCHING, PROMPT USER TO CHOOSE IF HE WANTS TO ADD A ANOTHER TAB WITH THAT FILE
- val fileTab = getFileTab(file)
- if (fileTab != null) {
- actionBar!!.selectTab(fileTab)
+ private fun addTab(uri: Uri) {
+ // Checking if the file is already opened
+ getFileTab(uri)?.also {
+ actionBar!!.selectTab(it)
return
}
- // Creating a new tab
- val actionBar = actionBar
- val tab = actionBar!!.newTab()
- tab.text = file.name
+ removePlaceholderFragmentIfNeeded()
+
+ // Getting the name of the file
+ val displayName = getFileName(applicationContext, uri)
- val fragment = EditorFragment()
- val arguments = Bundle()
- arguments.putString(EditorFragment.ARGUMENT_FILE_PATH, file.path)
- fragment.arguments = arguments
- fragment.retainInstance = true
- tab.tag = fragment
+ // Creating a new fragment
+ val fragment = EditorFragment().apply {
+ arguments = Bundle().also { it.putParcelable(EditorFragment.ARGUMENT_URI, uri) }
+ retainInstance = true
+ }
- tab.setTabListener(EditorTabListener())
- actionBar.addTab(tab)
+ // Creating a new tab
+ val tab = actionBar!!.newTab().apply {
+ text = displayName
+ tag = fragment
+ setTabListener(EditorTabListener())
+ }
- // Selecting the tab
- actionBar.selectTab(tab)
+ with (actionBar!!) {
+ addTab(tab)
+ selectTab(tab)
+ }
- LastFilesMaster.add(file)
+ addToLastFiles(uri)
}
/**
* Adds a tab with a blank editor for a new file
*/
private fun addTab() {
- // Detaching noEditorFragment
removePlaceholderFragmentIfNeeded()
- val actionBar = actionBar
- val tab = actionBar!!.newTab()
- tab.setText(R.string.new_document)
+ val fragment = EditorFragment().apply { retainInstance = true }
- val fragment = EditorFragment()
- fragment.retainInstance = true
- tab.tag = fragment
-
- tab.setTabListener(EditorTabListener())
- actionBar.addTab(tab)
+ val tab = actionBar!!.newTab().apply {
+ setText(R.string.new_document)
+ tag = fragment
+ setTabListener(EditorTabListener())
+ }
- // Selecting the tab
- actionBar.selectTab(tab)
+ with (actionBar!!) {
+ addTab(tab)
+ selectTab(tab)
+ }
}
/**
@@ -145,15 +124,18 @@ class MainActivity : FragmentActivity() {
}
override fun onCreateOptionsMenu(menu: Menu): Boolean {
- menuInflater.inflate(R.menu.options_new, menu)
- when (pref.getString(PREF_THEME, PREF_THEME_LIGHT)) {
- PREF_THEME_LIGHT -> menu.findItem(R.id.options_theme_light).isChecked = true
- PREF_THEME_DARK -> menu.findItem(R.id.options_theme_dark).isChecked = true
+ menuInflater.inflate(R.menu.options_main, menu)
+ when (Settings.theme) {
+ Settings.PREF_THEME_LIGHT -> menu.findItem(R.id.options_theme_light).isChecked = true
+ Settings.PREF_THEME_DARK -> menu.findItem(R.id.options_theme_dark).isChecked = true
}
- when (pref.getInt(PREF_TEXT_SIZE, SIZE_MEDIUM)) {
- SIZE_SMALL -> menu.findItem(R.id.options_textSize_small).isChecked = true
- SIZE_MEDIUM -> menu.findItem(R.id.options_textSize_medium).isChecked = true
- SIZE_LARGE -> menu.findItem(R.id.options_textSize_large).isChecked = true
+ when (Settings.textSize) {
+ Settings.SIZE_SMALL -> menu.findItem(R.id.options_textSize_small).isChecked = true
+ Settings.SIZE_MEDIUM -> menu.findItem(R.id.options_textSize_medium).isChecked = true
+ Settings.SIZE_LARGE -> menu.findItem(R.id.options_textSize_large).isChecked = true
+ }
+ if (Settings.useLegacyDialogs) {
+ menu.findItem(R.id.options_useLegacyDialogs).isChecked = true
}
return super.onCreateOptionsMenu(menu)
}
@@ -161,22 +143,7 @@ class MainActivity : FragmentActivity() {
override fun onOptionsItemSelected(item: MenuItem): Boolean {
when (item.itemId) {
R.id.options_open -> {
- // TODO: error when rotating screen on OpenDialog and then
- // refusing to rewrite when prompted, and then rotating OpenDialog
- lateinit var dialog : OpenDialog
- dialog = OpenDialog.create { file : File ->
- if (!isText(file)) {
- WrongFileTypeDialog.create {
- if (it) instance.addTab(file)
- else dialog.show(instance.supportFragmentManager, "openDialog")
- }.show(instance.supportFragmentManager, "WFTDialog")
- } else {
- // we use 'instance' because otherwise it adds a new
- // fragment to the old activity after orientation change
- instance.addTab(file)
- }
- }
- dialog.show(supportFragmentManager, "openDialog")
+ openFile()
return true
}
R.id.options_new -> {
@@ -184,42 +151,89 @@ class MainActivity : FragmentActivity() {
return true
}
R.id.options_theme_light -> {
- setThemeNow(PREF_THEME_LIGHT, item)
+ setThemeNow(Settings.PREF_THEME_LIGHT, item)
return true
}
R.id.options_theme_dark -> {
- setThemeNow(PREF_THEME_DARK, item)
+ setThemeNow(Settings.PREF_THEME_DARK, item)
return true
}
R.id.options_last_files -> {
LastFilesDialog.create {
- instance.addTab(File(it))
+ instance.addTab(it)
}.show(supportFragmentManager, "lastFilesDialog")
return true
}
R.id.options_textSize_small -> {
- setTextSize(SIZE_SMALL)
+ setTextSize(Settings.SIZE_SMALL)
item.isChecked = true
return true
}
R.id.options_textSize_medium -> {
- setTextSize(SIZE_MEDIUM)
+ setTextSize(Settings.SIZE_MEDIUM)
item.isChecked = true
return true
}
R.id.options_textSize_large -> {
- setTextSize(SIZE_LARGE)
+ setTextSize(Settings.SIZE_LARGE)
item.isChecked = true
return true
}
- }/*case R.id.options_test:
- Intent i = new Intent();
- i.setClass(this, TestingActivity.class);
- startActivity(i);
- return true;*/
+ R.id.options_loadingDialog -> {
+ LoadingDialog().show(supportFragmentManager, "loadingDialog")
+ return true
+ }
+ R.id.options_useLegacyDialogs -> {
+ item.isChecked = !item.isChecked
+ Settings.useLegacyDialogs = item.isChecked
+ return true
+ }
+ }
return super.onOptionsItemSelected(item)
}
+ private fun openFile() {
+ if (Build.VERSION.SDK_INT < Build.VERSION_CODES.KITKAT || Settings.useLegacyDialogs) {
+ lateinit var dialog: LegacyOpenDialog
+ dialog = LegacyOpenDialog.create { uri ->
+ if (!isText(uri)) {
+ WrongFileTypeDialog.create {
+ if (it) instance.addTab(uri)
+ else dialog.show(instance.supportFragmentManager, "openDialog")
+ }.show(instance.supportFragmentManager, "WFTDialog")
+ } else {
+ // we use 'instance' because otherwise it adds a new
+ // fragment to the old activity after orientation change
+ instance.addTab(uri)
+ }
+ }
+ dialog.show(supportFragmentManager, "openDialog")
+ return
+ }
+ val intent = Intent(Intent.ACTION_OPEN_DOCUMENT).apply {
+ addCategory(Intent.CATEGORY_OPENABLE)
+ type = "*/*"
+ val mimeTypes = resources.getStringArray(R.array.mimeTypes)
+ putExtra(Intent.EXTRA_MIME_TYPES, mimeTypes)
+ }
+ startActivityForResult(intent, PICK_TEXT_FILE)
+ }
+
+ override fun onActivityResult(requestCode: Int, resultCode: Int, resultData: Intent?) {
+ super.onActivityResult(requestCode, resultCode, resultData)
+ if (resultCode != RESULT_OK) return
+ when (requestCode) {
+ PICK_TEXT_FILE -> {
+ resultData?.data?.also { uri ->
+ if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT) {
+ takePersistablePermissions(this, uri)
+ }
+ addTab(uri)
+ }
+ }
+ }
+ }
+
override fun onBackPressed() {
for (i in 0 until actionBar!!.tabCount) {
val tab = actionBar!!.getTabAt(i)
@@ -237,17 +251,13 @@ class MainActivity : FragmentActivity() {
/**
* Finds a tab in which the specified file is opened.
*
- * @param file File which is opened in the tab we are looking for
+ * @param uriToFind File which is opened in the tab we are looking for
* @return ActionBar.Tab in which the specified file is opened, if such a tab exists, null otherwise
*/
- private fun getFileTab(file: File): ActionBar.Tab? {
- // Iterating over tabs
+ private fun getFileTab(uriToFind: Uri) : ActionBar.Tab? {
for (i in 0 until actionBar!!.tabCount) {
val tab = actionBar!!.getTabAt(i)
- val tabFragment = tab.tag as EditorFragment
- if (tabFragment.file != null && tabFragment.file == file) {
- return tab
- }
+ if ((tab.tag as EditorFragment).run { uri != null && uri!! == uriToFind }) return tab
}
return null
}
@@ -257,15 +267,12 @@ class MainActivity : FragmentActivity() {
*/
private fun addPlaceholderFragmentIfNeeded() {
if (actionBar!!.tabCount == 0) {
- var placeholder = supportFragmentManager.findFragmentByTag(PlaceholderFragment.TAG)
- if (placeholder == null) {
- placeholder = PlaceholderFragment()
+ val placeholder = supportFragmentManager.findFragmentByTag(PlaceholderFragment.TAG) ?: PlaceholderFragment()
+ with (supportFragmentManager.beginTransaction()) {
+ if (!placeholder.isAdded) add(android.R.id.content, placeholder, PlaceholderFragment.TAG)
+ attach(placeholder)
+ commitAllowingStateLoss()
}
-
- val sft = supportFragmentManager.beginTransaction()
- if (!placeholder.isAdded) sft.add(android.R.id.content, placeholder, PlaceholderFragment.TAG)
- sft.attach(placeholder)
- sft.commitAllowingStateLoss()
}
}
@@ -274,11 +281,13 @@ class MainActivity : FragmentActivity() {
*/
private fun removePlaceholderFragmentIfNeeded() {
if (actionBar!!.tabCount == 0) {
- val placeholder = supportFragmentManager.findFragmentByTag(PlaceholderFragment.TAG)
- if (placeholder != null && !placeholder.isDetached) {
- val sft = supportFragmentManager.beginTransaction()
- sft.detach(placeholder)
- sft.commitAllowingStateLoss()
+ supportFragmentManager.findFragmentByTag(PlaceholderFragment.TAG).let {
+ if (it != null && !it.isDetached) {
+ with (supportFragmentManager.beginTransaction()) {
+ detach(it)
+ commitAllowingStateLoss()
+ }
+ }
}
}
}
@@ -293,9 +302,7 @@ class MainActivity : FragmentActivity() {
private fun setThemeNow(theme: String, item: MenuItem) {
if (!item.isChecked) {
item.isChecked = true
- val prefEdit = pref.edit()
- prefEdit.putString(PREF_THEME, theme)
- prefEdit.apply()
+ Settings.theme = theme
recreate()
}
}
@@ -306,8 +313,6 @@ class MainActivity : FragmentActivity() {
if (actionBar!!.tabCount > 0) {
outState.putInt(BUNDLE_SELECTED_TAB_INDEX, actionBar!!.selectedTab.position)
}
-
- LastFilesMaster.saveSlots(this)
}
/**
@@ -317,20 +322,14 @@ class MainActivity : FragmentActivity() {
*/
private fun setTextSize(size: Int) {
for (i in 0 until actionBar!!.tabCount) {
- val tab = actionBar!!.getTabAt(i)
- val fragment = tab.tag as EditorFragment
- fragment.setEditorTextSize(size.toFloat())
+ (actionBar!!.getTabAt(i).tag as EditorFragment).setEditorTextSize(size.toFloat())
}
- val prefEditor = pref.edit()
- prefEditor.putInt(PREF_TEXT_SIZE, size)
- prefEditor.apply()
- // TODO: Consider recreating the activity at that point to avoid iterating through tabs
+ Settings.textSize = size
}
override fun onRetainCustomNonConfigurationInstance(): Any? {
- val tabCount = actionBar!!.tabCount
val tabs = LinkedHashMap()
- for (i in 0 until tabCount) {
+ for (i in 0 until actionBar!!.tabCount) {
val tab = actionBar!!.getTabAt(i)
tabs[(tab.tag as EditorFragment).tag!!] = tab
}
@@ -339,16 +338,18 @@ class MainActivity : FragmentActivity() {
companion object {
- /**
- * @return An instance of MainActivity
- */
- lateinit var instance: MainActivity
+ // Request codes
+ const val PICK_TEXT_FILE = 0
+
+ private const val BUNDLE_SELECTED_TAB_INDEX = "selectedTabIndex"
+
+ lateinit var instance: MainActivity
- private fun isText(file: File) : Boolean {
+ private fun isText(uri: Uri): Boolean {
return try {
- MimeTypeMap.getSingleton().getMimeTypeFromExtension(MimeTypeMap.getFileExtensionFromUrl(Uri.fromFile(file).toString()))!!.split("/")[0] == "text";
+ MimeTypeMap.getSingleton().getMimeTypeFromExtension(MimeTypeMap.getFileExtensionFromUrl(uri.toString()))!!.split("/")[0] == "text";
} catch (e: Exception) {
- false;
+ false
}
}
diff --git a/app/src/main/java/ua/leonidius/rtlnotepad/MyApplication.kt b/app/src/main/java/ua/leonidius/rtlnotepad/MyApplication.kt
index b66d5b2..827a7ac 100644
--- a/app/src/main/java/ua/leonidius/rtlnotepad/MyApplication.kt
+++ b/app/src/main/java/ua/leonidius/rtlnotepad/MyApplication.kt
@@ -1,12 +1,14 @@
package ua.leonidius.rtlnotepad
import android.app.Application
+import com.chibatching.kotpref.Kotpref
import ru.elifantiev.android.roboerrorreporter.RoboErrorReporter
class MyApplication : Application() {
override fun onCreate() {
RoboErrorReporter.bindReporter(this)
+ Kotpref.init(this)
super.onCreate()
}
diff --git a/app/src/main/java/ua/leonidius/rtlnotepad/Settings.kt b/app/src/main/java/ua/leonidius/rtlnotepad/Settings.kt
new file mode 100644
index 0000000..6e922a6
--- /dev/null
+++ b/app/src/main/java/ua/leonidius/rtlnotepad/Settings.kt
@@ -0,0 +1,19 @@
+package ua.leonidius.rtlnotepad
+
+import com.chibatching.kotpref.KotprefModel
+import java.util.*
+
+object Settings : KotprefModel() {
+
+ const val SIZE_SMALL = 14
+ const val SIZE_MEDIUM = 18
+ const val SIZE_LARGE = 22
+ const val PREF_THEME_LIGHT = "light"
+ const val PREF_THEME_DARK = "dark"
+
+ var useLegacyDialogs by booleanPref(false)
+ var theme by stringPref(PREF_THEME_LIGHT)
+ var textSize by intPref(SIZE_MEDIUM)
+ val lastFiles by stringSetPref { LinkedHashSet() }
+
+}
\ No newline at end of file
diff --git a/app/src/main/java/ua/leonidius/rtlnotepad/dialogs/LastFilesDialog.kt b/app/src/main/java/ua/leonidius/rtlnotepad/dialogs/LastFilesDialog.kt
index df41f1e..7294e1f 100644
--- a/app/src/main/java/ua/leonidius/rtlnotepad/dialogs/LastFilesDialog.kt
+++ b/app/src/main/java/ua/leonidius/rtlnotepad/dialogs/LastFilesDialog.kt
@@ -1,29 +1,26 @@
package ua.leonidius.rtlnotepad.dialogs
-import android.app.Activity
import android.app.AlertDialog
import android.app.Dialog
+import android.net.Uri
import android.os.Bundle
import android.view.View
import android.widget.AdapterView
import android.widget.ListView
-import android.widget.TextView
import androidx.lifecycle.ViewModel
import androidx.lifecycle.ViewModelProvider
import ua.leonidius.rtlnotepad.R
-import ua.leonidius.rtlnotepad.utils.LastFilesMaster
+import ua.leonidius.rtlnotepad.utils.LastFilesAdapter
class LastFilesDialog : BaseDialog(), AdapterView.OnItemClickListener {
- private lateinit var viewModel : Model
+ private lateinit var viewModel: Model
companion object {
- fun create(callback: (String) -> Unit) : LastFilesDialog {
- val dialog = LastFilesDialog()
- dialog.initializerFunction = {
- dialog.getViewModel().callback = callback
+ fun create(callback: (Uri) -> Unit): LastFilesDialog {
+ return LastFilesDialog().apply {
+ initializerFunction = { getViewModel().callback = callback }
}
- return dialog
}
}
@@ -32,13 +29,13 @@ class LastFilesDialog : BaseDialog(), AdapterView.OnItemClickListener {
adb.setTitle(R.string.last_files)
val lastFilesList = ListView(activity)
lastFilesList.onItemClickListener = this
- lastFilesList.adapter = LastFilesMaster.getAdapter(activity as Activity)
+ lastFilesList.adapter = LastFilesAdapter(activity!!)
adb.setView(lastFilesList)
return adb.create()
}
override fun onItemClick(p1: AdapterView<*>, item: View, p3: Int, p4: Long) {
- getViewModel().callback((item.findViewById(R.id.lastFilesItem_path) as TextView).text.toString())
+ getViewModel().callback(item.tag as Uri)
dialog?.cancel()
}
@@ -50,7 +47,7 @@ class LastFilesDialog : BaseDialog(), AdapterView.OnItemClickListener {
}
class Model : ViewModel() {
- internal lateinit var callback: ((String) -> Unit)
+ internal lateinit var callback: ((Uri) -> Unit)
}
}
\ No newline at end of file
diff --git a/app/src/main/java/ua/leonidius/rtlnotepad/dialogs/LoadingDialog.kt b/app/src/main/java/ua/leonidius/rtlnotepad/dialogs/LoadingDialog.kt
index 2c8c68d..aa04216 100644
--- a/app/src/main/java/ua/leonidius/rtlnotepad/dialogs/LoadingDialog.kt
+++ b/app/src/main/java/ua/leonidius/rtlnotepad/dialogs/LoadingDialog.kt
@@ -3,14 +3,16 @@ package ua.leonidius.rtlnotepad.dialogs
import android.app.AlertDialog
import android.app.Dialog
import android.os.Bundle
+import android.util.Log
import androidx.fragment.app.DialogFragment
import ua.leonidius.rtlnotepad.R
class LoadingDialog : DialogFragment() {
override fun onCreateDialog(savedInstanceState: Bundle?): Dialog {
+ Log.d("LoadingDialog", "Inside onCreateDialog")
val pdb = AlertDialog.Builder(activity)
- // TODO("Show progress bar")
+ pdb.setTitle(R.string.processing)
pdb.setMessage(R.string.processing)
pdb.setCancelable(false)
return pdb.create()
diff --git a/app/src/main/java/ua/leonidius/rtlnotepad/dialogs/PermissionRequestDialog.kt b/app/src/main/java/ua/leonidius/rtlnotepad/dialogs/PermissionRequestDialog.kt
deleted file mode 100644
index b6b7bd2..0000000
--- a/app/src/main/java/ua/leonidius/rtlnotepad/dialogs/PermissionRequestDialog.kt
+++ /dev/null
@@ -1,44 +0,0 @@
-package ua.leonidius.rtlnotepad.dialogs
-
-import android.app.AlertDialog
-import android.app.Dialog
-import android.content.DialogInterface
-import android.os.Bundle
-import androidx.fragment.app.DialogFragment
-import ua.leonidius.rtlnotepad.EditorFragment
-import ua.leonidius.rtlnotepad.R
-
-class PermissionRequestDialog : DialogFragment(), DialogInterface.OnClickListener {
-
- private var type: Int = 0
-
- override fun onCreateDialog(savedInstanceState: Bundle?): Dialog {
- type = arguments!!.getInt(TYPE)
- val adb = AlertDialog.Builder(context)
-
- if (type == EditorFragment.READ_PERMISSION_CODE) {
- adb.setMessage(R.string.grant_read_permissions)
- } else {
- adb.setMessage(R.string.grant_write_permissions)
- }
-
- adb.setPositiveButton(android.R.string.yes, this)
- adb.setNegativeButton(android.R.string.no, this)
-
- return adb.create()
- }
-
- override fun onClick(dialog: DialogInterface, which: Int) {
- if (which == AlertDialog.BUTTON_POSITIVE) {
- if (type == EditorFragment.READ_PERMISSION_CODE) {
- (parentFragment as EditorFragment).tryReadingFileAgain()
- } else
- (parentFragment as EditorFragment).tryWritingFileAgain()
- }
- }
-
- companion object {
- var TYPE = "type"
- }
-
-}
\ No newline at end of file
diff --git a/app/src/main/java/ua/leonidius/rtlnotepad/utils/EncodingAdapter.kt b/app/src/main/java/ua/leonidius/rtlnotepad/utils/EncodingAdapter.kt
index 9034cd9..011e115 100644
--- a/app/src/main/java/ua/leonidius/rtlnotepad/utils/EncodingAdapter.kt
+++ b/app/src/main/java/ua/leonidius/rtlnotepad/utils/EncodingAdapter.kt
@@ -12,13 +12,10 @@ import ua.leonidius.rtlnotepad.R
class EncodingAdapter(internal var context: Context, private var data: Array, var selectedEncoding: String) : ArrayAdapter(context, R.layout.encoding_list_item, R.id.radio_text, data) {
override fun getView(position: Int, convertView: View?, parent: ViewGroup): View {
- var view = convertView
+ val view : View = convertView ?:
+ LayoutInflater.from(context).inflate(R.layout.encoding_list_item, parent, false)
- if (view == null) {
- view = LayoutInflater.from(context).inflate(R.layout.encoding_list_item, parent, false)
- }
-
- val tv = view!!.findViewById(R.id.radio_text)
+ val tv = view.findViewById(R.id.radio_text)
tv.text = data[position]
val r = view.findViewById(R.id.radio_button)
diff --git a/app/src/main/java/ua/leonidius/rtlnotepad/utils/FileHelper.kt b/app/src/main/java/ua/leonidius/rtlnotepad/utils/FileHelper.kt
new file mode 100644
index 0000000..0dfe3a9
--- /dev/null
+++ b/app/src/main/java/ua/leonidius/rtlnotepad/utils/FileHelper.kt
@@ -0,0 +1,80 @@
+package ua.leonidius.rtlnotepad.utils
+
+import android.content.Context
+import android.content.Intent
+import android.content.pm.PackageManager
+import android.database.Cursor
+import android.net.Uri
+import android.os.Build
+import android.provider.DocumentsContract
+import android.provider.OpenableColumns
+
+fun getFileName(context: Context, uri: Uri) : String? {
+ val contentResolver = context.contentResolver
+ var output: String? = null
+ try {
+ val cursor: Cursor? = contentResolver.query(
+ uri, null, null, null, null, null)
+ cursor?.use {
+ output = if (!it.moveToFirst()) null
+ else it.getString(it.getColumnIndex(OpenableColumns.DISPLAY_NAME))
+ }
+ } catch (e: SecurityException) {
+ e.printStackTrace()
+ return null
+ }
+ if (output == null && uri.scheme == "file") {
+ output = uri.lastPathSegment
+ }
+ return output
+}
+
+fun takePersistablePermissions(context: Context, uri: Uri) {
+ if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT) {
+ val contentResolver = context.contentResolver
+ val takeFlags: Int = Intent.FLAG_GRANT_READ_URI_PERMISSION or
+ Intent.FLAG_GRANT_WRITE_URI_PERMISSION
+ contentResolver.takePersistableUriPermission(uri, takeFlags)
+ }
+}
+
+fun canWriteFile(context: Context, uri: Uri): Boolean {
+ if (Build.VERSION.SDK_INT < Build.VERSION_CODES.KITKAT) {
+ return true
+ }
+
+ // If no writing permissions...
+ if (context.checkCallingOrSelfUriPermission(uri, Intent.FLAG_GRANT_WRITE_URI_PERMISSION)
+ != PackageManager.PERMISSION_GRANTED) return false;
+
+ val contentResolver = context.contentResolver
+ try {
+ val cursor = contentResolver.query(uri, arrayOf(DocumentsContract.Document.COLUMN_FLAGS),
+ null, null, null)
+ cursor?.use {
+ return if (it.moveToFirst() && !it.isNull(0)) {
+ val flags = cursor.getInt(0)
+ (flags and DocumentsContract.Document.FLAG_SUPPORTS_WRITE) != 0
+ } else false
+ }
+ } catch (e: Exception) {
+ e.printStackTrace()
+ return false
+ }
+ return false
+}
+
+fun fileExists(context: Context, uri: Uri): Boolean {
+ val resolver = context.contentResolver
+ try {
+ val cursor = resolver.query(uri, arrayOf(DocumentsContract.Document.COLUMN_DOCUMENT_ID),
+ null, null, null)
+ cursor?.use {
+ return cursor.count > 0
+ }
+ return false
+ } catch (e: Exception) {
+ e.printStackTrace()
+ return false
+ }
+}
\ No newline at end of file
diff --git a/app/src/main/java/ua/leonidius/rtlnotepad/utils/LastFilesAdapter.kt b/app/src/main/java/ua/leonidius/rtlnotepad/utils/LastFilesAdapter.kt
new file mode 100644
index 0000000..233493c
--- /dev/null
+++ b/app/src/main/java/ua/leonidius/rtlnotepad/utils/LastFilesAdapter.kt
@@ -0,0 +1,44 @@
+package ua.leonidius.rtlnotepad.utils
+
+import android.content.Context
+import android.net.Uri
+import android.view.LayoutInflater
+import android.view.View
+import android.view.ViewGroup
+import android.widget.BaseAdapter
+import android.widget.ImageView
+import android.widget.TextView
+import ua.leonidius.rtlnotepad.R
+import ua.leonidius.rtlnotepad.Settings
+
+class LastFilesAdapter(private val context: Context): BaseAdapter() {
+
+ private val uriList = Settings.lastFiles.toList()
+
+ override fun getView(position: Int, convertView: View?, parent: ViewGroup?): View {
+ val view : View = convertView ?:
+ LayoutInflater.from(context).inflate(R.layout.last_files_item, parent, false)
+
+ val uri : Uri = Uri.parse(uriList[position])
+
+ view.findViewById(R.id.lastFilesItem_name).text = getFileName(context, uri)
+ view.findViewById(R.id.lastFilesItem_path).text = uri.path ?: ""
+ view.findViewById(R.id.lastFilesItem_image).setImageResource(R.drawable.file)
+ view.tag = uri
+
+ return view
+ }
+
+ override fun getItem(position: Int): Any {
+ return uriList[position]
+ }
+
+ override fun getItemId(position: Int): Long {
+ return position.toLong()
+ }
+
+ override fun getCount(): Int {
+ return uriList.size
+ }
+
+}
\ No newline at end of file
diff --git a/app/src/main/java/ua/leonidius/rtlnotepad/utils/LastFilesMaster.kt b/app/src/main/java/ua/leonidius/rtlnotepad/utils/LastFilesMaster.kt
index 030365a..7d37897 100644
--- a/app/src/main/java/ua/leonidius/rtlnotepad/utils/LastFilesMaster.kt
+++ b/app/src/main/java/ua/leonidius/rtlnotepad/utils/LastFilesMaster.kt
@@ -1,88 +1,16 @@
package ua.leonidius.rtlnotepad.utils
-import android.app.Activity
-import android.widget.SimpleAdapter
-import ua.leonidius.rtlnotepad.MainActivity
-import ua.leonidius.rtlnotepad.R
-import java.io.File
-import java.io.IOException
-import java.util.*
+import android.net.Uri
+import ua.leonidius.rtlnotepad.Settings
/**
- * This class manages the list of last opened files.
+ * This file manages the list of last opened files.
*/
-object LastFilesMaster {
- private lateinit var slots: LinkedList // stores files' paths
- private val slot_names = arrayOf("slot1", "slot2", "slot3", "slot4", "slot5")
- private const val EMPTY = "empty"
- private const val SLOTS_SIZE = 5
+private const val SLOTS_SIZE = 5
- fun add(file: File) {
- // Checking if file is already on the list and placing it to the top in this case
- for (fileInSlot in slots) {
- if (fileInSlot === file) {
- slots.remove(fileInSlot)
- slots.addFirst(file)
- return
- }
- }
-
- if (slots.size == SLOTS_SIZE) slots.removeLast()
- slots.addFirst(file)
- }
-
- // Retrieves recent files saved in Preferences. Called in main activity's onCreate()
- fun initSlots(activity: MainActivity) {
- slots = LinkedList()
- var path: String
- var file: File
- for (i in 0 until SLOTS_SIZE) {
- path = activity.pref.getString(slot_names[i], EMPTY) as String
- if (path != EMPTY) {
- file = File(path)
- if (file.exists())
- slots.add(file)
- else
- slots.add(null)
- } else
- slots.add(null)
- }
+fun addToLastFiles(uri: Uri) {
+ with (Settings.lastFiles) {
+ add(uri.toString())
+ if (size == SLOTS_SIZE) remove(first())
}
-
- // Saves recent files to the preferences. Called in main activity's onSaveInstanceState()
- fun saveSlots(activity: MainActivity) {
- val edit = activity.pref.edit()
- for (i in 0 until SLOTS_SIZE) {
- if (slots[i] == null || !slots[i]!!.exists()) {
- edit.putString(slot_names[i], EMPTY)
- continue
- }
- try {
- edit.putString(slot_names[i], slots[i]!!.canonicalPath)
- } catch (e: IOException) {
- edit.putString(slot_names[i], EMPTY)
- }
-
- }
- edit.apply()
- }
-
- // Returns an adapter for last files list for LastFilesDialog
- fun getAdapter(activity: Activity): SimpleAdapter {
- val data = ArrayList