Use built-in file explorer instead of OEM implementation
This commit is contained in:
parent
8de320bd48
commit
8c2f32dd97
15 changed files with 532 additions and 53 deletions
|
@ -4,6 +4,7 @@ buildscript {
|
||||||
}
|
}
|
||||||
|
|
||||||
dependencies {
|
dependencies {
|
||||||
|
//noinspection GradleDynamicVersion
|
||||||
classpath 'io.fabric.tools:gradle:1.+'
|
classpath 'io.fabric.tools:gradle:1.+'
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -25,7 +26,7 @@ android {
|
||||||
exclude 'META-INF/LICENSE'
|
exclude 'META-INF/LICENSE'
|
||||||
exclude 'META-INF/DEPENDENCIES'
|
exclude 'META-INF/DEPENDENCIES'
|
||||||
}
|
}
|
||||||
compileSdkVersion 26
|
compileSdkVersion 27
|
||||||
buildToolsVersion "26.0.2"
|
buildToolsVersion "26.0.2"
|
||||||
compileOptions {
|
compileOptions {
|
||||||
sourceCompatibility JavaVersion.VERSION_1_8
|
sourceCompatibility JavaVersion.VERSION_1_8
|
||||||
|
@ -34,9 +35,9 @@ android {
|
||||||
defaultConfig {
|
defaultConfig {
|
||||||
applicationId "com.wbrawner.simplemarkdown"
|
applicationId "com.wbrawner.simplemarkdown"
|
||||||
minSdkVersion 19
|
minSdkVersion 19
|
||||||
targetSdkVersion 26
|
targetSdkVersion 27
|
||||||
versionCode 2
|
versionCode 3
|
||||||
versionName "0.1.1"
|
versionName "0.1.2"
|
||||||
testInstrumentationRunner "android.support.test.runner.AndroidJUnitRunner"
|
testInstrumentationRunner "android.support.test.runner.AndroidJUnitRunner"
|
||||||
}
|
}
|
||||||
buildTypes {
|
buildTypes {
|
||||||
|
@ -50,15 +51,19 @@ android {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
ext {
|
||||||
|
support_version = "27.0.2"
|
||||||
|
}
|
||||||
|
|
||||||
dependencies {
|
dependencies {
|
||||||
compile fileTree(dir: 'libs', include: ['*.jar'])
|
compile fileTree(dir: 'libs', include: ['*.jar'])
|
||||||
androidTestCompile('com.android.support.test.espresso:espresso-core:2.2.2', {
|
androidTestCompile('com.android.support.test.espresso:espresso-core:2.2.2', {
|
||||||
exclude group: 'com.android.support', module: 'support-annotations'
|
exclude group: 'com.android.support', module: 'support-annotations'
|
||||||
})
|
})
|
||||||
compile 'com.android.support:appcompat-v7:26.0.0'
|
compile "com.android.support:appcompat-v7:$support_version"
|
||||||
compile 'com.android.support.constraint:constraint-layout:1.0.2'
|
compile 'com.android.support.constraint:constraint-layout:1.0.2'
|
||||||
compile 'com.android.support:design:26.0.0'
|
compile "com.android.support:design:$support_version"
|
||||||
compile 'com.android.support:support-v13:26.0.0'
|
compile "com.android.support:support-v13:$support_version"
|
||||||
compile 'com.commonsware.cwac:anddown:0.3.0'
|
compile 'com.commonsware.cwac:anddown:0.3.0'
|
||||||
compile 'com.google.dagger:dagger:2.11'
|
compile 'com.google.dagger:dagger:2.11'
|
||||||
compile 'com.jakewharton:butterknife:8.7.0'
|
compile 'com.jakewharton:butterknife:8.7.0'
|
||||||
|
@ -67,9 +72,12 @@ 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'
|
||||||
|
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'
|
||||||
annotationProcessor 'com.google.dagger:dagger-compiler:2.11'
|
annotationProcessor 'com.google.dagger:dagger-compiler:2.11'
|
||||||
annotationProcessor 'com.jakewharton:butterknife-compiler:8.7.0'
|
annotationProcessor 'com.jakewharton:butterknife-compiler:8.7.0'
|
||||||
compile('com.crashlytics.sdk.android:crashlytics:2.8.0@aar') {
|
compile('com.crashlytics.sdk.android:crashlytics:2.8.0@aar') {
|
||||||
transitive = true;
|
transitive = true
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -0,0 +1,71 @@
|
||||||
|
package com.wbrawner.simplemarkdown;
|
||||||
|
|
||||||
|
import android.content.Context;
|
||||||
|
import android.content.pm.ActivityInfo;
|
||||||
|
import android.support.test.InstrumentationRegistry;
|
||||||
|
import android.support.test.rule.ActivityTestRule;
|
||||||
|
import android.support.test.runner.AndroidJUnit4;
|
||||||
|
import android.support.test.uiautomator.UiDevice;
|
||||||
|
import android.support.test.uiautomator.UiObject;
|
||||||
|
import android.support.test.uiautomator.UiScrollable;
|
||||||
|
import android.support.test.uiautomator.UiSelector;
|
||||||
|
|
||||||
|
import com.wbrawner.simplemarkdown.view.activity.MainActivity;
|
||||||
|
|
||||||
|
import org.junit.Before;
|
||||||
|
import org.junit.Rule;
|
||||||
|
import org.junit.Test;
|
||||||
|
import org.junit.runner.RunWith;
|
||||||
|
|
||||||
|
import static android.support.test.InstrumentationRegistry.getInstrumentation;
|
||||||
|
import static android.support.test.espresso.Espresso.onView;
|
||||||
|
import static android.support.test.espresso.Espresso.openActionBarOverflowOrOptionsMenu;
|
||||||
|
import static android.support.test.espresso.action.ViewActions.click;
|
||||||
|
import static android.support.test.espresso.matcher.ViewMatchers.withText;
|
||||||
|
import static org.junit.Assert.assertEquals;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Instrumentation test, which will execute on an Android device.
|
||||||
|
*
|
||||||
|
* @see <a href="http://d.android.com/tools/testing">Testing documentation</a>
|
||||||
|
*/
|
||||||
|
@RunWith(AndroidJUnit4.class)
|
||||||
|
public class MainActivityTests {
|
||||||
|
|
||||||
|
@Rule
|
||||||
|
public ActivityTestRule<MainActivity> mActivityRule =
|
||||||
|
new ActivityTestRule(MainActivity.class);
|
||||||
|
|
||||||
|
@Before
|
||||||
|
public void setup() {
|
||||||
|
mActivityRule.getActivity()
|
||||||
|
.setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_PORTRAIT);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void openAppTest() throws Exception {
|
||||||
|
UiDevice mDevice = UiDevice.getInstance(getInstrumentation());
|
||||||
|
mDevice.pressHome();
|
||||||
|
// Bring up the default launcher by searching for a UI component
|
||||||
|
// that matches the content description for the launcher button.
|
||||||
|
UiObject allAppsButton = mDevice
|
||||||
|
.findObject(new UiSelector().description("Apps"));
|
||||||
|
|
||||||
|
// Perform a click on the button to load the launcher.
|
||||||
|
allAppsButton.clickAndWaitForNewWindow();
|
||||||
|
// Context of the app under test.
|
||||||
|
Context appContext = InstrumentationRegistry.getTargetContext();
|
||||||
|
|
||||||
|
assertEquals("com.wbrawner.simplemarkdown", appContext.getPackageName());
|
||||||
|
UiScrollable appView = new UiScrollable(new UiSelector().scrollable(true));
|
||||||
|
UiSelector simpleMarkdownSelector = new UiSelector().text("Simple Markdown");
|
||||||
|
appView.scrollIntoView(simpleMarkdownSelector);
|
||||||
|
mDevice.findObject(simpleMarkdownSelector).clickAndWaitForNewWindow();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void openFileWithoutFilesTest() {
|
||||||
|
openActionBarOverflowOrOptionsMenu(InstrumentationRegistry.getTargetContext());
|
||||||
|
onView(withText("Open")).perform(click());
|
||||||
|
}
|
||||||
|
}
|
|
@ -13,7 +13,8 @@
|
||||||
android:roundIcon="@mipmap/ic_launcher_round"
|
android:roundIcon="@mipmap/ic_launcher_round"
|
||||||
android:supportsRtl="true"
|
android:supportsRtl="true"
|
||||||
android:theme="@style/AppTheme">
|
android:theme="@style/AppTheme">
|
||||||
<activity android:name=".view.activity.SplashActivity"
|
<activity
|
||||||
|
android:name=".view.activity.SplashActivity"
|
||||||
android:theme="@style/AppTheme.Splash">
|
android:theme="@style/AppTheme.Splash">
|
||||||
<intent-filter>
|
<intent-filter>
|
||||||
<action android:name="android.intent.action.MAIN" />
|
<action android:name="android.intent.action.MAIN" />
|
||||||
|
@ -37,7 +38,7 @@
|
||||||
<data android:host="*" />
|
<data android:host="*" />
|
||||||
</intent-filter>
|
</intent-filter>
|
||||||
</activity>
|
</activity>
|
||||||
<activity android:name=".view.activity.MainActivity"></activity>
|
<activity android:name=".view.activity.MainActivity" />
|
||||||
|
|
||||||
<provider
|
<provider
|
||||||
android:name="android.support.v4.content.FileProvider"
|
android:name="android.support.v4.content.FileProvider"
|
||||||
|
@ -54,6 +55,10 @@
|
||||||
android:name="android.support.PARENT_ACTIVITY"
|
android:name="android.support.PARENT_ACTIVITY"
|
||||||
android:value=".view.activity.MainActivity" />
|
android:value=".view.activity.MainActivity" />
|
||||||
</activity>
|
</activity>
|
||||||
|
<activity
|
||||||
|
android:name=".view.activity.ExplorerActivity"
|
||||||
|
android:label="@string/title_activity_explorer"
|
||||||
|
android:theme="@style/AppTheme.NoActionBar"></activity>
|
||||||
</application>
|
</application>
|
||||||
|
|
||||||
</manifest>
|
</manifest>
|
|
@ -1,10 +1,7 @@
|
||||||
package com.wbrawner.simplemarkdown.model;
|
package com.wbrawner.simplemarkdown.model;
|
||||||
|
|
||||||
import android.util.Log;
|
|
||||||
|
|
||||||
import java.io.BufferedReader;
|
import java.io.BufferedReader;
|
||||||
import java.io.File;
|
import java.io.File;
|
||||||
import java.io.FileDescriptor;
|
|
||||||
import java.io.FileNotFoundException;
|
import java.io.FileNotFoundException;
|
||||||
import java.io.FileOutputStream;
|
import java.io.FileOutputStream;
|
||||||
import java.io.FileReader;
|
import java.io.FileReader;
|
||||||
|
@ -100,14 +97,16 @@ public class MarkdownFile {
|
||||||
}
|
}
|
||||||
|
|
||||||
public int load(String path) {
|
public int load(String path) {
|
||||||
Log.d(TAG, path);
|
return load(new File(path));
|
||||||
|
}
|
||||||
|
|
||||||
|
public int load(File markdownFile) {
|
||||||
int code;
|
int code;
|
||||||
File markdownFile = new File(path);
|
|
||||||
if (markdownFile.exists() && markdownFile.canRead()) {
|
if (markdownFile.exists() && markdownFile.canRead()) {
|
||||||
BufferedReader reader = null;
|
BufferedReader reader = null;
|
||||||
try {
|
try {
|
||||||
this.name = markdownFile.getName();
|
this.name = markdownFile.getName();
|
||||||
this.path = markdownFile.getPath();
|
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));
|
||||||
|
@ -171,6 +170,8 @@ public class MarkdownFile {
|
||||||
} else {
|
} else {
|
||||||
code = WRITE_ERROR;
|
code = WRITE_ERROR;
|
||||||
}
|
}
|
||||||
|
this.name = markdownFile.getName();
|
||||||
|
this.path = markdownFile.getParentFile().getAbsolutePath();
|
||||||
return code;
|
return code;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -6,6 +6,7 @@ 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;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -13,8 +14,10 @@ import java.io.InputStream;
|
||||||
*/
|
*/
|
||||||
|
|
||||||
public interface MarkdownPresenter extends LifecyclePresenter {
|
public interface MarkdownPresenter extends LifecyclePresenter {
|
||||||
|
File getFile();
|
||||||
void loadMarkdown(String filePath);
|
void loadMarkdown(String filePath);
|
||||||
void loadMarkdown(InputStream in);
|
void loadMarkdown(InputStream in);
|
||||||
|
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 setEditView(MarkdownEditView editView);
|
void setEditView(MarkdownEditView editView);
|
||||||
|
|
|
@ -10,6 +10,7 @@ import com.wbrawner.simplemarkdown.model.MarkdownFile;
|
||||||
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;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -41,8 +42,19 @@ public class MarkdownPresenterImpl implements MarkdownPresenter {
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void loadMarkdown(String filePath) {
|
public void loadMarkdown(String filePath) {
|
||||||
|
File markdownFile = new File(filePath);
|
||||||
|
loadMarkdown(markdownFile);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public File getFile() {
|
||||||
|
return new File(file.getFullPath());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void loadMarkdown(File file) {
|
||||||
Runnable fileLoader = () -> {
|
Runnable fileLoader = () -> {
|
||||||
int result = file.load(filePath);
|
int result = this.file.load(file);
|
||||||
if (editView != null) {
|
if (editView != null) {
|
||||||
if (result == MarkdownFile.SUCCESS) {
|
if (result == MarkdownFile.SUCCESS) {
|
||||||
editView.setMarkdown(getMarkdown());
|
editView.setMarkdown(getMarkdown());
|
||||||
|
@ -82,7 +94,6 @@ public class MarkdownPresenterImpl implements MarkdownPresenter {
|
||||||
} else {
|
} else {
|
||||||
listener.onError(result);
|
listener.onError(result);
|
||||||
}
|
}
|
||||||
tmpFile = null;
|
|
||||||
};
|
};
|
||||||
fileLoader.run();
|
fileLoader.run();
|
||||||
}
|
}
|
||||||
|
|
|
@ -0,0 +1,221 @@
|
||||||
|
package com.wbrawner.simplemarkdown.view.activity;
|
||||||
|
|
||||||
|
import android.content.Intent;
|
||||||
|
import android.os.Bundle;
|
||||||
|
import android.os.Environment;
|
||||||
|
import android.os.Handler;
|
||||||
|
import android.support.design.widget.FloatingActionButton;
|
||||||
|
import android.support.design.widget.Snackbar;
|
||||||
|
import android.support.v7.app.AppCompatActivity;
|
||||||
|
import android.support.v7.widget.Toolbar;
|
||||||
|
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 com.wbrawner.simplemarkdown.R;
|
||||||
|
|
||||||
|
import java.io.File;
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.HashMap;
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.Locale;
|
||||||
|
import java.util.TreeSet;
|
||||||
|
|
||||||
|
public class ExplorerActivity extends AppCompatActivity {
|
||||||
|
private Handler fileHandler = new Handler();
|
||||||
|
private ListView listView;
|
||||||
|
private File[] mounts;
|
||||||
|
private String docsDirPath;
|
||||||
|
private String defaultDocsDirPath;
|
||||||
|
private int requestCode;
|
||||||
|
private String filePath;
|
||||||
|
private boolean isSave = false;
|
||||||
|
private EditText fileName;
|
||||||
|
private Button saveButton;
|
||||||
|
|
||||||
|
@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(MainActivity.EXTRA_REQUEST_CODE)) {
|
||||||
|
finish();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
defaultDocsDirPath = Environment.getExternalStorageDirectory() + "/" +
|
||||||
|
Environment.DIRECTORY_DOCUMENTS;
|
||||||
|
docsDirPath = defaultDocsDirPath;
|
||||||
|
|
||||||
|
requestCode = intent.getIntExtra(MainActivity.EXTRA_REQUEST_CODE, -1);
|
||||||
|
switch (requestCode) {
|
||||||
|
case MainActivity.OPEN_FILE_REQUEST:
|
||||||
|
break;
|
||||||
|
case MainActivity.SAVE_FILE_REQUEST:
|
||||||
|
isSave = true;
|
||||||
|
fileName = findViewById(R.id.file_name);
|
||||||
|
fileName.setVisibility(View.VISIBLE);
|
||||||
|
if (intent.hasExtra(MainActivity.EXTRA_FILE)) {
|
||||||
|
File file = (File) intent.getSerializableExtra(MainActivity.EXTRA_FILE);
|
||||||
|
if (file.exists() && file.canWrite()) {
|
||||||
|
docsDirPath = file.getParentFile().getAbsolutePath();
|
||||||
|
fileName.setText(file.getName());
|
||||||
|
} else {
|
||||||
|
fileName.setText("Untitled.md");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
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,
|
||||||
|
fileName.getText().toString()
|
||||||
|
);
|
||||||
|
fileIntent.putExtra(MainActivity.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);
|
||||||
|
File docsDir = new File(docsDirPath);
|
||||||
|
if (!docsDir.exists()) {
|
||||||
|
docsDir = Environment.getExternalStorageDirectory();
|
||||||
|
}
|
||||||
|
updateListView(docsDir);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean onCreateOptionsMenu(Menu menu) {
|
||||||
|
if (hasRemovableStorage()) {
|
||||||
|
getMenuInflater().inflate(R.menu.menu_explorer, menu);
|
||||||
|
if (filePath.contains(mounts[1].getAbsolutePath())) {
|
||||||
|
menu.findItem(R.id.action_use_sdcard).setChecked(true);
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
@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(defaultDocsDirPath));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
private boolean hasRemovableStorage() {
|
||||||
|
mounts = getExternalFilesDirs(Environment.DIRECTORY_DOCUMENTS);
|
||||||
|
return mounts.length > 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
private List<HashMap<String, Object>> loadFiles(File docsDir) {
|
||||||
|
TreeSet files = new TreeSet<HashMap<String, Object>>((o1, o2) ->
|
||||||
|
((String) o1.get("name")).compareToIgnoreCase((String) o2.get("name"))) {
|
||||||
|
};
|
||||||
|
TreeSet dirs = new TreeSet<HashMap<String, Object>>((o1, o2) ->
|
||||||
|
((String) o1.get("name")).compareToIgnoreCase((String) o2.get("name"))) {
|
||||||
|
};
|
||||||
|
if (docsDir.getParentFile() != null && docsDir.getParentFile().canRead()) {
|
||||||
|
HashMap<String, Object> fileHashMap = new HashMap<>();
|
||||||
|
fileHashMap.put("name", "..");
|
||||||
|
fileHashMap.put("file", docsDir.getParentFile());
|
||||||
|
dirs.add(fileHashMap);
|
||||||
|
}
|
||||||
|
|
||||||
|
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 (!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<>(dirs);
|
||||||
|
sortedFiles.addAll(files);
|
||||||
|
|
||||||
|
return sortedFiles;
|
||||||
|
}
|
||||||
|
|
||||||
|
private void updateListView(File filesDir) {
|
||||||
|
setTitle(filesDir.getName());
|
||||||
|
filePath = filesDir.getAbsolutePath();
|
||||||
|
fileHandler.post(() -> {
|
||||||
|
List<HashMap<String, Object>> files = loadFiles(filesDir);
|
||||||
|
|
||||||
|
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 {
|
||||||
|
Intent fileIntent = new Intent();
|
||||||
|
fileIntent.putExtra(MainActivity.EXTRA_FILE, file);
|
||||||
|
setResult(RESULT_OK, fileIntent);
|
||||||
|
finish();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -1,15 +1,11 @@
|
||||||
package com.wbrawner.simplemarkdown.view.activity;
|
package com.wbrawner.simplemarkdown.view.activity;
|
||||||
|
|
||||||
import android.Manifest;
|
import android.Manifest;
|
||||||
import android.content.ActivityNotFoundException;
|
|
||||||
import android.content.Intent;
|
import android.content.Intent;
|
||||||
import android.content.pm.PackageManager;
|
import android.content.pm.PackageManager;
|
||||||
import android.content.res.Configuration;
|
import android.content.res.Configuration;
|
||||||
import android.database.Cursor;
|
|
||||||
import android.net.Uri;
|
|
||||||
import android.os.Build;
|
import android.os.Build;
|
||||||
import android.os.Environment;
|
import android.os.Environment;
|
||||||
import android.provider.OpenableColumns;
|
|
||||||
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;
|
||||||
|
@ -28,6 +24,8 @@ 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;
|
||||||
import com.wbrawner.simplemarkdown.view.adapter.EditPagerAdapter;
|
import com.wbrawner.simplemarkdown.view.adapter.EditPagerAdapter;
|
||||||
|
|
||||||
|
import java.io.File;
|
||||||
import java.io.InputStream;
|
import java.io.InputStream;
|
||||||
|
|
||||||
import javax.inject.Inject;
|
import javax.inject.Inject;
|
||||||
|
@ -38,8 +36,12 @@ import butterknife.ButterKnife;
|
||||||
public class MainActivity extends AppCompatActivity
|
public class MainActivity extends AppCompatActivity
|
||||||
implements ActivityCompat.OnRequestPermissionsResultCallback {
|
implements ActivityCompat.OnRequestPermissionsResultCallback {
|
||||||
|
|
||||||
public static final int WRITE_PERMISSION_REQUEST = 0;
|
static final int WRITE_PERMISSION_REQUEST = 0;
|
||||||
private static final int OPEN_FILE_REQUEST = 1;
|
static final int OPEN_FILE_REQUEST = 1;
|
||||||
|
static final int SAVE_FILE_REQUEST = 2;
|
||||||
|
static final String EXTRA_FILE = "EXTRA_FILE";
|
||||||
|
static final String EXTRA_FILE_PATH = "EXTRA_FILE_PATH";
|
||||||
|
static final String EXTRA_REQUEST_CODE = "EXTRA_REQUEST_CODE";
|
||||||
public static final String AUTHORITY = "com.wbrawner.simplemarkdown.fileprovider";
|
public static final String AUTHORITY = "com.wbrawner.simplemarkdown.fileprovider";
|
||||||
|
|
||||||
@Inject
|
@Inject
|
||||||
|
@ -93,11 +95,11 @@ public class MainActivity extends AppCompatActivity
|
||||||
MainActivity.this,
|
MainActivity.this,
|
||||||
Manifest.permission.WRITE_EXTERNAL_STORAGE
|
Manifest.permission.WRITE_EXTERNAL_STORAGE
|
||||||
) == PackageManager.PERMISSION_GRANTED)
|
) == PackageManager.PERMISSION_GRANTED)
|
||||||
showSaveDialog();
|
requestSave();
|
||||||
else {
|
else {
|
||||||
if (Build.VERSION.SDK_INT >= 23) {
|
if (Build.VERSION.SDK_INT >= 23) {
|
||||||
requestPermissions(
|
requestPermissions(
|
||||||
new String[] {Manifest.permission.WRITE_EXTERNAL_STORAGE},
|
new String[]{Manifest.permission.WRITE_EXTERNAL_STORAGE},
|
||||||
WRITE_PERMISSION_REQUEST
|
WRITE_PERMISSION_REQUEST
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
@ -113,7 +115,21 @@ public class MainActivity extends AppCompatActivity
|
||||||
));
|
));
|
||||||
break;
|
break;
|
||||||
case R.id.action_load:
|
case R.id.action_load:
|
||||||
requestOpen();
|
if (ContextCompat.checkSelfPermission(
|
||||||
|
MainActivity.this,
|
||||||
|
Manifest.permission.WRITE_EXTERNAL_STORAGE
|
||||||
|
) == PackageManager.PERMISSION_GRANTED)
|
||||||
|
requestOpen();
|
||||||
|
else {
|
||||||
|
if (Build.VERSION.SDK_INT >= 23) {
|
||||||
|
requestPermissions(
|
||||||
|
new String[]{Manifest.permission.WRITE_EXTERNAL_STORAGE},
|
||||||
|
OPEN_FILE_REQUEST
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case R.id.action_new:
|
||||||
break;
|
break;
|
||||||
case R.id.action_help:
|
case R.id.action_help:
|
||||||
showInfoActivity(R.id.action_help);
|
showInfoActivity(R.id.action_help);
|
||||||
|
@ -181,10 +197,6 @@ public class MainActivity extends AppCompatActivity
|
||||||
if (input.getText().length() > 0) {
|
if (input.getText().length() > 0) {
|
||||||
presenter.setFileName(input.getText().toString());
|
presenter.setFileName(input.getText().toString());
|
||||||
setTitle(presenter.getFileName());
|
setTitle(presenter.getFileName());
|
||||||
if (Environment.getExternalStorageState().equals(Environment.MEDIA_MOUNTED)) {
|
|
||||||
String path = getDocsPath() + input.getText();
|
|
||||||
presenter.saveMarkdown(path);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
builder.setNegativeButton("Cancel", (dialog, which) -> {
|
builder.setNegativeButton("Cancel", (dialog, which) -> {
|
||||||
|
@ -195,8 +207,8 @@ public class MainActivity extends AppCompatActivity
|
||||||
}
|
}
|
||||||
|
|
||||||
public String getDocsPath() {
|
public String getDocsPath() {
|
||||||
return Environment.getExternalStorageDirectory() + "/" +
|
return Environment.getExternalStorageDirectory() + "/" +
|
||||||
Environment.DIRECTORY_DOCUMENTS + "/";
|
Environment.DIRECTORY_DOCUMENTS + "/";
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
@ -214,7 +226,19 @@ public class MainActivity extends AppCompatActivity
|
||||||
Toast.makeText(MainActivity.this, R.string.no_permissions, Toast.LENGTH_SHORT)
|
Toast.makeText(MainActivity.this, R.string.no_permissions, Toast.LENGTH_SHORT)
|
||||||
.show();
|
.show();
|
||||||
}
|
}
|
||||||
return;
|
break;
|
||||||
|
}
|
||||||
|
case OPEN_FILE_REQUEST: {
|
||||||
|
if (grantResults.length > 0
|
||||||
|
&& grantResults[0] == PackageManager.PERMISSION_GRANTED) {
|
||||||
|
// Permission granted, open file save dialog
|
||||||
|
requestOpen();
|
||||||
|
} else {
|
||||||
|
// Permission denied, do nothing
|
||||||
|
Toast.makeText(MainActivity.this, R.string.no_permissions, Toast.LENGTH_SHORT)
|
||||||
|
.show();
|
||||||
|
}
|
||||||
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -231,29 +255,44 @@ public class MainActivity extends AppCompatActivity
|
||||||
protected void onActivityResult(int requestCode, int resultCode, Intent data) {
|
protected void onActivityResult(int requestCode, int resultCode, Intent data) {
|
||||||
switch (requestCode) {
|
switch (requestCode) {
|
||||||
case OPEN_FILE_REQUEST:
|
case OPEN_FILE_REQUEST:
|
||||||
if (resultCode == RESULT_OK) {
|
if (resultCode != RESULT_OK || data == null || !data.hasExtra(EXTRA_FILE)) {
|
||||||
presenter.loadFromUri(MainActivity.this, data.getData());
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
presenter.loadMarkdown((File) data.getSerializableExtra(EXTRA_FILE));
|
||||||
|
break;
|
||||||
|
case SAVE_FILE_REQUEST:
|
||||||
|
if (resultCode != RESULT_OK
|
||||||
|
|| data == null
|
||||||
|
|| !data.hasExtra(EXTRA_FILE_PATH)
|
||||||
|
|| data.getStringExtra(EXTRA_FILE_PATH).isEmpty()) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
String path = data.getStringExtra(EXTRA_FILE_PATH);
|
||||||
|
presenter.saveMarkdown(path);
|
||||||
|
setTitle(presenter.getFileName());
|
||||||
|
break;
|
||||||
}
|
}
|
||||||
super.onActivityResult(requestCode, resultCode, data);
|
super.onActivityResult(requestCode, resultCode, data);
|
||||||
}
|
}
|
||||||
|
|
||||||
private void requestOpen() {
|
private void requestOpen() {
|
||||||
Intent openIntent = new Intent(Intent.ACTION_GET_CONTENT);
|
Intent intent = new Intent(MainActivity.this, ExplorerActivity.class);
|
||||||
openIntent.setType("text/*");
|
intent.putExtra(EXTRA_REQUEST_CODE, OPEN_FILE_REQUEST);
|
||||||
openIntent.addCategory(Intent.CATEGORY_OPENABLE);
|
startActivityForResult(
|
||||||
try {
|
intent,
|
||||||
startActivityForResult(
|
OPEN_FILE_REQUEST
|
||||||
Intent.createChooser(
|
);
|
||||||
openIntent,
|
}
|
||||||
getString(R.string.open_file)
|
|
||||||
),
|
private void requestSave() {
|
||||||
OPEN_FILE_REQUEST
|
Intent intent = new Intent(MainActivity.this, ExplorerActivity.class);
|
||||||
);
|
intent.putExtra(EXTRA_REQUEST_CODE, SAVE_FILE_REQUEST);
|
||||||
} catch (ActivityNotFoundException e) {
|
intent.putExtra(EXTRA_FILE, presenter.getFile());
|
||||||
Toast.makeText(MainActivity.this, R.string.no_filebrowser, Toast.LENGTH_SHORT)
|
startActivityForResult(
|
||||||
.show();
|
intent,
|
||||||
}
|
SAVE_FILE_REQUEST
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
|
75
app/src/main/res/layout/activity_explorer.xml
Normal file
75
app/src/main/res/layout/activity_explorer.xml
Normal file
|
@ -0,0 +1,75 @@
|
||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<android.support.constraint.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
|
xmlns:app="http://schemas.android.com/apk/res-auto"
|
||||||
|
xmlns:tools="http://schemas.android.com/tools"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="match_parent"
|
||||||
|
tools:context="com.wbrawner.simplemarkdown.view.activity.ExplorerActivity">
|
||||||
|
|
||||||
|
<android.support.design.widget.AppBarLayout
|
||||||
|
android:id="@+id/toolbarContainer"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:theme="@style/AppTheme.AppBarOverlay"
|
||||||
|
app:layout_constraintEnd_toEndOf="parent"
|
||||||
|
app:layout_constraintStart_toStartOf="parent"
|
||||||
|
app:layout_constraintTop_toTopOf="parent">
|
||||||
|
|
||||||
|
<android.support.v7.widget.Toolbar
|
||||||
|
android:id="@+id/toolbar"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="?attr/actionBarSize"
|
||||||
|
android:background="?attr/colorPrimary"
|
||||||
|
app:popupTheme="@style/AppTheme.PopupOverlay" />
|
||||||
|
|
||||||
|
</android.support.design.widget.AppBarLayout>
|
||||||
|
|
||||||
|
<include
|
||||||
|
android:id="@+id/file_list_container"
|
||||||
|
layout="@layout/content_explorer"
|
||||||
|
android:layout_width="0dp"
|
||||||
|
android:layout_height="0dp"
|
||||||
|
app:layout_constraintBottom_toTopOf="@id/file_name"
|
||||||
|
app:layout_constraintEnd_toEndOf="parent"
|
||||||
|
app:layout_constraintStart_toStartOf="parent"
|
||||||
|
app:layout_constraintTop_toBottomOf="@id/toolbarContainer" />
|
||||||
|
|
||||||
|
<android.support.design.widget.FloatingActionButton
|
||||||
|
android:id="@+id/fab"
|
||||||
|
android:layout_width="wrap_content"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:layout_gravity="bottom|end"
|
||||||
|
android:layout_margin="@dimen/fab_margin"
|
||||||
|
android:layout_marginBottom="8dp"
|
||||||
|
android:layout_marginEnd="8dp"
|
||||||
|
android:visibility="gone"
|
||||||
|
app:layout_constraintBottom_toTopOf="@+id/file_name"
|
||||||
|
app:layout_constraintEnd_toEndOf="parent"
|
||||||
|
app:srcCompat="@android:drawable/ic_input_add" />
|
||||||
|
|
||||||
|
<EditText
|
||||||
|
android:id="@+id/file_name"
|
||||||
|
android:layout_width="0dp"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:visibility="gone"
|
||||||
|
android:layout_marginBottom="8dp"
|
||||||
|
android:layout_marginEnd="8dp"
|
||||||
|
android:layout_marginStart="8dp"
|
||||||
|
app:layout_constraintBottom_toBottomOf="parent"
|
||||||
|
app:layout_constraintEnd_toStartOf="@+id/button_save"
|
||||||
|
app:layout_constraintStart_toStartOf="parent" />
|
||||||
|
|
||||||
|
<Button
|
||||||
|
android:id="@+id/button_save"
|
||||||
|
style="@style/ButtonRed"
|
||||||
|
android:visibility="gone"
|
||||||
|
android:layout_width="wrap_content"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:layout_marginBottom="8dp"
|
||||||
|
android:layout_marginEnd="8dp"
|
||||||
|
android:text="@string/action_save"
|
||||||
|
android:textAllCaps="true"
|
||||||
|
app:layout_constraintBottom_toBottomOf="parent"
|
||||||
|
app:layout_constraintEnd_toEndOf="parent" />
|
||||||
|
|
||||||
|
</android.support.constraint.ConstraintLayout>
|
20
app/src/main/res/layout/content_explorer.xml
Normal file
20
app/src/main/res/layout/content_explorer.xml
Normal file
|
@ -0,0 +1,20 @@
|
||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<android.support.constraint.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
|
xmlns:app="http://schemas.android.com/apk/res-auto"
|
||||||
|
xmlns:tools="http://schemas.android.com/tools"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="match_parent"
|
||||||
|
app:layout_behavior="@string/appbar_scrolling_view_behavior"
|
||||||
|
tools:context="com.wbrawner.simplemarkdown.view.activity.ExplorerActivity"
|
||||||
|
tools:showIn="@layout/activity_explorer">
|
||||||
|
|
||||||
|
<ListView
|
||||||
|
android:id="@+id/file_list"
|
||||||
|
android:layout_width="0dp"
|
||||||
|
android:layout_height="0dp"
|
||||||
|
app:layout_constraintBottom_toBottomOf="parent"
|
||||||
|
app:layout_constraintEnd_toEndOf="parent"
|
||||||
|
app:layout_constraintStart_toStartOf="parent"
|
||||||
|
app:layout_constraintTop_toTopOf="parent" />
|
||||||
|
|
||||||
|
</android.support.constraint.ConstraintLayout>
|
|
@ -14,7 +14,13 @@
|
||||||
<item
|
<item
|
||||||
android:id="@+id/action_load"
|
android:id="@+id/action_load"
|
||||||
android:orderInCategory="100"
|
android:orderInCategory="100"
|
||||||
android:title="@string/action_load"
|
android:title="@string/action_open"
|
||||||
|
app:showAsAction="never" />
|
||||||
|
<item
|
||||||
|
android:id="@+id/action_new"
|
||||||
|
android:visible="false"
|
||||||
|
android:orderInCategory="150"
|
||||||
|
android:title="@string/action_new"
|
||||||
app:showAsAction="never" />
|
app:showAsAction="never" />
|
||||||
<item
|
<item
|
||||||
android:id="@+id/action_help"
|
android:id="@+id/action_help"
|
||||||
|
|
9
app/src/main/res/menu/menu_explorer.xml
Normal file
9
app/src/main/res/menu/menu_explorer.xml
Normal file
|
@ -0,0 +1,9 @@
|
||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<menu xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
|
xmlns:app="http://schemas.android.com/apk/res-auto">
|
||||||
|
<item
|
||||||
|
android:title="@string/action_use_sdcard"
|
||||||
|
android:id="@+id/action_use_sdcard"
|
||||||
|
app:showAsAction="never"
|
||||||
|
android:checkable="true" />
|
||||||
|
</menu>
|
|
@ -2,5 +2,5 @@
|
||||||
<resources>
|
<resources>
|
||||||
<color name="colorPrimary">#d32f2f</color>
|
<color name="colorPrimary">#d32f2f</color>
|
||||||
<color name="colorPrimaryDark">#b71c1c</color>
|
<color name="colorPrimaryDark">#b71c1c</color>
|
||||||
<color name="colorAccent">#ef9a9a</color>
|
<color name="colorAccent">#d32f2f</color>
|
||||||
</resources>
|
</resources>
|
||||||
|
|
|
@ -24,4 +24,9 @@
|
||||||
<string name="error_write">An error occurred while writing the file</string>
|
<string name="error_write">An error occurred while writing the file</string>
|
||||||
<string name="file_load_error">An error occurred while opening the file</string>
|
<string name="file_load_error">An error occurred while opening the file</string>
|
||||||
<string name="action_libraries">Libraries</string>
|
<string name="action_libraries">Libraries</string>
|
||||||
|
<string name="title_activity_explorer">ExplorerActivity</string>
|
||||||
|
<string name="action_new">New</string>
|
||||||
|
<string name="action_use_sdcard">Use SD Card</string>
|
||||||
|
<string name="action_done">Done</string>
|
||||||
|
<string name="action_open">Open</string>
|
||||||
</resources>
|
</resources>
|
||||||
|
|
|
@ -21,4 +21,9 @@
|
||||||
|
|
||||||
<style name="AppTheme.PopupOverlay" parent="ThemeOverlay.AppCompat.Light" />
|
<style name="AppTheme.PopupOverlay" parent="ThemeOverlay.AppCompat.Light" />
|
||||||
|
|
||||||
|
<style name="ButtonRed">
|
||||||
|
<item name="android:background">@color/colorAccent</item>
|
||||||
|
<item name="android:textColor">#FFFFFF</item>
|
||||||
|
</style>
|
||||||
|
|
||||||
</resources>
|
</resources>
|
||||||
|
|
Loading…
Reference in a new issue