diff --git a/app/build.gradle b/app/build.gradle index b4d4c64..07b13a3 100644 --- a/app/build.gradle +++ b/app/build.gradle @@ -88,14 +88,11 @@ dependencies { androidTestImplementation 'androidx.test.ext:junit:1.1.1' androidTestImplementation 'androidx.test.uiautomator:uiautomator:2.2.0' implementation 'androidx.appcompat:appcompat:1.1.0' + implementation 'androidx.preference:preference:1.1.0' implementation 'androidx.constraintlayout:constraintlayout:1.1.3' implementation 'com.google.android.material:material:1.1.0-beta01' implementation 'androidx.legacy:legacy-support-v13:1.0.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.android.billingclient:billing:1.2' implementation 'com.crashlytics.sdk.android:crashlytics:2.10.1' diff --git a/app/src/main/java/com/wbrawner/simplemarkdown/MarkdownApplication.kt b/app/src/main/java/com/wbrawner/simplemarkdown/MarkdownApplication.kt index 9a96bb7..1d033ae 100644 --- a/app/src/main/java/com/wbrawner/simplemarkdown/MarkdownApplication.kt +++ b/app/src/main/java/com/wbrawner/simplemarkdown/MarkdownApplication.kt @@ -2,12 +2,17 @@ package com.wbrawner.simplemarkdown import android.app.Application import android.os.StrictMode +import com.wbrawner.simplemarkdown.utility.CrashlyticsErrorHandler +import com.wbrawner.simplemarkdown.utility.ErrorHandler import com.wbrawner.simplemarkdown.viewmodel.MarkdownViewModelFactory class MarkdownApplication : Application() { val viewModelFactory: MarkdownViewModelFactory by lazy { MarkdownViewModelFactory() } + val errorHandler: ErrorHandler by lazy { + CrashlyticsErrorHandler() + } override fun onCreate() { if (BuildConfig.DEBUG) { diff --git a/app/src/main/java/com/wbrawner/simplemarkdown/utility/ErrorHandler.kt b/app/src/main/java/com/wbrawner/simplemarkdown/utility/ErrorHandler.kt index c83c53a..63f5e43 100644 --- a/app/src/main/java/com/wbrawner/simplemarkdown/utility/ErrorHandler.kt +++ b/app/src/main/java/com/wbrawner/simplemarkdown/utility/ErrorHandler.kt @@ -1,14 +1,15 @@ package com.wbrawner.simplemarkdown.utility import android.content.Context +import android.util.Log import com.crashlytics.android.Crashlytics -import io.fabric.sdk.android.BuildConfig +import com.wbrawner.simplemarkdown.BuildConfig import io.fabric.sdk.android.Fabric import java.util.concurrent.atomic.AtomicBoolean interface ErrorHandler { fun init(context: Context) - fun reportException(t: Throwable) + fun reportException(t: Throwable, message: String? = null) } class CrashlyticsErrorHandler : ErrorHandler { @@ -20,8 +21,13 @@ class CrashlyticsErrorHandler : ErrorHandler { } } - override fun reportException(t: Throwable) { - if (!isInitialized.get() || BuildConfig.DEBUG) return + override fun reportException(t: Throwable, message: String?) { + @Suppress("ConstantConditionIf") + if (BuildConfig.DEBUG) { + Log.e("CrashlyticsErrorHandler", "Caught exception: $message", t) + return + } + if (!isInitialized.get()) return Crashlytics.logException(t) } } \ No newline at end of file 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 ef410f9..8a6fcfd 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 @@ -8,7 +8,6 @@ import android.content.res.Configuration import android.net.Uri import android.os.Build import android.os.Bundle -import android.preference.PreferenceManager import android.view.Menu import android.view.MenuItem import android.view.View @@ -20,25 +19,19 @@ import androidx.core.app.ActivityCompat import androidx.core.content.ContextCompat import androidx.lifecycle.Observer import androidx.lifecycle.ViewModelProviders +import androidx.preference.PreferenceManager import com.wbrawner.simplemarkdown.MarkdownApplication 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.fragment.MainMenuFragment import com.wbrawner.simplemarkdown.viewmodel.MarkdownViewModel import kotlinx.android.synthetic.main.activity_main.* import kotlinx.coroutines.* import java.io.File -import java.io.FileInputStream -import javax.inject.Inject import kotlin.coroutines.CoroutineContext class MainActivity : AppCompatActivity(), ActivityCompat.OnRequestPermissionsResultCallback, CoroutineScope { - @Inject - lateinit var errorHandler: ErrorHandler private var shouldAutoSave = true override val coroutineContext: CoroutineContext = Dispatchers.Main private lateinit var viewModel: MarkdownViewModel @@ -47,6 +40,8 @@ class MainActivity : AppCompatActivity(), ActivityCompat.OnRequestPermissionsRes super.onCreate(savedInstanceState) setContentView(R.layout.activity_main) setSupportActionBar(toolbar) + supportActionBar?.setHomeAsUpIndicator(R.drawable.ic_menu_black_24dp) + supportActionBar?.setDisplayHomeAsUpEnabled(true) window.decorView.apply { systemUiVisibility = ( 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 // 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)) + val fileUri = Uri.fromFile(File(filesDir, viewModel.fileName.value!!)) if (viewModel.save(this@MainActivity, fileUri)) { fileUri } else { @@ -118,6 +113,13 @@ class MainActivity : AppCompatActivity(), ActivityCompat.OnRequestPermissionsRes override fun onOptionsItemSelected(item: MenuItem): Boolean { when (item.itemId) { + android.R.id.home -> { + MainMenuFragment() + .apply { + errorHandler = (application as MarkdownApplication).errorHandler + } + .show(supportFragmentManager, null) + } R.id.action_save -> { launch { if (!viewModel.save(this@MainActivity)) { @@ -149,50 +151,10 @@ class MainActivity : AppCompatActivity(), ActivityCompat.OnRequestPermissionsRes item.isChecked = !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) } - 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( requestCode: Int, permissions: Array, 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 index 9951497..a373061 100644 --- a/app/src/main/java/com/wbrawner/simplemarkdown/view/activity/SupportActivity.kt +++ b/app/src/main/java/com/wbrawner/simplemarkdown/view/activity/SupportActivity.kt @@ -5,6 +5,7 @@ import android.content.Intent import android.net.Uri import android.os.Bundle import android.view.MenuItem +import android.view.View import android.widget.Toast import androidx.appcompat.app.AppCompatActivity import androidx.browser.customtabs.CustomTabsIntent @@ -21,6 +22,13 @@ class SupportActivity : AppCompatActivity(), BillingClientStateListener, Purchas super.onCreate(savedInstanceState) setContentView(R.layout.activity_support) 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) supportActionBar?.setDisplayHomeAsUpEnabled(true) billingClient = BillingClient.newBuilder(applicationContext) 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 new file mode 100644 index 0000000..bedf810 --- /dev/null +++ b/app/src/main/java/com/wbrawner/simplemarkdown/view/fragment/MainMenuFragment.kt @@ -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() + } + } +} \ No newline at end of file diff --git a/app/src/main/res/drawable/ic_eye_black_24dp.xml b/app/src/main/res/drawable/ic_eye_black_24dp.xml new file mode 100644 index 0000000..64e7429 --- /dev/null +++ b/app/src/main/res/drawable/ic_eye_black_24dp.xml @@ -0,0 +1,9 @@ + + + diff --git a/app/src/main/res/drawable/ic_help_black_24dp.xml b/app/src/main/res/drawable/ic_help_black_24dp.xml new file mode 100644 index 0000000..b1d7a2c --- /dev/null +++ b/app/src/main/res/drawable/ic_help_black_24dp.xml @@ -0,0 +1,9 @@ + + + diff --git a/app/src/main/res/drawable/ic_info_black_24dp.xml b/app/src/main/res/drawable/ic_info_black_24dp.xml new file mode 100644 index 0000000..0cbb996 --- /dev/null +++ b/app/src/main/res/drawable/ic_info_black_24dp.xml @@ -0,0 +1,9 @@ + + + diff --git a/app/src/main/res/drawable/ic_settings_black_24dp.xml b/app/src/main/res/drawable/ic_settings_black_24dp.xml new file mode 100644 index 0000000..a671514 --- /dev/null +++ b/app/src/main/res/drawable/ic_settings_black_24dp.xml @@ -0,0 +1,9 @@ + + + diff --git a/app/src/main/res/layout/activity_support.xml b/app/src/main/res/layout/activity_support.xml index e2d812b..3e3eeb9 100644 --- a/app/src/main/res/layout/activity_support.xml +++ b/app/src/main/res/layout/activity_support.xml @@ -7,7 +7,7 @@ + \ No newline at end of file diff --git a/app/src/main/res/menu/menu_edit.xml b/app/src/main/res/menu/menu_edit.xml index 0c70280..f293b24 100644 --- a/app/src/main/res/menu/menu_edit.xml +++ b/app/src/main/res/menu/menu_edit.xml @@ -25,20 +25,4 @@ android:checkable="true" android:title="@string/action_lock_swipe" app:showAsAction="never" /> - - - - \ No newline at end of file diff --git a/app/src/main/res/menu/menu_main.xml b/app/src/main/res/menu/menu_main.xml new file mode 100644 index 0000000..dfe9495 --- /dev/null +++ b/app/src/main/res/menu/menu_main.xml @@ -0,0 +1,33 @@ + + + + + + + + + + + + \ No newline at end of file