From f82330a92f34675cbb5fb755043dda9237922e12 Mon Sep 17 00:00:00 2001 From: Billy Brawner Date: Sat, 27 Jan 2018 16:51:45 -0600 Subject: [PATCH] Add New file creation menu option --- app/build.gradle | 6 ++ .../com/wbrawner/simplemarkdown/Utils.java | 53 +++++++++- .../simplemarkdown/model/MarkdownFile.java | 14 +-- .../presentation/MarkdownPresenter.java | 14 +-- .../presentation/MarkdownPresenterImpl.java | 23 ++-- .../simplemarkdown/view/MarkdownEditView.java | 6 +- .../view/activity/MainActivity.java | 21 ++-- .../view/activity/SettingsActivity.java | 1 + .../view/activity/SplashActivity.java | 19 ++-- .../view/fragment/EditFragment.java | 5 + app/src/main/res/menu/menu_edit.xml | 1 - .../wbrawner/simplemarkdown/UtilsTest.java | 100 ++++++++++++++++++ 12 files changed, 214 insertions(+), 49 deletions(-) create mode 100644 app/src/test/java/com/wbrawner/simplemarkdown/UtilsTest.java diff --git a/app/build.gradle b/app/build.gradle index a180fe1..bab9850 100644 --- a/app/build.gradle +++ b/app/build.gradle @@ -49,6 +49,11 @@ android { dexOptions { jumboMode true } + testOptions { + unitTests { + includeAndroidResources = true + } + } } ext { @@ -72,6 +77,7 @@ dependencies { compile 'io.reactivex.rxjava2:rxandroid:2.0.1' compile 'io.reactivex.rxjava2:rxjava:2.1.0' 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:runner:1.0.1' androidTestCompile 'com.android.support.test.uiautomator:uiautomator-v18:2.1.3' diff --git a/app/src/main/java/com/wbrawner/simplemarkdown/Utils.java b/app/src/main/java/com/wbrawner/simplemarkdown/Utils.java index 8298b92..5db9e67 100644 --- a/app/src/main/java/com/wbrawner/simplemarkdown/Utils.java +++ b/app/src/main/java/com/wbrawner/simplemarkdown/Utils.java @@ -7,9 +7,11 @@ import android.preference.PreferenceManager; import com.wbrawner.simplemarkdown.view.activity.SettingsActivity; -/** - * Created by billy on 1/15/2018. - */ +import java.io.File; +import java.io.FilenameFilter; +import java.util.Locale; +import java.util.regex.Matcher; +import java.util.regex.Pattern; 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) { SharedPreferences prefs = PreferenceManager.getDefaultSharedPreferences(context); return prefs.getBoolean( - "autosave", + SettingsActivity.KEY_AUTOSAVE, true ); } diff --git a/app/src/main/java/com/wbrawner/simplemarkdown/model/MarkdownFile.java b/app/src/main/java/com/wbrawner/simplemarkdown/model/MarkdownFile.java index d26c0cb..8c17d4a 100644 --- a/app/src/main/java/com/wbrawner/simplemarkdown/model/MarkdownFile.java +++ b/app/src/main/java/com/wbrawner/simplemarkdown/model/MarkdownFile.java @@ -36,6 +36,9 @@ public class MarkdownFile { public MarkdownFile(String name, String path, String content) { this.name = name; + if (path == null || path.isEmpty()) { + path = defaultRootDir; + } this.path = path; this.content = content; } @@ -125,6 +128,7 @@ public class MarkdownFile { try { this.name = markdownFile.getName(); this.path = markdownFile.getParentFile().getAbsolutePath(); + StringBuilder sb = new StringBuilder(); String line; reader = new BufferedReader(new FileReader(markdownFile)); @@ -158,6 +162,10 @@ public class MarkdownFile { public int save(String path) { int code; + if (path == null) { + path = this.getFullPath(); + } + File markdownFile = new File(path); if (!markdownFile.exists()) { try { @@ -192,10 +200,4 @@ public class MarkdownFile { this.path = markdownFile.getParentFile().getAbsolutePath(); return code; } - - public int save() { - if (this.name.isEmpty()) - return PARAMETERS_MISSING; - return save(this.getFullPath()); - } } diff --git a/app/src/main/java/com/wbrawner/simplemarkdown/presentation/MarkdownPresenter.java b/app/src/main/java/com/wbrawner/simplemarkdown/presentation/MarkdownPresenter.java index 2d637b6..42ece35 100644 --- a/app/src/main/java/com/wbrawner/simplemarkdown/presentation/MarkdownPresenter.java +++ b/app/src/main/java/com/wbrawner/simplemarkdown/presentation/MarkdownPresenter.java @@ -9,10 +9,6 @@ import com.wbrawner.simplemarkdown.view.MarkdownPreviewView; import java.io.File; import java.io.InputStream; -/** - * Created by billy on 8/22/17. - */ - public interface MarkdownPresenter { File getFile(); void loadMarkdown(); @@ -21,10 +17,10 @@ public interface MarkdownPresenter { void loadMarkdown(File file); void loadFromUri(Context context, Uri fileUri); void loadTempMarkdown(InputStream in, OnTempFileLoadedListener listener); + void newFile(String path); void setEditView(MarkdownEditView editView); void setPreviewView(MarkdownPreviewView previewView); - void saveMarkdown(); - void saveMarkdown(String filePath); + void saveMarkdown(MarkdownSavedListener listener, String filePath); void onMarkdownEdited(); void onMarkdownEdited(String markdown); String getFileName(); @@ -35,8 +31,12 @@ public interface MarkdownPresenter { String getMarkdown(); void setMarkdown(String markdown); - public abstract class OnTempFileLoadedListener { + abstract class OnTempFileLoadedListener { public abstract void onSuccess(String markdown); public abstract void onError(int code); } + + interface MarkdownSavedListener { + void saveComplete(); + } } diff --git a/app/src/main/java/com/wbrawner/simplemarkdown/presentation/MarkdownPresenterImpl.java b/app/src/main/java/com/wbrawner/simplemarkdown/presentation/MarkdownPresenterImpl.java index 011f9d6..c8f6d8a 100644 --- a/app/src/main/java/com/wbrawner/simplemarkdown/presentation/MarkdownPresenterImpl.java +++ b/app/src/main/java/com/wbrawner/simplemarkdown/presentation/MarkdownPresenterImpl.java @@ -63,6 +63,7 @@ public class MarkdownPresenterImpl implements MarkdownPresenter { int result = this.file.load(file); if (editView != null) { if (result == MarkdownFile.SUCCESS) { + editView.setTitle(this.file.getName()); editView.setMarkdown(getMarkdown()); onMarkdownEdited(); } else { @@ -104,6 +105,16 @@ public class MarkdownPresenterImpl implements MarkdownPresenter { 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 public void setEditView(MarkdownEditView editView) { this.editView = editView; @@ -115,10 +126,15 @@ public class MarkdownPresenterImpl implements MarkdownPresenter { } @Override - public void saveMarkdown(String filePath) { + public void saveMarkdown(MarkdownSavedListener listener, String filePath) { Runnable fileSaver = () -> { int code; code = file.save(filePath); + + if (listener != null) { + listener.saveComplete(); + } + if (editView != null) { if (code == MarkdownFile.SUCCESS) { editView.showFileSavedMessage(); @@ -130,11 +146,6 @@ public class MarkdownPresenterImpl implements MarkdownPresenter { fileHandler.post(fileSaver); } - @Override - public void saveMarkdown() { - file.save(); - } - @Override public void onMarkdownEdited(String markdown) { setMarkdown(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 index 9b26695..00fdfbe 100644 --- a/app/src/main/java/com/wbrawner/simplemarkdown/view/MarkdownEditView.java +++ b/app/src/main/java/com/wbrawner/simplemarkdown/view/MarkdownEditView.java @@ -1,13 +1,9 @@ package com.wbrawner.simplemarkdown.view; -/** - * Created by billy on 8/22/17. - */ - public interface MarkdownEditView { String getMarkdown(); void setMarkdown(String markdown); - + void setTitle(String title); void showFileSavedMessage(); void showFileSavedError(int code); void showFileLoadedMessage(); 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 index 3619163..424b3be 100644 --- a/app/src/main/java/com/wbrawner/simplemarkdown/view/activity/MainActivity.java +++ b/app/src/main/java/com/wbrawner/simplemarkdown/view/activity/MainActivity.java @@ -2,24 +2,18 @@ package com.wbrawner.simplemarkdown.view.activity; import android.Manifest; import android.content.Intent; -import android.content.SharedPreferences; import android.content.pm.PackageManager; import android.content.res.Configuration; import android.os.Build; -import android.os.Environment; -import android.preference.PreferenceManager; 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; @@ -56,6 +50,7 @@ public class MainActivity extends AppCompatActivity TabLayout tabLayout; private static final String TAG = MainActivity.class.getSimpleName(); + private NewFileHandler newFileHandler; @Override protected void onCreate(Bundle savedInstanceState) { @@ -74,13 +69,14 @@ public class MainActivity extends AppCompatActivity == Configuration.ORIENTATION_LANDSCAPE) { tabLayout.setVisibility(View.GONE); } + newFileHandler = new NewFileHandler(); } @Override protected void onPause() { super.onPause(); if (Utils.isAutosaveEnabled(this)) { - presenter.saveMarkdown(); + presenter.saveMarkdown(null, null); } } @@ -142,6 +138,7 @@ public class MainActivity extends AppCompatActivity } break; case R.id.action_new: + presenter.saveMarkdown(newFileHandler, null); break; case R.id.action_help: showInfoActivity(R.id.action_help); @@ -262,7 +259,7 @@ public class MainActivity extends AppCompatActivity break; } String path = data.getStringExtra(EXTRA_FILE_PATH); - presenter.saveMarkdown(path); + presenter.saveMarkdown(null, path); setTitle(presenter.getFileName()); break; } @@ -293,4 +290,12 @@ public class MainActivity extends AppCompatActivity super.onResume(); setTitle(presenter.getFileName()); } + + private class NewFileHandler implements MarkdownPresenter.MarkdownSavedListener { + @Override + public void saveComplete() { + String newFile = Utils.getDefaultFileName(MainActivity.this); + presenter.newFile(newFile); + } + } } diff --git a/app/src/main/java/com/wbrawner/simplemarkdown/view/activity/SettingsActivity.java b/app/src/main/java/com/wbrawner/simplemarkdown/view/activity/SettingsActivity.java index 5950be2..67ef403 100644 --- a/app/src/main/java/com/wbrawner/simplemarkdown/view/activity/SettingsActivity.java +++ b/app/src/main/java/com/wbrawner/simplemarkdown/view/activity/SettingsActivity.java @@ -9,6 +9,7 @@ import com.wbrawner.simplemarkdown.R; public class SettingsActivity extends AppCompatActivity { + public static final String KEY_AUTOSAVE = "autosave"; public static final String KEY_DOCS_PATH = "defaultRootDir"; @Override diff --git a/app/src/main/java/com/wbrawner/simplemarkdown/view/activity/SplashActivity.java b/app/src/main/java/com/wbrawner/simplemarkdown/view/activity/SplashActivity.java index b56d203..28fe09a 100644 --- a/app/src/main/java/com/wbrawner/simplemarkdown/view/activity/SplashActivity.java +++ b/app/src/main/java/com/wbrawner/simplemarkdown/view/activity/SplashActivity.java @@ -1,27 +1,16 @@ package com.wbrawner.simplemarkdown.view.activity; -import android.content.Context; import android.content.Intent; -import android.database.Cursor; -import android.net.Uri; import android.os.Bundle; -import android.provider.OpenableColumns; import android.support.annotation.Nullable; import android.support.v7.app.AppCompatActivity; -import android.widget.Toast; import com.wbrawner.simplemarkdown.MarkdownApplication; -import com.wbrawner.simplemarkdown.R; +import com.wbrawner.simplemarkdown.Utils; import com.wbrawner.simplemarkdown.presentation.MarkdownPresenter; -import java.io.InputStream; - import javax.inject.Inject; -/** - * Created by billy on 8/23/17. - */ - public class SplashActivity extends AppCompatActivity { @Inject @@ -31,10 +20,16 @@ public class SplashActivity extends AppCompatActivity { protected void onCreate(@Nullable Bundle savedInstanceState) { super.onCreate(savedInstanceState); ((MarkdownApplication) getApplication()).getComponent().inject(this); + + String defaultName = Utils.getDefaultFileName(this); + Intent intent = getIntent(); if (intent != null && intent.getData() != null) { presenter.loadFromUri(getApplicationContext(), intent.getData()); + } else { + presenter.setFileName(defaultName); } + startActivity(new Intent(this, MainActivity.class)); finish(); } 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 index 19458bd..69bc34f 100644 --- a/app/src/main/java/com/wbrawner/simplemarkdown/view/fragment/EditFragment.java +++ b/app/src/main/java/com/wbrawner/simplemarkdown/view/fragment/EditFragment.java @@ -87,6 +87,11 @@ public class EditFragment extends Fragment implements MarkdownEditView { return markdownEditor.getText().toString(); } + @Override + public void setTitle(String title) { + getActivity().setTitle(title); + } + @Override public void showFileSavedMessage() { String location = Utils.getDocsPath(getActivity()) + presenter.getFileName(); diff --git a/app/src/main/res/menu/menu_edit.xml b/app/src/main/res/menu/menu_edit.xml index e9f9ce1..0733ff9 100644 --- a/app/src/main/res/menu/menu_edit.xml +++ b/app/src/main/res/menu/menu_edit.xml @@ -18,7 +18,6 @@ app:showAsAction="never" /> diff --git a/app/src/test/java/com/wbrawner/simplemarkdown/UtilsTest.java b/app/src/test/java/com/wbrawner/simplemarkdown/UtilsTest.java new file mode 100644 index 0000000..60b2305 --- /dev/null +++ b/app/src/test/java/com/wbrawner/simplemarkdown/UtilsTest.java @@ -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(); + } +} \ No newline at end of file