Use system file browser instead of built-in one

This commit is contained in:
Billy Brawner 2019-05-17 21:37:59 -07:00 committed by William Brawner
parent f00f894d3a
commit 4fa6cc462d
11 changed files with 82 additions and 581 deletions

View file

@ -1,80 +0,0 @@
package com.wbrawner.simplemarkdown.view.activity;
import android.Manifest;
import android.content.Intent;
import android.widget.TextView;
import androidx.test.rule.ActivityTestRule;
import androidx.test.rule.GrantPermissionRule;
import com.wbrawner.simplemarkdown.R;
import com.wbrawner.simplemarkdown.utility.Constants;
import org.junit.Rule;
import org.junit.Test;
import java.util.HashMap;
import static androidx.test.espresso.Espresso.onData;
import static androidx.test.espresso.Espresso.onView;
import static androidx.test.espresso.action.ViewActions.click;
import static androidx.test.espresso.assertion.ViewAssertions.matches;
import static androidx.test.espresso.matcher.ViewMatchers.withId;
import static androidx.test.espresso.matcher.ViewMatchers.withParent;
import static androidx.test.espresso.matcher.ViewMatchers.withText;
import static org.hamcrest.Matchers.allOf;
import static org.hamcrest.Matchers.instanceOf;
import static org.hamcrest.Matchers.is;
public class ExplorerActivityTest {
@Rule
public ActivityTestRule<ExplorerActivity> explorerActivityActivityTestRule
= new ActivityTestRule<>(ExplorerActivity.class, false, false);
@Rule
public GrantPermissionRule readFilesPermissionRule =
GrantPermissionRule.grant(Manifest.permission.READ_EXTERNAL_STORAGE);
@Rule
public GrantPermissionRule writeFilesPermissionRule =
GrantPermissionRule.grant(Manifest.permission.WRITE_EXTERNAL_STORAGE);
@Test
public void firstItemInDirectoryIsGoUpTest() {
Intent startIntent = new Intent();
startIntent.putExtra(Constants.EXTRA_REQUEST_CODE, Constants.REQUEST_OPEN_FILE);
explorerActivityActivityTestRule.launchActivity(startIntent);
// We should start in the Documents directory
onView(allOf(instanceOf(TextView.class), withParent(withId(R.id.toolbar))))
.check(matches(withText("Documents")));
// Check that the first item in the list is our navigate up a directory item and click it
onData(is(instanceOf(HashMap.class)))
.atPosition(0)
.check(matches(withText(
explorerActivityActivityTestRule.getActivity().getString(R.string.directory_up)
)))
.perform(click());
// Check that we're in the parent of the Documents directory
onView(allOf(instanceOf(TextView.class), withParent(withId(R.id.toolbar))))
.check(matches(withText("0")));
// Enter the Android/data/data directory, which should almost certainly have other
// directories, and check that each directory shows the "Go up" option as the first option
// in the list
onView(withText("Android"))
.perform(click());
onData(is(instanceOf(HashMap.class)))
.atPosition(0)
.check(matches(withText(
explorerActivityActivityTestRule.getActivity().getString(R.string.directory_up)
)));
onView(withText("data"))
.perform(click());
onData(is(instanceOf(HashMap.class)))
.atPosition(0)
.check(matches(withText(
explorerActivityActivityTestRule.getActivity().getString(R.string.directory_up)
)));
}
}

View file

