Migrate some classes to Kotlin

This commit is contained in:
Billy Brawner 2019-05-17 18:17:14 -07:00 committed by William Brawner
parent e937c59651
commit 7992b80787
13 changed files with 520 additions and 628 deletions

View file

@ -33,6 +33,7 @@ android {
versionCode 18 versionCode 18
versionName "0.6.0" versionName "0.6.0"
testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner" testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"
buildConfigField "boolean", "ENABLE_CUSTOM_CSS", "true"
} }
signingConfigs { signingConfigs {
release { release {
@ -47,6 +48,7 @@ android {
minifyEnabled true minifyEnabled true
proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro' proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
signingConfig signingConfigs.release signingConfig signingConfigs.release
buildConfigField "boolean", "ENABLE_CUSTOM_CSS", "false"
} }
} }
flavorDimensions "platform" flavorDimensions "platform"
@ -62,8 +64,6 @@ android {
dependencies { dependencies {
def lifecycle_version = "2.0.0" def lifecycle_version = "2.0.0"
annotationProcessor 'com.google.dagger:dagger-compiler:2.21'
annotationProcessor 'com.jakewharton:butterknife-compiler:8.7.0'
testImplementation 'junit:junit:4.12' testImplementation 'junit:junit:4.12'
testImplementation 'org.robolectric:robolectric:4.2' testImplementation 'org.robolectric:robolectric:4.2'
implementation fileTree(include: ['*.jar'], dir: 'libs') implementation fileTree(include: ['*.jar'], dir: 'libs')
@ -79,19 +79,21 @@ dependencies {
implementation 'com.google.android.material:material:1.0.0' implementation 'com.google.android.material:material:1.0.0'
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.21' implementation 'com.google.dagger:dagger:2.22.1'
implementation 'com.jakewharton:butterknife:8.8.1' annotationProcessor 'com.google.dagger:dagger-compiler:2.22.1'
kapt 'com.google.dagger:dagger-android-processor:2.22.1'
kapt 'com.google.dagger:dagger-compiler:2.22.1'
implementation 'com.jakewharton.rxbinding2:rxbinding:2.0.0' implementation 'com.jakewharton.rxbinding2:rxbinding:2.0.0'
implementation 'com.jakewharton.rxbinding2:rxbinding-design:2.0.0' implementation 'com.jakewharton.rxbinding2:rxbinding-design:2.0.0'
implementation 'io.reactivex.rxjava2:rxandroid:2.1.1' implementation 'io.reactivex.rxjava2:rxandroid:2.1.1'
implementation 'io.reactivex.rxjava2:rxjava:2.2.7' implementation 'io.reactivex.rxjava2:rxjava:2.2.7'
implementation 'com.google.firebase:firebase-core:16.0.8' implementation 'com.google.firebase:firebase-core:16.0.9'
implementation 'com.android.billingclient:billing:1.2' implementation 'com.android.billingclient:billing:1.2'
implementation 'com.crashlytics.sdk.android:crashlytics:2.9.9' implementation 'com.crashlytics.sdk.android:crashlytics:2.10.0'
implementation 'androidx.multidex:multidex:2.0.1' implementation 'androidx.multidex:multidex:2.0.1'
implementation "androidx.lifecycle:lifecycle-viewmodel-ktx:$lifecycle_version" implementation "androidx.lifecycle:lifecycle-viewmodel-ktx:$lifecycle_version"
kapt "androidx.lifecycle:lifecycle-compiler:$lifecycle_version" kapt "androidx.lifecycle:lifecycle-compiler:$lifecycle_version"
compile "androidx.core:core-ktx:1.0.1" compile "androidx.core:core-ktx:1.0.2"
implementation "org.jetbrains.kotlin:kotlin-stdlib-jdk7:$kotlin_version" implementation "org.jetbrains.kotlin:kotlin-stdlib-jdk7:$kotlin_version"
implementation 'androidx.lifecycle:lifecycle-extensions:2.0.0' implementation 'androidx.lifecycle:lifecycle-extensions:2.0.0'
} }

View file

@ -1,281 +0,0 @@
package com.wbrawner.simplemarkdown.view.activity;
import android.Manifest;
import android.content.Intent;
import android.content.pm.PackageManager;
import android.content.res.AssetManager;
import android.content.res.Configuration;
import android.os.Build;
import android.os.Bundle;
import android.view.Menu;
import android.view.MenuItem;
import android.view.View;
import android.widget.Toast;
import androidx.annotation.NonNull;
import androidx.appcompat.app.AppCompatActivity;
import androidx.core.app.ActivityCompat;
import androidx.lifecycle.ViewModelProviders;
import com.crashlytics.android.Crashlytics;
import com.google.android.material.tabs.TabLayout;
import com.wbrawner.simplemarkdown.MarkdownApplication;
import com.wbrawner.simplemarkdown.R;
import com.wbrawner.simplemarkdown.presentation.MarkdownPresenter;
import com.wbrawner.simplemarkdown.utility.Constants;
import com.wbrawner.simplemarkdown.utility.Utils;
import com.wbrawner.simplemarkdown.view.DisableableViewPager;
import com.wbrawner.simplemarkdown.view.adapter.EditPagerAdapter;
import java.io.File;
import java.io.InputStream;
import javax.inject.Inject;
import butterknife.BindView;
import butterknife.ButterKnife;
public class MainActivity extends AppCompatActivity
implements ActivityCompat.OnRequestPermissionsResultCallback {
@Inject
MarkdownPresenter presenter;
@BindView(R.id.pager)
DisableableViewPager pager;
@BindView(R.id.layout_tab)
TabLayout tabLayout;
private boolean shouldAutoSave = true;
private NewFileHandler newFileHandler;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
((MarkdownApplication) getApplication()).getComponent().inject(this);
ButterKnife.bind(this);
pager.setAdapter(
new EditPagerAdapter(getSupportFragmentManager(), MainActivity.this)
);
pager.setPageMargin(1);
pager.setPageMarginDrawable(R.color.colorAccent);
if (getResources().getConfiguration().orientation
== Configuration.ORIENTATION_LANDSCAPE) {
tabLayout.setVisibility(View.GONE);
}
newFileHandler = new NewFileHandler();
if (getIntent().getBooleanExtra(Constants.EXTRA_EXPLORER, false)) {
requestFileOp(Constants.REQUEST_OPEN_FILE);
}
}
@Override
protected void onUserLeaveHint() {
super.onUserLeaveHint();
if (shouldAutoSave && !presenter.getMarkdown().isEmpty() && Utils.isAutosaveEnabled(this)) {
presenter.saveMarkdown(null, null);
}
}
@Override
public void onConfigurationChanged(Configuration newConfig) {
super.onConfigurationChanged(newConfig);
if (newConfig.orientation == Configuration.ORIENTATION_LANDSCAPE)
tabLayout.setVisibility(View.GONE);
else
tabLayout.setVisibility(View.VISIBLE);
}
@Override
public boolean onCreateOptionsMenu(Menu menu) {
getMenuInflater().inflate(R.menu.menu_edit, menu);
return true;
}
@Override
public boolean onOptionsItemSelected(MenuItem item) {
switch (item.getItemId()) {
case R.id.action_save:
requestFileOp(Constants.REQUEST_SAVE_FILE);
break;
case R.id.action_share:
Intent shareIntent = new Intent(Intent.ACTION_SEND);
shareIntent.putExtra(Intent.EXTRA_TEXT, presenter.getMarkdown());
shareIntent.setType("text/plain");
startActivity(Intent.createChooser(
shareIntent,
getString(R.string.share_file)
));
break;
case R.id.action_load:
requestFileOp(Constants.REQUEST_OPEN_FILE);
break;
case R.id.action_new:
presenter.saveMarkdown(newFileHandler, null);
break;
case R.id.action_lock_swipe:
item.setChecked(!item.isChecked());
pager.setSwipeLocked(item.isChecked());
break;
case R.id.action_help:
showInfoActivity(R.id.action_help);
break;
case R.id.action_settings:
Intent settingsIntent = new Intent(MainActivity.this, SettingsActivity.class);
startActivity(settingsIntent);
break;
case R.id.action_libraries:
showInfoActivity(R.id.action_libraries);
break;
case R.id.action_privacy:
showInfoActivity(R.id.action_privacy);
break;
}
return super.onOptionsItemSelected(item);
}
private void showInfoActivity(int action) {
Intent infoIntent = new Intent(MainActivity.this, MarkdownInfoActivity.class);
String fileName = "";
String title = "";
switch (action) {
case R.id.action_help:
fileName = "Cheatsheet.md";
title = getString(R.string.action_help);
break;
case R.id.action_libraries:
fileName = "Libraries.md";
title = getString(R.string.action_libraries);
break;
case R.id.action_privacy:
fileName = "Privacy Policy.md";
title = getString(R.string.action_privacy);
break;
}
infoIntent.putExtra("title", title);
InputStream in = null;
try {
AssetManager assetManager = getAssets();
if (assetManager != null) {
in = assetManager.open(fileName);
}
presenter.loadMarkdown(fileName, in, new MarkdownPresenter.OnTempFileLoadedListener() {
@Override
public void onSuccess(String html) {
infoIntent.putExtra("html", html);
startActivity(infoIntent);
}
@Override
public void onError() {
Toast.makeText(MainActivity.this, R.string.file_load_error, Toast.LENGTH_SHORT)
.show();
}
});
} catch (Exception e) {
Crashlytics.logException(e);
Toast.makeText(MainActivity.this, R.string.file_load_error, Toast.LENGTH_SHORT).show();
}
}
@Override
public void onRequestPermissionsResult(
int requestCode,
@NonNull String permissions[],
@NonNull int[] grantResults
) {
switch (requestCode) {
case Constants.REQUEST_SAVE_FILE:
case Constants.REQUEST_OPEN_FILE: {
// If request is cancelled, the result arrays are empty.
if (grantResults.length > 0
&& grantResults[0] == PackageManager.PERMISSION_GRANTED) {
// Permission granted, open file save dialog
requestFileOp(requestCode);
} else {
// Permission denied, do nothing
Toast.makeText(MainActivity.this, R.string.no_permissions, Toast.LENGTH_SHORT)
.show();
}
break;
}
}
}
@Override
public void onBackPressed() {
if (pager.getCurrentItem() == EditPagerAdapter.FRAGMENT_EDIT)
super.onBackPressed();
else
pager.setCurrentItem(EditPagerAdapter.FRAGMENT_EDIT);
}
@Override
protected void onActivityResult(int requestCode, int resultCode, Intent data) {
switch (requestCode) {
case Constants.REQUEST_OPEN_FILE:
if (resultCode != RESULT_OK || data == null || !data.hasExtra(Constants.EXTRA_FILE)) {
break;
}
File markdownFile = (File) data.getSerializableExtra(Constants.EXTRA_FILE);
presenter.loadMarkdown(markdownFile);
break;
case Constants.REQUEST_SAVE_FILE:
if (resultCode != RESULT_OK
|| data == null
|| !data.hasExtra(Constants.EXTRA_FILE_PATH)
|| data.getStringExtra(Constants.EXTRA_FILE_PATH).isEmpty()) {
break;
}
String path = data.getStringExtra(Constants.EXTRA_FILE_PATH);
presenter.saveMarkdown(null, path);
break;
}
super.onActivityResult(requestCode, resultCode, data);
}
private void requestFileOp(int requestType) {
if (Utils.canAccessFiles(MainActivity.this)) {
// If the user is going to save the file, we don't want to auto-save it for them
shouldAutoSave = false;
Intent intent = new Intent(MainActivity.this, ExplorerActivity.class);
intent.putExtra(Constants.EXTRA_REQUEST_CODE, requestType);
intent.putExtra(Constants.EXTRA_FILE, presenter.getFile());
startActivityForResult(
intent,
requestType
);
} else if (Build.VERSION.SDK_INT >= 23) {
requestPermissions(
new String[]{Manifest.permission.WRITE_EXTERNAL_STORAGE},
requestType
);
}
}
@Override
protected void onResume() {
super.onResume();
setTitle(presenter.getFileName());
shouldAutoSave = true;
}
private class NewFileHandler implements MarkdownPresenter.MarkdownSavedListener {
@Override
public void saveComplete(boolean success) {
if (success) {
String newFile = Utils.getDefaultFileName(MainActivity.this);
presenter.newFile(newFile);
} else {
Toast.makeText(
MainActivity.this,
R.string.file_save_error,
Toast.LENGTH_SHORT
).show();
}
}
}
}

View file

@ -0,0 +1,235 @@
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.os.Build
import android.os.Bundle
import android.view.Menu
import android.view.MenuItem
import android.view.View
import android.widget.Toast
import androidx.appcompat.app.AppCompatActivity
import androidx.core.app.ActivityCompat
import com.crashlytics.android.Crashlytics
import com.wbrawner.simplemarkdown.MarkdownApplication
import com.wbrawner.simplemarkdown.R
import com.wbrawner.simplemarkdown.presentation.MarkdownPresenter
import com.wbrawner.simplemarkdown.utility.Constants
import com.wbrawner.simplemarkdown.utility.Utils
import com.wbrawner.simplemarkdown.view.adapter.EditPagerAdapter
import kotlinx.android.synthetic.main.activity_main.*
import java.io.File
import java.io.InputStream
import javax.inject.Inject
class MainActivity : AppCompatActivity(), ActivityCompat.OnRequestPermissionsResultCallback {
@Inject
lateinit var presenter: MarkdownPresenter
private var shouldAutoSave = true
private var newFileHandler: NewFileHandler? = null
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
(application as MarkdownApplication).component.inject(this)
pager.adapter = EditPagerAdapter(supportFragmentManager, this@MainActivity)
pager.pageMargin = 1
pager.setPageMarginDrawable(R.color.colorAccent)
if (resources.configuration.orientation == Configuration.ORIENTATION_LANDSCAPE) {
tabLayout!!.visibility = View.GONE
}
newFileHandler = NewFileHandler()
if (intent.getBooleanExtra(Constants.EXTRA_EXPLORER, false)) {
requestFileOp(Constants.REQUEST_OPEN_FILE)
}
}
override fun onUserLeaveHint() {
super.onUserLeaveHint()
if (shouldAutoSave && !presenter!!.markdown.isEmpty() && Utils.isAutosaveEnabled(this)) {
presenter!!.saveMarkdown(null, null)
}
}
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) {
R.id.action_save -> requestFileOp(Constants.REQUEST_SAVE_FILE)
R.id.action_share -> {
val shareIntent = Intent(Intent.ACTION_SEND)
shareIntent.putExtra(Intent.EXTRA_TEXT, presenter!!.markdown)
shareIntent.type = "text/plain"
startActivity(Intent.createChooser(
shareIntent,
getString(R.string.share_file)
))
}
R.id.action_load -> requestFileOp(Constants.REQUEST_OPEN_FILE)
R.id.action_new -> presenter!!.saveMarkdown(newFileHandler, null)
R.id.action_lock_swipe -> {
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)
startActivity(settingsIntent)
}
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)
var `in`: InputStream? = null
try {
val assetManager = assets
if (assetManager != null) {
`in` = assetManager.open(fileName)
}
presenter!!.loadMarkdown(fileName, `in`, object : MarkdownPresenter.OnTempFileLoadedListener {
override fun onSuccess(html: String) {
infoIntent.putExtra("html", html)
startActivity(infoIntent)
}
override fun onError() {
Toast.makeText(this@MainActivity, R.string.file_load_error, Toast.LENGTH_SHORT)
.show()
}
})
} catch (e: Exception) {
Crashlytics.logException(e)
Toast.makeText(this@MainActivity, R.string.file_load_error, Toast.LENGTH_SHORT).show()
}
}
override fun onRequestPermissionsResult(
requestCode: Int,
permissions: Array<String>,
grantResults: IntArray
) {
when (requestCode) {
Constants.REQUEST_SAVE_FILE, Constants.REQUEST_OPEN_FILE -> {
// If request is cancelled, the result arrays are empty.
if (grantResults.size > 0 && 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) {
Constants.REQUEST_OPEN_FILE -> {
if (resultCode != Activity.RESULT_OK || data == null || !data.hasExtra(Constants.EXTRA_FILE)) {
return
}
val markdownFile = data.getSerializableExtra(Constants.EXTRA_FILE) as File
presenter!!.loadMarkdown(markdownFile)
}
Constants.REQUEST_SAVE_FILE -> {
if (resultCode != Activity.RESULT_OK
|| data == null
|| !data.hasExtra(Constants.EXTRA_FILE_PATH)
|| data.getStringExtra(Constants.EXTRA_FILE_PATH).isEmpty()) {
return
}
val path = data.getStringExtra(Constants.EXTRA_FILE_PATH)
presenter!!.saveMarkdown(null, path)
}
}
super.onActivityResult(requestCode, resultCode, data)
}
private fun requestFileOp(requestType: Int) {
if (Utils.canAccessFiles(this@MainActivity)) {
// If the user is going to save the file, we don't want to auto-save it for them
shouldAutoSave = false
val intent = Intent(this@MainActivity, ExplorerActivity::class.java)
intent.putExtra(Constants.EXTRA_REQUEST_CODE, requestType)
intent.putExtra(Constants.EXTRA_FILE, presenter!!.file)
startActivityForResult(
intent,
requestType
)
} else if (Build.VERSION.SDK_INT >= 23) {
requestPermissions(
arrayOf(Manifest.permission.WRITE_EXTERNAL_STORAGE),
requestType
)
}
}
override fun onResume() {
super.onResume()
title = presenter!!.fileName
shouldAutoSave = true
}
private inner class NewFileHandler : MarkdownPresenter.MarkdownSavedListener {
override fun saveComplete(success: Boolean) {
if (success) {
val newFile = Utils.getDefaultFileName(this@MainActivity)
presenter!!.newFile(newFile)
} else {
Toast.makeText(
this@MainActivity,
R.string.file_save_error,
Toast.LENGTH_SHORT
).show()
}
}
}
}

View file

@ -1,59 +0,0 @@
package com.wbrawner.simplemarkdown.view.activity;
import android.content.Intent;
import android.os.Bundle;
import android.view.MenuItem;
import android.webkit.WebView;
import androidx.appcompat.app.ActionBar;
import androidx.appcompat.app.AppCompatActivity;
import com.wbrawner.simplemarkdown.R;
import butterknife.BindView;
import butterknife.ButterKnife;
public class MarkdownInfoActivity extends AppCompatActivity {
public static String FORMAT_CSS = "<style>" +
"%s" +
"</style>";
@BindView(R.id.info_webview)
WebView infoWebview;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_markdown_info);
ButterKnife.bind(this);
ActionBar supportActionBar = getSupportActionBar();
if (supportActionBar != null) {
supportActionBar.setDisplayHomeAsUpEnabled(true);
}
Intent intent = getIntent();
if (intent == null || !intent.hasExtra("title") || !intent.hasExtra("html")) {
finish();
return;
}
setTitle(intent.getStringExtra("title"));
infoWebview.loadDataWithBaseURL(
null,
String.format(FORMAT_CSS,
getString(R.string.pref_custom_css_default)
) + intent.getStringExtra("html"),
"text/html",
"UTF-8",
null
);
}
@Override
public boolean onOptionsItemSelected(MenuItem item) {
if (item.getItemId() == android.R.id.home) {
onBackPressed();
return true;
}
return super.onOptionsItemSelected(item);
}
}

