Convert (most) Activities to Fragments and use AndroidX navigation component

Signed-off-by: William Brawner <me@wbrawner.com>
This commit is contained in:
William Brawner 2020-06-12 22:54:32 -07:00
parent a88eeb79e4
commit d9afb67d44
21 changed files with 651 additions and 570 deletions

View file

@ -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"

View file

@ -43,10 +43,6 @@
</intent-filter>
</activity>
<activity android:name=".view.activity.MainActivity" />
<activity
android:name=".view.activity.SettingsActivity"
android:label="@string/title_activity_settings" />
<provider
android:name="androidx.core.content.FileProvider"
android:authorities="${applicationId}.fileprovider"
@ -57,14 +53,6 @@
android:resource="@xml/file_paths" />
</provider>
<activity android:name=".view.activity.MarkdownInfoActivity">
<meta-data
android:name="android.support.PARENT_ACTIVITY"
android:value=".view.activity.MainActivity" />
</activity>
<activity android:name=".view.activity.SupportActivity" />
<meta-data
android:name="io.sentry.dsn"
android:value="${sentryDsn}" />

View file

@ -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<String>,
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"
}
}

View file

@ -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
}
}

View file

@ -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)
}
}

View file

@ -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<String>,
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"
}
}

View file

@ -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()
}
}

View file

@ -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)

View file

@ -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)
})
}

View file

@ -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()
}

View file

@ -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)
// }
}

View file

@ -1,47 +0,0 @@
<?xml version="1.0" encoding="utf-8"?>
<androidx.coordinatorlayout.widget.CoordinatorLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
android:layout_width="match_parent"
android:layout_height="match_parent">
<com.wbrawner.simplemarkdown.view.DisableableViewPager
android:id="@+id/pager"
android:layout_width="match_parent"
android:layout_height="match_parent"
app:layout_behavior="@string/appbar_scrolling_view_behavior" />
<com.google.android.material.appbar.AppBarLayout
android:id="@+id/bottomSheet"
android:layout_width="match_parent"
android:layout_height="wrap_content">
<com.google.android.material.appbar.MaterialToolbar
android:id="@+id/toolbar"
android:layout_width="match_parent"
android:layout_height="?attr/actionBarSize"
android:background="@color/colorBackground"
app:layout_scrollFlags="scroll|enterAlways" />
<com.google.android.material.tabs.TabLayout
android:id="@+id/tabLayout"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:background="@color/colorBackground"
android:visibility="gone">
<com.google.android.material.tabs.TabItem
android:id="@+id/editTab"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:text="@string/action_edit" />
<com.google.android.material.tabs.TabItem
android:id="@+id/previewTab"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:text="@string/action_preview" />
</com.google.android.material.tabs.TabLayout>
</com.google.android.material.appbar.AppBarLayout>
</androidx.coordinatorlayout.widget.CoordinatorLayout>

View file

@ -0,0 +1,61 @@
<?xml version="1.0" encoding="utf-8"?>
<androidx.drawerlayout.widget.DrawerLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:id="@+id/drawerLayout">
<androidx.coordinatorlayout.widget.CoordinatorLayout
android:layout_width="match_parent"
android:layout_height="match_parent">
<com.wbrawner.simplemarkdown.view.DisableableViewPager
android:id="@+id/pager"
android:layout_width="match_parent"
android:layout_height="match_parent"
app:layout_behavior="@string/appbar_scrolling_view_behavior" />
<com.google.android.material.appbar.AppBarLayout
android:id="@+id/bottomSheet"
android:layout_width="match_parent"
android:layout_height="wrap_content">
<com.google.android.material.appbar.MaterialToolbar
android:id="@+id/toolbar"
android:layout_width="match_parent"
android:layout_height="?attr/actionBarSize"
android:background="@color/colorBackground"
app:layout_scrollFlags="scroll|enterAlways" />
<com.google.android.material.tabs.TabLayout
android:id="@+id/tabLayout"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:background="@color/colorBackground"
android:visibility="gone">
<com.google.android.material.tabs.TabItem
android:id="@+id/editTab"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:text="@string/action_edit" />
<com.google.android.material.tabs.TabItem
android:id="@+id/previewTab"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:text="@string/action_preview" />
</com.google.android.material.tabs.TabLayout>
</com.google.android.material.appbar.AppBarLayout>
</androidx.coordinatorlayout.widget.CoordinatorLayout>
<com.google.android.material.navigation.NavigationView
android:id="@+id/navigationView"
android:layout_width="wrap_content"
android:layout_height="match_parent"
android:layout_gravity="start"
android:fitsSystemWindows="true"
app:menu="@menu/menu_main" />
</androidx.drawerlayout.widget.DrawerLayout>

View file

