Autosave files in onPause

This commit is contained in:
William Brawner 2018-01-16 00:04:04 +01:00 committed by William Brawner
parent 9cb9c36e06
commit db57b56965
17 changed files with 334 additions and 55 deletions

View file

@ -39,6 +39,10 @@
</intent-filter>
</activity>
<activity android:name=".view.activity.MainActivity" />
<activity
android:name=".view.activity.SettingsActivity"
android:label="@string/title_activity_settings"
android:theme="@style/AppTheme" />
<provider
android:name="android.support.v4.content.FileProvider"
@ -58,7 +62,7 @@
<activity
android:name=".view.activity.ExplorerActivity"
android:label="@string/title_activity_explorer"
android:theme="@style/AppTheme.NoActionBar"></activity>
android:theme="@style/AppTheme.NoActionBar" />
</application>
</manifest>

View file

@ -0,0 +1,33 @@
package com.wbrawner.simplemarkdown;
import android.content.Context;
import android.content.SharedPreferences;
import android.os.Environment;
import android.preference.PreferenceManager;
import com.wbrawner.simplemarkdown.view.activity.SettingsActivity;
/**
* Created by billy on 1/15/2018.
*/
public class Utils {
public static String getDocsPath(Context context) {
SharedPreferences prefs = PreferenceManager.getDefaultSharedPreferences(context);
return prefs.getString(
SettingsActivity.KEY_DOCS_PATH,
Environment.getExternalStorageDirectory() + "/" +
Environment.DIRECTORY_DOCUMENTS + "/"
);
}
public static boolean isAutosaveEnabled(Context context) {
SharedPreferences prefs = PreferenceManager.getDefaultSharedPreferences(context);
return prefs.getBoolean(
"autosave",
true
);
}
}

View file

