Add New file creation menu option

This commit is contained in:
William Brawner 2018-01-27 16:51:45 -06:00 committed by William Brawner
parent 6f844994da
commit f82330a92f
12 changed files with 214 additions and 49 deletions

View file

@ -49,6 +49,11 @@ android {
dexOptions { dexOptions {
jumboMode true jumboMode true
} }
testOptions {
unitTests {
includeAndroidResources = true
}
}
} }
ext { ext {
@ -72,6 +77,7 @@ dependencies {
compile 'io.reactivex.rxjava2:rxandroid:2.0.1' compile 'io.reactivex.rxjava2:rxandroid:2.0.1'
compile 'io.reactivex.rxjava2:rxjava:2.1.0' compile 'io.reactivex.rxjava2:rxjava:2.1.0'
testCompile 'junit:junit:4.12' testCompile 'junit:junit:4.12'
testImplementation "org.robolectric:robolectric:3.6.1"
androidTestCompile 'com.android.support.test.espresso:espresso-core:3.0.1' androidTestCompile 'com.android.support.test.espresso:espresso-core:3.0.1'
androidTestCompile 'com.android.support.test:runner:1.0.1' androidTestCompile 'com.android.support.test:runner:1.0.1'
androidTestCompile 'com.android.support.test.uiautomator:uiautomator-v18:2.1.3' androidTestCompile 'com.android.support.test.uiautomator:uiautomator-v18:2.1.3'

View file

@ -7,9 +7,11 @@ import android.preference.PreferenceManager;
import com.wbrawner.simplemarkdown.view.activity.SettingsActivity; import com.wbrawner.simplemarkdown.view.activity.SettingsActivity;
/** import java.io.File;
* Created by billy on 1/15/2018. import java.io.FilenameFilter;
*/ import java.util.Locale;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
public class Utils { public class Utils {
@ -22,10 +24,53 @@ public class Utils {
); );
} }
public static String getDefaultFileName(Context context) {
File docsDir = new File(Utils.getDocsPath(context));
Pattern defaultFilePattern = Pattern.compile("Untitled(-([0-9]+))*.md");
File[] files = docsDir.listFiles();
String defaultFileName = "Untitled.md";
if (files != null && files.length > 0) {
int count = 0;
for (File file : files) {
if (!file.isFile()) {
continue;
}
Matcher fileMatcher = defaultFilePattern.matcher(file.getName());
if (!fileMatcher.find()) {
continue;
}
if (file.getName().equals("Untitled.md")) {
if (count == 0) {
count = 1;
}
continue;
}
String defaultFileCount = fileMatcher.group(2);
int fileCount = Integer.parseInt(defaultFileCount);
if (fileCount >= count) {
count = fileCount + 1;
}
}
if (count > 0) {
defaultFileName = String.format(
Locale.ENGLISH,
"Untitled-%d.md",
count
);
}
}
return defaultFileName;
}
public static boolean isAutosaveEnabled(Context context) { public static boolean isAutosaveEnabled(Context context) {
SharedPreferences prefs = PreferenceManager.getDefaultSharedPreferences(context); SharedPreferences prefs = PreferenceManager.getDefaultSharedPreferences(context);
return prefs.getBoolean( return prefs.getBoolean(
"autosave", SettingsActivity.KEY_AUTOSAVE,
true true
); );
} }

View file

@ -36,6 +36,9 @@ public class MarkdownFile {
public MarkdownFile(String name, String path, String content) { public MarkdownFile(String name, String path, String content) {
this.name = name; this.name = name;
if (path == null || path.isEmpty()) {
path = defaultRootDir;
}
this.path = path; this.path = path;
this.content = content; this.content = content;
} }
@ -125,6 +128,7 @@ public class MarkdownFile {
try { try {
this.name = markdownFile.getName(); this.name = markdownFile.getName();
this.path = markdownFile.getParentFile().getAbsolutePath(); this.path = markdownFile.getParentFile().getAbsolutePath();
StringBuilder sb = new StringBuilder(); StringBuilder sb = new StringBuilder();
String line; String line;
reader = new BufferedReader(new FileReader(markdownFile)); reader = new BufferedReader(new FileReader(markdownFile));
@ -158,6 +162,10 @@ public class MarkdownFile {
public int save(String path) { public int save(String path) {
int code; int code;
if (path == null) {
path = this.getFullPath();
}
File markdownFile = new File(path); File markdownFile = new File(path);
if (!markdownFile.exists()) { if (!markdownFile.exists()) {
try { try {
@ -192,10 +200,4 @@ public class MarkdownFile {
this.path = markdownFile.getParentFile().getAbsolutePath(); this.path = markdownFile.getParentFile().getAbsolutePath();
return code; return code;
} }
public int save() {
if (this.name.isEmpty())
return PARAMETERS_MISSING;
return save(this.getFullPath());
}
} }

View file

@ -9,10 +9,6 @@ import com.wbrawner.simplemarkdown.view.MarkdownPreviewView;
import java.io.File; import java.io.File;
import java.io.InputStream; import java.io.InputStream;
/**
* Created by billy on 8/22/17.
*/
public interface MarkdownPresenter { public interface MarkdownPresenter {
File getFile(); File getFile();
void loadMarkdown(); void loadMarkdown();
@ -21,10 +17,10 @@ public interface MarkdownPresenter {
void loadMarkdown(File file); void loadMarkdown(File file);
void loadFromUri(Context context, Uri fileUri); void loadFromUri(Context context, Uri fileUri);
void loadTempMarkdown(InputStream in, OnTempFileLoadedListener listener); void loadTempMarkdown(InputStream in, OnTempFileLoadedListener listener);
void newFile(String path);
void setEditView(MarkdownEditView editView); void setEditView(MarkdownEditView editView);
void setPreviewView(MarkdownPreviewView previewView); void setPreviewView(MarkdownPreviewView previewView);
void saveMarkdown(); void saveMarkdown(MarkdownSavedListener listener, String filePath);
void saveMarkdown(String filePath);
void onMarkdownEdited(); void onMarkdownEdited();
void onMarkdownEdited(String markdown); void onMarkdownEdited(String markdown);
String getFileName(); String getFileName();
@ -35,8 +31,12 @@ public interface MarkdownPresenter {
String getMarkdown(); String getMarkdown();
void setMarkdown(String markdown); void setMarkdown(String markdown);
public abstract class OnTempFileLoadedListener { abstract class OnTempFileLoadedListener {
public abstract void onSuccess(String markdown); public abstract void onSuccess(String markdown);
public abstract void onError(int code); public abstract void onError(int code);
} }
interface MarkdownSavedListener {
void saveComplete();
}
} }

View file

@ -63,6 +63,7 @@ public class MarkdownPresenterImpl implements MarkdownPresenter {
int result = this.file.load(file); int result = this.file.load(file);
if (editView != null) { if (editView != null) {
if (result == MarkdownFile.SUCCESS) { if (result == MarkdownFile.SUCCESS) {
editView.setTitle(this.file.getName());
editView.setMarkdown(getMarkdown()); editView.setMarkdown(getMarkdown());
onMarkdownEdited(); onMarkdownEdited();
} else { } else {
@ -104,6 +105,16 @@ public class MarkdownPresenterImpl implements MarkdownPresenter {
fileHandler.post(fileLoader); fileHandler.post(fileLoader);
} }
@Override
public void newFile(String newName) {
if (this.editView != null) {
this.file.setContent(this.editView.getMarkdown());
this.editView.setTitle(newName);
this.editView.setMarkdown("");
}
this.file = new MarkdownFile(newName, "", "");
}
@Override @Override
public void setEditView(MarkdownEditView editView) { public void setEditView(MarkdownEditView editView) {
this.editView = editView; this.editView = editView;
@ -115,10 +126,15 @@ public class MarkdownPresenterImpl implements MarkdownPresenter {
} }
@Override @Override
public void saveMarkdown(String filePath) { public void saveMarkdown(MarkdownSavedListener listener, String filePath) {
Runnable fileSaver = () -> { Runnable fileSaver = () -> {
int code; int code;
code = file.save(filePath); code = file.save(filePath);
if (listener != null) {
listener.saveComplete();
}
if (editView != null) { if (editView != null) {
if (code == MarkdownFile.SUCCESS) { if (code == MarkdownFile.SUCCESS) {
editView.showFileSavedMessage(); editView.showFileSavedMessage();
@ -130,11 +146,6 @@ public class MarkdownPresenterImpl implements MarkdownPresenter {
fileHandler.post(fileSaver); fileHandler.post(fileSaver);
} }
@Override
public void saveMarkdown() {
file.save();
}
@Override @Override
public void onMarkdownEdited(String markdown) { public void onMarkdownEdited(String markdown) {
setMarkdown(markdown); setMarkdown(markdown);

View file

@ -1,13 +1,9 @@
package com.wbrawner.simplemarkdown.view; package com.wbrawner.simplemarkdown.view;
/**
* Created by billy on 8/22/17.
*/
public interface MarkdownEditView { public interface MarkdownEditView {
String getMarkdown(); String getMarkdown();
void setMarkdown(String markdown); void setMarkdown(String markdown);
void setTitle(String title);
void showFileSavedMessage(); void showFileSavedMessage();
void showFileSavedError(int code); void showFileSavedError(int code);
void showFileLoadedMessage(); void showFileLoadedMessage();

View file

@ -2,24 +2,18 @@ package com.wbrawner.simplemarkdown.view.activity;
import android.Manifest; import android.Manifest;
import android.content.Intent; import android.content.Intent;
import android.content.SharedPreferences;
import android.content.pm.PackageManager; import android.content.pm.PackageManager;
import android.content.res.Configuration; import android.content.res.Configuration;
import android.os.Build; import android.os.Build;
import android.os.Environment;
import android.preference.PreferenceManager;
import android.support.design.widget.TabLayout; import android.support.design.widget.TabLayout;
import android.support.v4.app.ActivityCompat; import android.support.v4.app.ActivityCompat;
import android.support.v4.content.ContextCompat; import android.support.v4.content.ContextCompat;
import android.support.v4.view.ViewPager; import android.support.v4.view.ViewPager;
import android.support.v7.app.AlertDialog;
import android.support.v7.app.AppCompatActivity; import android.support.v7.app.AppCompatActivity;
import android.os.Bundle; import android.os.Bundle;
import android.text.InputType;
import android.view.Menu; import android.view.Menu;
import android.view.MenuItem; import android.view.MenuItem;
import android.view.View; import android.view.View;
import android.widget.EditText;
import android.widget.Toast; import android.widget.Toast;
import com.wbrawner.simplemarkdown.MarkdownApplication; import com.wbrawner.simplemarkdown.MarkdownApplication;
@ -56,6 +50,7 @@ public class MainActivity extends AppCompatActivity
TabLayout tabLayout; TabLayout tabLayout;
private static final String TAG = MainActivity.class.getSimpleName(); private static final String TAG = MainActivity.class.getSimpleName();
private NewFileHandler newFileHandler;
@Override @Override
protected void onCreate(Bundle savedInstanceState) { protected void onCreate(Bundle savedInstanceState) {
@ -74,13 +69,14 @@ public class MainActivity extends AppCompatActivity
== Configuration.ORIENTATION_LANDSCAPE) { == Configuration.ORIENTATION_LANDSCAPE) {
tabLayout.setVisibility(View.GONE); tabLayout.setVisibility(View.GONE);
} }
newFileHandler = new NewFileHandler();
} }
@Override @Override
protected void onPause() { protected void onPause() {
super.onPause(); super.onPause();
if (Utils.isAutosaveEnabled(this)) { if (Utils.isAutosaveEnabled(this)) {
presenter.saveMarkdown(); presenter.saveMarkdown(null, null);
} }
} }
@ -142,6 +138,7 @@ public class MainActivity extends AppCompatActivity
} }
break; break;
case R.id.action_new: case R.id.action_new:
presenter.saveMarkdown(newFileHandler, null);
break; break;
case R.id.action_help: case R.id.action_help:
showInfoActivity(R.id.action_help); showInfoActivity(R.id.action_help);
@ -262,7 +259,7 @@ public class MainActivity extends AppCompatActivity
break; break;
} }
String path = data.getStringExtra(EXTRA_FILE_PATH); String path = data.getStringExtra(EXTRA_FILE_PATH);
presenter.saveMarkdown(path); presenter.saveMarkdown(null, path);
setTitle(presenter.getFileName()); setTitle(presenter.getFileName());
break; break;
} }
@ -293,4 +290,12 @@ public class MainActivity extends AppCompatActivity
super.onResume(); super.onResume();
setTitle(presenter.getFileName()); setTitle(presenter.getFileName());
} }
private class NewFileHandler implements MarkdownPresenter.MarkdownSavedListener {
@Override
public void saveComplete() {
String newFile = Utils.getDefaultFileName(MainActivity.this);
presenter.newFile(newFile);
}
}
} }

View file

@ -9,6 +9,7 @@ import com.wbrawner.simplemarkdown.R;
public class SettingsActivity extends AppCompatActivity { public class SettingsActivity extends AppCompatActivity {
public static final String KEY_AUTOSAVE = "autosave";
public static final String KEY_DOCS_PATH = "defaultRootDir"; public static final String KEY_DOCS_PATH = "defaultRootDir";
@Override @Override

View file

@ -1,27 +1,16 @@
package com.wbrawner.simplemarkdown.view.activity; package com.wbrawner.simplemarkdown.view.activity;
import android.content.Context;
import android.content.Intent; import android.content.Intent;
import android.database.Cursor;
import android.net.Uri;
import android.os.Bundle; import android.os.Bundle;
import android.provider.OpenableColumns;
import android.support.annotation.Nullable; import android.support.annotation.Nullable;
import android.support.v7.app.AppCompatActivity; import android.support.v7.app.AppCompatActivity;
import android.widget.Toast;
import com.wbrawner.simplemarkdown.MarkdownApplication; import com.wbrawner.simplemarkdown.MarkdownApplication;
import com.wbrawner.simplemarkdown.R; import com.wbrawner.simplemarkdown.Utils;
import com.wbrawner.simplemarkdown.presentation.MarkdownPresenter; import com.wbrawner.simplemarkdown.presentation.MarkdownPresenter;
import java.io.InputStream;
import javax.inject.Inject; import javax.inject.Inject;
/**
* Created by billy on 8/23/17.
*/
public class SplashActivity extends AppCompatActivity { public class SplashActivity extends AppCompatActivity {
@Inject @Inject
@ -31,10 +20,16 @@ public class SplashActivity extends AppCompatActivity {
protected void onCreate(@Nullable Bundle savedInstanceState) { protected void onCreate(@Nullable Bundle savedInstanceState) {
super.onCreate(savedInstanceState); super.onCreate(savedInstanceState);
((MarkdownApplication) getApplication()).getComponent().inject(this); ((MarkdownApplication) getApplication()).getComponent().inject(this);
String defaultName = Utils.getDefaultFileName(this);
Intent intent = getIntent(); Intent intent = getIntent();
if (intent != null && intent.getData() != null) { if (intent != null && intent.getData() != null) {
presenter.loadFromUri(getApplicationContext(), intent.getData()); presenter.loadFromUri(getApplicationContext(), intent.getData());
} else {
presenter.setFileName(defaultName);
} }
startActivity(new Intent(this, MainActivity.class)); startActivity(new Intent(this, MainActivity.class));
finish(); finish();
} }

View file

@ -87,6 +87,11 @@ public class EditFragment extends Fragment implements MarkdownEditView {
return markdownEditor.getText().toString(); return markdownEditor.getText().toString();
} }
@Override
public void setTitle(String title) {
getActivity().setTitle(title);
}
@Override @Override
public void showFileSavedMessage() { public void showFileSavedMessage() {
String location = Utils.getDocsPath(getActivity()) + presenter.getFileName(); String location = Utils.getDocsPath(getActivity()) + presenter.getFileName();

View file

@ -18,7 +18,6 @@
app:showAsAction="never" /> app:showAsAction="never" />
<item <item
android:id="@+id/action_new" android:id="@+id/action_new"
android:visible="false"
android:orderInCategory="150" android:orderInCategory="150"
android:title="@string/action_new" android:title="@string/action_new"
app:showAsAction="never" /> app:showAsAction="never" />

View file

@ -0,0 +1,100 @@
package com.wbrawner.simplemarkdown;
import android.content.Context;
import android.content.SharedPreferences;
import android.preference.PreferenceManager;
import com.wbrawner.simplemarkdown.view.activity.SettingsActivity;
import org.junit.After;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.robolectric.RobolectricTestRunner;
import org.robolectric.RuntimeEnvironment;
import java.io.File;
import java.io.IOException;
import static org.junit.Assert.*;
@RunWith(RobolectricTestRunner.class)
@SuppressWarnings("ResultOfMethodCallIgnored")
public class UtilsTest {
private Context context;
private SharedPreferences sharedPreferences;
private String rootDir;
@Before
public void setup() {
context = RuntimeEnvironment.application.getApplicationContext();
sharedPreferences = PreferenceManager.getDefaultSharedPreferences(context);
sharedPreferences.edit().clear().apply();
rootDir = System.getProperty("java.io.tmpdir") + "/SimpleMarkdown";
new File(rootDir).mkdir();
}
@After
public void tearDown() {
rmdir(new File(rootDir));
}
@Test
public void getDocsPath() throws Exception {
sharedPreferences.edit().putString(SettingsActivity.KEY_DOCS_PATH, rootDir).apply();
assertEquals(rootDir, Utils.getDocsPath(context));
}
@Test
public void getDefaultFileName() throws Exception {
sharedPreferences.edit().putString(SettingsActivity.KEY_DOCS_PATH, rootDir).apply();
new File(rootDir, "dummy.md").createNewFile();
new File(rootDir, "dummy1.md").createNewFile();
new File(rootDir, "Untitled-a.md").createNewFile();
String firstDefaultName = Utils.getDefaultFileName(context);
assertEquals("Untitled.md", firstDefaultName);
File firstFile = new File(rootDir, firstDefaultName);
firstFile.createNewFile();
String secondDefaultName = Utils.getDefaultFileName(context);
assertEquals("Untitled-1.md", secondDefaultName);
File secondFile = new File(rootDir, secondDefaultName);
secondFile.createNewFile();
String thirdDefaultName = Utils.getDefaultFileName(context);
assertEquals("Untitled-2.md", thirdDefaultName);
}
@Test
public void getDefaultFileNameDoubleDigitTest() throws IOException {
sharedPreferences.edit().putString(SettingsActivity.KEY_DOCS_PATH, rootDir).apply();
for (int i = 0; i < 11; i++) {
new File(rootDir, "Untitled-" + i + ".md").createNewFile();
}
assertTrue(new File(rootDir, "Untitled-10.md").exists());
String defaultName = Utils.getDefaultFileName(context);
assertEquals("Untitled-11.md", defaultName);
}
@Test
public void isAutosaveEnabled() throws Exception {
assertTrue(Utils.isAutosaveEnabled(context));
sharedPreferences.edit().putBoolean(SettingsActivity.KEY_AUTOSAVE, false).apply();
assertFalse(Utils.isAutosaveEnabled(context));
}
private void rmdir(File dir) {
for (File file : dir.listFiles()) {
if (file.isFile()) {
file.delete();
} else {
rmdir(file);
}
}
dir.delete();
}
}