View file

@ -0,0 +1,45 @@
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 kotlinx.android.synthetic.main.activity_markdown_info.*
class MarkdownInfoActivity : AppCompatActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_markdown_info)
val supportActionBar = supportActionBar
supportActionBar?.setDisplayHomeAsUpEnabled(true)
val intent = intent
if (intent == null || !intent.hasExtra("title") || !intent.hasExtra("html")) {
finish()
return
}
title = intent.getStringExtra("title")
infoWebview.loadDataWithBaseURL(null,
String.format(FORMAT_CSS,
getString(R.string.pref_custom_css_default)
) + intent.getStringExtra("html"),
"text/html",
"UTF-8", null
)
}
override fun onOptionsItemSelected(item: MenuItem): Boolean {
if (item.itemId == android.R.id.home) {
onBackPressed()
return true
}
return super.onOptionsItemSelected(item)
}
companion object {
var FORMAT_CSS = "<style>" +
"%s" +
"</style>"
}
}

View file

@ -1,154 +0,0 @@
package com.wbrawner.simplemarkdown.view.fragment;
import android.annotation.SuppressLint;
import android.app.Activity;
import android.content.Context;
import android.os.Bundle;
import android.view.LayoutInflater;
import android.view.MotionEvent;
import android.view.View;
import android.view.ViewGroup;
import android.view.inputmethod.InputMethodManager;
import android.widget.EditText;
import android.widget.ScrollView;
import android.widget.Toast;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import androidx.fragment.app.Fragment;
import com.jakewharton.rxbinding2.widget.RxTextView;
import com.wbrawner.simplemarkdown.MarkdownApplication;
import com.wbrawner.simplemarkdown.R;
import com.wbrawner.simplemarkdown.presentation.MarkdownPresenter;
import com.wbrawner.simplemarkdown.utility.MarkdownObserver;
import com.wbrawner.simplemarkdown.utility.Utils;
import com.wbrawner.simplemarkdown.view.MarkdownEditView;
import java.util.concurrent.TimeUnit;
import javax.inject.Inject;
import butterknife.BindView;
import butterknife.ButterKnife;
import butterknife.Unbinder;
import io.reactivex.Observable;
import io.reactivex.android.schedulers.AndroidSchedulers;
import io.reactivex.schedulers.Schedulers;
public class EditFragment extends Fragment implements MarkdownEditView {
@Inject
MarkdownPresenter presenter;
@BindView(R.id.markdown_edit)
EditText markdownEditor;
@BindView(R.id.markdown_edit_container)
ScrollView markdownEditorScroller;
private Unbinder unbinder;
private int lastScrollEvent = -1;
public EditFragment() {
// Required empty public constructor
}
@SuppressLint("ClickableViewAccessibility")
@Override
public View onCreateView(@NonNull LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState) {
// Inflate the layout for this fragment
View view = inflater.inflate(R.layout.fragment_edit, container, false);
unbinder = ButterKnife.bind(this, view);
Activity activity = getActivity();
if (activity != null) {
((MarkdownApplication) activity.getApplication()).getComponent().inject(this);
}
Observable<String> obs = RxTextView.textChanges(markdownEditor)
.debounce(50, TimeUnit.MILLISECONDS)
.map(CharSequence::toString)
.subscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread());
obs.subscribe(new MarkdownObserver(presenter, obs));
return view;
}
@Override
public void onViewCreated(@NonNull View view, @Nullable Bundle savedInstanceState) {
super.onViewCreated(view, savedInstanceState);
presenter.setEditView(EditFragment.this);
presenter.loadMarkdown();
//noinspection AndroidLintClickableViewAccessibility
markdownEditorScroller.setOnTouchListener((v, event) -> {
// The focus should only be set if this was a click, and not a scroll
if (lastScrollEvent == MotionEvent.ACTION_DOWN && event.getAction() == MotionEvent.ACTION_UP) {
if (getActivity() == null) {
return false;
}
InputMethodManager imm = (InputMethodManager) getActivity()
.getSystemService(Context.INPUT_METHOD_SERVICE);
if (imm == null) {
return false;
}
imm.showSoftInput(markdownEditor, InputMethodManager.SHOW_IMPLICIT);
markdownEditor.requestFocus();
}
lastScrollEvent = event.getAction();
return false;
});
}
@Override
public void onResume() {
super.onResume();
presenter.setEditView(this);
setMarkdown(presenter.getMarkdown());
}
@Override
public void onPause() {
super.onPause();
presenter.setEditView(null);
}
@Override
public String getMarkdown() {
return markdownEditor.getText().toString();
}
@Override
public void setMarkdown(String markdown) {
markdownEditor.setText(markdown);
}
@Override
public void setTitle(String title) {
Activity activity = getActivity();
if (activity != null) {
activity.setTitle(title);
}
}
@Override
public void onFileSaved(boolean success) {
String location = Utils.getDocsPath(getActivity()) + presenter.getFileName();
String message;
if (success) {
message = getString(R.string.file_saved, location);
} else {
message = getString(R.string.file_save_error);
}
Toast.makeText(getActivity(), message, Toast.LENGTH_SHORT).show();
}
@Override
public void onFileLoaded(boolean success) {
// TODO: Investigate why this fires off so often
// int message = success ? R.string.file_loaded : R.string.file_load_error;
// Toast.makeText(getActivity(), message, Toast.LENGTH_SHORT).show();
}
@Override
public void onDestroyView() {
super.onDestroyView();
unbinder.unbind();
}
}