@ -4,13 +4,10 @@ import com.crashlytics.android.Crashlytics;
import com.wbrawner.simplemarkdown.utility.Utils; import com.wbrawner.simplemarkdown.utility.Utils;
import java.io.BufferedReader; import java.io.BufferedReader;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException; import java.io.IOException;
import java.io.InputStream; import java.io.InputStream;
import java.io.InputStreamReader; import java.io.InputStreamReader;
import java.io.OutputStream;
import java.io.OutputStreamWriter; import java.io.OutputStreamWriter;
/** /**
@ -18,43 +15,20 @@ import java.io.OutputStreamWriter;
* as to keep track of the data itself in memory. * as to keep track of the data itself in memory.
*/ */
public class MarkdownFile { public class MarkdownFile {
private static String defaultRootDir = "";
private String name; private String name;
private String path;
private String content; private String content;
public MarkdownFile(String name, String path, String content) { public MarkdownFile(String name, String content) {
this.name = name; this.name = name;
if (path == null || path.isEmpty()) {
path = defaultRootDir;
}
this.path = path;
this.content = content; this.content = content;
} }
public MarkdownFile(String path) {
if (load(path)) {
this.name = path.substring(
path.lastIndexOf("/") + 1
);
this.path = path.substring(
0,
path.lastIndexOf("/")
);
this.content = "";
}
}
public MarkdownFile() { public MarkdownFile() {
this.name = "Untitled.md"; this.name = "Untitled.md";
this.path = "";
this.content = ""; this.content = "";
} }
public static void setDefaultRootDir(String defaultRootDir) {
MarkdownFile.defaultRootDir = defaultRootDir;
}
public String getName() { public String getName() {
return name; return name;
} }
@ -63,34 +37,6 @@ public class MarkdownFile {
this.name = name; this.name = name;
} }
public String getPath() {
return path;
}
public void setPath(String path) {
this.path = path;
}
public String getFullPath() {
String fullPath;
if (this.path.isEmpty()) {
this.path = defaultRootDir;
}
if (this.path.endsWith(this.name)) {
return this.path;
}
if (!this.path.endsWith("/")) {
fullPath = this.path + "/";
} else {
fullPath = this.path;
}
return fullPath + this.name;
}
public String getContent() { public String getContent() {
return content; return content;
} }
@ -99,7 +45,7 @@ public class MarkdownFile {
this.content = content; this.content = content;
} }
public boolean load(InputStream in) { public boolean load(String name, InputStream in) {
StringBuilder sb = new StringBuilder(); StringBuilder sb = new StringBuilder();
BufferedReader reader = null; BufferedReader reader = null;
try { try {
@ -108,6 +54,7 @@ public class MarkdownFile {
while ((line = reader.readLine()) != null) { while ((line = reader.readLine()) != null) {
sb.append(line).append('\n'); sb.append(line).append('\n');
} }
this.name = name;
this.content = sb.toString(); this.content = sb.toString();
return true; return true;
} catch (IOException e) { } catch (IOException e) {
@ -118,61 +65,12 @@ public class MarkdownFile {
} }
} }
private boolean load(String path) { public boolean save(String name, OutputStream outputStream) {
return load(new File(path));
}
private boolean load(File markdownFile) {
if (!markdownFile.exists() || !markdownFile.canRead()) {
return false;
}
try {
this.name = markdownFile.getName();
this.path = markdownFile.getParentFile().getAbsolutePath();
return load(new FileInputStream(markdownFile));
} catch (FileNotFoundException e) {
Crashlytics.logException(e);
return false;
}
}
public boolean load() {
return !this.name.isEmpty() && load(getFullPath());
}
public boolean save(String path) {
if (path == null) {
path = this.getFullPath();
}
File markdownFile = new File(path);
File parentFile = markdownFile.getParentFile();
if (!parentFile.exists() && !parentFile.mkdirs()) {
return false;
}
if (!markdownFile.exists()) {
try {
if (!markdownFile.createNewFile()) {
return false;
}
} catch (IOException e) {
Crashlytics.logException(e);
return false;
}
}
if (!markdownFile.canWrite()) {
return false;
}
OutputStreamWriter writer = null; OutputStreamWriter writer = null;
try { try {
writer = new OutputStreamWriter( writer = new OutputStreamWriter(outputStream);
new FileOutputStream(markdownFile)
);
writer.write(this.content); writer.write(this.content);
this.name = name;
} catch (IOException e) { } catch (IOException e) {
Crashlytics.logException(e); Crashlytics.logException(e);
return false; return false;
@ -182,13 +80,9 @@ public class MarkdownFile {
writer.close(); writer.close();
} catch (IOException e) { } catch (IOException e) {
Crashlytics.logException(e); Crashlytics.logException(e);
// closing the reader failed
} }
} }
} }
this.name = markdownFile.getName();
this.path = markdownFile.getParentFile().getAbsolutePath();
return true; return true;
} }
} }

View file