@ -8,14 +8,15 @@ import java.io.FileReader;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStreamWriter;
import java.util.Locale;
import java.util.Scanner;
import static android.content.ContentValues.TAG;
/**
* Created by billy on 8/22/17.
* This class serves as a wrapper to manage the manage the file input and output operations, as well
* as to keep track of the data itself in memory.
*/
public class MarkdownFile {
public static final int SUCCESS = 0;
public static final int FILE_EXISTS = 1;
@ -23,6 +24,12 @@ public class MarkdownFile {
public static final int READ_ERROR = 3;
public static final int WRITE_ERROR = 4;
public static final int PARAMETERS_MISSING = 5;
public static void setDefaultRootDir(String defaultRootDir) {
MarkdownFile.defaultRootDir = defaultRootDir;
}
private static String defaultRootDir = "";
private String name;
private String path;
private String content;
@ -67,10 +74,21 @@ public class MarkdownFile {
public String getFullPath() {
String fullPath;
if (!this.path.endsWith("/"))
if (this.path.isEmpty()) {
this.path = defaultRootDir;
}
if (this.path.endsWith(this.name)) {
return this.path;
}
if (!this.path.endsWith("/")) {
fullPath = this.path + "/";
else
} else {
fullPath = this.path;
}
return fullPath + this.name;
}
@ -101,6 +119,7 @@ public class MarkdownFile {
}
public int load(File markdownFile) {
System.out.println("Attempting to load file from " + markdownFile.getAbsolutePath());
int code;
if (markdownFile.exists() && markdownFile.canRead()) {
BufferedReader reader = null;
@ -114,6 +133,16 @@ public class MarkdownFile {
sb.append(line + "\n");
this.content = sb.toString();
code = SUCCESS;
System.out.println(String.format(
Locale.ENGLISH,
"Successfully loaded file from %s",
markdownFile.getAbsolutePath()
));
System.out.println(String.format(
Locale.ENGLISH,
"File contents %s",
this.content
));
} catch (FileNotFoundException e) {
code = FILE_NOT_EXISTS;
} catch (IOException e) {
@ -133,12 +162,13 @@ public class MarkdownFile {
}
public int load() {
if (!parametersOk())
if (this.name.isEmpty())
return PARAMETERS_MISSING;
return load(getFullPath());
}
public int save(String path) {
System.out.println("Attempting to save file to " + path);
int code;
File markdownFile = new File(path);
if (!markdownFile.exists()) {
@ -156,8 +186,12 @@ public class MarkdownFile {
);
writer.write(this.content);
code = SUCCESS;
}
catch (IOException e) {
System.out.println(String.format(
Locale.ENGLISH,
"File successfully saved to %s",
path
));
} catch (IOException e) {
code = WRITE_ERROR;
}
if (writer != null) {
@ -176,24 +210,30 @@ public class MarkdownFile {
}
public int save() {
if (!parametersOk())
if (this.name.isEmpty())
return PARAMETERS_MISSING;
return save(this.getFullPath());
}
public int fileExists(String path) {
public static int fileExists(String path) {
if (new File(path).exists())
return FILE_EXISTS;
return FILE_NOT_EXISTS;
}
public int fileExists() {
if (parametersOk())
if (!this.name.isEmpty())
return fileExists(getFullPath());
return PARAMETERS_MISSING;
}
private boolean parametersOk() {
return !this.name.isEmpty() && !this.path.isEmpty();
public static void deleteTempFile(String s) {
File tempFile = new File(s);
if (tempFile.exists()) {
try {
tempFile.delete();
} catch (Exception e) {
}
}
}
}

View file

@ -1,10 +0,0 @@
package com.wbrawner.simplemarkdown.presentation;
/**
* Created by billy on 8/22/17.
*/
public interface LifecyclePresenter {
void resume();
void pause();
}

View file

@ -13,8 +13,9 @@ import java.io.InputStream;
* Created by billy on 8/22/17.
*/
public interface MarkdownPresenter extends LifecyclePresenter {
public interface MarkdownPresenter {
File getFile();
void loadMarkdown();
void loadMarkdown(String filePath);
void loadMarkdown(InputStream in);
void loadMarkdown(File file);
@ -22,15 +23,19 @@ public interface MarkdownPresenter extends LifecyclePresenter {
void loadTempMarkdown(InputStream in, OnTempFileLoadedListener listener);
void setEditView(MarkdownEditView editView);
void setPreviewView(MarkdownPreviewView previewView);
void saveMarkdown();
void saveMarkdown(String filePath);
void onMarkdownEdited();
void onMarkdownEdited(String markdown);
String getFileName();
void setFileName(String name);
void setRootDir(String path);
String generateHTML();
String generateHTML(String markdown);
String getMarkdown();
void setMarkdown(String markdown);
void loadTempFile();
public abstract class OnTempFileLoadedListener {
public abstract void onSuccess(String markdown);
public abstract void onError(int code);

View file

@ -3,7 +3,9 @@ package com.wbrawner.simplemarkdown.presentation;
import android.content.Context;
import android.database.Cursor;
import android.net.Uri;
import android.os.Handler;
import android.provider.OpenableColumns;
import android.util.Log;
import com.commonsware.cwac.anddown.AndDown;
import com.wbrawner.simplemarkdown.model.MarkdownFile;
@ -13,10 +15,6 @@ import com.wbrawner.simplemarkdown.view.MarkdownPreviewView;
import java.io.File;
import java.io.InputStream;
/**
* Created by billy on 8/22/17.
*/
public class MarkdownPresenterImpl implements MarkdownPresenter {
private MarkdownFile file;
private MarkdownEditView editView;
@ -26,20 +24,12 @@ public class MarkdownPresenterImpl implements MarkdownPresenter {
AndDown.HOEDOWN_EXT_STRIKETHROUGH | AndDown.HOEDOWN_EXT_TABLES |
AndDown.HOEDOWN_EXT_UNDERLINE | AndDown.HOEDOWN_EXT_SUPERSCRIPT |
AndDown.HOEDOWN_EXT_FENCED_CODE;
private Handler fileHandler = new Handler();
public MarkdownPresenterImpl(MarkdownFile file) {
this.file = file;
}
@Override
public void resume() {
}
@Override
public void pause() {
saveMarkdown("");
}
@Override
public void loadMarkdown(String filePath) {
File markdownFile = new File(filePath);
@ -51,6 +41,22 @@ public class MarkdownPresenterImpl implements MarkdownPresenter {
return new File(file.getFullPath());
}
@Override
public void loadMarkdown() {
Runnable fileLoader = () -> {
int result = this.file.load();
if (editView != null) {
if (result == MarkdownFile.SUCCESS) {
editView.setMarkdown(getMarkdown());
onMarkdownEdited();
} else {
editView.showFileLoadeddError(result);
}
}
};
fileHandler.post(fileLoader);
}
@Override
public void loadMarkdown(File file) {
Runnable fileLoader = () -> {
@ -64,7 +70,7 @@ public class MarkdownPresenterImpl implements MarkdownPresenter {
}
}
};
fileLoader.run();
fileHandler.post(fileLoader);
}
@Override
@ -80,7 +86,7 @@ public class MarkdownPresenterImpl implements MarkdownPresenter {
editView.showFileLoadeddError(result);
}
};
fileLoader.run();
fileHandler.post(fileLoader);
}
@Override
@ -95,7 +101,7 @@ public class MarkdownPresenterImpl implements MarkdownPresenter {
listener.onError(result);
}
};
fileLoader.run();
fileHandler.post(fileLoader);
}
@Override
@ -121,7 +127,12 @@ public class MarkdownPresenterImpl implements MarkdownPresenter {
}
}
};
fileSaver.run();
fileHandler.post(fileSaver);
}
@Override
public void saveMarkdown() {
file.save();
}
@Override
@ -131,7 +142,7 @@ public class MarkdownPresenterImpl implements MarkdownPresenter {
if (previewView != null)
previewView.updatePreview(generateHTML());
};
generateMarkdown.run();
fileHandler.post(generateMarkdown);
}
@Override
@ -160,6 +171,11 @@ public class MarkdownPresenterImpl implements MarkdownPresenter {
file.setName(name);
}
@Override
public void setRootDir(String path) {
MarkdownFile.setDefaultRootDir(path);
}
@Override
public String getMarkdown() {
return file.getContent();
@ -193,4 +209,12 @@ public class MarkdownPresenterImpl implements MarkdownPresenter {
editView.showFileLoadeddError(MarkdownFile.READ_ERROR);
}
}
@Override
public void loadTempFile() {
String tempFileName = "auto-" + getFileName();
String tempFilePath = editView.getTempFilePath();
loadMarkdown(tempFilePath + tempFileName);
MarkdownFile.deleteTempFile(tempFilePath + tempFileName);
}
}

View file

@ -12,4 +12,6 @@ public interface MarkdownEditView {
void showFileSavedError(int code);
void showFileLoadedMessage();
void showFileLoadeddError(int code);
String getTempFilePath();
}

View file

@ -17,6 +17,7 @@ import android.widget.ListView;
import android.widget.SimpleAdapter;
import com.wbrawner.simplemarkdown.R;
import com.wbrawner.simplemarkdown.Utils;
import java.io.File;
import java.util.ArrayList;
@ -50,9 +51,7 @@ public class ExplorerActivity extends AppCompatActivity {
return;
}
defaultDocsDirPath = Environment.getExternalStorageDirectory() + "/" +
Environment.DIRECTORY_DOCUMENTS;
docsDirPath = defaultDocsDirPath;
docsDirPath = Utils.getDocsPath(this);
requestCode = intent.getIntExtra(MainActivity.EXTRA_REQUEST_CODE, -1);
switch (requestCode) {

View file

@ -2,10 +2,12 @@ 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;
@ -22,6 +24,7 @@ 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 com.wbrawner.simplemarkdown.view.adapter.EditPagerAdapter;
@ -61,6 +64,7 @@ public class MainActivity extends AppCompatActivity
((MarkdownApplication) getApplication()).getComponent().inject(this);
ButterKnife.bind(this);
presenter.setRootDir(Utils.getDocsPath(this));
pager.setAdapter(
new EditPagerAdapter(getSupportFragmentManager(), MainActivity.this)
);
@ -72,6 +76,14 @@ public class MainActivity extends AppCompatActivity
}
}
@Override
protected void onPause() {
super.onPause();
if (Utils.isAutosaveEnabled(this)) {
presenter.saveMarkdown();
}
}
@Override
public void onConfigurationChanged(Configuration newConfig) {
super.onConfigurationChanged(newConfig);
@ -134,6 +146,10 @@ public class MainActivity extends AppCompatActivity
case R.id.action_help:
showInfoActivity(R.id.action_help);
break;
case R.id.action_settings:
Intent settingsIntent = new Intent(MainActivity.this, SettingsActivity.class);
startActivity(settingsIntent);
break;
case R.id.action_libraries:
showInfoActivity(R.id.action_libraries);
break;
@ -206,11 +222,6 @@ public class MainActivity extends AppCompatActivity
builder.show();
}
public String getDocsPath() {
return Environment.getExternalStorageDirectory() + "/" +
Environment.DIRECTORY_DOCUMENTS + "/";
}
@Override
public void onRequestPermissionsResult(int requestCode,
String permissions[], int[] grantResults) {

View file

@ -0,0 +1,36 @@
package com.wbrawner.simplemarkdown.view.activity;
import android.os.Bundle;
import android.support.annotation.Nullable;
import android.support.v7.app.AppCompatActivity;
import android.view.MenuItem;
import com.wbrawner.simplemarkdown.R;
/**
* Created by billy on 1/14/2018.
*/
public class SettingsActivity extends AppCompatActivity {
public static final String KEY_DOCS_PATH = "defaultRootDir";
@Override
protected void onCreate(@Nullable Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_settings);
if (getSupportActionBar() != null) {
getSupportActionBar().setDisplayHomeAsUpEnabled(true);
}
}
@Override
public boolean onOptionsItemSelected(MenuItem item) {
switch (item.getItemId()) {
case android.R.id.home:
onBackPressed();
return true;
}
return super.onOptionsItemSelected(item);
}
}

View file

@ -1,6 +1,8 @@
package com.wbrawner.simplemarkdown.view.fragment;
import android.app.AlertDialog;
import android.os.Bundle;
import android.support.annotation.Nullable;
import android.support.v4.app.Fragment;
import android.view.LayoutInflater;
import android.view.View;
@ -11,10 +13,10 @@ import android.widget.Toast;
import com.jakewharton.rxbinding2.widget.RxTextView;
import com.wbrawner.simplemarkdown.MarkdownApplication;
import com.wbrawner.simplemarkdown.R;
import com.wbrawner.simplemarkdown.Utils;
import com.wbrawner.simplemarkdown.model.MarkdownFile;
import com.wbrawner.simplemarkdown.presentation.MarkdownPresenter;
import com.wbrawner.simplemarkdown.view.MarkdownEditView;
import com.wbrawner.simplemarkdown.view.activity.MainActivity;
import java.util.concurrent.TimeUnit;
@ -50,7 +52,6 @@ public class EditFragment extends Fragment implements MarkdownEditView {
View view = inflater.inflate(R.layout.fragment_edit, container, false);
unbinder = ButterKnife.bind(this, view);
((MarkdownApplication) getActivity().getApplication()).getComponent().inject(this);
Observable<String> obs = RxTextView.textChanges(markdownEditor)
.debounce(50, TimeUnit.MILLISECONDS).map(CharSequence::toString);
obs.subscribeOn(Schedulers.io());
@ -61,6 +62,13 @@ public class EditFragment extends Fragment implements MarkdownEditView {
return view;
}
@Override
public void onViewCreated(View view, @Nullable Bundle savedInstanceState) {
super.onViewCreated(view, savedInstanceState);
presenter.setEditView(EditFragment.this);
presenter.loadMarkdown();
}
@Override
public void onResume() {
super.onResume();
@ -81,7 +89,7 @@ public class EditFragment extends Fragment implements MarkdownEditView {
@Override
public void showFileSavedMessage() {
String location = ((MainActivity) getActivity()).getDocsPath() + presenter.getFileName();
String location = Utils.getDocsPath(getActivity()) + presenter.getFileName();
String message = getString(R.string.file_saved, location);
Toast.makeText(getActivity(), message, Toast.LENGTH_SHORT).show();
}
@ -120,4 +128,9 @@ public class EditFragment extends Fragment implements MarkdownEditView {
super.onDestroyView();
unbinder.unbind();
}
@Override
public String getTempFilePath() {
return getActivity().getFilesDir().getAbsolutePath() + "/";
}
}

View file

@ -0,0 +1,19 @@
package com.wbrawner.simplemarkdown.view.fragment;
import android.os.Bundle;
import android.preference.PreferenceFragment;
import android.support.annotation.Nullable;
import com.wbrawner.simplemarkdown.R;
/**
* Created by billy on 1/14/2018.
*/
public class SettingsFragment extends PreferenceFragment {
@Override
public void onCreate(@Nullable Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
addPreferencesFromResource(R.xml.pref_general);
}
}

View file

@ -0,0 +1,12 @@
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent"
xmlns:app="http://schemas.android.com/apk/res-auto">
<fragment
android:id="@+id/fragment_settings"
android:layout_width="match_parent"
android:layout_height="match_parent"
class="com.wbrawner.simplemarkdown.view.fragment.SettingsFragment" />
</LinearLayout>

View file

@ -16,5 +16,4 @@
android:paddingRight="8dp"
android:hint="@string/markdown_here"
android:scrollHorizontally="false" />
</LinearLayout>

View file

@ -22,6 +22,11 @@
android:orderInCategory="150"
android:title="@string/action_new"
app:showAsAction="never" />
<item
android:id="@+id/action_settings"
android:orderInCategory="200"
android:title="@string/action_settings"
app:showAsAction="never" />
<item
android:id="@+id/action_help"
android:orderInCategory="200"

View file

@ -29,4 +29,82 @@
<string name="action_use_sdcard">Use SD Card</string>
<string name="action_done">Done</string>
<string name="action_open">Open</string>
<string name="recover_file">An autosaved version of this file was found. Would you like to recover it?</string>
<string name="yes">Yes</string>
<string name="no">No</string>
<string name="title_activity_settings">Settings</string>
<!-- Strings related to Settings -->
<!-- Example General settings -->
<string name="pref_header_general">General</string>
<string name="pref_title_social_recommendations">Enable social recommendations</string>
<string name="pref_description_social_recommendations">Recommendations for people to contact
based on your message history
</string>
<string name="pref_title_display_name">Display name</string>
<string name="pref_default_display_name">John Smith</string>
<string name="pref_title_add_friends_to_messages">Add friends to messages</string>
<string-array name="pref_example_list_titles">
<item>Always</item>
<item>When possible</item>
<item>Never</item>
</string-array>
<string-array name="pref_example_list_values">
<item>1</item>
<item>0</item>
<item>-1</item>
</string-array>
<!-- Example settings for Data & Sync -->
<string name="pref_header_data_sync">Data &amp; sync</string>
<string name="pref_title_sync_frequency">Sync frequency</string>
<string-array name="pref_sync_frequency_titles">
<item>15 minutes</item>
<item>30 minutes</item>
<item>1 hour</item>
<item>3 hours</item>
<item>6 hours</item>
<item>Never</item>
</string-array>
<string-array name="pref_sync_frequency_values">
<item>15</item>
<item>30</item>
<item>60</item>
<item>180</item>
<item>360</item>
<item>-1</item>
</string-array>
<string-array name="list_preference_entries">
<item>Entry 1</item>
<item>Entry 2</item>
<item>Entry 3</item>
</string-array>
<string-array name="list_preference_entry_values">
<item>1</item>
<item>2</item>
<item>3</item>
</string-array>
<string-array name="multi_select_list_preference_default_value" />
<string name="pref_title_system_sync_settings">System sync settings</string>
<!-- Example settings for Notifications -->
<string name="pref_header_notifications">Notifications</string>
<string name="pref_title_new_message_notifications">New message notifications</string>
<string name="pref_title_ringtone">Ringtone</string>
<string name="pref_ringtone_silent">Silent</string>
<string name="pref_title_vibrate">Vibrate</string>
<string name="pref_title_autosave">Enable autosave</string>
<string name="pref_description_autosave">Automatically save files when closing the app</string>
</resources>

View file

@ -0,0 +1,9 @@
<PreferenceScreen xmlns:android="http://schemas.android.com/apk/res/android">
<SwitchPreference
android:defaultValue="true"
android:key="autosave"
android:summary="@string/pref_description_autosave"
android:title="@string/pref_title_autosave" />
</PreferenceScreen>