View file

@ -0,0 +1,120 @@
package com.wbrawner.simplemarkdown.view.fragment
import android.annotation.SuppressLint
import android.content.Context
import android.os.Bundle
import android.view.LayoutInflater
import android.view.MotionEvent
import android.view.View
import android.view.ViewGroup
import android.view.inputmethod.InputMethodManager
import android.widget.EditText
import android.widget.ScrollView
import android.widget.Toast
import androidx.fragment.app.Fragment
import com.jakewharton.rxbinding2.widget.RxTextView
import com.wbrawner.simplemarkdown.MarkdownApplication
import com.wbrawner.simplemarkdown.R
import com.wbrawner.simplemarkdown.presentation.MarkdownPresenter
import com.wbrawner.simplemarkdown.utility.MarkdownObserver
import com.wbrawner.simplemarkdown.utility.Utils
import com.wbrawner.simplemarkdown.view.MarkdownEditView
import io.reactivex.android.schedulers.AndroidSchedulers
import io.reactivex.schedulers.Schedulers
import java.util.concurrent.TimeUnit
import javax.inject.Inject
class EditFragment : Fragment(), MarkdownEditView {
@Inject
lateinit var presenter: MarkdownPresenter
private var markdownEditor: EditText? = null
private var markdownEditorScroller: ScrollView? = null
private var lastScrollEvent = -1
@SuppressLint("ClickableViewAccessibility")
override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?,
savedInstanceState: Bundle?): View? {
// Inflate the layout for this fragment
val view = inflater.inflate(R.layout.fragment_edit, container, false)
markdownEditor = view.findViewById(R.id.markdown_edit)
markdownEditorScroller = view.findViewById(R.id.markdown_edit_container)
val activity = activity
if (activity != null) {
(activity.application as MarkdownApplication).component.inject(this)
}
val obs = RxTextView.textChanges(markdownEditor!!)
.debounce(50, TimeUnit.MILLISECONDS)
.map { it.toString() }
.subscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread())
obs.subscribe(MarkdownObserver(presenter, obs))
return view
}
@SuppressLint("ClickableViewAccessibility")
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState)
presenter!!.setEditView(this@EditFragment)
presenter!!.loadMarkdown()
markdownEditorScroller!!.setOnTouchListener { v, event ->
// The focus should only be set if this was a click, and not a scroll
if (lastScrollEvent == MotionEvent.ACTION_DOWN && event.action == MotionEvent.ACTION_UP) {
if (activity == null) {
return@setOnTouchListener false
}
val imm = activity
?.getSystemService(Context.INPUT_METHOD_SERVICE) as? InputMethodManager
?: return@setOnTouchListener false
imm.showSoftInput(markdownEditor, InputMethodManager.SHOW_IMPLICIT)
markdownEditor!!.requestFocus()
}
lastScrollEvent = event.action
false
}
}
override fun onResume() {
super.onResume()
presenter!!.setEditView(this)
markdown = presenter!!.markdown
}
override fun onPause() {
super.onPause()
presenter!!.setEditView(null)
}
override fun getMarkdown(): String {
return markdownEditor!!.text.toString()
}
override fun setMarkdown(markdown: String) {
markdownEditor!!.setText(markdown)
}
override fun setTitle(title: String) {
val activity = activity
if (activity != null) {
activity.title = title
}
}
override fun onFileSaved(success: Boolean) {
val location = Utils.getDocsPath(activity) + presenter!!.fileName
val message: String
message = if (success) {
getString(R.string.file_saved, location)
} else {
getString(R.string.file_save_error)
}
Toast.makeText(activity, message, Toast.LENGTH_SHORT).show()
}
override fun onFileLoaded(success: Boolean) {
// TODO: Investigate why this fires off so often
// int message = success ? R.string.file_loaded : R.string.file_load_error;
// Toast.makeText(getActivity(), message, Toast.LENGTH_SHORT).show();
}
}// Required empty public constructor