@ -1,48 +1,9 @@
<?xml version="1.0" encoding="utf-8"?>
<androidx.coordinatorlayout.widget.CoordinatorLayout xmlns:android="http://schemas.android.com/apk/res/android"
<androidx.fragment.app.FragmentContainerView xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
android:id="@+id/content"
android:name="androidx.navigation.fragment.NavHostFragment"
android:layout_width="match_parent"
android:layout_height="match_parent">
<com.wbrawner.simplemarkdown.view.DisableableViewPager
android:id="@+id/pager"
android:layout_width="match_parent"
android:layout_height="match_parent"
app:layout_behavior="@string/appbar_scrolling_view_behavior" />
<com.google.android.material.appbar.AppBarLayout
android:id="@+id/bottomSheet"
android:layout_width="match_parent"
android:layout_height="wrap_content"
app:liftOnScroll="true">
<com.google.android.material.appbar.MaterialToolbar
android:id="@+id/toolbar"
android:layout_width="match_parent"
android:layout_height="?attr/actionBarSize"
app:layout_scrollFlags="scroll|enterAlways|snap"
android:background="@color/colorBackground" />
<com.google.android.material.tabs.TabLayout
android:id="@+id/tabLayout"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_gravity="bottom"
android:background="@color/colorBackground">
<com.google.android.material.tabs.TabItem
android:id="@+id/editTab"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:text="@string/action_edit" />
<com.google.android.material.tabs.TabItem
android:id="@+id/previewTab"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:text="@string/action_preview" />
</com.google.android.material.tabs.TabLayout>
</com.google.android.material.appbar.AppBarLayout>
</androidx.coordinatorlayout.widget.CoordinatorLayout>
android:layout_height="match_parent"
app:defaultNavHost="true"
app:navGraph="@navigation/nav_graph" />

View file

@ -0,0 +1,63 @@
<?xml version="1.0" encoding="utf-8"?>
<androidx.drawerlayout.widget.DrawerLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:background="@color/colorBackground"
android:id="@+id/drawerLayout">
<androidx.coordinatorlayout.widget.CoordinatorLayout
android:layout_width="match_parent"
android:layout_height="match_parent">
<com.wbrawner.simplemarkdown.view.DisableableViewPager
android:id="@+id/pager"
android:layout_width="match_parent"
android:layout_height="match_parent"
app:layout_behavior="@string/appbar_scrolling_view_behavior" />
<com.google.android.material.appbar.AppBarLayout
android:id="@+id/bottomSheet"
android:layout_width="match_parent"
android:layout_height="wrap_content"
app:liftOnScroll="true">
<com.google.android.material.appbar.MaterialToolbar
android:id="@+id/toolbar"
android:layout_width="match_parent"
android:layout_height="?attr/actionBarSize"
android:background="@color/colorBackground"
app:layout_scrollFlags="scroll|enterAlways|snap" />
<com.google.android.material.tabs.TabLayout
android:id="@+id/tabLayout"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_gravity="bottom"
android:background="@color/colorBackground">
<com.google.android.material.tabs.TabItem
android:id="@+id/editTab"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:text="@string/action_edit" />
<com.google.android.material.tabs.TabItem
android:id="@+id/previewTab"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:text="@string/action_preview" />
</com.google.android.material.tabs.TabLayout>
</com.google.android.material.appbar.AppBarLayout>
</androidx.coordinatorlayout.widget.CoordinatorLayout>
<com.google.android.material.navigation.NavigationView
android:id="@+id/navigationView"
android:layout_width="wrap_content"
android:layout_height="match_parent"
android:layout_gravity="start"
android:fitsSystemWindows="true"
app:menu="@menu/menu_main" />
</androidx.drawerlayout.widget.DrawerLayout>

View file

@ -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">
<com.google.android.material.appbar.AppBarLayout
android:id="@+id/appBarLayout"

View file

@ -1,7 +0,0 @@
<?xml version="1.0" encoding="utf-8"?>
<com.google.android.material.navigation.NavigationView xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent"
xmlns:app="http://schemas.android.com/apk/res-auto"
android:id="@+id/mainMenuNavigationView"
app:menu="@menu/menu_main" />

View file

@ -1,15 +1,13 @@
<?xml version="1.0" encoding="utf-8"?>
<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:fitsSystemWindows="true">
android:orientation="vertical">
<com.google.android.material.appbar.AppBarLayout
android:id="@+id/appBarLayout"
android:layout_width="match_parent"
android:layout_height="wrap_content"
app:layout_constraintTop_toTopOf="parent">
android:layout_height="wrap_content">
<com.google.android.material.appbar.MaterialToolbar
android:id="@+id/toolbar"
@ -20,11 +18,9 @@
<androidx.fragment.app.FragmentContainerView
android:id="@+id/fragment_settings"
android:name="com.wbrawner.simplemarkdown.view.fragment.SettingsFragment"
android:layout_width="match_parent"
android:layout_height="0dp"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@+id/appBarLayout" />
android:layout_weight="1" />
</androidx.constraintlayout.widget.ConstraintLayout>
</LinearLayout>

