diff --git a/app/src/main/java/com/wbrawner/simplemarkdown/EditFragment.java b/app/src/main/java/com/wbrawner/simplemarkdown/EditFragment.java index 8fb7511..26eb1d1 100644 --- a/app/src/main/java/com/wbrawner/simplemarkdown/EditFragment.java +++ b/app/src/main/java/com/wbrawner/simplemarkdown/EditFragment.java @@ -1,5 +1,7 @@ package com.wbrawner.simplemarkdown; +import android.arch.lifecycle.ViewModelProvider; +import android.arch.lifecycle.ViewModelProviders; import android.content.BroadcastReceiver; import android.content.Context; import android.content.Intent; @@ -44,159 +46,29 @@ import static android.content.ContentValues.TAG; public class EditFragment extends Fragment { public static final String SAVE_ACTION = "com.wbrawner.simplemarkdown.ACTION_SAVE"; public static final String LOAD_ACTION = "com.wbrawner.simplemarkdown.ACTION_LOAD"; - private static EditText mMarkdownEditor; - private FileUtils mFileUtils; + private MarkdownViewModel markdownViewModel; @BindView(R.id.markdown_edit) EditText markdownEditor; - private FragmentActivity mContext; - - private File mTmpFile; - private boolean loadTmpFile = true; - public EditFragment() { // Required empty public constructor } - @Override - public void onCreate(@Nullable Bundle savedInstanceState) { - super.onCreate(savedInstanceState); - IntentFilter filter = new IntentFilter(); - filter.addAction(SAVE_ACTION); - filter.addAction(LOAD_ACTION); - LocalBroadcastManager.getInstance(getContext()).registerReceiver( - new EditFragment.MarkdownBroadcastSaveReceiver(), - filter - ); - mContext = getActivity(); - mFileUtils = new FileUtils(mContext); - } - @Override public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) { // Inflate the layout for this fragment View view = inflater.inflate(R.layout.fragment_edit, container, false); ButterKnife.bind(this, view); - mMarkdownEditor = markdownEditor; - if (mMarkdownEditor.requestFocus()) { - getActivity().getWindow().setSoftInputMode(WindowManager.LayoutParams.SOFT_INPUT_STATE_ALWAYS_VISIBLE); - } - - Observable obs = RxTextView.textChanges(mMarkdownEditor) + markdownViewModel = ViewModelProviders.of(getActivity()).get(MarkdownViewModel.class); + Observable obs = RxTextView.textChanges(markdownEditor) .debounce(50, TimeUnit.MILLISECONDS).map(editable -> editable.toString()); obs.subscribeOn(Schedulers.io()); obs.observeOn(AndroidSchedulers.mainThread()); - obs.subscribe(string -> { - Log.d(TAG, "debounced " + string); - updatePreview(mContext); + obs.subscribe(data -> { + markdownViewModel.updateMarkdown(data); }); - return view; } - - @Override - public void onViewCreated(View view, @Nullable Bundle savedInstanceState) { - super.onViewCreated(view, savedInstanceState); - if (getActivity().getIntent().getAction().equals(Intent.ACTION_MAIN)) { - File tmpFile = new File(getActivity().getFilesDir() + "/" + MainActivity.getTempFileName()); - if (tmpFile.exists()) { - FileLoadTask loadTask = new FileLoadTask(mContext, EditFragment.this); - loadTask.execute(FileProvider.getUriForFile(mContext, MainActivity.AUTHORITY, tmpFile)); - } - } - } - - public static void updatePreview(Context context) { - Intent broadcastIntent = new Intent(PreviewFragment.PREVIEW_ACTION); - broadcastIntent.putExtra("markdownData", mMarkdownEditor.getText().toString()); - Layout layout = mMarkdownEditor.getLayout(); - if (layout != null) { - int line = - layout.getLineForOffset(mMarkdownEditor.getSelectionStart()); - int baseline = layout.getLineBaseline(line); - int ascent = layout.getLineAscent(line); - float yPos = (baseline + ascent) * 1.0f; - float yPercent = yPos / mMarkdownEditor.getMeasuredHeight(); - broadcastIntent.putExtra("scrollY", yPercent); - } - LocalBroadcastManager manager = LocalBroadcastManager.getInstance(context); - manager.sendBroadcast(broadcastIntent); - } - - public void save(String data, String filePath) { - // TODO: move this to AsyncTask - if (!mFileUtils.isExternalStorageWriteable()) { - mFileUtils.requestWritePermissions(); - return; - } - if (filePath == null) { - filePath = MainActivity.getFilePath() + MainActivity.getFileName(); - } - FileOutputStream out = null; - try { - Log.d(TAG, "File path: " + filePath); - File tmpFile = new File(filePath); - out = new FileOutputStream(tmpFile); - out.write(data.getBytes()); - } catch (Exception e) { - Log.e(TAG, "Error saving temp file:", e); - } finally { - try { - if (out != null) { - out.close(); - Toast.makeText(mContext, getString(R.string.file_saved, filePath), Toast.LENGTH_SHORT) - .show(); - } - } catch (IOException e) { - Log.e(TAG, "Error closing write stream", e); - } - } - } - - public void save(String data) { - save(data, null); - } - - public void save() { - save(mMarkdownEditor.getText().toString(), null); - } - - @Override - public void onPause() { - save(mMarkdownEditor.getText().toString(), - MainActivity.getTempFilePath() + MainActivity.getFileName()); - super.onPause(); - } - - public void setEditorText(String s) { - mMarkdownEditor.setText(s); - } - - private class MarkdownBroadcastSaveReceiver extends BroadcastReceiver { - @Override - public void onReceive(Context context, Intent intent) { - Log.d(TAG, "Intent received: " + intent.getAction()); - switch (intent.getAction()) { - case SAVE_ACTION: - if (intent.hasExtra("fileName")) { - String fileName = intent.getStringExtra("fileName"); - if (!fileName.contains("/")) { - fileName = MainActivity.getFilePath() + "/" + fileName; - } - if (!fileName.endsWith(".md")) - fileName += ".md"; - save(mMarkdownEditor.getText().toString(), fileName); - } - break; - case LOAD_ACTION: - if (intent.hasExtra("fileUri")) { - FileLoadTask loadTask = new FileLoadTask(mContext, EditFragment.this); - loadTask.execute(Uri.parse(intent.getStringExtra("fileUri"))); - } - break; - } - } - } } diff --git a/app/src/main/java/com/wbrawner/simplemarkdown/FileLoadTask.java b/app/src/main/java/com/wbrawner/simplemarkdown/FileLoadTask.java deleted file mode 100644 index 8d1dc0d..0000000 --- a/app/src/main/java/com/wbrawner/simplemarkdown/FileLoadTask.java +++ /dev/null @@ -1,76 +0,0 @@ -package com.wbrawner.simplemarkdown; - -import android.content.Context; -import android.net.Uri; -import android.os.AsyncTask; -import android.util.Log; -import android.widget.EditText; - -import java.io.BufferedReader; -import java.io.File; -import java.io.FileOutputStream; -import java.io.InputStream; -import java.io.InputStreamReader; - -import static android.content.ContentValues.TAG; - -/** - * Created by billy on 7/25/17. - */ - -public class FileLoadTask extends AsyncTask { - - private Context mContext; - private EditFragment mEditFragment; - - public FileLoadTask(Context context, EditFragment editFragment) { - mContext = context; - mEditFragment = editFragment; - } - - @Override - protected String doInBackground(Uri... uris) { - if (mContext == null) { - return null; - } - BufferedReader reader = null; - StringBuilder sb = new StringBuilder(); - FileOutputStream out = null; - try { - InputStream in = mContext.getContentResolver().openInputStream(uris[0]); - File tmpFile = new File(mContext.getFilesDir() + "/" + MainActivity.getTempFileName()); - if (tmpFile.exists()) - tmpFile.delete(); - out = new FileOutputStream(tmpFile); - reader = new BufferedReader(new InputStreamReader(in)); - String line; - while ((line = reader.readLine()) != null) { - sb.append(line); - sb.append("\r\n"); - out.write(line.getBytes()); - out.write("\r\n".getBytes()); - } - } catch (Exception e) { - Log.e(TAG, "Error opening file:", e); - } finally { - if (reader != null) { - try { - reader.close(); - } catch (Exception e) { - } - } - if (out != null) { - try { - out.close(); - } catch (Exception e) {} - } - } - return sb.toString(); - } - - @Override - protected void onPostExecute(String s) { - super.onPostExecute(s); - mEditFragment.setEditorText(s); - } -} \ No newline at end of file diff --git a/app/src/main/java/com/wbrawner/simplemarkdown/MainActivity.java b/app/src/main/java/com/wbrawner/simplemarkdown/MainActivity.java index 0d9c4c0..52834dc 100644 --- a/app/src/main/java/com/wbrawner/simplemarkdown/MainActivity.java +++ b/app/src/main/java/com/wbrawner/simplemarkdown/MainActivity.java @@ -60,32 +60,6 @@ public class MainActivity extends AppCompatActivity TabLayout tabLayout; private static final String TAG = MainActivity.class.getSimpleName(); - private static String fileName; - - public static String getTempFileName() { - return "tmp_" + getFileName(); - } - - public static String getFileName() { - if (fileName == null) { - return "untitled.md"; - } - if (!fileName.endsWith(".md")) - return fileName + ".md"; - return fileName; - } - - public static String getTempFilePath() { - return mFilesDir + "/tmp/"; - } - - public static String getFilePath() { - return Environment.getExternalStoragePublicDirectory(Environment.DIRECTORY_DOCUMENTS) + "/"; - } - - public static void setFileName(String fileName) { - MainActivity.fileName = fileName; - } @Override protected void onCreate(Bundle savedInstanceState) { @@ -127,79 +101,79 @@ public class MainActivity extends AppCompatActivity return true; } - @Override - public boolean onOptionsItemSelected(MenuItem item) { - switch (item.getItemId()) { - case R.id.action_save: - //TODO: Create popup for file name - AlertDialog.Builder builder = new AlertDialog.Builder(this); - builder.setTitle(R.string.action_save); - - final EditText input = new EditText(this); - input.setInputType(InputType.TYPE_CLASS_TEXT); - input.setHint(R.string.hint_filename); - input.setText(getFileName()); - builder.setView(input); - - builder.setPositiveButton("OK", new DialogInterface.OnClickListener() { - @Override - public void onClick(DialogInterface dialog, int which) { - if (input.getText().length() > 0) { - setFileName(input.getText().toString()); - requestSave(input.getText().toString()); - } - } - }); - builder.setNegativeButton("Cancel", new DialogInterface.OnClickListener() { - @Override - public void onClick(DialogInterface dialog, int which) { - dialog.cancel(); - } - }); - - builder.show(); - break; - case R.id.action_share: - //TODO: Fix this - File tmpFile = new File(getTempFilePath() + getFileName()); - if (!tmpFile.exists()) { - Intent saveIntent = new Intent(EditFragment.SAVE_ACTION); - saveIntent.putExtra("fileName", getTempFilePath() + getFileName()); - LocalBroadcastManager.getInstance(getApplicationContext()) - .sendBroadcast(saveIntent); - } - Uri fileUri = FileProvider.getUriForFile(MainActivity.this, AUTHORITY, tmpFile); - if (fileUri != null) { - try { - Intent shareIntent = new Intent(Intent.ACTION_SEND); - shareIntent.setDataAndType( - fileUri, - getContentResolver().getType(fileUri) - ); - shareIntent.putExtra(Intent.EXTRA_STREAM, fileUri); - shareIntent.setFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION); - startActivity( - Intent.createChooser( - shareIntent, - getString(R.string.share_file) - ) - ); - } catch (ActivityNotFoundException e) { - Log.e(TAG, "Error sharing file", e); - Toast.makeText( - MainActivity.this, - R.string.no_shareable_apps, - Toast.LENGTH_SHORT - ).show(); - } - } - break; - case R.id.action_load: - requestOpen(); - break; - } - return super.onOptionsItemSelected(item); - } +// @Override +// public boolean onOptionsItemSelected(MenuItem item) { +// switch (item.getItemId()) { +// case R.id.action_save: +// //TODO: Create popup for file name +// AlertDialog.Builder builder = new AlertDialog.Builder(this); +// builder.setTitle(R.string.action_save); +// +// final EditText input = new EditText(this); +// input.setInputType(InputType.TYPE_CLASS_TEXT); +// input.setHint(R.string.hint_filename); +// input.setText(getFileName()); +// builder.setView(input); +// +// builder.setPositiveButton("OK", new DialogInterface.OnClickListener() { +// @Override +// public void onClick(DialogInterface dialog, int which) { +// if (input.getText().length() > 0) { +// setFileName(input.getText().toString()); +// requestSave(input.getText().toString()); +// } +// } +// }); +// builder.setNegativeButton("Cancel", new DialogInterface.OnClickListener() { +// @Override +// public void onClick(DialogInterface dialog, int which) { +// dialog.cancel(); +// } +// }); +// +// builder.show(); +// break; +// case R.id.action_share: +// //TODO: Fix this +// File tmpFile = new File(getTempFilePath() + getFileName()); +// if (!tmpFile.exists()) { +// Intent saveIntent = new Intent(EditFragment.SAVE_ACTION); +// saveIntent.putExtra("fileName", getTempFilePath() + getFileName()); +// LocalBroadcastManager.getInstance(getApplicationContext()) +// .sendBroadcast(saveIntent); +// } +// Uri fileUri = FileProvider.getUriForFile(MainActivity.this, AUTHORITY, tmpFile); +// if (fileUri != null) { +// try { +// Intent shareIntent = new Intent(Intent.ACTION_SEND); +// shareIntent.setDataAndType( +// fileUri, +// getContentResolver().getType(fileUri) +// ); +// shareIntent.putExtra(Intent.EXTRA_STREAM, fileUri); +// shareIntent.setFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION); +// startActivity( +// Intent.createChooser( +// shareIntent, +// getString(R.string.share_file) +// ) +// ); +// } catch (ActivityNotFoundException e) { +// Log.e(TAG, "Error sharing file", e); +// Toast.makeText( +// MainActivity.this, +// R.string.no_shareable_apps, +// Toast.LENGTH_SHORT +// ).show(); +// } +// } +// break; +// case R.id.action_load: +// requestOpen(); +// break; +// } +// return super.onOptionsItemSelected(item); +// } private void requestSave(String text) { Intent saveIntent = new Intent(EditFragment.SAVE_ACTION); @@ -226,25 +200,25 @@ public class MainActivity extends AppCompatActivity } } - @Override - public void onRequestPermissionsResult(int requestCode, - String permissions[], int[] grantResults) { - switch (requestCode) { - case FileUtils.WRITE_PERMISSION_REQUEST: { - // If request is cancelled, the result arrays are empty. - if (grantResults.length > 0 - && grantResults[0] == PackageManager.PERMISSION_GRANTED) { - // Permission granted, open file chooser dialog - requestSave(getFileName()); - } else { - // Permission denied, do nothing - Toast.makeText(MainActivity.this, R.string.no_permissions, Toast.LENGTH_SHORT) - .show(); - } - return; - } - } - } +// @Override +// public void onRequestPermissionsResult(int requestCode, +// String permissions[], int[] grantResults) { +// switch (requestCode) { +// case FileUtils.WRITE_PERMISSION_REQUEST: { +// // If request is cancelled, the result arrays are empty. +// if (grantResults.length > 0 +// && grantResults[0] == PackageManager.PERMISSION_GRANTED) { +// // Permission granted, open file chooser dialog +// requestSave(getFileName()); +// } else { +// // Permission denied, do nothing +// Toast.makeText(MainActivity.this, R.string.no_permissions, Toast.LENGTH_SHORT) +// .show(); +// } +// return; +// } +// } +// } @Override public void onBackPressed() { if (pager.getCurrentItem() == FRAGMENT_EDIT) diff --git a/app/src/main/java/com/wbrawner/simplemarkdown/MarkdownViewModel.java b/app/src/main/java/com/wbrawner/simplemarkdown/MarkdownViewModel.java new file mode 100644 index 0000000..5129173 --- /dev/null +++ b/app/src/main/java/com/wbrawner/simplemarkdown/MarkdownViewModel.java @@ -0,0 +1,37 @@ +package com.wbrawner.simplemarkdown; + +import android.arch.lifecycle.LiveData; +import android.arch.lifecycle.MutableLiveData; +import android.arch.lifecycle.ViewModel; + +import com.commonsware.cwac.anddown.AndDown; + +public class MarkdownViewModel extends ViewModel { + + private MutableLiveData markdownLiveData; + private MutableLiveData htmlLiveData = new MutableLiveData<>();; + + public MarkdownViewModel() { + markdownLiveData = new MutableLiveData<>(); + } + + public void updateMarkdown(String data) { + if (markdownLiveData == null) + markdownLiveData = new MutableLiveData<>(); + markdownLiveData.postValue(data); + Runnable generateMarkdown = () -> { + AndDown andDown = new AndDown(); + int hoedownFlags = + AndDown.HOEDOWN_EXT_STRIKETHROUGH | AndDown.HOEDOWN_EXT_TABLES | + AndDown.HOEDOWN_EXT_UNDERLINE | AndDown.HOEDOWN_EXT_SUPERSCRIPT | + AndDown.HOEDOWN_EXT_FENCED_CODE; + htmlLiveData.postValue(andDown.markdownToHtml(markdownLiveData.getValue(), hoedownFlags, 0)); + }; + if (markdownLiveData.getValue() != null) + generateMarkdown.run(); + } + + public LiveData getHtml() { + return htmlLiveData; + } +} diff --git a/app/src/main/java/com/wbrawner/simplemarkdown/PreviewFragment.java b/app/src/main/java/com/wbrawner/simplemarkdown/PreviewFragment.java index 0c4cf29..e04f382 100644 --- a/app/src/main/java/com/wbrawner/simplemarkdown/PreviewFragment.java +++ b/app/src/main/java/com/wbrawner/simplemarkdown/PreviewFragment.java @@ -1,6 +1,9 @@ package com.wbrawner.simplemarkdown; import android.Manifest; +import android.arch.lifecycle.LifecycleFragment; +import android.arch.lifecycle.Observer; +import android.arch.lifecycle.ViewModelProviders; import android.content.BroadcastReceiver; import android.content.Context; import android.content.Intent; @@ -22,13 +25,20 @@ import android.webkit.WebView; import com.commonsware.cwac.anddown.AndDown; +import org.reactivestreams.Subscriber; +import org.reactivestreams.Subscription; + import butterknife.BindView; import butterknife.ButterKnife; +import io.reactivex.Observable; +import io.reactivex.processors.PublishProcessor; +import io.reactivex.subjects.PublishSubject; +import io.reactivex.subjects.Subject; -public class PreviewFragment extends Fragment { +public class PreviewFragment extends LifecycleFragment { private static final String TAG = PreviewFragment.class.getSimpleName(); private static final int INTERNET_REQUEST = 0; - private WebView mMarkdownView; + private MarkdownViewModel markdownViewModel; @BindView(R.id.markdown_view) WebView markdownView; @@ -40,76 +50,17 @@ public class PreviewFragment extends Fragment { // Required empty public constructor } - @Override - public void onCreate(@Nullable Bundle savedInstanceState) { - super.onCreate(savedInstanceState); - IntentFilter filter = new IntentFilter(); - filter.addAction(PREVIEW_ACTION); - LocalBroadcastManager.getInstance(getContext()).registerReceiver( - new MarkdownBroadcastSender(), - filter - ); - checkPermissions(); - } - - private void checkPermissions() { - if (ContextCompat.checkSelfPermission(getActivity(), - Manifest.permission.INTERNET) - != PackageManager.PERMISSION_GRANTED) { - ActivityCompat.requestPermissions(getActivity(), - new String[]{Manifest.permission.INTERNET}, - INTERNET_REQUEST); - } - } - @Override public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) { // Inflate the layout for this fragment View view = inflater.inflate(R.layout.fragment_preview, container, false); ButterKnife.bind(this, view); - mMarkdownView = markdownView; + markdownViewModel = ViewModelProviders.of(getActivity()).get(MarkdownViewModel.class); + markdownViewModel.getHtml().observe(this, s -> markdownView.loadData(s, "text/html", "UTF-8")); if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT) { WebView.setWebContentsDebuggingEnabled(true); } return view; } - - private class MarkdownBroadcastSender extends BroadcastReceiver { - @Override - public void onReceive(Context context, Intent intent) { - Log.d(TAG, "Intent received: " + intent.getAction()); - switch (intent.getAction()) { - case PREVIEW_ACTION: - if (intent.hasExtra("markdownData")) { - String data = intent.getStringExtra("markdownData"); - int yPos = 0; - if (intent.hasExtra("scrollY")) { - float yPercent = intent.getFloatExtra("scrollY", 0); - Log.d(TAG, "Scrolling to: " + yPercent); - yPos = Math.round(mMarkdownView.getContentHeight() * yPercent); - } - markdown(data, yPos); - } - break; - } - } - } - - private void markdown(final String text, final int scrollY) { - Thread setMarkdown = new Thread() { - @Override - public void run() { - AndDown andDown = new AndDown(); - int hoedownFlags = - AndDown.HOEDOWN_EXT_STRIKETHROUGH | AndDown.HOEDOWN_EXT_TABLES | - AndDown.HOEDOWN_EXT_UNDERLINE | AndDown.HOEDOWN_EXT_SUPERSCRIPT | - AndDown.HOEDOWN_EXT_FENCED_CODE; - String html = andDown.markdownToHtml(text, hoedownFlags, 0); - mMarkdownView.loadDataWithBaseURL(null, html, "text/html", "UTF-8", null); - mMarkdownView.scrollTo(0, scrollY); - } - }; - setMarkdown.run(); - } }