View file

@ -1,118 +0,0 @@
package com.wbrawner.simplemarkdown.view.fragment;
import android.app.Activity;
import android.content.SharedPreferences;
import android.os.Bundle;
import android.preference.PreferenceManager;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.webkit.WebView;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import androidx.fragment.app.Fragment;
import com.wbrawner.simplemarkdown.BuildConfig;
import com.wbrawner.simplemarkdown.MarkdownApplication;
import com.wbrawner.simplemarkdown.R;
import com.wbrawner.simplemarkdown.presentation.MarkdownPresenter;
import com.wbrawner.simplemarkdown.view.MarkdownPreviewView;
import javax.inject.Inject;
import butterknife.BindView;
import butterknife.ButterKnife;
import butterknife.Unbinder;
public class PreviewFragment extends Fragment implements MarkdownPreviewView {
private static final String TAG = PreviewFragment.class.getSimpleName();
public static String FORMAT_CSS = "<style>" +
"%s" +
"</style>";
@Inject
MarkdownPresenter presenter;
@BindView(R.id.markdown_view)
WebView markdownPreview;
private Unbinder unbinder;
private SharedPreferences sharedPreferences;
public PreviewFragment() {
// Required empty constructor
}
@Override
public View onCreateView(
@NonNull LayoutInflater inflater,
ViewGroup container,
Bundle savedInstanceState
) {
sharedPreferences = PreferenceManager.getDefaultSharedPreferences(container.getContext());
// Inflate the layout for this fragment
View view = inflater.inflate(R.layout.fragment_preview, container, false);
unbinder = ButterKnife.bind(this, view);
Activity activity = getActivity();
if (activity != null) {
((MarkdownApplication) activity.getApplication()).getComponent().inject(this);
}
if (BuildConfig.DEBUG)
WebView.setWebContentsDebuggingEnabled(true);
return view;
}
@Override
public void onViewCreated(@NonNull View view, @Nullable Bundle savedInstanceState) {
super.onViewCreated(view, savedInstanceState);
}
@Override
public void updatePreview(String html) {
if (markdownPreview == null) {
return;
}
markdownPreview.post(() -> {
if (markdownPreview == null) {
return;
}
String style = String.format(
FORMAT_CSS,
sharedPreferences.getString(
getString(R.string.pref_custom_css),
""
)
);
markdownPreview.loadDataWithBaseURL(
null,
style + html,
"text/html",
"UTF-8",
null
);
});
}
@Override
public void onResume() {
super.onResume();
presenter.setPreviewView(this);
presenter.onMarkdownEdited();
}
@Override
public void onDestroyView() {
if (markdownPreview != null) {
((ViewGroup) markdownPreview.getParent()).removeView(markdownPreview);
markdownPreview.destroy();
}
unbinder.unbind();
super.onDestroyView();
}
@Override
public void onDestroy() {
super.onDestroy();
presenter.setPreviewView(null);
}
}