View file

@ -3,29 +3,29 @@
xmlns:app="http://schemas.android.com/apk/res-auto">
<group android:id="@+id/mainGroup">
<item
android:id="@+id/action_settings"
android:id="@+id/action_mainFragment_to_settingsContainerFragment"
android:title="@string/action_settings"
android:icon="@drawable/ic_settings_black_24dp"
app:showAsAction="never" />
<item
android:id="@+id/action_support"
android:id="@+id/action_mainFragment_to_supportFragment"
android:title="@string/support_title"
android:icon="@drawable/ic_favorite_black_24dp"
app:showAsAction="never" />
</group>
<group android:id="@+id/addtionalInfoGroup">
<item
android:id="@+id/action_help"
android:id="@+id/action_mainFragment_to_helpFragment"
android:title="@string/action_help"
android:icon="@drawable/ic_help_black_24dp"
app:showAsAction="never" />
<item
android:id="@+id/action_libraries"
android:id="@+id/action_mainFragment_to_librariesFragment"
android:title="@string/action_libraries"
android:icon="@drawable/ic_info_black_24dp"
app:showAsAction="never" />
<item
android:id="@+id/action_privacy"
android:id="@+id/action_mainFragment_to_privacyFragment"
android:title="@string/action_privacy"
android:icon="@drawable/ic_eye_black_24dp"
app:showAsAction="never" />

View file

@ -0,0 +1,87 @@
<?xml version="1.0" encoding="utf-8"?>
<navigation xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:id="@+id/nav_graph"
app:startDestination="@+id/mainFragment">
<fragment
android:id="@+id/settingsContainerFragment"
android:name="com.wbrawner.simplemarkdown.view.fragment.SettingsContainerFragment"
android:label="@string/title_activity_settings" />
<fragment
android:id="@+id/mainFragment"
android:name="com.wbrawner.simplemarkdown.view.fragment.MainFragment"
android:label="">
<action
android:id="@+id/action_mainFragment_to_helpFragment"
app:destination="@id/helpFragment"
app:enterAnim="@android:anim/slide_in_left"
app:exitAnim="@android:anim/slide_out_right"
app:popExitAnim="@android:anim/slide_out_right"
app:popUpTo="@id/mainFragment" />
<action
android:id="@+id/action_mainFragment_to_privacyFragment"
app:destination="@id/privacyFragment"
app:enterAnim="@android:anim/slide_in_left"
app:exitAnim="@android:anim/slide_out_right"
app:popExitAnim="@android:anim/slide_out_right"
app:popUpTo="@id/mainFragment" />
<action
android:id="@+id/action_mainFragment_to_librariesFragment"
app:destination="@id/librariesFragment"
app:enterAnim="@android:anim/slide_in_left"
app:exitAnim="@android:anim/slide_out_right"
app:popExitAnim="@android:anim/slide_out_right"
app:popUpTo="@id/mainFragment" />
<action
android:id="@+id/action_mainFragment_to_settingsContainerFragment"
app:destination="@id/settingsContainerFragment"
app:enterAnim="@android:anim/slide_in_left"
app:exitAnim="@android:anim/slide_out_right"
app:popExitAnim="@android:anim/slide_out_right"
app:popUpTo="@id/mainFragment" />
<action
android:id="@+id/action_mainFragment_to_supportFragment"
app:destination="@id/supportFragment"
app:enterAnim="@anim/nav_default_enter_anim"
app:exitAnim="@anim/nav_default_exit_anim"
app:popEnterAnim="@anim/nav_default_pop_enter_anim"
app:popExitAnim="@anim/nav_default_pop_exit_anim"
app:popUpTo="@id/mainFragment" />
</fragment>
<fragment
android:id="@+id/supportFragment"
android:name="com.wbrawner.simplemarkdown.view.fragment.SupportFragment"
android:label="@string/support_title" />
<fragment
android:id="@+id/helpFragment"
android:name="com.wbrawner.simplemarkdown.view.fragment.MarkdownInfoFragment"
android:label="@string/action_help"
tools:layout="@layout/fragment_markdown_info">
<argument
android:name="file"
app:argType="string"
android:defaultValue="Cheatsheet.md" />
</fragment>
<fragment
android:id="@+id/privacyFragment"
android:name="com.wbrawner.simplemarkdown.view.fragment.MarkdownInfoFragment"
android:label="@string/action_privacy"
tools:layout="@layout/fragment_markdown_info">
<argument
android:name="file"
android:defaultValue="Privacy Policy.md"
app:argType="string" />
</fragment>
<fragment
android:id="@+id/librariesFragment"
android:name="com.wbrawner.simplemarkdown.view.fragment.MarkdownInfoFragment"
android:label="@string/action_libraries"
tools:layout="@layout/fragment_markdown_info">
<argument
android:name="file"
android:defaultValue="Libraries.md"
app:argType="string" />
</fragment>
</navigation>