@ -6,27 +6,23 @@ import android.net.Uri;
import com.wbrawner.simplemarkdown.view.MarkdownEditView; import com.wbrawner.simplemarkdown.view.MarkdownEditView;
import com.wbrawner.simplemarkdown.view.MarkdownPreviewView; import com.wbrawner.simplemarkdown.view.MarkdownPreviewView;
import java.io.File;
import java.io.InputStream; import java.io.InputStream;
import java.io.OutputStream;
public interface MarkdownPresenter { public interface MarkdownPresenter {
File getFile();
void loadMarkdown();
void loadMarkdown(String fileName, InputStream in); void loadMarkdown(String fileName, InputStream in);
void loadMarkdown(File file);
void loadFromUri(Context context, Uri fileUri); void loadFromUri(Context context, Uri fileUri);
void loadMarkdown(String fileName, InputStream in, OnTempFileLoadedListener listener); void loadMarkdown(String fileName, InputStream in, OnTempFileLoadedListener listener);
void newFile(String path); void newFile(String path);
void setEditView(MarkdownEditView editView); void setEditView(MarkdownEditView editView);
void setPreviewView(MarkdownPreviewView previewView); void setPreviewView(MarkdownPreviewView previewView);
void saveMarkdown(MarkdownSavedListener listener, String filePath);
void saveMarkdown(MarkdownSavedListener listener, String name, OutputStream outputStream);
void onMarkdownEdited(); void onMarkdownEdited();
void onMarkdownEdited(String markdown); void onMarkdownEdited(String markdown);
String getFileName(); String getFileName();
void setFileName(String name); void setFileName(String name);
void setRootDir(String path);
String generateHTML(); String generateHTML();
String generateHTML(String markdown); String generateHTML(String markdown);
String getMarkdown(); String getMarkdown();

View file

@ -13,10 +13,8 @@ import com.wbrawner.simplemarkdown.utility.Utils;
import com.wbrawner.simplemarkdown.view.MarkdownEditView; import com.wbrawner.simplemarkdown.view.MarkdownEditView;
import com.wbrawner.simplemarkdown.view.MarkdownPreviewView; import com.wbrawner.simplemarkdown.view.MarkdownPreviewView;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.InputStream; import java.io.InputStream;
import java.io.OutputStream;
public class MarkdownPresenterImpl implements MarkdownPresenter { public class MarkdownPresenterImpl implements MarkdownPresenter {
private final Object fileLock = new Object(); private final Object fileLock = new Object();
@ -31,39 +29,6 @@ public class MarkdownPresenterImpl implements MarkdownPresenter {
} }
} }
@Override
public File getFile() {
return new File(file.getFullPath());
}
@Override
public void loadMarkdown() {
Runnable fileLoader = () -> {
boolean result;
synchronized (fileLock) {
result = this.file.load();
}
MarkdownEditView currentEditView = editView;
if (currentEditView != null) {
currentEditView.onFileLoaded(result);
currentEditView.setMarkdown(getMarkdown());
onMarkdownEdited();
}
};
fileHandler.post(fileLoader);
}
@Override
public void loadMarkdown(File file) {
try {
InputStream in = new FileInputStream(file);
loadMarkdown(file.getName(), in);
} catch (FileNotFoundException e) {
Crashlytics.logException(e);
}
}
@Override @Override
public void loadMarkdown(final String fileName, final InputStream in) { public void loadMarkdown(final String fileName, final InputStream in) {
this.loadMarkdown(fileName, in, null); this.loadMarkdown(fileName, in, null);
@ -77,8 +42,7 @@ public class MarkdownPresenterImpl implements MarkdownPresenter {
) { ) {
Runnable fileLoader = () -> { Runnable fileLoader = () -> {
MarkdownFile tmpFile = new MarkdownFile(); MarkdownFile tmpFile = new MarkdownFile();
if (tmpFile.load(in)) { if (tmpFile.load(fileName, in)) {
tmpFile.setName(fileName);
if (listener != null) { if (listener != null) {
String html = generateHTML(tmpFile.getContent()); String html = generateHTML(tmpFile.getContent());
listener.onSuccess(html); listener.onSuccess(html);
@ -112,13 +76,14 @@ public class MarkdownPresenterImpl implements MarkdownPresenter {
currentEditView.setTitle(newName); currentEditView.setTitle(newName);
currentEditView.setMarkdown(""); currentEditView.setMarkdown("");
} }
file = new MarkdownFile(newName, "", ""); file = new MarkdownFile(newName, "");
} }
} }
@Override @Override
public void setEditView(MarkdownEditView editView) { public void setEditView(MarkdownEditView editView) {
this.editView = editView; this.editView = editView;
onMarkdownEdited();
} }
@Override @Override
@ -127,11 +92,11 @@ public class MarkdownPresenterImpl implements MarkdownPresenter {
} }
@Override @Override
public void saveMarkdown(MarkdownSavedListener listener, String filePath) { public void saveMarkdown(MarkdownSavedListener listener, String name, OutputStream outputStream) {
Runnable fileSaver = () -> { Runnable fileSaver = () -> {
boolean result; boolean result;
synchronized (fileLock) { synchronized (fileLock) {
result = file.save(filePath); result = file.save(name, outputStream);
} }
if (listener != null) { if (listener != null) {
listener.saveComplete(result); listener.saveComplete(result);
@ -191,11 +156,6 @@ public class MarkdownPresenterImpl implements MarkdownPresenter {
} }
} }
@Override
public void setRootDir(String path) {
MarkdownFile.setDefaultRootDir(path);
}
@Override @Override
public String getMarkdown() { public String getMarkdown() {
synchronized (fileLock) { synchronized (fileLock) {

View file

@ -1,258 +0,0 @@
package com.wbrawner.simplemarkdown.view.activity;
import android.annotation.SuppressLint;
import android.content.Intent;
import android.os.Bundle;
import android.os.Environment;
import android.os.Handler;
import android.view.Menu;
import android.view.MenuItem;
import android.view.View;
import android.widget.Button;
import android.widget.EditText;
import android.widget.ListView;
import android.widget.SimpleAdapter;
import androidx.appcompat.app.AppCompatActivity;
import androidx.appcompat.widget.Toolbar;
import com.crashlytics.android.Crashlytics;
import com.wbrawner.simplemarkdown.R;
import com.wbrawner.simplemarkdown.utility.Constants;
import com.wbrawner.simplemarkdown.utility.Utils;
import java.io.File;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Locale;
import java.util.TreeSet;
import java.util.concurrent.atomic.AtomicReference;
public class ExplorerActivity extends AppCompatActivity {
private Handler fileHandler = Utils.createSafeHandler("ExplorerThread");
private ListView listView;
private File[] mounts;
private String docsDirPath;
private AtomicReference<String> filePath = new AtomicReference<>("");
private volatile boolean isSave = false;
private volatile boolean showFiles = true;
private EditText fileName;
@SuppressLint("SetTextI18n")
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_explorer);
Toolbar toolbar = findViewById(R.id.toolbar);
setSupportActionBar(toolbar);
Intent intent = getIntent();
if (intent == null || !intent.hasExtra(Constants.EXTRA_REQUEST_CODE)) {
finish();
return;
}
docsDirPath = Utils.getDocsPath(this);
int requestCode = intent.getIntExtra(Constants.EXTRA_REQUEST_CODE, -1);
switch (requestCode) {
case Constants.REQUEST_OPEN_FILE:
break;
case Constants.REQUEST_ROOT_DIR:
showFiles = false;
break;
case Constants.REQUEST_SAVE_FILE:
isSave = true;
fileName = findViewById(R.id.file_name);
fileName.setVisibility(View.VISIBLE);
if (intent.hasExtra(Constants.EXTRA_FILE)) {
File file = (File) intent.getSerializableExtra(Constants.EXTRA_FILE);
if (file.exists() && file.canWrite()) {
docsDirPath = file.getParentFile().getAbsolutePath();
fileName.setText(file.getName());
} else {
fileName.setText("Untitled.md");
}
}
Button saveButton = findViewById(R.id.button_save);
saveButton.setVisibility(View.VISIBLE);
saveButton.setOnClickListener((v) -> {
Intent fileIntent = new Intent();
String absolutePath = String.format(
Locale.ENGLISH,
"%s/%s",
filePath.get(),
fileName.getText().toString()
);
fileIntent.putExtra(Constants.EXTRA_FILE_PATH, absolutePath);
setResult(RESULT_OK, fileIntent);
finish();
});
break;
default:
finish();
return;
}
// FloatingActionButton fab = findViewById(R.id.fab);
// fab.setOnClickListener(view ->
// Snackbar.make(view, "Replace with your own action", Snackbar.LENGTH_LONG)
// .setAction("Action", null)
// .show()
// );
listView = findViewById(R.id.file_list);
updateListView();
}
@Override
public boolean onCreateOptionsMenu(Menu menu) {
getMenuInflater().inflate(R.menu.menu_explorer, menu);
if (!showFiles) {
menu.findItem(R.id.action_select).setVisible(true);
}
if (hasRemovableStorage()) {
menu.findItem(R.id.action_use_sdcard).setVisible(true);
boolean sdcardSelected = false;
try {
sdcardSelected = filePath.get().contains(mounts[1].getAbsolutePath());
} catch (NullPointerException e) {
Crashlytics.logException(e);
updateListView();
menu.findItem(R.id.action_use_sdcard).setVisible(false);
}
if (sdcardSelected) {
menu.findItem(R.id.action_use_sdcard).setChecked(true);
}
}
return true;
}
@Override
public boolean onOptionsItemSelected(MenuItem item) {
switch (item.getItemId()) {
case R.id.action_use_sdcard:
if (!hasRemovableStorage()) {
// We shouldn't get here to begin with but better safe than sorry
break;
}
item.setChecked(!item.isChecked());
if (item.isChecked()) {
updateListView(mounts[1]);
} else {
updateListView(new File(docsDirPath));
}
break;
case R.id.action_select:
replyWithFile(new File(filePath.get()));
break;
}
return true;
}
private boolean hasRemovableStorage() {
mounts = getExternalFilesDirs(Environment.DIRECTORY_DOCUMENTS);
return mounts.length > 1;
}
@SuppressWarnings("unchecked")
private List<HashMap<String, Object>> loadFiles(File docsDir) {
TreeSet<HashMap<String, Object>> files = new TreeSet<HashMap<String, Object>>((o1, o2) ->
((String) o1.get("name")).compareToIgnoreCase((String) o2.get("name"))) {
};
TreeSet<HashMap<String, Object>> dirs = new TreeSet<HashMap<String, Object>>((o1, o2) ->
((String) o1.get("name")).compareToIgnoreCase((String) o2.get("name"))) {
};
if (docsDir.listFiles() != null) {
for (File file : docsDir.listFiles()) {
if (file.isDirectory()) {
HashMap<String, Object> fileHashMap = new HashMap<>();
fileHashMap.put("name", file.getName());
fileHashMap.put("file", file);
dirs.add(fileHashMap);
continue;
}
if (!showFiles ||
(!file.getName().endsWith("md")
&& !file.getName().endsWith("markdown")
&& !file.getName().endsWith("text"))) {
continue;
}
HashMap<String, Object> fileHashMap = new HashMap<>();
fileHashMap.put("name", file.getName());
fileHashMap.put("file", file);
files.add(fileHashMap);
}
}
List<HashMap<String, Object>> sortedFiles = new ArrayList<>();
if (docsDir.getParentFile() != null && docsDir.getParentFile().canRead()) {
HashMap<String, Object> fileHashMap = new HashMap<>();
fileHashMap.put("name", getString(R.string.directory_up));
fileHashMap.put("file", docsDir.getParentFile());
sortedFiles.add(fileHashMap);
}
sortedFiles.addAll(dirs);
sortedFiles.addAll(files);
return sortedFiles;
}
private void updateListView() {
File docsDir = new File(docsDirPath);
if (!docsDir.exists()) {
docsDir = Environment.getExternalStorageDirectory();
}
updateListView(docsDir);
}
private void updateListView(File filesDir) {
setTitle(filesDir.getName());
filePath.set(filesDir.getAbsolutePath());
fileHandler.post(() -> {
final List<HashMap<String, Object>> files = loadFiles(filesDir);
runOnUiThread(() -> {
listView.setAdapter(new SimpleAdapter(
this,
files,
android.R.layout.simple_list_item_1,
new String[]{"name"},
new int[]{android.R.id.text1}
));
listView.setOnItemClickListener((parent, view, position, id) -> {
File clickedFile = (File) files.get(position).get("file");
if (clickedFile.isFile()) {
handleFileClick(clickedFile);
} else if (clickedFile.isDirectory()) {
updateListView(clickedFile);
}
});
});
});
}
void handleFileClick(File file) {
if (isSave) {
if (fileName != null) {
fileName.setText(file.getName());
}
} else {
replyWithFile(file);
}
}
private void replyWithFile(File file) {
Intent fileIntent = new Intent();
fileIntent.putExtra(Constants.EXTRA_FILE, file);
setResult(RESULT_OK, fileIntent);
finish();
}
}

View file

@ -7,6 +7,7 @@ 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.Bundle import android.os.Bundle
import android.provider.OpenableColumns
import android.view.Menu import android.view.Menu
import android.view.MenuItem import android.view.MenuItem
import android.view.View import android.view.View
@ -21,7 +22,7 @@ import com.wbrawner.simplemarkdown.utility.Constants
import com.wbrawner.simplemarkdown.utility.Utils import com.wbrawner.simplemarkdown.utility.Utils
import com.wbrawner.simplemarkdown.view.adapter.EditPagerAdapter import com.wbrawner.simplemarkdown.view.adapter.EditPagerAdapter
import kotlinx.android.synthetic.main.activity_main.* import kotlinx.android.synthetic.main.activity_main.*
import java.io.File import java.io.FileInputStream
import java.io.InputStream import java.io.InputStream
import javax.inject.Inject import javax.inject.Inject
@ -53,7 +54,7 @@ class MainActivity : AppCompatActivity(), ActivityCompat.OnRequestPermissionsRes
override fun onUserLeaveHint() { override fun onUserLeaveHint() {
super.onUserLeaveHint() super.onUserLeaveHint()
if (shouldAutoSave && presenter.markdown.isNotEmpty() && Utils.isAutosaveEnabled(this)) { if (shouldAutoSave && presenter.markdown.isNotEmpty() && Utils.isAutosaveEnabled(this)) {
presenter.saveMarkdown(null, null) // presenter.saveMarkdown(null, null)
} }
} }
@ -83,7 +84,7 @@ class MainActivity : AppCompatActivity(), ActivityCompat.OnRequestPermissionsRes
)) ))
} }
R.id.action_load -> requestFileOp(Constants.REQUEST_OPEN_FILE) R.id.action_load -> requestFileOp(Constants.REQUEST_OPEN_FILE)
R.id.action_new -> presenter.saveMarkdown(newFileHandler, null) // R.id.action_new -> presenter.saveMarkdown(newFileHandler, null)
R.id.action_lock_swipe -> { R.id.action_lock_swipe -> {
item.isChecked = !item.isChecked item.isChecked = !item.isChecked
pager!!.setSwipeLocked(item.isChecked) pager!!.setSwipeLocked(item.isChecked)
@ -172,44 +173,80 @@ class MainActivity : AppCompatActivity(), ActivityCompat.OnRequestPermissionsRes
override fun onActivityResult(requestCode: Int, resultCode: Int, data: Intent?) { override fun onActivityResult(requestCode: Int, resultCode: Int, data: Intent?) {
when (requestCode) { when (requestCode) {
Constants.REQUEST_OPEN_FILE -> { Constants.REQUEST_OPEN_FILE -> {
if (resultCode != Activity.RESULT_OK || data == null || !data.hasExtra(Constants.EXTRA_FILE)) { if (resultCode != Activity.RESULT_OK || data?.data == null) {
return return
} }
val markdownFile = data.getSerializableExtra(Constants.EXTRA_FILE) as? File?: return val mimeType: String? = data.data?.let { returnUri ->
presenter.loadMarkdown(markdownFile) contentResolver.getType(returnUri)
}
val fileName = contentResolver.query(data.data!!, null, null, null, null)
?.use { cursor ->
val nameIndex = cursor.getColumnIndex(OpenableColumns.DISPLAY_NAME)
cursor.moveToFirst()
cursor.getString(nameIndex)
} ?: "Untitled.md"
contentResolver.openFileDescriptor(data.data!!, "r")?.let {
val fileInput = FileInputStream(it.fileDescriptor)
presenter.loadMarkdown(fileName, fileInput)
}
} }
Constants.REQUEST_SAVE_FILE -> { Constants.REQUEST_SAVE_FILE -> {
if (resultCode != Activity.RESULT_OK if (resultCode != Activity.RESULT_OK
|| data == null || data?.data == null) {
|| !data.hasExtra(Constants.EXTRA_FILE_PATH)
|| data.getStringExtra(Constants.EXTRA_FILE_PATH).isEmpty()) {
return return
} }
val path = data.getStringExtra(Constants.EXTRA_FILE_PATH)
presenter.saveMarkdown(null, path) val fileName = contentResolver.query(data.data!!, null, null, null, null)
?.use { cursor ->
val nameIndex = cursor.getColumnIndex(OpenableColumns.DISPLAY_NAME)
cursor.moveToFirst()
cursor.getString(nameIndex)
} ?: "Untitled.md"
presenter.saveMarkdown(
null,
fileName,
contentResolver.openOutputStream(data.data!!)
)
} }
} }
super.onActivityResult(requestCode, resultCode, data) super.onActivityResult(requestCode, resultCode, data)
} }
private fun requestFileOp(requestType: Int) { private fun requestFileOp(requestType: Int) {
if (Utils.canAccessFiles(this@MainActivity)) { if (!Utils.canAccessFiles(this@MainActivity)) {
// If the user is going to save the file, we don't want to auto-save it for them if (Build.VERSION.SDK_INT < 23) return
shouldAutoSave = false
val intent = Intent(this@MainActivity, ExplorerActivity::class.java)
intent.putExtra(Constants.EXTRA_REQUEST_CODE, requestType)
intent.putExtra(Constants.EXTRA_FILE, presenter.file)
startActivityForResult(
intent,
requestType
)
} else if (Build.VERSION.SDK_INT >= 23) {
requestPermissions( requestPermissions(
arrayOf(Manifest.permission.WRITE_EXTERNAL_STORAGE), arrayOf(Manifest.permission.WRITE_EXTERNAL_STORAGE),
requestType requestType
) )
return
} }
// If the user is going to save the file, we don't want to auto-save it for them
shouldAutoSave = false
val intent = when (requestType) {
Constants.REQUEST_SAVE_FILE -> {
Intent(Intent.ACTION_CREATE_DOCUMENT).apply {
type = "text/markdown"
putExtra(Intent.EXTRA_TITLE, presenter.fileName)
}
}
Constants.REQUEST_OPEN_FILE -> {
Intent(Intent.ACTION_OPEN_DOCUMENT).apply {
type = "*/*"
putExtra(Intent.EXTRA_MIME_TYPES, arrayOf("text/plain", "text/markdown"))
}
}
else -> null
} ?: return
intent.addCategory(Intent.CATEGORY_OPENABLE)
startActivityForResult(
intent,
requestType
)
} }
override fun onResume() { override fun onResume() {

View file

@ -9,6 +9,7 @@ import androidx.annotation.Nullable;
import androidx.appcompat.app.AppCompatActivity; import androidx.appcompat.app.AppCompatActivity;
import com.crashlytics.android.Crashlytics; import com.crashlytics.android.Crashlytics;
import com.wbrawner.simplemarkdown.BuildConfig;
import com.wbrawner.simplemarkdown.MarkdownApplication; import com.wbrawner.simplemarkdown.MarkdownApplication;
import com.wbrawner.simplemarkdown.R; import com.wbrawner.simplemarkdown.R;
import com.wbrawner.simplemarkdown.presentation.MarkdownPresenter; import com.wbrawner.simplemarkdown.presentation.MarkdownPresenter;
@ -28,7 +29,8 @@ public class SplashActivity extends AppCompatActivity {
protected void onCreate(@Nullable Bundle savedInstanceState) { protected void onCreate(@Nullable Bundle savedInstanceState) {
super.onCreate(savedInstanceState); super.onCreate(savedInstanceState);
SharedPreferences sharedPreferences = PreferenceManager.getDefaultSharedPreferences(this); SharedPreferences sharedPreferences = PreferenceManager.getDefaultSharedPreferences(this);
if (sharedPreferences.getBoolean(getString(R.string.error_reports_enabled), true)) { if (sharedPreferences.getBoolean(getString(R.string.error_reports_enabled), true)
&& !BuildConfig.DEBUG) {
Fabric.with(this, new Crashlytics()); Fabric.with(this, new Crashlytics());
} }
((MarkdownApplication) getApplication()).getComponent().inject(this); ((MarkdownApplication) getApplication()).getComponent().inject(this);
@ -42,10 +44,6 @@ public class SplashActivity extends AppCompatActivity {
presenter.setFileName(defaultName); presenter.setFileName(defaultName);
} }
String defaultRootDir =
sharedPreferences.getString(Constants.KEY_DOCS_PATH, Utils.getDocsPath(this));
presenter.setRootDir(defaultRootDir);
Intent startIntent = new Intent(this, MainActivity.class); Intent startIntent = new Intent(this, MainActivity.class);
String startScreen = PreferenceManager.getDefaultSharedPreferences(this) String startScreen = PreferenceManager.getDefaultSharedPreferences(this)
.getString( .getString(

View file

@ -55,8 +55,7 @@ class EditFragment : Fragment(), MarkdownEditView {
@SuppressLint("ClickableViewAccessibility") @SuppressLint("ClickableViewAccessibility")
override fun onViewCreated(view: View, savedInstanceState: Bundle?) { override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState) super.onViewCreated(view, savedInstanceState)
presenter!!.setEditView(this@EditFragment) presenter.setEditView(this@EditFragment)
presenter!!.loadMarkdown()
markdownEditorScroller!!.setOnTouchListener { v, event -> markdownEditorScroller!!.setOnTouchListener { v, event ->
// The focus should only be set if this was a click, and not a scroll // The focus should only be set if this was a click, and not a scroll

View file

@ -72,8 +72,8 @@ class PreviewFragment : Fragment(), MarkdownPreviewView {
override fun onResume() { override fun onResume() {
super.onResume() super.onResume()
presenter!!.setPreviewView(this) presenter.setPreviewView(this)
presenter!!.onMarkdownEdited() presenter.onMarkdownEdited()
} }
override fun onDestroyView() { override fun onDestroyView() {
@ -86,11 +86,10 @@ class PreviewFragment : Fragment(), MarkdownPreviewView {
override fun onDestroy() { override fun onDestroy() {
super.onDestroy() super.onDestroy()
presenter!!.setPreviewView(null) presenter.setPreviewView(null)
} }
companion object { companion object {
private val TAG = PreviewFragment::class.java.simpleName
var FORMAT_CSS = "<style>" + var FORMAT_CSS = "<style>" +
"%s" + "%s" +
"</style>" "</style>"

View file

@ -1,6 +1,5 @@
package com.wbrawner.simplemarkdown.view.fragment; package com.wbrawner.simplemarkdown.view.fragment;
import android.content.Intent;
import android.content.SharedPreferences; import android.content.SharedPreferences;
import android.os.Bundle; import android.os.Bundle;
import android.preference.ListPreference; import android.preference.ListPreference;
@ -16,13 +15,6 @@ import androidx.annotation.Nullable;
import com.wbrawner.simplemarkdown.BuildConfig; import com.wbrawner.simplemarkdown.BuildConfig;
import com.wbrawner.simplemarkdown.R; import com.wbrawner.simplemarkdown.R;
import com.wbrawner.simplemarkdown.utility.Constants;
import com.wbrawner.simplemarkdown.utility.Utils;
import com.wbrawner.simplemarkdown.view.activity.ExplorerActivity;
import java.io.File;
import static android.app.Activity.RESULT_OK;
public class SettingsFragment extends PreferenceFragment public class SettingsFragment extends PreferenceFragment
implements SharedPreferences.OnSharedPreferenceChangeListener { implements SharedPreferences.OnSharedPreferenceChangeListener {
@ -40,14 +32,6 @@ public class SettingsFragment extends PreferenceFragment
if (!BuildConfig.ENABLE_CUSTOM_CSS) { if (!BuildConfig.ENABLE_CUSTOM_CSS) {
getPreferenceScreen().removePreference(findPreference(getString(R.string.pref_custom_css))); getPreferenceScreen().removePreference(findPreference(getString(R.string.pref_custom_css)));
} }
Preference defaultRoot = findPreference(Constants.KEY_DOCS_PATH);
defaultRoot.setSummary(Utils.getDocsPath(getActivity()));
defaultRoot.setOnPreferenceClickListener((preference) -> {
Intent intent = new Intent(getActivity(), ExplorerActivity.class);
intent.putExtra(Constants.EXTRA_REQUEST_CODE, Constants.REQUEST_ROOT_DIR);
startActivityForResult(intent, Constants.REQUEST_ROOT_DIR);
return true;
});
} }
@Override @Override
@ -82,29 +66,4 @@ public class SettingsFragment extends PreferenceFragment
String summary = listPreference.getEntries()[index].toString(); String summary = listPreference.getEntries()[index].toString();
preference.setSummary(summary); preference.setSummary(summary);
} }
@Override
public void onActivityResult(int requestCode, int resultCode, Intent data) {
if (resultCode != RESULT_OK || data == null) {
// If the user cancelled the request, then we don't care about the response
return;
}
switch (requestCode) {
case Constants.REQUEST_ROOT_DIR:
File root = (File) data.getSerializableExtra(Constants.EXTRA_FILE);
if (root == null) {
// TODO: Report this?
// Crashlytics.logException(new RuntimeException("Got null/empty response from setting default root dir"));
return;
}
Preference defaultRoot = findPreference(Constants.KEY_DOCS_PATH);
defaultRoot.setSummary(root.getAbsolutePath());
PreferenceManager.getDefaultSharedPreferences(getActivity())
.edit()
.putString(Constants.KEY_DOCS_PATH, root.getAbsolutePath())
.apply();
break;
}
}
} }

View file

@ -12,9 +12,6 @@
android:entryValues="@array/pref_values_default_view" android:entryValues="@array/pref_values_default_view"
android:key="@string/key_default_view" android:key="@string/key_default_view"
android:title="@string/pref_title_default_view" /> android:title="@string/pref_title_default_view" />
<Preference
android:key="defaultRootDir"
android:title="@string/pref_description_default_root" />
<SwitchPreference <SwitchPreference
android:defaultValue="true" android:defaultValue="true"
android:key="@string/error_reports_enabled" android:key="@string/error_reports_enabled"