Split overflow menu into navigation menu

I also removed Dagger since it wasn't really being used, and fixed the CrashlyticsErrorHandler as I had previously imported the incorrect BuildConfig class.
This commit is contained in:
Billy Brawner 2019-11-07 12:05:07 -06:00 committed by William Brawner
parent 070d580044
commit d0736458b6
14 changed files with 205 additions and 75 deletions

View file

@ -88,14 +88,11 @@ dependencies {
androidTestImplementation 'androidx.test.ext:junit:1.1.1' androidTestImplementation 'androidx.test.ext:junit:1.1.1'
androidTestImplementation 'androidx.test.uiautomator:uiautomator:2.2.0' androidTestImplementation 'androidx.test.uiautomator:uiautomator:2.2.0'
implementation 'androidx.appcompat:appcompat:1.1.0' implementation 'androidx.appcompat:appcompat:1.1.0'
implementation 'androidx.preference:preference:1.1.0'
implementation 'androidx.constraintlayout:constraintlayout:1.1.3' implementation 'androidx.constraintlayout:constraintlayout:1.1.3'
implementation 'com.google.android.material:material:1.1.0-beta01' implementation 'com.google.android.material:material:1.1.0-beta01'
implementation 'androidx.legacy:legacy-support-v13:1.0.0' implementation 'androidx.legacy:legacy-support-v13:1.0.0'
implementation 'com.commonsware.cwac:anddown:0.3.0' implementation 'com.commonsware.cwac:anddown:0.3.0'
implementation 'com.google.dagger:dagger:2.23.1'
annotationProcessor 'com.google.dagger:dagger-compiler:2.23.1'
kapt 'com.google.dagger:dagger-android-processor:2.22.1'
kapt 'com.google.dagger:dagger-compiler:2.23.1'
implementation 'com.google.firebase:firebase-core:17.2.1' implementation 'com.google.firebase:firebase-core:17.2.1'
implementation 'com.android.billingclient:billing:1.2' implementation 'com.android.billingclient:billing:1.2'
implementation 'com.crashlytics.sdk.android:crashlytics:2.10.1' implementation 'com.crashlytics.sdk.android:crashlytics:2.10.1'

View file

@ -2,12 +2,17 @@ package com.wbrawner.simplemarkdown
import android.app.Application import android.app.Application
import android.os.StrictMode import android.os.StrictMode
import com.wbrawner.simplemarkdown.utility.CrashlyticsErrorHandler
import com.wbrawner.simplemarkdown.utility.ErrorHandler
import com.wbrawner.simplemarkdown.viewmodel.MarkdownViewModelFactory import com.wbrawner.simplemarkdown.viewmodel.MarkdownViewModelFactory
class MarkdownApplication : Application() { class MarkdownApplication : Application() {
val viewModelFactory: MarkdownViewModelFactory by lazy { val viewModelFactory: MarkdownViewModelFactory by lazy {
MarkdownViewModelFactory() MarkdownViewModelFactory()
} }
val errorHandler: ErrorHandler by lazy {
CrashlyticsErrorHandler()
}
override fun onCreate() { override fun onCreate() {
if (BuildConfig.DEBUG) { if (BuildConfig.DEBUG) {

View file

@ -1,14 +1,15 @@
package com.wbrawner.simplemarkdown.utility package com.wbrawner.simplemarkdown.utility
import android.content.Context import android.content.Context
import android.util.Log
import com.crashlytics.android.Crashlytics import com.crashlytics.android.Crashlytics
import io.fabric.sdk.android.BuildConfig import com.wbrawner.simplemarkdown.BuildConfig
import io.fabric.sdk.android.Fabric import io.fabric.sdk.android.Fabric
import java.util.concurrent.atomic.AtomicBoolean import java.util.concurrent.atomic.AtomicBoolean
interface ErrorHandler { interface ErrorHandler {
fun init(context: Context) fun init(context: Context)
fun reportException(t: Throwable) fun reportException(t: Throwable, message: String? = null)
} }
class CrashlyticsErrorHandler : ErrorHandler { class CrashlyticsErrorHandler : ErrorHandler {
@ -20,8 +21,13 @@ class CrashlyticsErrorHandler : ErrorHandler {
} }
} }
override fun reportException(t: Throwable) { override fun reportException(t: Throwable, message: String?) {
if (!isInitialized.get() || BuildConfig.DEBUG) return @Suppress("ConstantConditionIf")
if (BuildConfig.DEBUG) {
Log.e("CrashlyticsErrorHandler", "Caught exception: $message", t)
return
}
if (!isInitialized.get()) return
Crashlytics.logException(t) Crashlytics.logException(t)
} }
} }

View file

@ -8,7 +8,6 @@ import android.content.res.Configuration
import android.net.Uri import android.net.Uri
import android.os.Build import android.os.Build
import android.os.Bundle import android.os.Bundle
import android.preference.PreferenceManager
import android.view.Menu import android.view.Menu
import android.view.MenuItem import android.view.MenuItem
import android.view.View import android.view.View
@ -20,25 +19,19 @@ import androidx.core.app.ActivityCompat
import androidx.core.content.ContextCompat import androidx.core.content.ContextCompat
import androidx.lifecycle.Observer import androidx.lifecycle.Observer
import androidx.lifecycle.ViewModelProviders import androidx.lifecycle.ViewModelProviders
import androidx.preference.PreferenceManager
import com.wbrawner.simplemarkdown.MarkdownApplication import com.wbrawner.simplemarkdown.MarkdownApplication
import com.wbrawner.simplemarkdown.R import com.wbrawner.simplemarkdown.R
import com.wbrawner.simplemarkdown.utility.ErrorHandler
import com.wbrawner.simplemarkdown.utility.getName
import com.wbrawner.simplemarkdown.utility.readAssetToString
import com.wbrawner.simplemarkdown.utility.toHtml
import com.wbrawner.simplemarkdown.view.adapter.EditPagerAdapter import com.wbrawner.simplemarkdown.view.adapter.EditPagerAdapter
import com.wbrawner.simplemarkdown.view.fragment.MainMenuFragment
import com.wbrawner.simplemarkdown.viewmodel.MarkdownViewModel import com.wbrawner.simplemarkdown.viewmodel.MarkdownViewModel
import kotlinx.android.synthetic.main.activity_main.* import kotlinx.android.synthetic.main.activity_main.*
import kotlinx.coroutines.* import kotlinx.coroutines.*
import java.io.File import java.io.File
import java.io.FileInputStream
import javax.inject.Inject
import kotlin.coroutines.CoroutineContext import kotlin.coroutines.CoroutineContext
class MainActivity : AppCompatActivity(), ActivityCompat.OnRequestPermissionsResultCallback, CoroutineScope { class MainActivity : AppCompatActivity(), ActivityCompat.OnRequestPermissionsResultCallback, CoroutineScope {
@Inject
lateinit var errorHandler: ErrorHandler
private var shouldAutoSave = true private var shouldAutoSave = true
override val coroutineContext: CoroutineContext = Dispatchers.Main override val coroutineContext: CoroutineContext = Dispatchers.Main
private lateinit var viewModel: MarkdownViewModel private lateinit var viewModel: MarkdownViewModel
@ -47,6 +40,8 @@ class MainActivity : AppCompatActivity(), ActivityCompat.OnRequestPermissionsRes
super.onCreate(savedInstanceState) super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main) setContentView(R.layout.activity_main)
setSupportActionBar(toolbar) setSupportActionBar(toolbar)
supportActionBar?.setHomeAsUpIndicator(R.drawable.ic_menu_black_24dp)
supportActionBar?.setDisplayHomeAsUpEnabled(true)
window.decorView.apply { window.decorView.apply {
systemUiVisibility = ( systemUiVisibility = (
View.SYSTEM_UI_FLAG_LAYOUT_HIDE_NAVIGATION View.SYSTEM_UI_FLAG_LAYOUT_HIDE_NAVIGATION
@ -89,7 +84,7 @@ class MainActivity : AppCompatActivity(), ActivityCompat.OnRequestPermissionsRes
// The user has left the app, with autosave enabled, and we don't already have a // 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 // 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 // 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)) val fileUri = Uri.fromFile(File(filesDir, viewModel.fileName.value!!))
if (viewModel.save(this@MainActivity, fileUri)) { if (viewModel.save(this@MainActivity, fileUri)) {
fileUri fileUri
} else { } else {
@ -118,6 +113,13 @@ class MainActivity : AppCompatActivity(), ActivityCompat.OnRequestPermissionsRes
override fun onOptionsItemSelected(item: MenuItem): Boolean { override fun onOptionsItemSelected(item: MenuItem): Boolean {
when (item.itemId) { when (item.itemId) {
android.R.id.home -> {
MainMenuFragment()
.apply {
errorHandler = (application as MarkdownApplication).errorHandler
}
.show(supportFragmentManager, null)
}
R.id.action_save -> { R.id.action_save -> {
launch { launch {
if (!viewModel.save(this@MainActivity)) { if (!viewModel.save(this@MainActivity)) {
@ -149,50 +151,10 @@ class MainActivity : AppCompatActivity(), ActivityCompat.OnRequestPermissionsRes
item.isChecked = !item.isChecked item.isChecked = !item.isChecked
pager!!.setSwipeLocked(item.isChecked) pager!!.setSwipeLocked(item.isChecked)
} }
R.id.action_help -> showInfoActivity(R.id.action_help)
R.id.action_settings -> {
val settingsIntent = Intent(this@MainActivity, SettingsActivity::class.java)
startActivityForResult(settingsIntent, REQUEST_DARK_MODE)
}
R.id.action_libraries -> showInfoActivity(R.id.action_libraries)
R.id.action_privacy -> showInfoActivity(R.id.action_privacy)
} }
return super.onOptionsItemSelected(item) return super.onOptionsItemSelected(item)
} }
private fun showInfoActivity(action: Int) {
val infoIntent = Intent(this@MainActivity, MarkdownInfoActivity::class.java)
var fileName = ""
var title = ""
when (action) {
R.id.action_help -> {
fileName = "Cheatsheet.md"
title = getString(R.string.action_help)
}
R.id.action_libraries -> {
fileName = "Libraries.md"
title = getString(R.string.action_libraries)
}
R.id.action_privacy -> {
fileName = "Privacy Policy.md"
title = getString(R.string.action_privacy)
}
}
infoIntent.putExtra("title", title)
launch {
try {
val html = assets?.readAssetToString(fileName)
?.toHtml()
?: throw RuntimeException("Unable to open stream to $fileName")
infoIntent.putExtra("html", html)
startActivity(infoIntent)
} catch (e: Exception) {
errorHandler.reportException(e)
Toast.makeText(this@MainActivity, R.string.file_load_error, Toast.LENGTH_SHORT).show()
}
}
}
override fun onRequestPermissionsResult( override fun onRequestPermissionsResult(
requestCode: Int, requestCode: Int,
permissions: Array<String>, permissions: Array<String>,

View file

@ -5,6 +5,7 @@ import android.content.Intent
import android.net.Uri import android.net.Uri
import android.os.Bundle import android.os.Bundle
import android.view.MenuItem import android.view.MenuItem
import android.view.View
import android.widget.Toast import android.widget.Toast
import androidx.appcompat.app.AppCompatActivity import androidx.appcompat.app.AppCompatActivity
import androidx.browser.customtabs.CustomTabsIntent import androidx.browser.customtabs.CustomTabsIntent
@ -21,6 +22,13 @@ class SupportActivity : AppCompatActivity(), BillingClientStateListener, Purchas
super.onCreate(savedInstanceState) super.onCreate(savedInstanceState)
setContentView(R.layout.activity_support) setContentView(R.layout.activity_support)
setSupportActionBar(toolbar) setSupportActionBar(toolbar)
window.decorView.apply {
systemUiVisibility = (
View.SYSTEM_UI_FLAG_LAYOUT_HIDE_NAVIGATION
or View.SYSTEM_UI_FLAG_LAYOUT_STABLE
or View.SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN
)
}
setTitle(R.string.support_title) setTitle(R.string.support_title)
supportActionBar?.setDisplayHomeAsUpEnabled(true) supportActionBar?.setDisplayHomeAsUpEnabled(true)
billingClient = BillingClient.newBuilder(applicationContext) billingClient = BillingClient.newBuilder(applicationContext)

View file

@ -0,0 +1,92 @@
package com.wbrawner.simplemarkdown.view.fragment
import android.content.Context
import android.content.Intent
import android.os.Bundle
import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
import android.widget.Toast
import com.google.android.material.bottomsheet.BottomSheetDialogFragment
import com.wbrawner.simplemarkdown.R
import com.wbrawner.simplemarkdown.utility.ErrorHandler
import com.wbrawner.simplemarkdown.utility.readAssetToString
import com.wbrawner.simplemarkdown.utility.toHtml
import com.wbrawner.simplemarkdown.view.activity.MainActivity
import com.wbrawner.simplemarkdown.view.activity.MarkdownInfoActivity
import com.wbrawner.simplemarkdown.view.activity.SettingsActivity
import com.wbrawner.simplemarkdown.view.activity.SupportActivity
import kotlinx.android.synthetic.main.fragment_menu_main.*
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.launch
import kotlin.coroutines.CoroutineContext
class MainMenuFragment : BottomSheetDialogFragment(), CoroutineScope {
override val coroutineContext: CoroutineContext = Dispatchers.Main
lateinit var errorHandler: ErrorHandler
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 {
when (it.itemId) {
R.id.action_help -> showInfoActivity(context, R.id.action_help)
R.id.action_settings -> {
val settingsIntent = Intent(context, SettingsActivity::class.java)
startActivityForResult(settingsIntent, MainActivity.REQUEST_DARK_MODE)
dialog?.dismiss()
}
R.id.action_libraries -> showInfoActivity(context, R.id.action_libraries)
R.id.action_privacy -> showInfoActivity(context, R.id.action_privacy)
R.id.action_support -> Intent(context, SupportActivity::class.java)
.apply {
startActivity(this)
dialog?.dismiss()
}
}
true
}
}
private fun showInfoActivity(context: Context?, action: Int) {
val infoIntent = Intent(context, MarkdownInfoActivity::class.java)
var fileName = ""
var title = ""
when (action) {
R.id.action_help -> {
fileName = "Cheatsheet.md"
title = getString(R.string.action_help)
}
R.id.action_libraries -> {
fileName = "Libraries.md"
title = getString(R.string.action_libraries)
}
R.id.action_privacy -> {
fileName = "Privacy Policy.md"
title = getString(R.string.action_privacy)
}
}
infoIntent.putExtra("title", title)
launch {
// TODO: Refactor this to have the info activity load the markdown instead of doing
// it here
try {
val html = context?.assets?.readAssetToString(fileName)
?.toHtml()
?: throw RuntimeException("Unable to open stream to $fileName")
infoIntent.putExtra("html", html)
startActivity(infoIntent, null)
} catch (e: Exception) {
errorHandler.reportException(e)
Toast.makeText(context, R.string.file_load_error, Toast.LENGTH_SHORT).show()
}
dialog?.dismiss()
}
}
}

View file

@ -0,0 +1,9 @@
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="24dp"
android:height="24dp"
android:viewportWidth="24.0"
android:viewportHeight="24.0">
<path
android:fillColor="#FF000000"
android:pathData="M12,4.5C7,4.5 2.73,7.61 1,12c1.73,4.39 6,7.5 11,7.5s9.27,-3.11 11,-7.5c-1.73,-4.39 -6,-7.5 -11,-7.5zM12,17c-2.76,0 -5,-2.24 -5,-5s2.24,-5 5,-5 5,2.24 5,5 -2.24,5 -5,5zM12,9c-1.66,0 -3,1.34 -3,3s1.34,3 3,3 3,-1.34 3,-3 -1.34,-3 -3,-3z" />
</vector>

View file

@ -0,0 +1,9 @@
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="24dp"
android:height="24dp"
android:viewportWidth="24.0"
android:viewportHeight="24.0">
<path
android:fillColor="#FF000000"
android:pathData="M12,2C6.48,2 2,6.48 2,12s4.48,10 10,10 10,-4.48 10,-10S17.52,2 12,2zM13,19h-2v-2h2v2zM15.07,11.25l-0.9,0.92C13.45,12.9 13,13.5 13,15h-2v-0.5c0,-1.1 0.45,-2.1 1.17,-2.83l1.24,-1.26c0.37,-0.36 0.59,-0.86 0.59,-1.41 0,-1.1 -0.9,-2 -2,-2s-2,0.9 -2,2L8,9c0,-2.21 1.79,-4 4,-4s4,1.79 4,4c0,0.88 -0.36,1.68 -0.93,2.25z" />
</vector>

View file

@ -0,0 +1,9 @@
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="24dp"
android:height="24dp"
android:viewportWidth="24.0"
android:viewportHeight="24.0">
<path
android:fillColor="#FF000000"
android:pathData="M12,2C6.48,2 2,6.48 2,12s4.48,10 10,10 10,-4.48 10,-10S17.52,2 12,2zM13,17h-2v-6h2v6zM13,9h-2L11,7h2v2z" />
</vector>

View file

@ -0,0 +1,9 @@
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="24dp"
android:height="24dp"
android:viewportWidth="24"
android:viewportHeight="24">
<path
android:fillColor="#FF000000"
android:pathData="M19.1,12.9a2.8,2.8 0,0 0,0.1 -0.9,2.8 2.8,0 0,0 -0.1,-0.9l2.1,-1.6a0.7,0.7 0,0 0,0.1 -0.6L19.4,5.5a0.7,0.7 0,0 0,-0.6 -0.2l-2.4,1a6.5,6.5 0,0 0,-1.6 -0.9l-0.4,-2.6a0.5,0.5 0,0 0,-0.5 -0.4H10.1a0.5,0.5 0,0 0,-0.5 0.4L9.3,5.4a5.6,5.6 0,0 0,-1.7 0.9l-2.4,-1a0.4,0.4 0,0 0,-0.5 0.2l-2,3.4c-0.1,0.2 0,0.4 0.2,0.6l2,1.6a2.8,2.8 0,0 0,-0.1 0.9,2.8 2.8,0 0,0 0.1,0.9L2.8,14.5a0.7,0.7 0,0 0,-0.1 0.6l1.9,3.4a0.7,0.7 0,0 0,0.6 0.2l2.4,-1a6.5,6.5 0,0 0,1.6 0.9l0.4,2.6a0.5,0.5 0,0 0,0.5 0.4h3.8a0.5,0.5 0,0 0,0.5 -0.4l0.3,-2.6a5.6,5.6 0,0 0,1.7 -0.9l2.4,1a0.4,0.4 0,0 0,0.5 -0.2l2,-3.4c0.1,-0.2 0,-0.4 -0.2,-0.6ZM12,15.6A3.6,3.6 0,1 1,15.6 12,3.6 3.6,0 0,1 12,15.6Z" />
</vector>

View file

@ -7,7 +7,7 @@
<ScrollView <ScrollView
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="wrap_content" android:layout_height="0dp"
android:clipChildren="false" android:clipChildren="false"
android:clipToPadding="false" android:clipToPadding="false"
android:padding="16dp" android:padding="16dp"

View file

@ -0,0 +1,7 @@
<?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

@ -25,20 +25,4 @@
android:checkable="true" android:checkable="true"
android:title="@string/action_lock_swipe" android:title="@string/action_lock_swipe"
app:showAsAction="never" /> app:showAsAction="never" />
<item
android:id="@+id/action_settings"
android:title="@string/action_settings"
app:showAsAction="never" />
<item
android:id="@+id/action_help"
android:title="@string/action_help"
app:showAsAction="never" />
<item
android:id="@+id/action_libraries"
android:title="@string/action_libraries"
app:showAsAction="never" />
<item
android:id="@+id/action_privacy"
android:title="@string/action_privacy"
app:showAsAction="never" />
</menu> </menu>

View file

@ -0,0 +1,33 @@
<?xml version="1.0" encoding="utf-8"?>
<menu xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto">
<group android:id="@+id/mainGroup">
<item
android:id="@+id/action_settings"
android:title="@string/action_settings"
android:icon="@drawable/ic_settings_black_24dp"
app:showAsAction="never" />
<item
android:id="@+id/action_support"
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:title="@string/action_help"
android:icon="@drawable/ic_help_black_24dp"
app:showAsAction="never" />
<item
android:id="@+id/action_libraries"
android:title="@string/action_libraries"
android:icon="@drawable/ic_info_black_24dp"
app:showAsAction="never" />
<item
android:id="@+id/action_privacy"
android:title="@string/action_privacy"
android:icon="@drawable/ic_eye_black_24dp"
app:showAsAction="never" />
</group>
</menu>