View file

@ -0,0 +1,98 @@
package com.wbrawner.simplemarkdown.view.fragment
import android.content.SharedPreferences
import android.os.Bundle
import android.preference.PreferenceManager
import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
import android.webkit.WebView
import androidx.fragment.app.Fragment
import com.wbrawner.simplemarkdown.BuildConfig
import com.wbrawner.simplemarkdown.MarkdownApplication
import com.wbrawner.simplemarkdown.R
import com.wbrawner.simplemarkdown.presentation.MarkdownPresenter
import com.wbrawner.simplemarkdown.view.MarkdownPreviewView
import javax.inject.Inject
class PreviewFragment : Fragment(), MarkdownPreviewView {
@Inject
lateinit var presenter: MarkdownPresenter
private var markdownPreview: WebView? = null
private var sharedPreferences: SharedPreferences? = null
override fun onCreateView(
inflater: LayoutInflater,
container: ViewGroup?,
savedInstanceState: Bundle?
): View? {
sharedPreferences = PreferenceManager.getDefaultSharedPreferences(container!!.context)
// Inflate the layout for this fragment
val view = inflater.inflate(R.layout.fragment_preview, container, false)
markdownPreview = view.findViewById(R.id.markdown_view)
val activity = activity
if (activity != null) {
(activity.application as MarkdownApplication).component.inject(this)
}
if (BuildConfig.DEBUG)
WebView.setWebContentsDebuggingEnabled(true)
return view
}
override fun updatePreview(html: String) {
if (markdownPreview == null) {
return
}
markdownPreview!!.post {
if (markdownPreview == null) {
return@post
}
val css: String? = if (!BuildConfig.ENABLE_CUSTOM_CSS) {
sharedPreferences!!.getString(
getString(R.string.pref_custom_css_default),
""
)
} else {
sharedPreferences!!.getString(
getString(R.string.pref_custom_css),
getString(R.string.pref_custom_css_default)
)
}
val style = String.format(FORMAT_CSS, css)
markdownPreview!!.loadDataWithBaseURL(null,
style + html,
"text/html",
"UTF-8", null
)
}
}
override fun onResume() {
super.onResume()
presenter!!.setPreviewView(this)
presenter!!.onMarkdownEdited()
}
override fun onDestroyView() {
markdownPreview?.let {
(it.parent as ViewGroup).removeView(it)
it.destroy()
}
super.onDestroyView()
}
override fun onDestroy() {
super.onDestroy()
presenter!!.setPreviewView(null)
}
companion object {
private val TAG = PreviewFragment::class.java.simpleName
var FORMAT_CSS = "<style>" +
"%s" +
"</style>"
}
}

