diff --git a/app/build.gradle b/app/build.gradle
index bcc6be2..8e6459a 100644
--- a/app/build.gradle
+++ b/app/build.gradle
@@ -41,8 +41,8 @@ android {
applicationId "com.wbrawner.simplemarkdown"
minSdkVersion 21
targetSdkVersion 29
- versionCode 26
- versionName "0.8.4"
+ versionCode 27
+ versionName "0.8.5"
testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"
manifestPlaceholders = [
sentryDsn: "https://399270639b2e4b10a028a2be9192d1d3@sentry.brawner.dev/2"
@@ -89,6 +89,8 @@ android {
}
dependencies {
+ implementation 'androidx.navigation:navigation-fragment-ktx:2.2.2'
+ implementation 'androidx.navigation:navigation-ui-ktx:2.2.2'
testImplementation 'junit:junit:4.12'
testImplementation 'org.robolectric:robolectric:4.2.1'
implementation fileTree(include: ['*.jar'], dir: 'libs')
@@ -118,7 +120,6 @@ dependencies {
def coroutines_version = "1.3.4"
implementation "org.jetbrains.kotlinx:kotlinx-coroutines-core:$coroutines_version"
implementation "org.jetbrains.kotlinx:kotlinx-coroutines-android:$coroutines_version"
-
def lifecycle_version = "2.2.0"
implementation "androidx.lifecycle:lifecycle-extensions:$lifecycle_version"
kapt "androidx.lifecycle:lifecycle-common-java8:$lifecycle_version"
diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml
index 2f9652c..fd885fb 100644
--- a/app/src/main/AndroidManifest.xml
+++ b/app/src/main/AndroidManifest.xml
@@ -43,10 +43,6 @@
-
-
-
-
-
-
-
-
diff --git a/app/src/main/java/com/wbrawner/simplemarkdown/view/activity/MainActivity.kt b/app/src/main/java/com/wbrawner/simplemarkdown/view/activity/MainActivity.kt
index 14d8e62..196ce48 100644
--- a/app/src/main/java/com/wbrawner/simplemarkdown/view/activity/MainActivity.kt
+++ b/app/src/main/java/com/wbrawner/simplemarkdown/view/activity/MainActivity.kt
@@ -1,61 +1,23 @@
package com.wbrawner.simplemarkdown.view.activity
-import android.Manifest
-import android.app.Activity
-import android.content.Intent
-import android.content.pm.PackageManager
-import android.content.res.Configuration
-import android.net.Uri
-import android.os.Build
import android.os.Bundle
-import android.view.Menu
-import android.view.MenuItem
-import android.view.View
-import android.webkit.MimeTypeMap
-import android.widget.Toast
import androidx.activity.viewModels
-import androidx.appcompat.app.AlertDialog
import androidx.appcompat.app.AppCompatActivity
import androidx.core.app.ActivityCompat
-import androidx.core.content.ContextCompat
-import androidx.lifecycle.Observer
-import androidx.preference.PreferenceManager
-import com.wbrawner.simplemarkdown.MarkdownApplication
+import androidx.navigation.findNavController
import com.wbrawner.simplemarkdown.R
-import com.wbrawner.simplemarkdown.utility.hideKeyboard
-import com.wbrawner.simplemarkdown.view.adapter.EditPagerAdapter
-import com.wbrawner.simplemarkdown.view.fragment.MainMenuFragment
import com.wbrawner.simplemarkdown.viewmodel.MarkdownViewModel
-import kotlinx.android.synthetic.main.activity_main.*
import kotlinx.coroutines.*
-import java.io.File
import kotlin.coroutines.CoroutineContext
class MainActivity : AppCompatActivity(), ActivityCompat.OnRequestPermissionsResultCallback, CoroutineScope {
- private var shouldAutoSave = true
override val coroutineContext: CoroutineContext = Dispatchers.Main
private val viewModel: MarkdownViewModel by viewModels()
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
- setSupportActionBar(toolbar)
- supportActionBar?.setHomeAsUpIndicator(R.drawable.ic_menu_black_24dp)
- supportActionBar?.setDisplayHomeAsUpEnabled(true)
- val adapter = EditPagerAdapter(supportFragmentManager, this@MainActivity)
- pager.adapter = adapter
- pager.addOnPageChangeListener(adapter)
- pager.pageMargin = 1
- pager.setPageMarginDrawable(R.color.colorAccent)
- tabLayout.setupWithViewPager(pager)
- if (resources.configuration.orientation == Configuration.ORIENTATION_LANDSCAPE) {
- tabLayout!!.visibility = View.GONE
- }
- @Suppress("CAST_NEVER_SUCCEEDS")
- viewModel.fileName.observe(this, Observer {
- toolbar?.title = it
- })
intent?.data?.let {
launch {
viewModel.load(this@MainActivity, it)
@@ -63,214 +25,8 @@ class MainActivity : AppCompatActivity(), ActivityCompat.OnRequestPermissionsRes
}
}
- override fun onStart() {
- super.onStart()
- launch {
- withContext(Dispatchers.IO) {
- val enableErrorReports = PreferenceManager.getDefaultSharedPreferences(this@MainActivity)
- .getBoolean(getString(R.string.error_reports_enabled), true)
- (application as MarkdownApplication).errorHandler.enable(enableErrorReports)
- }
- }
- }
-
- override fun onUserLeaveHint() {
- super.onUserLeaveHint()
- launch {
- withContext(Dispatchers.IO) {
- val sharedPrefs = PreferenceManager.getDefaultSharedPreferences(this@MainActivity)
- val isAutoSaveEnabled = sharedPrefs.getBoolean(KEY_AUTOSAVE, true)
- if (!shouldAutoSave || !isAutoSaveEnabled) {
- return@withContext
- }
-
- val uri = if (viewModel.save(this@MainActivity)) {
- viewModel.uri.value
- } else {
- // The user has left the app, with autosave enabled, and we don't already have a
- // Uri for them or for some reason we were unable to save to the original Uri. In
- // this case, we need to just save to internal file storage so that we can recover
- val fileUri = Uri.fromFile(File(filesDir, viewModel.fileName.value!!))
- if (viewModel.save(this@MainActivity, fileUri)) {
- fileUri
- } else {
- null
- }
- } ?: return@withContext
- sharedPrefs.edit()
- .putString(getString(R.string.pref_key_autosave_uri), uri.toString())
- .apply()
- }
- }
- }
-
- override fun onConfigurationChanged(newConfig: Configuration) {
- super.onConfigurationChanged(newConfig)
- if (newConfig.orientation == Configuration.ORIENTATION_LANDSCAPE)
- tabLayout!!.visibility = View.GONE
- else
- tabLayout!!.visibility = View.VISIBLE
- }
-
- override fun onCreateOptionsMenu(menu: Menu): Boolean {
- menuInflater.inflate(R.menu.menu_edit, menu)
- return true
- }
-
- override fun onOptionsItemSelected(item: MenuItem): Boolean {
- when (item.itemId) {
- android.R.id.home -> {
- MainMenuFragment().show(supportFragmentManager, null)
- window.decorView.hideKeyboard()
- }
- R.id.action_save -> {
- launch {
- if (!viewModel.save(this@MainActivity)) {
- requestFileOp(REQUEST_SAVE_FILE)
- } else {
- Toast.makeText(
- this@MainActivity,
- getString(R.string.file_saved, viewModel.fileName.value),
- Toast.LENGTH_SHORT
- ).show()
- }
- }
- }
- R.id.action_save_as -> {
- requestFileOp(REQUEST_SAVE_FILE)
- }
- R.id.action_share -> {
- val shareIntent = Intent(Intent.ACTION_SEND)
- shareIntent.putExtra(Intent.EXTRA_TEXT, viewModel.markdownUpdates.value)
- shareIntent.type = "text/plain"
- startActivity(Intent.createChooser(
- shareIntent,
- getString(R.string.share_file)
- ))
- }
- R.id.action_load -> requestFileOp(REQUEST_OPEN_FILE)
- R.id.action_new -> promptSaveOrDiscardChanges()
- R.id.action_lock_swipe -> {
- item.isChecked = !item.isChecked
- pager!!.setSwipeLocked(item.isChecked)
- }
- }
- return super.onOptionsItemSelected(item)
- }
-
- override fun onRequestPermissionsResult(
- requestCode: Int,
- permissions: Array,
- grantResults: IntArray
- ) {
- when (requestCode) {
- REQUEST_SAVE_FILE, REQUEST_OPEN_FILE -> {
- // If request is cancelled, the result arrays are empty.
- if (grantResults.isNotEmpty() && grantResults[0] == PackageManager.PERMISSION_GRANTED) {
- // Permission granted, open file save dialog
- requestFileOp(requestCode)
- } else {
- // Permission denied, do nothing
- Toast.makeText(this@MainActivity, R.string.no_permissions, Toast.LENGTH_SHORT)
- .show()
- }
- }
- }
- }
-
override fun onBackPressed() {
- if (pager!!.currentItem == EditPagerAdapter.FRAGMENT_EDIT)
- super.onBackPressed()
- else
- pager!!.currentItem = EditPagerAdapter.FRAGMENT_EDIT
- }
-
- override fun onActivityResult(requestCode: Int, resultCode: Int, data: Intent?) {
- when (requestCode) {
- REQUEST_OPEN_FILE -> {
- if (resultCode != Activity.RESULT_OK || data?.data == null) {
- return
- }
-
- launch {
- val fileLoaded = viewModel.load(this@MainActivity, data.data)
- if (!fileLoaded) {
- Toast.makeText(this@MainActivity, R.string.file_load_error, Toast.LENGTH_SHORT)
- .show()
- }
- }
- }
- REQUEST_SAVE_FILE -> {
- if (resultCode != Activity.RESULT_OK || data?.data == null) {
- return
- }
-
- launch {
- viewModel.save(this@MainActivity, data.data)
- }
- }
- }
- super.onActivityResult(requestCode, resultCode, data)
- }
-
- private fun promptSaveOrDiscardChanges() {
- if (viewModel.originalMarkdown.value == viewModel.markdownUpdates.value) {
- viewModel.reset("Untitled.md")
- return
- }
- AlertDialog.Builder(this)
- .setTitle(R.string.save_changes)
- .setMessage(R.string.prompt_save_changes)
- .setNegativeButton(R.string.action_discard) { _, _ ->
- viewModel.reset("Untitled.md")
- }
- .setPositiveButton(R.string.action_save) { _, _ ->
- requestFileOp(REQUEST_SAVE_FILE)
- }
- .create()
- .show()
- }
-
- private fun requestFileOp(requestType: Int) {
- if (ContextCompat.checkSelfPermission(this, Manifest.permission.WRITE_EXTERNAL_STORAGE)
- != PackageManager.PERMISSION_GRANTED && Build.VERSION.SDK_INT > 22) {
- requestPermissions(
- arrayOf(Manifest.permission.WRITE_EXTERNAL_STORAGE),
- requestType
- )
- return
- }
- // If the user is going to save the file, we don't want to auto-save it for them
- shouldAutoSave = false
- val intent = when (requestType) {
- REQUEST_SAVE_FILE -> {
- Intent(Intent.ACTION_CREATE_DOCUMENT).apply {
- type = "text/markdown"
- putExtra(Intent.EXTRA_TITLE, viewModel.fileName.value)
- }
- }
- REQUEST_OPEN_FILE -> {
- Intent(Intent.ACTION_OPEN_DOCUMENT).apply {
- type = "*/*"
- if (MimeTypeMap.getSingleton().hasMimeType("md")) {
- // If the device doesn't recognize markdown files then we're not going to be
- // able to open them at all, so there's no sense in filtering them out.
- putExtra(Intent.EXTRA_MIME_TYPES, arrayOf("text/plain", "text/markdown"))
- }
- }
- }
- else -> null
- } ?: return
- intent.addCategory(Intent.CATEGORY_OPENABLE)
- startActivityForResult(
- intent,
- requestType
- )
- }
-
- override fun onResume() {
- super.onResume()
- shouldAutoSave = true
+ findNavController(R.id.content).navigateUp()
}
override fun onDestroy() {
@@ -279,12 +35,4 @@ class MainActivity : AppCompatActivity(), ActivityCompat.OnRequestPermissionsRes
cancel()
}
}
-
- companion object {
- // Request codes
- const val REQUEST_OPEN_FILE = 1
- const val REQUEST_SAVE_FILE = 2
- const val REQUEST_DARK_MODE = 4
- const val KEY_AUTOSAVE = "autosave"
- }
}
diff --git a/app/src/main/java/com/wbrawner/simplemarkdown/view/activity/SettingsActivity.kt b/app/src/main/java/com/wbrawner/simplemarkdown/view/activity/SettingsActivity.kt
deleted file mode 100644
index cb053fe..0000000
--- a/app/src/main/java/com/wbrawner/simplemarkdown/view/activity/SettingsActivity.kt
+++ /dev/null
@@ -1,29 +0,0 @@
-package com.wbrawner.simplemarkdown.view.activity
-
-import android.os.Bundle
-import android.view.MenuItem
-import androidx.appcompat.app.AppCompatActivity
-import com.wbrawner.simplemarkdown.R
-import com.wbrawner.simplemarkdown.view.fragment.SettingsFragment
-
-class SettingsActivity : AppCompatActivity() {
-
- override fun onCreate(savedInstanceState: Bundle?) {
- super.onCreate(savedInstanceState)
- setContentView(R.layout.activity_settings)
- setSupportActionBar(findViewById(R.id.toolbar))
- supportActionBar?.setDisplayHomeAsUpEnabled(true)
- if (supportFragmentManager.fragments.isEmpty()) {
- supportFragmentManager.beginTransaction()
- .add(R.id.fragment_settings, SettingsFragment())
- .commit()
- }
- }
-
- override fun onOptionsItemSelected(item: MenuItem): Boolean {
- // The only menu item is the back button
- setResult(RESULT_OK)
- finish()
- return true
- }
-}
diff --git a/app/src/main/java/com/wbrawner/simplemarkdown/view/activity/SupportActivity.kt b/app/src/main/java/com/wbrawner/simplemarkdown/view/activity/SupportActivity.kt
deleted file mode 100644
index 91ef1f0..0000000
--- a/app/src/main/java/com/wbrawner/simplemarkdown/view/activity/SupportActivity.kt
+++ /dev/null
@@ -1,58 +0,0 @@
-package com.wbrawner.simplemarkdown.view.activity
-
-import android.content.ActivityNotFoundException
-import android.content.Intent
-import android.net.Uri
-import android.os.Bundle
-import android.view.MenuItem
-import androidx.appcompat.app.AppCompatActivity
-import androidx.browser.customtabs.CustomTabsIntent
-import androidx.lifecycle.Observer
-import com.wbrawner.simplemarkdown.R
-import com.wbrawner.simplemarkdown.utility.SupportLinkProvider
-import kotlinx.android.synthetic.main.activity_support.*
-
-
-class SupportActivity : AppCompatActivity() {
- override fun onCreate(savedInstanceState: Bundle?) {
- super.onCreate(savedInstanceState)
- setContentView(R.layout.activity_support)
- setSupportActionBar(toolbar)
- setTitle(R.string.support_title)
- supportActionBar?.setDisplayHomeAsUpEnabled(true)
- githubButton.setOnClickListener {
- CustomTabsIntent.Builder()
- .addDefaultShareMenuItem()
- .build()
- .launchUrl(this@SupportActivity, Uri.parse("https://github.com/wbrawner/SimpleMarkdown"))
- }
- rateButton.setOnClickListener {
- val playStoreIntent = Intent(Intent.ACTION_VIEW)
- .apply {
- data = Uri.parse("market://details?id=${packageName}")
- addFlags(Intent.FLAG_ACTIVITY_NO_HISTORY or
- Intent.FLAG_ACTIVITY_NEW_DOCUMENT or
- Intent.FLAG_ACTIVITY_MULTIPLE_TASK)
- }
- try {
- startActivity(playStoreIntent)
- } catch (ignored: ActivityNotFoundException) {
- playStoreIntent.data = Uri.parse("https://play.google.com/store/apps/details?id=${packageName}")
- startActivity(playStoreIntent)
- }
- }
- SupportLinkProvider(this).supportLinks.observe(this, Observer { links ->
- links.forEach {
- supportButtons.addView(it)
- }
- })
- }
-
- override fun onOptionsItemSelected(item: MenuItem): Boolean {
- if (item.itemId == android.R.id.home) {
- finish()
- return true
- }
- return super.onOptionsItemSelected(item)
- }
-}
\ No newline at end of file
diff --git a/app/src/main/java/com/wbrawner/simplemarkdown/view/fragment/MainFragment.kt b/app/src/main/java/com/wbrawner/simplemarkdown/view/fragment/MainFragment.kt
new file mode 100644
index 0000000..d06ac70
--- /dev/null
+++ b/app/src/main/java/com/wbrawner/simplemarkdown/view/fragment/MainFragment.kt
@@ -0,0 +1,301 @@
+package com.wbrawner.simplemarkdown.view.fragment
+
+import android.Manifest
+import android.app.Activity
+import android.content.Intent
+import android.content.pm.PackageManager
+import android.content.res.Configuration
+import android.net.Uri
+import android.os.Build
+import android.os.Bundle
+import android.view.LayoutInflater
+import android.view.View
+import android.view.ViewGroup
+import android.webkit.MimeTypeMap
+import android.widget.Toast
+import androidx.appcompat.app.AlertDialog
+import androidx.core.app.ActivityCompat
+import androidx.core.content.ContextCompat
+import androidx.fragment.app.Fragment
+import androidx.fragment.app.activityViewModels
+import androidx.lifecycle.Observer
+import androidx.navigation.fragment.findNavController
+import androidx.navigation.ui.AppBarConfiguration
+import androidx.navigation.ui.onNavDestinationSelected
+import androidx.navigation.ui.setupWithNavController
+import androidx.preference.PreferenceManager
+import com.wbrawner.simplemarkdown.MarkdownApplication
+import com.wbrawner.simplemarkdown.R
+import com.wbrawner.simplemarkdown.view.adapter.EditPagerAdapter
+import com.wbrawner.simplemarkdown.viewmodel.MarkdownViewModel
+import kotlinx.android.synthetic.main.fragment_main.*
+import kotlinx.coroutines.*
+import java.io.File
+import kotlin.coroutines.CoroutineContext
+
+class MainFragment : Fragment(), ActivityCompat.OnRequestPermissionsResultCallback, CoroutineScope {
+
+ private var shouldAutoSave = true
+ override val coroutineContext: CoroutineContext = Dispatchers.Main
+ private val viewModel: MarkdownViewModel by activityViewModels()
+ private var appBarConfiguration: AppBarConfiguration? = null
+
+ override fun onCreate(savedInstanceState: Bundle?) {
+ super.onCreate(savedInstanceState)
+ setHasOptionsMenu(true)
+ }
+
+ override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View? =
+ inflater.inflate(R.layout.fragment_main, container, false)
+
+ override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
+ with(findNavController()) {
+ appBarConfiguration = AppBarConfiguration(graph, drawerLayout)
+ toolbar.setupWithNavController(this, appBarConfiguration!!)
+ toolbar.inflateMenu(R.menu.menu_edit)
+ toolbar.setOnMenuItemClickListener { item ->
+ return@setOnMenuItemClickListener when (item.itemId) {
+ R.id.action_save -> {
+ launch {
+ if (!viewModel.save(requireContext())) {
+ requestFileOp(REQUEST_SAVE_FILE)
+ } else {
+ Toast.makeText(
+ requireContext(),
+ getString(R.string.file_saved, viewModel.fileName.value),
+ Toast.LENGTH_SHORT
+ ).show()
+ }
+ }
+ true
+ }
+ R.id.action_save_as -> {
+ requestFileOp(REQUEST_SAVE_FILE)
+ true
+ }
+ R.id.action_share -> {
+ val shareIntent = Intent(Intent.ACTION_SEND)
+ shareIntent.putExtra(Intent.EXTRA_TEXT, viewModel.markdownUpdates.value)
+ shareIntent.type = "text/plain"
+ startActivity(Intent.createChooser(
+ shareIntent,
+ getString(R.string.share_file)
+ ))
+ true
+ }
+ R.id.action_load -> {
+ requestFileOp(REQUEST_OPEN_FILE)
+ true
+ }
+ R.id.action_new -> {
+ promptSaveOrDiscardChanges()
+ true
+ }
+ R.id.action_lock_swipe -> {
+ item.isChecked = !item.isChecked
+ pager!!.setSwipeLocked(item.isChecked)
+ true
+ }
+ else -> item.onNavDestinationSelected(findNavController())
+ }
+ }
+ navigationView.setupWithNavController(this)
+ }
+ val adapter = EditPagerAdapter(childFragmentManager, view.context)
+ pager.adapter = adapter
+ pager.addOnPageChangeListener(adapter)
+ pager.pageMargin = 1
+ pager.setPageMarginDrawable(R.color.colorAccent)
+ tabLayout.setupWithViewPager(pager)
+ if (resources.configuration.orientation == Configuration.ORIENTATION_LANDSCAPE) {
+ tabLayout!!.visibility = View.GONE
+ }
+ @Suppress("CAST_NEVER_SUCCEEDS")
+ viewModel.fileName.observe(viewLifecycleOwner, Observer {
+ toolbar?.title = it
+ })
+ }
+
+ override fun onStart() {
+ super.onStart()
+ launch {
+ withContext(Dispatchers.IO) {
+ val enableErrorReports = PreferenceManager.getDefaultSharedPreferences(requireContext())
+ .getBoolean(getString(R.string.error_reports_enabled), true)
+ (requireActivity().application as MarkdownApplication).errorHandler.enable(enableErrorReports)
+ }
+ }
+ }
+
+ override fun onPause() {
+ super.onPause()
+ launch {
+ val context = context?.applicationContext ?: return@launch
+ withContext(Dispatchers.IO) {
+ val sharedPrefs = PreferenceManager.getDefaultSharedPreferences(context)
+ val isAutoSaveEnabled = sharedPrefs.getBoolean(KEY_AUTOSAVE, true)
+ if (!shouldAutoSave || !isAutoSaveEnabled) {
+ return@withContext
+ }
+
+ val uri = if (viewModel.save(context)) {
+ viewModel.uri.value
+ } else {
+ // The user has left the app, with autosave enabled, and we don't already have a
+ // Uri for them or for some reason we were unable to save to the original Uri. In
+ // this case, we need to just save to internal file storage so that we can recover
+ val fileUri = Uri.fromFile(File(context.filesDir, viewModel.fileName.value!!))
+ if (viewModel.save(context, fileUri)) {
+ fileUri
+ } else {
+ null
+ }
+ } ?: return@withContext
+ sharedPrefs.edit()
+ .putString(getString(R.string.pref_key_autosave_uri), uri.toString())
+ .apply()
+ }
+ }
+ }
+
+ override fun onConfigurationChanged(newConfig: Configuration) {
+ super.onConfigurationChanged(newConfig)
+ if (newConfig.orientation == Configuration.ORIENTATION_LANDSCAPE)
+ tabLayout!!.visibility = View.GONE
+ else
+ tabLayout!!.visibility = View.VISIBLE
+ }
+
+ override fun onRequestPermissionsResult(
+ requestCode: Int,
+ permissions: Array,
+ grantResults: IntArray
+ ) {
+ when (requestCode) {
+ REQUEST_SAVE_FILE, REQUEST_OPEN_FILE -> {
+ // If request is cancelled, the result arrays are empty.
+ if (grantResults.isNotEmpty() && grantResults[0] == PackageManager.PERMISSION_GRANTED) {
+ // Permission granted, open file save dialog
+ requestFileOp(requestCode)
+ } else {
+ // Permission denied, do nothing
+ context?.let {
+ Toast.makeText(it, R.string.no_permissions, Toast.LENGTH_SHORT)
+ .show()
+ }
+ }
+ }
+ }
+ }
+
+ override fun onActivityResult(requestCode: Int, resultCode: Int, data: Intent?) {
+ when (requestCode) {
+ REQUEST_OPEN_FILE -> {
+ if (resultCode != Activity.RESULT_OK || data?.data == null) {
+ return
+ }
+
+ launch {
+ val fileLoaded = context?.let {
+ viewModel.load(it, data.data)
+ }
+ if (fileLoaded == false) {
+ context?.let {
+ Toast.makeText(it, R.string.file_load_error, Toast.LENGTH_SHORT)
+ .show()
+ }
+ }
+ }
+ }
+ REQUEST_SAVE_FILE -> {
+ if (resultCode != Activity.RESULT_OK || data?.data == null) {
+ return
+ }
+
+ launch {
+ context?.let {
+ viewModel.save(it, data.data)
+ }
+ }
+ }
+ }
+ super.onActivityResult(requestCode, resultCode, data)
+ }
+
+ private fun promptSaveOrDiscardChanges() {
+ if (viewModel.originalMarkdown.value == viewModel.markdownUpdates.value) {
+ viewModel.reset("Untitled.md")
+ return
+ }
+ val context = context ?: return
+ AlertDialog.Builder(context)
+ .setTitle(R.string.save_changes)
+ .setMessage(R.string.prompt_save_changes)
+ .setNegativeButton(R.string.action_discard) { _, _ ->
+ viewModel.reset("Untitled.md")
+ }
+ .setPositiveButton(R.string.action_save) { _, _ ->
+ requestFileOp(REQUEST_SAVE_FILE)
+ }
+ .create()
+ .show()
+ }
+
+ private fun requestFileOp(requestType: Int) {
+ val context = context ?: return
+ if (ContextCompat.checkSelfPermission(context, Manifest.permission.WRITE_EXTERNAL_STORAGE)
+ != PackageManager.PERMISSION_GRANTED && Build.VERSION.SDK_INT > 22) {
+ requestPermissions(
+ arrayOf(Manifest.permission.WRITE_EXTERNAL_STORAGE),
+ requestType
+ )
+ return
+ }
+ // If the user is going to save the file, we don't want to auto-save it for them
+ shouldAutoSave = false
+ val intent = when (requestType) {
+ REQUEST_SAVE_FILE -> {
+ Intent(Intent.ACTION_CREATE_DOCUMENT).apply {
+ type = "text/markdown"
+ putExtra(Intent.EXTRA_TITLE, viewModel.fileName.value)
+ }
+ }
+ REQUEST_OPEN_FILE -> {
+ Intent(Intent.ACTION_OPEN_DOCUMENT).apply {
+ type = "*/*"
+ if (MimeTypeMap.getSingleton().hasMimeType("md")) {
+ // If the device doesn't recognize markdown files then we're not going to be
+ // able to open them at all, so there's no sense in filtering them out.
+ putExtra(Intent.EXTRA_MIME_TYPES, arrayOf("text/plain", "text/markdown"))
+ }
+ }
+ }
+ else -> null
+ } ?: return
+ intent.addCategory(Intent.CATEGORY_OPENABLE)
+ startActivityForResult(
+ intent,
+ requestType
+ )
+ }
+
+ override fun onResume() {
+ super.onResume()
+ shouldAutoSave = true
+ }
+
+ override fun onDestroy() {
+ super.onDestroy()
+ coroutineContext[Job]?.let {
+ cancel()
+ }
+ }
+
+ companion object {
+ // Request codes
+ const val REQUEST_OPEN_FILE = 1
+ const val REQUEST_SAVE_FILE = 2
+ const val REQUEST_DARK_MODE = 4
+ const val KEY_AUTOSAVE = "autosave"
+ }
+}
diff --git a/app/src/main/java/com/wbrawner/simplemarkdown/view/fragment/MainMenuFragment.kt b/app/src/main/java/com/wbrawner/simplemarkdown/view/fragment/MainMenuFragment.kt
deleted file mode 100644
index 5063908..0000000
--- a/app/src/main/java/com/wbrawner/simplemarkdown/view/fragment/MainMenuFragment.kt
+++ /dev/null
@@ -1,81 +0,0 @@
-package com.wbrawner.simplemarkdown.view.fragment
-
-import android.content.Intent
-import android.os.Bundle
-import android.view.LayoutInflater
-import android.view.View
-import android.view.ViewGroup
-import com.google.android.material.bottomsheet.BottomSheetDialogFragment
-import com.wbrawner.simplemarkdown.R
-import com.wbrawner.simplemarkdown.view.activity.MainActivity
-import com.wbrawner.simplemarkdown.view.activity.MarkdownInfoActivity
-import com.wbrawner.simplemarkdown.view.activity.MarkdownInfoActivity.Companion.EXTRA_FILE
-import com.wbrawner.simplemarkdown.view.activity.MarkdownInfoActivity.Companion.EXTRA_TITLE
-import com.wbrawner.simplemarkdown.view.activity.SettingsActivity
-import com.wbrawner.simplemarkdown.view.activity.SupportActivity
-import kotlinx.android.synthetic.main.fragment_menu_main.*
-
-class MainMenuFragment : BottomSheetDialogFragment() {
- override fun onCreateView(
- inflater: LayoutInflater,
- container: ViewGroup?,
- savedInstanceState: Bundle?
- ): View? = inflater.inflate(R.layout.fragment_menu_main, container, false)
-
- override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
- super.onViewCreated(view, savedInstanceState)
- mainMenuNavigationView.setNavigationItemSelectedListener { menuItem ->
- val (intentClass, fileName, title) = when (menuItem.itemId) {
- R.id.action_help -> Triple(
- MarkdownInfoActivity::class.java,
- "Cheatsheet.md",
- R.string.action_help
- )
- R.id.action_settings -> Triple(
- SettingsActivity::class.java,
- null,
- null
- )
- R.id.action_libraries -> Triple(
- MarkdownInfoActivity::class.java,
- "Libraries.md",
- R.string.action_libraries
- )
- R.id.action_privacy -> Triple(
- MarkdownInfoActivity::class.java,
- "Privacy Policy.md",
- R.string.action_privacy
- )
- R.id.action_support -> Triple(
- SupportActivity::class.java,
- null,
- null
- )
- else -> throw IllegalStateException("This shouldn't happen")
- }
- val intent = Intent(context, intentClass)
- fileName?.let {
- intent.putExtra(EXTRA_FILE, it)
- }
- title?.let {
- intent.putExtra(EXTRA_TITLE, getString(it))
- }
- if (intentClass == SettingsActivity::class.java) {
- startActivityForResult(intent, MainActivity.REQUEST_DARK_MODE)
- } else {
- startActivity(intent)
- dialog?.dismiss()
- }
- true
- }
- }
-
- override fun onActivityResult(requestCode: Int, resultCode: Int, data: Intent?) {
- if (requestCode == MainActivity.REQUEST_DARK_MODE) {
- activity?.recreate()
- } else {
- super.onActivityResult(requestCode, resultCode, data)
- }
- dialog?.dismiss()
- }
-}
\ No newline at end of file
diff --git a/app/src/main/java/com/wbrawner/simplemarkdown/view/activity/MarkdownInfoActivity.kt b/app/src/main/java/com/wbrawner/simplemarkdown/view/fragment/MarkdownInfoFragment.kt
similarity index 62%
rename from app/src/main/java/com/wbrawner/simplemarkdown/view/activity/MarkdownInfoActivity.kt
rename to app/src/main/java/com/wbrawner/simplemarkdown/view/fragment/MarkdownInfoFragment.kt
index 1e1c2ee..25c888a 100644
--- a/app/src/main/java/com/wbrawner/simplemarkdown/view/activity/MarkdownInfoActivity.kt
+++ b/app/src/main/java/com/wbrawner/simplemarkdown/view/fragment/MarkdownInfoFragment.kt
@@ -1,38 +1,46 @@
-package com.wbrawner.simplemarkdown.view.activity
+package com.wbrawner.simplemarkdown.view.fragment
import android.content.res.Configuration
import android.os.Bundle
+import android.view.LayoutInflater
import android.view.MenuItem
+import android.view.View
+import android.view.ViewGroup
import android.widget.Toast
-import androidx.appcompat.app.AppCompatActivity
import androidx.appcompat.app.AppCompatDelegate
+import androidx.fragment.app.Fragment
+import androidx.navigation.fragment.findNavController
+import androidx.navigation.ui.setupWithNavController
import com.wbrawner.simplemarkdown.MarkdownApplication
import com.wbrawner.simplemarkdown.R
import com.wbrawner.simplemarkdown.utility.readAssetToString
import com.wbrawner.simplemarkdown.utility.toHtml
-import kotlinx.android.synthetic.main.activity_markdown_info.*
+import kotlinx.android.synthetic.main.fragment_markdown_info.*
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.Job
import kotlinx.coroutines.launch
import kotlin.coroutines.CoroutineContext
-class MarkdownInfoActivity : AppCompatActivity(), CoroutineScope {
+class MarkdownInfoFragment : Fragment(), CoroutineScope {
override val coroutineContext: CoroutineContext = Dispatchers.Main
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
- setContentView(R.layout.activity_markdown_info)
- setSupportActionBar(toolbar)
- supportActionBar?.setDisplayHomeAsUpEnabled(true)
- val title = intent?.getStringExtra(EXTRA_TITLE)
- val fileName = intent?.getStringExtra(EXTRA_FILE)
- if (title.isNullOrBlank() || fileName.isNullOrBlank()) {
- finish()
+ setHasOptionsMenu(true)
+ }
+
+ override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View? =
+ inflater.inflate(R.layout.fragment_markdown_info, container, false)
+
+ override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
+ val fileName = arguments?.getString(EXTRA_FILE)
+ if (fileName.isNullOrBlank()) {
+ findNavController().navigateUp()
return
}
+ toolbar.setupWithNavController(findNavController())
- setTitle(title)
val isNightMode = AppCompatDelegate.getDefaultNightMode() ==
AppCompatDelegate.MODE_NIGHT_YES
|| resources.configuration.uiMode and Configuration.UI_MODE_NIGHT_MASK == Configuration.UI_MODE_NIGHT_YES
@@ -44,7 +52,7 @@ class MarkdownInfoActivity : AppCompatActivity(), CoroutineScope {
val css: String? = getString(defaultCssId)
launch {
try {
- val html = assets?.readAssetToString(fileName)
+ val html = view.context.assets?.readAssetToString(fileName)
?.toHtml()
?: throw RuntimeException("Unable to open stream to $fileName")
infoWebview.loadDataWithBaseURL(null,
@@ -53,9 +61,9 @@ class MarkdownInfoActivity : AppCompatActivity(), CoroutineScope {
"UTF-8", null
)
} catch (e: Exception) {
- (application as MarkdownApplication).errorHandler.reportException(e)
- Toast.makeText(this@MarkdownInfoActivity, R.string.file_load_error, Toast.LENGTH_SHORT).show()
- finish()
+ (requireActivity().application as MarkdownApplication).errorHandler.reportException(e)
+ Toast.makeText(view.context, R.string.file_load_error, Toast.LENGTH_SHORT).show()
+ findNavController().navigateUp()
}
}
}
@@ -68,7 +76,7 @@ class MarkdownInfoActivity : AppCompatActivity(), CoroutineScope {
override fun onOptionsItemSelected(item: MenuItem): Boolean {
if (item.itemId == android.R.id.home) {
- onBackPressed()
+ findNavController().navigateUp()
return true
}
return super.onOptionsItemSelected(item)
diff --git a/app/src/main/java/com/wbrawner/simplemarkdown/view/fragment/PreviewFragment.kt b/app/src/main/java/com/wbrawner/simplemarkdown/view/fragment/PreviewFragment.kt
index 4faeaf3..8931524 100644
--- a/app/src/main/java/com/wbrawner/simplemarkdown/view/fragment/PreviewFragment.kt
+++ b/app/src/main/java/com/wbrawner/simplemarkdown/view/fragment/PreviewFragment.kt
@@ -64,7 +64,7 @@ class PreviewFragment : Fragment(), CoroutineScope {
override fun onAttach(context: Context) {
super.onAttach(context)
updateWebContent(viewModel.markdownUpdates.value ?: "")
- viewModel.markdownUpdates.observe(viewLifecycleOwner, Observer {
+ viewModel.markdownUpdates.observe(this, Observer {
updateWebContent(it)
})
}
diff --git a/app/src/main/java/com/wbrawner/simplemarkdown/view/fragment/SettingsContainerFragment.kt b/app/src/main/java/com/wbrawner/simplemarkdown/view/fragment/SettingsContainerFragment.kt
new file mode 100644
index 0000000..e9d80e4
--- /dev/null
+++ b/app/src/main/java/com/wbrawner/simplemarkdown/view/fragment/SettingsContainerFragment.kt
@@ -0,0 +1,28 @@
+package com.wbrawner.simplemarkdown.view.fragment
+
+import android.os.Bundle
+import android.view.LayoutInflater
+import android.view.MenuItem
+import android.view.View
+import android.view.ViewGroup
+import androidx.fragment.app.Fragment
+import androidx.navigation.fragment.findNavController
+import androidx.navigation.ui.setupWithNavController
+import com.wbrawner.simplemarkdown.R
+import kotlinx.android.synthetic.main.fragment_settings.*
+
+class SettingsContainerFragment : Fragment() {
+ override fun onCreate(savedInstanceState: Bundle?) {
+ super.onCreate(savedInstanceState)
+ setHasOptionsMenu(true)
+ }
+
+ override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View? =
+ inflater.inflate(R.layout.fragment_settings, container, false)
+
+ override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
+ toolbar.setupWithNavController(findNavController())
+ }
+
+ override fun onOptionsItemSelected(item: MenuItem): Boolean = findNavController().navigateUp()
+}
diff --git a/app/src/main/java/com/wbrawner/simplemarkdown/view/fragment/SupportFragment.kt b/app/src/main/java/com/wbrawner/simplemarkdown/view/fragment/SupportFragment.kt
new file mode 100644
index 0000000..8a5eff8
--- /dev/null
+++ b/app/src/main/java/com/wbrawner/simplemarkdown/view/fragment/SupportFragment.kt
@@ -0,0 +1,61 @@
+package com.wbrawner.simplemarkdown.view.fragment
+
+import android.content.ActivityNotFoundException
+import android.content.Intent
+import android.net.Uri
+import android.os.Bundle
+import android.view.LayoutInflater
+import android.view.View
+import android.view.ViewGroup
+import androidx.browser.customtabs.CustomTabsIntent
+import androidx.fragment.app.Fragment
+import androidx.lifecycle.Observer
+import androidx.navigation.fragment.findNavController
+import androidx.navigation.ui.setupWithNavController
+import com.wbrawner.simplemarkdown.R
+import com.wbrawner.simplemarkdown.utility.SupportLinkProvider
+import kotlinx.android.synthetic.main.fragment_support.*
+
+class SupportFragment : Fragment() {
+ override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View? =
+ inflater.inflate(R.layout.fragment_support, container, false)
+
+ override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
+ toolbar.setupWithNavController(findNavController())
+ githubButton.setOnClickListener {
+ CustomTabsIntent.Builder()
+ .addDefaultShareMenuItem()
+ .build()
+ .launchUrl(view.context, Uri.parse("https://github" +
+ ".com/wbrawner/SimpleMarkdown"))
+ }
+ rateButton.setOnClickListener {
+ val playStoreIntent = Intent(Intent.ACTION_VIEW)
+ .apply {
+ data = Uri.parse("market://details?id=${view.context.packageName}")
+ addFlags(Intent.FLAG_ACTIVITY_NO_HISTORY or
+ Intent.FLAG_ACTIVITY_NEW_DOCUMENT or
+ Intent.FLAG_ACTIVITY_MULTIPLE_TASK)
+ }
+ try {
+ startActivity(playStoreIntent)
+ } catch (ignored: ActivityNotFoundException) {
+ playStoreIntent.data = Uri.parse("https://play.google.com/store/apps/details?id=${view.context.packageName}")
+ startActivity(playStoreIntent)
+ }
+ }
+ SupportLinkProvider(requireActivity()).supportLinks.observe(viewLifecycleOwner, Observer { links ->
+ links.forEach {
+ supportButtons.addView(it)
+ }
+ })
+ }
+
+// override fun onOptionsItemSelected(item: MenuItem): Boolean {
+// if (item.itemId == android.R.id.home) {
+// findNavController().navigateUp()
+// return true
+// }
+// return super.onOptionsItemSelected(item)
+// }
+}
\ No newline at end of file
diff --git a/app/src/main/res/layout-land/activity_main.xml b/app/src/main/res/layout-land/activity_main.xml
deleted file mode 100644
index 33207f6..0000000
--- a/app/src/main/res/layout-land/activity_main.xml
+++ /dev/null
@@ -1,47 +0,0 @@
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
\ No newline at end of file
diff --git a/app/src/main/res/layout-land/fragment_main.xml b/app/src/main/res/layout-land/fragment_main.xml
new file mode 100644
index 0000000..fe45a2b
--- /dev/null
+++ b/app/src/main/res/layout-land/fragment_main.xml
@@ -0,0 +1,61 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/app/src/main/res/layout/activity_main.xml b/app/src/main/res/layout/activity_main.xml
index fa01013..1a47a1a 100644
--- a/app/src/main/res/layout/activity_main.xml
+++ b/app/src/main/res/layout/activity_main.xml
@@ -1,48 +1,9 @@
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
\ No newline at end of file
+ android:layout_height="match_parent"
+ app:defaultNavHost="true"
+ app:navGraph="@navigation/nav_graph" />
\ No newline at end of file
diff --git a/app/src/main/res/layout/fragment_main.xml b/app/src/main/res/layout/fragment_main.xml
new file mode 100644
index 0000000..6c77982
--- /dev/null
+++ b/app/src/main/res/layout/fragment_main.xml
@@ -0,0 +1,63 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/app/src/main/res/layout/activity_markdown_info.xml b/app/src/main/res/layout/fragment_markdown_info.xml
similarity index 95%
rename from app/src/main/res/layout/activity_markdown_info.xml
rename to app/src/main/res/layout/fragment_markdown_info.xml
index 8446872..987b398 100644
--- a/app/src/main/res/layout/activity_markdown_info.xml
+++ b/app/src/main/res/layout/fragment_markdown_info.xml
@@ -4,7 +4,7 @@
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
- tools:context="com.wbrawner.simplemarkdown.view.activity.MarkdownInfoActivity">
+ tools:context="com.wbrawner.simplemarkdown.view.fragment.MarkdownInfoFragment">
-
\ No newline at end of file
diff --git a/app/src/main/res/layout/activity_settings.xml b/app/src/main/res/layout/fragment_settings.xml
similarity index 56%
rename from app/src/main/res/layout/activity_settings.xml
rename to app/src/main/res/layout/fragment_settings.xml
index 005fbd3..6dc19de 100644
--- a/app/src/main/res/layout/activity_settings.xml
+++ b/app/src/main/res/layout/fragment_settings.xml
@@ -1,15 +1,13 @@
-
+ android:orientation="vertical">
+ android:layout_height="wrap_content">
+ android:layout_weight="1" />
-
\ No newline at end of file
+
\ No newline at end of file
diff --git a/app/src/main/res/layout/activity_support.xml b/app/src/main/res/layout/fragment_support.xml
similarity index 100%
rename from app/src/main/res/layout/activity_support.xml
rename to app/src/main/res/layout/fragment_support.xml
diff --git a/app/src/main/res/menu/menu_main.xml b/app/src/main/res/menu/menu_main.xml
index dfe9495..30f818c 100644
--- a/app/src/main/res/menu/menu_main.xml
+++ b/app/src/main/res/menu/menu_main.xml
@@ -3,29 +3,29 @@
xmlns:app="http://schemas.android.com/apk/res-auto">
diff --git a/app/src/main/res/navigation/nav_graph.xml b/app/src/main/res/navigation/nav_graph.xml
new file mode 100644
index 0000000..522612a
--- /dev/null
+++ b/app/src/main/res/navigation/nav_graph.xml
@@ -0,0 +1,87 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file