From 8c2f32dd97941b7e88aad7d5721601c9d2b9a63b Mon Sep 17 00:00:00 2001 From: Billy Brawner Date: Sat, 23 Dec 2017 22:27:38 -0600 Subject: [PATCH] Use built-in file explorer instead of OEM implementation --- app/build.gradle | 24 +- .../simplemarkdown/MainActivityTests.java | 71 ++++++ app/src/main/AndroidManifest.xml | 9 +- .../simplemarkdown/model/MarkdownFile.java | 13 +- .../presentation/MarkdownPresenter.java | 3 + .../presentation/MarkdownPresenterImpl.java | 15 +- .../view/activity/ExplorerActivity.java | 221 ++++++++++++++++++ .../view/activity/MainActivity.java | 105 ++++++--- app/src/main/res/layout/activity_explorer.xml | 75 ++++++ app/src/main/res/layout/content_explorer.xml | 20 ++ app/src/main/res/menu/menu_edit.xml | 8 +- app/src/main/res/menu/menu_explorer.xml | 9 + app/src/main/res/values/colors.xml | 2 +- app/src/main/res/values/strings.xml | 5 + app/src/main/res/values/styles.xml | 5 + 15 files changed, 532 insertions(+), 53 deletions(-) create mode 100644 app/src/androidTest/java/com/wbrawner/simplemarkdown/MainActivityTests.java create mode 100644 app/src/main/java/com/wbrawner/simplemarkdown/view/activity/ExplorerActivity.java create mode 100644 app/src/main/res/layout/activity_explorer.xml create mode 100644 app/src/main/res/layout/content_explorer.xml create mode 100644 app/src/main/res/menu/menu_explorer.xml diff --git a/app/build.gradle b/app/build.gradle index 89ec346..e50b4ce 100644 --- a/app/build.gradle +++ b/app/build.gradle @@ -4,6 +4,7 @@ buildscript { } dependencies { + //noinspection GradleDynamicVersion classpath 'io.fabric.tools:gradle:1.+' } } @@ -25,7 +26,7 @@ android { exclude 'META-INF/LICENSE' exclude 'META-INF/DEPENDENCIES' } - compileSdkVersion 26 + compileSdkVersion 27 buildToolsVersion "26.0.2" compileOptions { sourceCompatibility JavaVersion.VERSION_1_8 @@ -34,9 +35,9 @@ android { defaultConfig { applicationId "com.wbrawner.simplemarkdown" minSdkVersion 19 - targetSdkVersion 26 - versionCode 2 - versionName "0.1.1" + targetSdkVersion 27 + versionCode 3 + versionName "0.1.2" testInstrumentationRunner "android.support.test.runner.AndroidJUnitRunner" } buildTypes { @@ -50,15 +51,19 @@ android { } } +ext { + support_version = "27.0.2" +} + dependencies { compile fileTree(dir: 'libs', include: ['*.jar']) androidTestCompile('com.android.support.test.espresso:espresso-core:2.2.2', { 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:design:26.0.0' - compile 'com.android.support:support-v13:26.0.0' + compile "com.android.support:design:$support_version" + compile "com.android.support:support-v13:$support_version" compile 'com.commonsware.cwac:anddown:0.3.0' compile 'com.google.dagger:dagger:2.11' compile 'com.jakewharton:butterknife:8.7.0' @@ -67,9 +72,12 @@ dependencies { compile 'io.reactivex.rxjava2:rxandroid:2.0.1' compile 'io.reactivex.rxjava2:rxjava:2.1.0' 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.jakewharton:butterknife-compiler:8.7.0' compile('com.crashlytics.sdk.android:crashlytics:2.8.0@aar') { - transitive = true; + transitive = true } } diff --git a/app/src/androidTest/java/com/wbrawner/simplemarkdown/MainActivityTests.java b/app/src/androidTest/java/com/wbrawner/simplemarkdown/MainActivityTests.java new file mode 100644 index 0000000..0f203f8 --- /dev/null +++ b/app/src/androidTest/java/com/wbrawner/simplemarkdown/MainActivityTests.java @@ -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 Testing documentation + */ +@RunWith(AndroidJUnit4.class) +public class MainActivityTests { + + @Rule + public ActivityTestRule 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()); + } +} diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml index 2d3457f..756004e 100644 --- a/app/src/main/AndroidManifest.xml +++ b/app/src/main/AndroidManifest.xml @@ -13,7 +13,8 @@ android:roundIcon="@mipmap/ic_launcher_round" android:supportsRtl="true" android:theme="@style/AppTheme"> - @@ -37,7 +38,7 @@ - + + \ No newline at end of file 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 06d60af..21b0e9e 100644 --- a/app/src/main/java/com/wbrawner/simplemarkdown/model/MarkdownFile.java +++ b/app/src/main/java/com/wbrawner/simplemarkdown/model/MarkdownFile.java @@ -1,10 +1,7 @@ package com.wbrawner.simplemarkdown.model; -import android.util.Log; - import java.io.BufferedReader; import java.io.File; -import java.io.FileDescriptor; import java.io.FileNotFoundException; import java.io.FileOutputStream; import java.io.FileReader; @@ -100,14 +97,16 @@ public class MarkdownFile { } public int load(String path) { - Log.d(TAG, path); + return load(new File(path)); + } + + public int load(File markdownFile) { int code; - File markdownFile = new File(path); if (markdownFile.exists() && markdownFile.canRead()) { BufferedReader reader = null; try { this.name = markdownFile.getName(); - this.path = markdownFile.getPath(); + this.path = markdownFile.getParentFile().getAbsolutePath(); StringBuilder sb = new StringBuilder(); String line; reader = new BufferedReader(new FileReader(markdownFile)); @@ -171,6 +170,8 @@ public class MarkdownFile { } else { code = WRITE_ERROR; } + this.name = markdownFile.getName(); + this.path = markdownFile.getParentFile().getAbsolutePath(); return code; } 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 cd7a4ab..124dbfc 100644 --- a/app/src/main/java/com/wbrawner/simplemarkdown/presentation/MarkdownPresenter.java +++ b/app/src/main/java/com/wbrawner/simplemarkdown/presentation/MarkdownPresenter.java @@ -6,6 +6,7 @@ import android.net.Uri; import com.wbrawner.simplemarkdown.view.MarkdownEditView; import com.wbrawner.simplemarkdown.view.MarkdownPreviewView; +import java.io.File; import java.io.InputStream; /** @@ -13,8 +14,10 @@ import java.io.InputStream; */ public interface MarkdownPresenter extends LifecyclePresenter { + File getFile(); void loadMarkdown(String filePath); void loadMarkdown(InputStream in); + void loadMarkdown(File file); void loadFromUri(Context context, Uri fileUri); void loadTempMarkdown(InputStream in, OnTempFileLoadedListener listener); void setEditView(MarkdownEditView editView); 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 eef2c46..d77627f 100644 --- a/app/src/main/java/com/wbrawner/simplemarkdown/presentation/MarkdownPresenterImpl.java +++ b/app/src/main/java/com/wbrawner/simplemarkdown/presentation/MarkdownPresenterImpl.java @@ -10,6 +10,7 @@ import com.wbrawner.simplemarkdown.model.MarkdownFile; import com.wbrawner.simplemarkdown.view.MarkdownEditView; import com.wbrawner.simplemarkdown.view.MarkdownPreviewView; +import java.io.File; import java.io.InputStream; /** @@ -41,8 +42,19 @@ public class MarkdownPresenterImpl implements MarkdownPresenter { @Override 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 = () -> { - int result = file.load(filePath); + int result = this.file.load(file); if (editView != null) { if (result == MarkdownFile.SUCCESS) { editView.setMarkdown(getMarkdown()); @@ -82,7 +94,6 @@ public class MarkdownPresenterImpl implements MarkdownPresenter { } else { listener.onError(result); } - tmpFile = null; }; fileLoader.run(); } diff --git a/app/src/main/java/com/wbrawner/simplemarkdown/view/activity/ExplorerActivity.java b/app/src/main/java/com/wbrawner/simplemarkdown/view/activity/ExplorerActivity.java new file mode 100644 index 0000000..5fb9a93 --- /dev/null +++ b/app/src/main/java/com/wbrawner/simplemarkdown/view/activity/ExplorerActivity.java @@ -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> loadFiles(File docsDir) { + TreeSet files = new TreeSet>((o1, o2) -> + ((String) o1.get("name")).compareToIgnoreCase((String) o2.get("name"))) { + }; + TreeSet dirs = new TreeSet>((o1, o2) -> + ((String) o1.get("name")).compareToIgnoreCase((String) o2.get("name"))) { + }; + if (docsDir.getParentFile() != null && docsDir.getParentFile().canRead()) { + HashMap 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 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 fileHashMap = new HashMap<>(); + fileHashMap.put("name", file.getName()); + fileHashMap.put("file", file); + files.add(fileHashMap); + } + } + + List> sortedFiles = new ArrayList<>(dirs); + sortedFiles.addAll(files); + + return sortedFiles; + } + + private void updateListView(File filesDir) { + setTitle(filesDir.getName()); + filePath = filesDir.getAbsolutePath(); + fileHandler.post(() -> { + List> 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(); + } + } +} 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 7e55fc0..e86fb8f 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 @@ -1,15 +1,11 @@ package com.wbrawner.simplemarkdown.view.activity; import android.Manifest; -import android.content.ActivityNotFoundException; import android.content.Intent; import android.content.pm.PackageManager; import android.content.res.Configuration; -import android.database.Cursor; -import android.net.Uri; import android.os.Build; import android.os.Environment; -import android.provider.OpenableColumns; import android.support.design.widget.TabLayout; import android.support.v4.app.ActivityCompat; import android.support.v4.content.ContextCompat; @@ -28,6 +24,8 @@ import com.wbrawner.simplemarkdown.MarkdownApplication; import com.wbrawner.simplemarkdown.R; import com.wbrawner.simplemarkdown.presentation.MarkdownPresenter; import com.wbrawner.simplemarkdown.view.adapter.EditPagerAdapter; + +import java.io.File; import java.io.InputStream; import javax.inject.Inject; @@ -38,8 +36,12 @@ import butterknife.ButterKnife; public class MainActivity extends AppCompatActivity implements ActivityCompat.OnRequestPermissionsResultCallback { - public static final int WRITE_PERMISSION_REQUEST = 0; - private static final int OPEN_FILE_REQUEST = 1; + static final int WRITE_PERMISSION_REQUEST = 0; + 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"; @Inject @@ -93,11 +95,11 @@ public class MainActivity extends AppCompatActivity MainActivity.this, Manifest.permission.WRITE_EXTERNAL_STORAGE ) == PackageManager.PERMISSION_GRANTED) - showSaveDialog(); + requestSave(); else { if (Build.VERSION.SDK_INT >= 23) { requestPermissions( - new String[] {Manifest.permission.WRITE_EXTERNAL_STORAGE}, + new String[]{Manifest.permission.WRITE_EXTERNAL_STORAGE}, WRITE_PERMISSION_REQUEST ); } @@ -113,7 +115,21 @@ public class MainActivity extends AppCompatActivity )); break; 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; case R.id.action_help: showInfoActivity(R.id.action_help); @@ -181,10 +197,6 @@ public class MainActivity extends AppCompatActivity if (input.getText().length() > 0) { presenter.setFileName(input.getText().toString()); setTitle(presenter.getFileName()); - if (Environment.getExternalStorageState().equals(Environment.MEDIA_MOUNTED)) { - String path = getDocsPath() + input.getText(); - presenter.saveMarkdown(path); - } } }); builder.setNegativeButton("Cancel", (dialog, which) -> { @@ -195,8 +207,8 @@ public class MainActivity extends AppCompatActivity } public String getDocsPath() { - return Environment.getExternalStorageDirectory() + "/" + - Environment.DIRECTORY_DOCUMENTS + "/"; + return Environment.getExternalStorageDirectory() + "/" + + Environment.DIRECTORY_DOCUMENTS + "/"; } @Override @@ -214,7 +226,19 @@ public class MainActivity extends AppCompatActivity Toast.makeText(MainActivity.this, R.string.no_permissions, Toast.LENGTH_SHORT) .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) { switch (requestCode) { case OPEN_FILE_REQUEST: - if (resultCode == RESULT_OK) { - presenter.loadFromUri(MainActivity.this, data.getData()); + if (resultCode != RESULT_OK || data == null || !data.hasExtra(EXTRA_FILE)) { + 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); } private void requestOpen() { - Intent openIntent = new Intent(Intent.ACTION_GET_CONTENT); - openIntent.setType("text/*"); - openIntent.addCategory(Intent.CATEGORY_OPENABLE); - try { - startActivityForResult( - Intent.createChooser( - openIntent, - getString(R.string.open_file) - ), - OPEN_FILE_REQUEST - ); - } catch (ActivityNotFoundException e) { - Toast.makeText(MainActivity.this, R.string.no_filebrowser, Toast.LENGTH_SHORT) - .show(); - } + Intent intent = new Intent(MainActivity.this, ExplorerActivity.class); + intent.putExtra(EXTRA_REQUEST_CODE, OPEN_FILE_REQUEST); + startActivityForResult( + intent, + OPEN_FILE_REQUEST + ); + } + + private void requestSave() { + Intent intent = new Intent(MainActivity.this, ExplorerActivity.class); + intent.putExtra(EXTRA_REQUEST_CODE, SAVE_FILE_REQUEST); + intent.putExtra(EXTRA_FILE, presenter.getFile()); + startActivityForResult( + intent, + SAVE_FILE_REQUEST + ); } @Override diff --git a/app/src/main/res/layout/activity_explorer.xml b/app/src/main/res/layout/activity_explorer.xml new file mode 100644 index 0000000..9c75bfc --- /dev/null +++ b/app/src/main/res/layout/activity_explorer.xml @@ -0,0 +1,75 @@ + + + + + + + + + + + + + + + +