diff --git a/app/build.gradle b/app/build.gradle index 39b5cbb..17c68d6 100644 --- a/app/build.gradle +++ b/app/build.gradle @@ -2,6 +2,9 @@ apply plugin: 'com.android.application' apply plugin: 'me.tatarka.retrolambda' android { + configurations.all { + resolutionStrategy.force 'com.google.code.findbugs:jsr305:3.0.1' + } packagingOptions { exclude 'META-INF/LICENSE-LGPL-2.1.txt' exclude 'META-INF/LICENSE-LGPL-3.txt' @@ -49,9 +52,8 @@ dependencies { compile 'io.reactivex.rxjava2:rxjava:2.1.0' compile 'com.jakewharton.rxbinding2:rxbinding:2.0.0' compile 'com.jakewharton.rxbinding2:rxbinding-design:2.0.0' - compile "android.arch.lifecycle:runtime:1.0.0-alpha5" - compile "android.arch.lifecycle:extensions:1.0.0-alpha5" + compile 'com.google.dagger:dagger:2.11' + annotationProcessor 'com.google.dagger:dagger-compiler:2.11' testCompile 'junit:junit:4.12' - annotationProcessor "android.arch.lifecycle:compiler:1.0.0-alpha5" annotationProcessor 'com.jakewharton:butterknife-compiler:8.7.0' } diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml index c5f17bd..f3d5d59 100644 --- a/app/src/main/AndroidManifest.xml +++ b/app/src/main/AndroidManifest.xml @@ -5,13 +5,14 @@ - + diff --git a/app/src/main/java/com/wbrawner/simplemarkdown/AppComponent.java b/app/src/main/java/com/wbrawner/simplemarkdown/AppComponent.java new file mode 100644 index 0000000..78d66f0 --- /dev/null +++ b/app/src/main/java/com/wbrawner/simplemarkdown/AppComponent.java @@ -0,0 +1,22 @@ +package com.wbrawner.simplemarkdown; + +import com.wbrawner.simplemarkdown.view.activity.MainActivity; +import com.wbrawner.simplemarkdown.view.fragment.EditFragment; +import com.wbrawner.simplemarkdown.view.fragment.PreviewFragment; + +import javax.inject.Singleton; + +import dagger.Component; + +/** + * Created by billy on 8/22/17. + */ + +@Singleton +@Component(modules = { AppModule.class }) +public interface AppComponent { + void inject(MarkdownApplication application); + void inject(MainActivity activity); + void inject(EditFragment fragment); + void inject(PreviewFragment fragment); +} diff --git a/app/src/main/java/com/wbrawner/simplemarkdown/AppModule.java b/app/src/main/java/com/wbrawner/simplemarkdown/AppModule.java new file mode 100644 index 0000000..20fe822 --- /dev/null +++ b/app/src/main/java/com/wbrawner/simplemarkdown/AppModule.java @@ -0,0 +1,27 @@ +package com.wbrawner.simplemarkdown; + +import com.wbrawner.simplemarkdown.model.MarkdownFile; +import com.wbrawner.simplemarkdown.presentation.MarkdownPresenter; +import com.wbrawner.simplemarkdown.presentation.MarkdownPresenterImpl; + +import javax.inject.Singleton; + +import dagger.Module; +import dagger.Provides; + +/** + * Created by billy on 8/22/17. + */ + +@Module +public class AppModule { + @Provides + public MarkdownFile provideMarkdownFile() { + return new MarkdownFile(); + } + + @Provides @Singleton + public MarkdownPresenter provideMarkdownPresenter(MarkdownFile file) { + return new MarkdownPresenterImpl(file); + } +} \ No newline at end of file diff --git a/app/src/main/java/com/wbrawner/simplemarkdown/EditFragment.java b/app/src/main/java/com/wbrawner/simplemarkdown/EditFragment.java deleted file mode 100644 index 26eb1d1..0000000 --- a/app/src/main/java/com/wbrawner/simplemarkdown/EditFragment.java +++ /dev/null @@ -1,74 +0,0 @@ -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; -import android.content.IntentFilter; -import android.net.Uri; -import android.os.Bundle; -import android.os.Environment; -import android.support.annotation.Nullable; -import android.support.v4.app.Fragment; -import android.support.v4.app.FragmentActivity; -import android.support.v4.content.FileProvider; -import android.support.v4.content.LocalBroadcastManager; -import android.text.Editable; -import android.text.Layout; -import android.text.TextWatcher; -import android.util.Log; -import android.view.LayoutInflater; -import android.view.View; -import android.view.ViewGroup; -import android.view.WindowManager; -import android.widget.EditText; -import android.widget.Toast; - -import com.jakewharton.rxbinding2.widget.RxTextView; - -import java.io.BufferedReader; -import java.io.File; -import java.io.FileOutputStream; -import java.io.IOException; -import java.util.concurrent.TimeUnit; - -import butterknife.BindView; -import butterknife.ButterKnife; -import io.reactivex.Observable; -import io.reactivex.Scheduler; -import io.reactivex.android.schedulers.AndroidSchedulers; -import io.reactivex.schedulers.Schedulers; - -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 MarkdownViewModel markdownViewModel; - - @BindView(R.id.markdown_edit) - EditText markdownEditor; - - public EditFragment() { - // Required empty public constructor - } - - @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); - 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(data -> { - markdownViewModel.updateMarkdown(data); - }); - return view; - } -} diff --git a/app/src/main/java/com/wbrawner/simplemarkdown/FileUtils.java b/app/src/main/java/com/wbrawner/simplemarkdown/FileUtils.java deleted file mode 100644 index 1e35052..0000000 --- a/app/src/main/java/com/wbrawner/simplemarkdown/FileUtils.java +++ /dev/null @@ -1,65 +0,0 @@ -package com.wbrawner.simplemarkdown; - -import android.Manifest; -import android.app.Activity; -import android.content.pm.PackageManager; -import android.os.Environment; -import android.support.v4.app.ActivityCompat; -import android.support.v4.content.ContextCompat; -import android.support.v7.app.AppCompatActivity; - -/** - * Created by billy on 7/27/2017. - */ - -public class FileUtils { - - public static final int WRITE_PERMISSION_REQUEST = 0; - public static final int OPEN_FILE_REQUEST = 1; - - private Activity mContext; - - public FileUtils(Activity context) { - mContext = context; - } - - public boolean isExternalStorageWritable() { - String state = Environment.getExternalStorageState(); - if (Environment.MEDIA_MOUNTED.equals(state)) { - return true; - } - return false; - } - - public boolean isExternalStorageReadable() { - String state = Environment.getExternalStorageState(); - if (Environment.MEDIA_MOUNTED.equals(state) || - Environment.MEDIA_MOUNTED_READ_ONLY.equals(state)) { - return true; - } - return false; - } - - public boolean isExternalStorageWriteable() { - String state = Environment.getExternalStorageState(); - if (Environment.MEDIA_MOUNTED.equals(state) && checkWritePermission()) { - return true; - } - return false; - } - - public boolean checkWritePermission() { - return (ContextCompat.checkSelfPermission( - mContext, - Manifest.permission.WRITE_EXTERNAL_STORAGE - ) == PackageManager.PERMISSION_GRANTED); - } - - public void requestWritePermissions() { - ActivityCompat.requestPermissions( - mContext, - new String[] {Manifest.permission.WRITE_EXTERNAL_STORAGE}, - WRITE_PERMISSION_REQUEST - ); - } -} diff --git a/app/src/main/java/com/wbrawner/simplemarkdown/MainActivity.java b/app/src/main/java/com/wbrawner/simplemarkdown/MainActivity.java deleted file mode 100644 index 52834dc..0000000 --- a/app/src/main/java/com/wbrawner/simplemarkdown/MainActivity.java +++ /dev/null @@ -1,243 +0,0 @@ -package com.wbrawner.simplemarkdown; - -import android.Manifest; -import android.app.ProgressDialog; -import android.content.ActivityNotFoundException; -import android.content.ClipData; -import android.content.Context; -import android.content.DialogInterface; -import android.content.Intent; -import android.content.pm.PackageManager; -import android.content.res.Configuration; -import android.graphics.drawable.ColorDrawable; -import android.net.Uri; -import android.os.Environment; -import android.support.annotation.Nullable; -import android.support.annotation.VisibleForTesting; -import android.support.design.widget.TabLayout; -import android.support.v4.app.ActivityCompat; -import android.support.v4.app.Fragment; -import android.support.v4.app.FragmentManager; -import android.support.v4.app.FragmentPagerAdapter; -import android.support.v4.content.ContextCompat; -import android.support.v4.content.FileProvider; -import android.support.v4.content.LocalBroadcastManager; -import android.support.v4.os.EnvironmentCompat; -import android.support.v4.view.ViewPager; -import android.support.v7.app.AlertDialog; -import android.support.v7.app.AppCompatActivity; -import android.os.Bundle; -import android.text.Editable; -import android.text.InputType; -import android.util.Log; -import android.view.Menu; -import android.view.MenuItem; -import android.view.SearchEvent; -import android.view.View; -import android.view.WindowManager; -import android.webkit.MimeTypeMap; -import android.widget.EditText; -import android.widget.Toast; - -import java.io.File; - -import butterknife.BindView; -import butterknife.ButterKnife; - -public class MainActivity extends AppCompatActivity - implements ActivityCompat.OnRequestPermissionsResultCallback { - - public static final String AUTHORITY = "com.wbrawner.simplemarkdown.fileprovider"; - private static final int REQUEST_WRITE_STORAGE = 0; - private static File mFilesDir; - public static final int FRAGMENT_EDIT = 0; - public static final int FRAGMENT_PREVIEW = 1; - public static final int NUM_PAGES = 2; - - @BindView(R.id.pager) - ViewPager pager; - @BindView(R.id.layout_tab) - TabLayout tabLayout; - - private static final String TAG = MainActivity.class.getSimpleName(); - - @Override - protected void onCreate(Bundle savedInstanceState) { - super.onCreate(savedInstanceState); - setContentView(R.layout.activity_main); - getWindow().setBackgroundDrawable(new ColorDrawable(0xFFFFFFFF)); - ButterKnife.bind(this); - pager.setAdapter( - new EditPagerAdapter(getSupportFragmentManager(), MainActivity.this) - ); - pager.setPageMargin(1); - pager.setPageMarginDrawable(R.color.colorAccent); - mFilesDir = getFilesDir(); - Intent intent = getIntent(); - if (intent != null && !intent.getAction().equals(Intent.ACTION_MAIN) && intent.getData() != null) { - Intent loadIntent = new Intent(EditFragment.LOAD_ACTION); - loadIntent.putExtra("fileUri", intent.getData().toString()); - LocalBroadcastManager.getInstance(getApplicationContext()) - .sendBroadcast(loadIntent); - } - if (getResources().getConfiguration().orientation - == Configuration.ORIENTATION_LANDSCAPE) { - tabLayout.setVisibility(View.GONE); - } - } - - @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: -// //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); - saveIntent.putExtra("fileName", text); - LocalBroadcastManager.getInstance(getApplicationContext()) - .sendBroadcast(saveIntent); - } - - private void requestOpen() { - Intent openIntent = new Intent(Intent.ACTION_GET_CONTENT); - openIntent.setType("*/*"); - openIntent.addCategory(Intent.CATEGORY_OPENABLE); - try { - startActivityForResult( - Intent.createChooser( - openIntent, - getString(R.string.open_file) - ), - FileUtils.OPEN_FILE_REQUEST - ); - } catch (ActivityNotFoundException e) { - Toast.makeText(MainActivity.this, R.string.no_filebrowser, Toast.LENGTH_SHORT) - .show(); - } - } - -// @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) - super.onBackPressed(); - else - pager.setCurrentItem(FRAGMENT_EDIT); - } - - @Override - protected void onActivityResult(int requestCode, int resultCode, Intent data) { - switch (requestCode) { - case FileUtils.OPEN_FILE_REQUEST: - if (resultCode == RESULT_OK) { - Uri fileUri = data.getData(); - Intent loadIntent = new Intent(EditFragment.LOAD_ACTION); - loadIntent.putExtra("fileUri", fileUri.toString()); - LocalBroadcastManager.getInstance(MainActivity.this).sendBroadcast(loadIntent); - } - } - super.onActivityResult(requestCode, resultCode, data); - } -} diff --git a/app/src/main/java/com/wbrawner/simplemarkdown/MarkdownApplication.java b/app/src/main/java/com/wbrawner/simplemarkdown/MarkdownApplication.java new file mode 100644 index 0000000..93bbb9e --- /dev/null +++ b/app/src/main/java/com/wbrawner/simplemarkdown/MarkdownApplication.java @@ -0,0 +1,24 @@ +package com.wbrawner.simplemarkdown; + +import android.app.Application; + +/** + * Created by billy on 8/22/17. + */ + +public class MarkdownApplication extends Application { + + private AppComponent component; + + @Override + public void onCreate() { + super.onCreate(); + component = DaggerAppComponent.builder() + .appModule(new AppModule()) + .build(); + } + + public AppComponent getComponent() { + return component; + } +} diff --git a/app/src/main/java/com/wbrawner/simplemarkdown/MarkdownViewModel.java b/app/src/main/java/com/wbrawner/simplemarkdown/MarkdownViewModel.java index 5129173..3cf17fa 100644 --- a/app/src/main/java/com/wbrawner/simplemarkdown/MarkdownViewModel.java +++ b/app/src/main/java/com/wbrawner/simplemarkdown/MarkdownViewModel.java @@ -1,14 +1,10 @@ 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; +public class MarkdownViewModel { +/* + private static final String TAG = MarkdownViewModel.class.getSimpleName(); + private File file; + public MutableLiveData markdownLiveData; private MutableLiveData htmlLiveData = new MutableLiveData<>();; public MarkdownViewModel() { @@ -34,4 +30,47 @@ public class MarkdownViewModel extends ViewModel { public LiveData getHtml() { return htmlLiveData; } + + public void openFile(String filePath) { + file = new File(filePath); + } + + public String getFileName() { + if (file == null || file.getName().isEmpty()) + return "Untitled.md"; + return file.getName(); + } + + public boolean saveFile(String filePath, @Nullable String fileName) { + if (fileName == null) { + if (file != null) + fileName = file.getName(); + else + fileName = "Untitled.md"; + } + if (!filePath.endsWith("/")) + filePath += "/"; + final boolean result; + new AsyncTask() { + @Override + protected Void doInBackground(String... strings) { + try { + PrintWriter writer = new PrintWriter(strings[0], "UTF-8"); + writer.write(markdownLiveData.getValue()); + } catch (IOException e) { + Log.e(TAG, "Error saving file: ", e); + } + return null; + } + }.execute(filePath + fileName); + return true; + } + + public void requestSave(String s) { + // Do something to save the file? + } + + public String getMarkdown() { + return markdownLiveData.getValue(); + } */ } diff --git a/app/src/main/java/com/wbrawner/simplemarkdown/PreviewFragment.java b/app/src/main/java/com/wbrawner/simplemarkdown/PreviewFragment.java deleted file mode 100644 index e04f382..0000000 --- a/app/src/main/java/com/wbrawner/simplemarkdown/PreviewFragment.java +++ /dev/null @@ -1,66 +0,0 @@ -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; -import android.content.IntentFilter; -import android.content.pm.PackageManager; -import android.os.Build; -import android.os.Bundle; -import android.os.Handler; -import android.support.annotation.Nullable; -import android.support.v4.app.ActivityCompat; -import android.support.v4.app.Fragment; -import android.support.v4.content.ContextCompat; -import android.support.v4.content.LocalBroadcastManager; -import android.util.Log; -import android.view.LayoutInflater; -import android.view.View; -import android.view.ViewGroup; -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 LifecycleFragment { - private static final String TAG = PreviewFragment.class.getSimpleName(); - private static final int INTERNET_REQUEST = 0; - private MarkdownViewModel markdownViewModel; - - @BindView(R.id.markdown_view) - WebView markdownView; - - public static final String SCROLL_ACTION = "com.wbrawner.simplemarkdown.scroll"; - public static final String PREVIEW_ACTION = "com.wbrawner.simplemarkdown.preview"; - - public PreviewFragment() { - // Required empty public constructor - } - - @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); - 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; - } -} diff --git a/app/src/main/java/com/wbrawner/simplemarkdown/model/MarkdownFile.java b/app/src/main/java/com/wbrawner/simplemarkdown/model/MarkdownFile.java new file mode 100644 index 0000000..06d60af --- /dev/null +++ b/app/src/main/java/com/wbrawner/simplemarkdown/model/MarkdownFile.java @@ -0,0 +1,198 @@ +package com.wbrawner.simplemarkdown.model; + +import android.util.Log; + +import java.io.BufferedReader; +import java.io.File; +import java.io.FileDescriptor; +import java.io.FileNotFoundException; +import java.io.FileOutputStream; +import java.io.FileReader; +import java.io.IOException; +import java.io.InputStream; +import java.io.OutputStreamWriter; +import java.util.Scanner; + +import static android.content.ContentValues.TAG; + +/** + * Created by billy on 8/22/17. + */ + +public class MarkdownFile { + public static final int SUCCESS = 0; + public static final int FILE_EXISTS = 1; + public static final int FILE_NOT_EXISTS = 2; + public static final int READ_ERROR = 3; + public static final int WRITE_ERROR = 4; + public static final int PARAMETERS_MISSING = 5; + private String name; + private String path; + private String content; + + public MarkdownFile(String name, String path, String content) { + this.name = name; + this.path = path; + this.content = content; + } + + public MarkdownFile(String path) { + int code = load(path); + if (code != SUCCESS) { + this.name = path.substring( + path.lastIndexOf("/") + 1 + ); + this.path = path.substring( + 0, + path.lastIndexOf("/") + ); + this.content = ""; + } + } + + public MarkdownFile() { + this.name = "Untitled.md"; + this.path = ""; + this.content = ""; + } + + public String getName() { + return name; + } + + public void setName(String name) { + this.name = name; + } + + public String getPath() { + return path; + } + + public String getFullPath() { + String fullPath; + if (!this.path.endsWith("/")) + fullPath = this.path + "/"; + else + fullPath = this.path; + return fullPath + this.name; + } + + public void setPath(String path) { + this.path = path; + } + + public String getContent() { + return content; + } + + public void setContent(String content) { + this.content = content; + } + + public int load(InputStream in) { + StringBuilder sb = new StringBuilder(); + Scanner s = new java.util.Scanner(in).useDelimiter("\\n"); + while(s.hasNext()) { + sb.append(s.next() + "\n"); + } + this.content = sb.toString(); + return SUCCESS; + } + + public int load(String path) { + Log.d(TAG, path); + int code; + File markdownFile = new File(path); + if (markdownFile.exists() && markdownFile.canRead()) { + BufferedReader reader = null; + try { + this.name = markdownFile.getName(); + this.path = markdownFile.getPath(); + StringBuilder sb = new StringBuilder(); + String line; + reader = new BufferedReader(new FileReader(markdownFile)); + while ((line = reader.readLine()) != null) + sb.append(line + "\n"); + this.content = sb.toString(); + code = SUCCESS; + } catch (FileNotFoundException e) { + code = FILE_NOT_EXISTS; + } catch (IOException e) { + code = READ_ERROR; + } + if (reader != null) { + try { + reader.close(); + } catch (IOException e) { + // closing the reader failed + } + } + } else { + code = READ_ERROR; + } + return code; + } + + public int load() { + if (!parametersOk()) + return PARAMETERS_MISSING; + return load(getFullPath()); + } + + public int save(String path) { + int code; + File markdownFile = new File(path); + if (!markdownFile.exists()) { + try { + markdownFile.createNewFile(); + } catch (IOException e) { + return WRITE_ERROR; + } + } + if (markdownFile.canWrite()) { + OutputStreamWriter writer = null; + try { + writer = new OutputStreamWriter( + new FileOutputStream(markdownFile) + ); + writer.write(this.content); + code = SUCCESS; + } + catch (IOException e) { + code = WRITE_ERROR; + } + if (writer != null) { + try { + writer.close(); + } catch (IOException e) { + // closing the reader failed + } + } + } else { + code = WRITE_ERROR; + } + return code; + } + + public int save() { + if (!parametersOk()) + return PARAMETERS_MISSING; + return save(this.getFullPath()); + } + + public int fileExists(String path) { + if (new File(path).exists()) + return FILE_EXISTS; + return FILE_NOT_EXISTS; + } + + public int fileExists() { + if (parametersOk()) + return fileExists(getFullPath()); + return PARAMETERS_MISSING; + } + + private boolean parametersOk() { + return !this.name.isEmpty() && !this.path.isEmpty(); + } +} diff --git a/app/src/main/java/com/wbrawner/simplemarkdown/presentation/LifecyclePresenter.java b/app/src/main/java/com/wbrawner/simplemarkdown/presentation/LifecyclePresenter.java new file mode 100644 index 0000000..18b15d4 --- /dev/null +++ b/app/src/main/java/com/wbrawner/simplemarkdown/presentation/LifecyclePresenter.java @@ -0,0 +1,10 @@ +package com.wbrawner.simplemarkdown.presentation; + +/** + * Created by billy on 8/22/17. + */ + +public interface LifecyclePresenter { + void resume(); + void pause(); +} diff --git a/app/src/main/java/com/wbrawner/simplemarkdown/presentation/MarkdownPresenter.java b/app/src/main/java/com/wbrawner/simplemarkdown/presentation/MarkdownPresenter.java new file mode 100644 index 0000000..c335599 --- /dev/null +++ b/app/src/main/java/com/wbrawner/simplemarkdown/presentation/MarkdownPresenter.java @@ -0,0 +1,24 @@ +package com.wbrawner.simplemarkdown.presentation; + +import com.wbrawner.simplemarkdown.view.MarkdownEditView; +import com.wbrawner.simplemarkdown.view.MarkdownPreviewView; + +import java.io.InputStream; + +/** + * Created by billy on 8/22/17. + */ + +public interface MarkdownPresenter extends LifecyclePresenter { + void loadMarkdown(String filePath); + void loadMarkdown(InputStream in); + void setEditView(MarkdownEditView editView); + void setPreviewView(MarkdownPreviewView previewView); + void saveMarkdown(String filePath); + void onMarkdownEdited(); + void onMarkdownEdited(String markdown); + String getFileName(); + void setFileName(String name); + String getMarkdown(); + void setMarkdown(String markdown); +} diff --git a/app/src/main/java/com/wbrawner/simplemarkdown/presentation/MarkdownPresenterImpl.java b/app/src/main/java/com/wbrawner/simplemarkdown/presentation/MarkdownPresenterImpl.java new file mode 100644 index 0000000..540d5ee --- /dev/null +++ b/app/src/main/java/com/wbrawner/simplemarkdown/presentation/MarkdownPresenterImpl.java @@ -0,0 +1,122 @@ +package com.wbrawner.simplemarkdown.presentation; + +import com.commonsware.cwac.anddown.AndDown; +import com.wbrawner.simplemarkdown.model.MarkdownFile; +import com.wbrawner.simplemarkdown.view.MarkdownEditView; +import com.wbrawner.simplemarkdown.view.MarkdownPreviewView; + +import java.io.InputStream; + +/** + * Created by billy on 8/22/17. + */ + +public class MarkdownPresenterImpl implements MarkdownPresenter { + private MarkdownFile file; + private MarkdownEditView editView; + private MarkdownPreviewView previewView; + private String TAG = MarkdownPresenterImpl.class.getSimpleName(); + + public MarkdownPresenterImpl(MarkdownFile file) { + this.file = file; + } + + @Override + public void resume() {} + + @Override + public void pause() { + saveMarkdown(""); + } + + @Override + public void loadMarkdown(String filePath) { + Runnable fileLoader = () -> { + int result = file.load(filePath); + if (result == MarkdownFile.SUCCESS) { + editView.setMarkdown(getMarkdown()); + onMarkdownEdited(); + } else { + editView.showFileLoadeddError(result); + } + }; + fileLoader.run(); + } + + @Override + public void loadMarkdown(InputStream in) { + Runnable fileLoader = () -> { + int result = file.load(in); + if (result == MarkdownFile.SUCCESS) { + editView.setMarkdown(getMarkdown()); + onMarkdownEdited(); + } else { + editView.showFileLoadeddError(result); + } + }; + fileLoader.run(); + } + + @Override + public void setEditView(MarkdownEditView editView) { + this.editView = editView; + } + + @Override + public void setPreviewView(MarkdownPreviewView previewView) { + this.previewView = previewView; + } + + @Override + public void saveMarkdown(String filePath) { + Runnable fileSaver = () -> { + int code; + code = file.save(filePath); + if (code == MarkdownFile.SUCCESS) { + editView.showFileSavedMessage(); + } else { + editView.showFileSavedError(code); + } + }; + fileSaver.run(); + } + + @Override + public void onMarkdownEdited(String markdown) { + 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; + if (previewView != null) + previewView.updatePreview(andDown.markdownToHtml(markdown, hoedownFlags, 0)); + }; + generateMarkdown.run(); + } + + @Override + public void onMarkdownEdited() { + onMarkdownEdited(getMarkdown()); + } + + @Override + public String getFileName() { + return file.getName(); + } + + @Override + public void setFileName(String name) { + file.setName(name); + } + + @Override + public String getMarkdown() { + return file.getContent(); + } + + @Override + public void setMarkdown(String markdown) { + file.setContent(markdown); + } +} diff --git a/app/src/main/java/com/wbrawner/simplemarkdown/view/MarkdownEditView.java b/app/src/main/java/com/wbrawner/simplemarkdown/view/MarkdownEditView.java new file mode 100644 index 0000000..9b26695 --- /dev/null +++ b/app/src/main/java/com/wbrawner/simplemarkdown/view/MarkdownEditView.java @@ -0,0 +1,15 @@ +package com.wbrawner.simplemarkdown.view; + +/** + * Created by billy on 8/22/17. + */ + +public interface MarkdownEditView { + String getMarkdown(); + void setMarkdown(String markdown); + + void showFileSavedMessage(); + void showFileSavedError(int code); + void showFileLoadedMessage(); + void showFileLoadeddError(int code); +} diff --git a/app/src/main/java/com/wbrawner/simplemarkdown/view/MarkdownPreviewView.java b/app/src/main/java/com/wbrawner/simplemarkdown/view/MarkdownPreviewView.java new file mode 100644 index 0000000..f23179b --- /dev/null +++ b/app/src/main/java/com/wbrawner/simplemarkdown/view/MarkdownPreviewView.java @@ -0,0 +1,9 @@ +package com.wbrawner.simplemarkdown.view; + +/** + * Created by billy on 8/22/17. + */ + +public interface MarkdownPreviewView { + void updatePreview(String html); +} diff --git a/app/src/main/java/com/wbrawner/simplemarkdown/view/activity/MainActivity.java b/app/src/main/java/com/wbrawner/simplemarkdown/view/activity/MainActivity.java new file mode 100644 index 0000000..9207ac8 --- /dev/null +++ b/app/src/main/java/com/wbrawner/simplemarkdown/view/activity/MainActivity.java @@ -0,0 +1,236 @@ +package com.wbrawner.simplemarkdown.view.activity; + +import android.Manifest; +import android.content.ActivityNotFoundException; +import android.content.Intent; +import android.content.pm.PackageManager; +import android.content.res.Configuration; +import android.database.Cursor; +import android.graphics.drawable.ColorDrawable; +import android.net.Uri; +import android.os.Build; +import android.os.Environment; +import android.provider.OpenableColumns; +import android.support.design.widget.TabLayout; +import android.support.v4.app.ActivityCompat; +import android.support.v4.content.ContextCompat; +import android.support.v4.view.ViewPager; +import android.support.v7.app.AlertDialog; +import android.support.v7.app.AppCompatActivity; +import android.os.Bundle; +import android.text.InputType; +import android.view.Menu; +import android.view.MenuItem; +import android.view.View; +import android.widget.EditText; +import android.widget.Toast; + +import com.wbrawner.simplemarkdown.MarkdownApplication; +import com.wbrawner.simplemarkdown.R; +import com.wbrawner.simplemarkdown.presentation.MarkdownPresenter; +import com.wbrawner.simplemarkdown.view.adapter.EditPagerAdapter; +import java.io.InputStream; + +import javax.inject.Inject; + +import butterknife.BindView; +import butterknife.ButterKnife; + +public class MainActivity extends AppCompatActivity + implements ActivityCompat.OnRequestPermissionsResultCallback { + + public static final int WRITE_PERMISSION_REQUEST = 0; + private static final int OPEN_FILE_REQUEST = 1; + public static final String AUTHORITY = "com.wbrawner.simplemarkdown.fileprovider"; + + @Inject + MarkdownPresenter presenter; + + @BindView(R.id.pager) + ViewPager pager; + @BindView(R.id.layout_tab) + TabLayout tabLayout; + + private static final String TAG = MainActivity.class.getSimpleName(); + + @Override + protected void onCreate(Bundle savedInstanceState) { + super.onCreate(savedInstanceState); + setContentView(R.layout.activity_main); + + ((MarkdownApplication) getApplication()).getComponent().inject(this); + + // Reset the background color + getWindow().setBackgroundDrawable(new ColorDrawable(0xFFFFFFFF)); + ButterKnife.bind(this); + pager.setAdapter( + new EditPagerAdapter(getSupportFragmentManager(), MainActivity.this) + ); + pager.setPageMargin(1); + pager.setPageMarginDrawable(R.color.colorAccent); + Intent intent = getIntent(); + if (intent != null && !intent.getAction().equals(Intent.ACTION_MAIN) && intent.getData() != null) { + loadFromUri(intent.getData()); + } + if (getResources().getConfiguration().orientation + == Configuration.ORIENTATION_LANDSCAPE) { + tabLayout.setVisibility(View.GONE); + } + } + + @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: + if (ContextCompat.checkSelfPermission( + MainActivity.this, + Manifest.permission.WRITE_EXTERNAL_STORAGE + ) == PackageManager.PERMISSION_GRANTED) + showSaveDialog(); + else { + if (Build.VERSION.SDK_INT >= 23) { + requestPermissions( + new String[] {Manifest.permission.WRITE_EXTERNAL_STORAGE}, + WRITE_PERMISSION_REQUEST + ); + } + } + 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: + requestOpen(); + break; + } + return super.onOptionsItemSelected(item); + } + + private void showSaveDialog() { + 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(presenter.getFileName()); + builder.setView(input); + + builder.setPositiveButton("OK", (dialog, which) -> { + if (input.getText().length() > 0) { + presenter.setFileName(input.getText().toString()); + setTitle(input.getText()); + if (Environment.getExternalStorageState().equals(Environment.MEDIA_MOUNTED)) { + String path = Environment.getExternalStorageDirectory() + "/" + + Environment.DIRECTORY_DOCUMENTS + "/" + input.getText(); + presenter.saveMarkdown(path); + } + } + }); + builder.setNegativeButton("Cancel", (dialog, which) -> { + dialog.cancel(); + }); + + builder.show(); + } + + @Override + public void onRequestPermissionsResult(int requestCode, + String permissions[], int[] grantResults) { + switch (requestCode) { + case 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 save dialog + showSaveDialog(); + } 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() == EditPagerAdapter.FRAGMENT_EDIT) + super.onBackPressed(); + else + pager.setCurrentItem(EditPagerAdapter.FRAGMENT_EDIT); + } + + @Override + protected void onActivityResult(int requestCode, int resultCode, Intent data) { + switch (requestCode) { + case OPEN_FILE_REQUEST: + if (resultCode == RESULT_OK) { + loadFromUri(data.getData()); + } + } + super.onActivityResult(requestCode, resultCode, data); + } + + private void loadFromUri(Uri fileUri) { + try { + InputStream in = + getContentResolver().openInputStream(fileUri); + Cursor retCur = getContentResolver() + .query(fileUri, null, null, null, null); + if (retCur != null) { + int nameIndex = retCur + .getColumnIndex(OpenableColumns.DISPLAY_NAME); + retCur.moveToFirst(); + presenter.setFileName(retCur.getString(nameIndex)); + } + presenter.loadMarkdown(in); + } catch (Exception e) { + Toast.makeText(MainActivity.this, R.string.file_load_error, Toast.LENGTH_SHORT) + .show(); + } + + } + + private void requestOpen() { + Intent openIntent = new Intent(Intent.ACTION_GET_CONTENT); + openIntent.setType("text/*"); + openIntent.addCategory(Intent.CATEGORY_OPENABLE); + try { + startActivityForResult( + Intent.createChooser( + openIntent, + getString(R.string.open_file) + ), + OPEN_FILE_REQUEST + ); + } catch (ActivityNotFoundException e) { + Toast.makeText(MainActivity.this, R.string.no_filebrowser, Toast.LENGTH_SHORT) + .show(); + } + } + + +} diff --git a/app/src/main/java/com/wbrawner/simplemarkdown/EditPagerAdapter.java b/app/src/main/java/com/wbrawner/simplemarkdown/view/adapter/EditPagerAdapter.java similarity index 77% rename from app/src/main/java/com/wbrawner/simplemarkdown/EditPagerAdapter.java rename to app/src/main/java/com/wbrawner/simplemarkdown/view/adapter/EditPagerAdapter.java index f2e52cc..9b10d5e 100644 --- a/app/src/main/java/com/wbrawner/simplemarkdown/EditPagerAdapter.java +++ b/app/src/main/java/com/wbrawner/simplemarkdown/view/adapter/EditPagerAdapter.java @@ -1,4 +1,4 @@ -package com.wbrawner.simplemarkdown; +package com.wbrawner.simplemarkdown.view.adapter; /** * Created by billy on 7/29/2017. @@ -10,11 +10,15 @@ import android.support.v4.app.Fragment; import android.support.v4.app.FragmentManager; import android.support.v4.app.FragmentPagerAdapter; -import static com.wbrawner.simplemarkdown.MainActivity.FRAGMENT_EDIT; -import static com.wbrawner.simplemarkdown.MainActivity.FRAGMENT_PREVIEW; -import static com.wbrawner.simplemarkdown.MainActivity.NUM_PAGES; +import com.wbrawner.simplemarkdown.R; +import com.wbrawner.simplemarkdown.view.fragment.EditFragment; +import com.wbrawner.simplemarkdown.view.fragment.PreviewFragment; + +public class EditPagerAdapter extends FragmentPagerAdapter { + public static final int FRAGMENT_EDIT = 0; + public static final int FRAGMENT_PREVIEW = 1; + public static final int NUM_PAGES = 2; -class EditPagerAdapter extends FragmentPagerAdapter { private Context mContext; public EditPagerAdapter(FragmentManager fm, Context context) { diff --git a/app/src/main/java/com/wbrawner/simplemarkdown/view/fragment/EditFragment.java b/app/src/main/java/com/wbrawner/simplemarkdown/view/fragment/EditFragment.java new file mode 100644 index 0000000..303f27a --- /dev/null +++ b/app/src/main/java/com/wbrawner/simplemarkdown/view/fragment/EditFragment.java @@ -0,0 +1,112 @@ +package com.wbrawner.simplemarkdown.view.fragment; + +import android.os.Bundle; +import android.support.v4.app.Fragment; +import android.view.LayoutInflater; +import android.view.View; +import android.view.ViewGroup; +import android.widget.EditText; +import android.widget.Toast; + +import com.jakewharton.rxbinding2.widget.RxTextView; +import com.wbrawner.simplemarkdown.MarkdownApplication; +import com.wbrawner.simplemarkdown.R; +import com.wbrawner.simplemarkdown.model.MarkdownFile; +import com.wbrawner.simplemarkdown.presentation.MarkdownPresenter; +import com.wbrawner.simplemarkdown.view.MarkdownEditView; + +import java.util.concurrent.TimeUnit; + +import javax.inject.Inject; + +import butterknife.BindView; +import butterknife.ButterKnife; +import io.reactivex.Observable; +import io.reactivex.android.schedulers.AndroidSchedulers; +import io.reactivex.schedulers.Schedulers; + +public class EditFragment extends Fragment implements MarkdownEditView { + public static final String SAVE_ACTION = "com.wbrawner.simplemarkdown.ACTION_SAVE"; + public static final String LOAD_ACTION = "com.wbrawner.simplemarkdown.ACTION_LOAD"; + private String TAG = EditFragment.class.getSimpleName(); + + @Inject + MarkdownPresenter presenter; + + @BindView(R.id.markdown_edit) + EditText markdownEditor; + + public EditFragment() { + // Required empty public constructor + } + + @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); + ((MarkdownApplication) getActivity().getApplication()).getComponent().inject(this); + + Observable obs = RxTextView.textChanges(markdownEditor) + .debounce(50, TimeUnit.MILLISECONDS).map(CharSequence::toString); + obs.subscribeOn(Schedulers.io()); + obs.observeOn(AndroidSchedulers.mainThread()); + obs.subscribe(markdown -> { + presenter.onMarkdownEdited(markdown); + }); + return view; + } + + @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 showFileSavedMessage() { + Toast.makeText(getActivity(), R.string.file_saved, Toast.LENGTH_SHORT).show(); + } + + @Override + public void showFileSavedError(int code) { + String message = ""; + switch (code) { + case MarkdownFile.WRITE_ERROR: + message = getString(R.string.error_write); + break; + default: + message = getString(R.string.file_save_error); + } + Toast.makeText(getActivity(), message, Toast.LENGTH_SHORT).show(); + } + + @Override + public void showFileLoadedMessage() { + Toast.makeText(getActivity(), R.string.file_loaded, Toast.LENGTH_SHORT).show(); + } + + @Override + public void showFileLoadeddError(int code) { + String message = ""; + Toast.makeText(getActivity(), message, Toast.LENGTH_SHORT).show(); + } + + @Override + public void setMarkdown(String markdown) { + markdownEditor.setText(markdown); + } +} diff --git a/app/src/main/java/com/wbrawner/simplemarkdown/view/fragment/PreviewFragment.java b/app/src/main/java/com/wbrawner/simplemarkdown/view/fragment/PreviewFragment.java new file mode 100644 index 0000000..89fd514 --- /dev/null +++ b/app/src/main/java/com/wbrawner/simplemarkdown/view/fragment/PreviewFragment.java @@ -0,0 +1,74 @@ +package com.wbrawner.simplemarkdown.view.fragment; + +import android.os.Build; +import android.os.Bundle; +import android.support.annotation.Nullable; +import android.support.v4.app.Fragment; +import android.view.LayoutInflater; +import android.view.View; +import android.view.ViewGroup; +import android.webkit.WebView; + +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; + +public class PreviewFragment extends Fragment implements MarkdownPreviewView { + private static final String TAG = PreviewFragment.class.getSimpleName(); + + @Inject + MarkdownPresenter presenter; + + @BindView(R.id.markdown_view) + WebView markdownPreview; + + public PreviewFragment() { + // Required empty public constructor + } + + @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); + ((MarkdownApplication) getActivity().getApplication()).getComponent().inject(this); + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT) { + WebView.setWebContentsDebuggingEnabled(true); + } + return view; + } + + @Override + public void onViewCreated(View view, @Nullable Bundle savedInstanceState) { + super.onViewCreated(view, savedInstanceState); + } + + + + @Override + public void updatePreview(String html) { + markdownPreview.post(() -> { + markdownPreview.loadData(html, "text/html", "UTF-8"); + }); + } + + @Override + public void onResume() { + super.onResume(); + presenter.setPreviewView(this); + presenter.onMarkdownEdited(); + } + + @Override + public void onDestroy() { + super.onDestroy(); + presenter.setPreviewView(null); + } +} diff --git a/app/src/main/res/layout/fragment_edit.xml b/app/src/main/res/layout/fragment_edit.xml index 6b97ca6..d257fe0 100644 --- a/app/src/main/res/layout/fragment_edit.xml +++ b/app/src/main/res/layout/fragment_edit.xml @@ -2,7 +2,7 @@ xmlns:tools="http://schemas.android.com/tools" android:layout_width="match_parent" android:layout_height="match_parent" - tools:context="com.wbrawner.simplemarkdown.EditFragment"> + tools:context="com.wbrawner.simplemarkdown.view.fragment.EditFragment"> + tools:context="com.wbrawner.simplemarkdown.view.fragment.PreviewFragment"> - + app:showAsAction="ifRoom" /> + android:title="@string/action_save" + android:id="@+id/action_save" + android:icon="@drawable/ic_action_save" + app:showAsAction="never" /> + diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml index c89ac24..2d2dc53 100644 --- a/app/src/main/res/values/strings.xml +++ b/app/src/main/res/values/strings.xml @@ -19,4 +19,8 @@ Load Select a file to open No file browser apps found + An error occurred while saving the file + File successfully loaded + An error occurred while writing the file + An error occurred while opening the file