View file

@ -14,6 +14,7 @@ import android.view.ViewGroup;
import androidx.annotation.Nullable; import androidx.annotation.Nullable;
import com.wbrawner.simplemarkdown.BuildConfig;
import com.wbrawner.simplemarkdown.R; import com.wbrawner.simplemarkdown.R;
import com.wbrawner.simplemarkdown.utility.Constants; import com.wbrawner.simplemarkdown.utility.Constants;
import com.wbrawner.simplemarkdown.utility.Utils; import com.wbrawner.simplemarkdown.utility.Utils;
@ -36,6 +37,9 @@ public class SettingsFragment extends PreferenceFragment
sharedPreferences, sharedPreferences,
findPreference(getString(R.string.key_default_view)) findPreference(getString(R.string.key_default_view))
); );
if (!BuildConfig.ENABLE_CUSTOM_CSS) {
getPreferenceScreen().removePreference(findPreference(getString(R.string.pref_custom_css)));
}
Preference defaultRoot = findPreference(Constants.KEY_DOCS_PATH); Preference defaultRoot = findPreference(Constants.KEY_DOCS_PATH);
defaultRoot.setSummary(Utils.getDocsPath(getActivity())); defaultRoot.setSummary(Utils.getDocsPath(getActivity()));
defaultRoot.setOnPreferenceClickListener((preference) -> { defaultRoot.setOnPreferenceClickListener((preference) -> {

View file

@ -5,19 +5,19 @@
android:layout_height="match_parent"> android:layout_height="match_parent">
<com.google.android.material.tabs.TabLayout <com.google.android.material.tabs.TabLayout
android:id="@+id/layout_tab" android:id="@+id/tabLayout"
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:layout_gravity="top"> android:layout_gravity="top">
<com.google.android.material.tabs.TabItem <com.google.android.material.tabs.TabItem
android:id="@+id/tab_edit" android:id="@+id/editTab"
android:layout_width="0dp" android:layout_width="0dp"
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:text="@string/action_edit" /> android:text="@string/action_edit" />
<com.google.android.material.tabs.TabItem <com.google.android.material.tabs.TabItem
android:id="@+id/tab_preview" android:id="@+id/previewTab"
android:layout_width="0dp" android:layout_width="0dp"
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:text="@string/action_preview" /> android:text="@string/action_preview" />

View file

@ -8,5 +8,5 @@
<WebView <WebView
android:layout_height="match_parent" android:layout_height="match_parent"
android:layout_width="match_parent" android:layout_width="match_parent"
android:id="@+id/info_webview" /> android:id="@+id/infoWebview" />
</FrameLayout> </FrameLayout>

View file

@ -21,10 +21,10 @@
android:summaryOff="@string/pref_error_reports_off" android:summaryOff="@string/pref_error_reports_off"
android:summaryOn="@string/pref_error_reports_on" android:summaryOn="@string/pref_error_reports_on"
android:title="@string/pref_title_error_reports" /> android:title="@string/pref_title_error_reports" />
<!--<EditTextPreference--> <EditTextPreference
<!--android:defaultValue="@string/pref_custom_css_default"--> android:defaultValue="@string/pref_custom_css_default"
<!--android:key="@string/pref_custom_css"--> android:key="@string/pref_custom_css"
<!--android:title="@string/pref_title_custom_css"--> android:summary="@string/pref_description_custom_css"
<!--android:summary="@string/pref_description_custom_css" />--> android:title="@string/pref_title_custom_css" />
</PreferenceScreen> </PreferenceScreen>