diff --git a/Clover/app/src/main/java/org/floens/chan/controller/Controller.java b/Clover/app/src/main/java/org/floens/chan/controller/Controller.java index a119e7fc..88814863 100644 --- a/Clover/app/src/main/java/org/floens/chan/controller/Controller.java +++ b/Clover/app/src/main/java/org/floens/chan/controller/Controller.java @@ -18,6 +18,7 @@ package org.floens.chan.controller; import android.content.Context; +import android.content.Intent; import android.content.res.Configuration; import android.view.KeyEvent; import android.view.LayoutInflater; @@ -135,6 +136,9 @@ public abstract class Controller { controller.navigationController = navigationController; } controller.onCreate(); + if (controller.view == null) { + throw new IllegalArgumentException("Controller has no view"); + } } public boolean removeChildController(Controller controller) { @@ -186,6 +190,12 @@ public abstract class Controller { return false; } + public void onActivityResult(int requestCode, int resultCode, Intent data) { + for (Controller childController : childControllers) { + childController.onActivityResult(requestCode, resultCode, data); + } + } + public void presentController(Controller controller) { presentController(controller, true); } diff --git a/Clover/app/src/main/java/org/floens/chan/core/saver/ImageSaveTask.java b/Clover/app/src/main/java/org/floens/chan/core/saver/ImageSaveTask.java index 74d4815a..5d85350e 100644 --- a/Clover/app/src/main/java/org/floens/chan/core/saver/ImageSaveTask.java +++ b/Clover/app/src/main/java/org/floens/chan/core/saver/ImageSaveTask.java @@ -19,15 +19,14 @@ package org.floens.chan.core.saver; import android.content.Intent; import android.graphics.Bitmap; -import android.media.MediaScannerConnection; import android.net.Uri; -import org.floens.chan.core.cache.FileCacheListener; import org.floens.chan.core.cache.FileCache; import org.floens.chan.core.cache.FileCacheDownloader; +import org.floens.chan.core.cache.FileCacheListener; import org.floens.chan.core.model.PostImage; +import org.floens.chan.core.storage.StorageFile; import org.floens.chan.utils.AndroidUtils; -import org.floens.chan.utils.IOUtils; import org.floens.chan.utils.ImageDecoder; import org.floens.chan.utils.Logger; @@ -38,7 +37,6 @@ import javax.inject.Inject; import static org.floens.chan.Chan.inject; import static org.floens.chan.utils.AndroidUtils.dp; -import static org.floens.chan.utils.AndroidUtils.getAppContext; public class ImageSaveTask extends FileCacheListener implements Runnable { private static final String TAG = "ImageSaveTask"; @@ -48,7 +46,7 @@ public class ImageSaveTask extends FileCacheListener implements Runnable { private PostImage postImage; private ImageSaveTaskCallback callback; - private File destination; + private StorageFile destination; private boolean share; private boolean makeBitmap; private Bitmap bitmap; @@ -78,11 +76,11 @@ public class ImageSaveTask extends FileCacheListener implements Runnable { return postImage; } - public void setDestination(File destination) { + public void setDestination(StorageFile destination) { this.destination = destination; } - public File getDestination() { + public StorageFile getDestination() { return destination; } @@ -165,46 +163,32 @@ public class ImageSaveTask extends FileCacheListener implements Runnable { success = true; scanDestination(); if (makeBitmap) { - bitmap = ImageDecoder.decodeFile(destination, dp(512), dp(256)); + try { + bitmap = ImageDecoder.decodeFile(destination.inputStream(), dp(512), dp(256)); + } catch (IOException e) { + Logger.e(TAG, "onDestination decodeFile", e); + bitmap = null; + } } } private boolean copyToDestination(File source) { - boolean result = false; - try { - File parent = destination.getParentFile(); - if (!parent.mkdirs() && !parent.isDirectory()) { - throw new IOException("Could not create parent directory"); - } - - if (destination.isDirectory()) { - throw new IOException("Destination file is already a directory"); - } - - IOUtils.copyFile(source, destination); - - result = true; + destination.copyFrom(source); + return true; } catch (IOException e) { - Logger.e(TAG, "Error writing to file", e); + Logger.e(TAG, "copyToDestination copyFrom", e); } - return result; + return false; } private void scanDestination() { - MediaScannerConnection.scanFile(getAppContext(), new String[]{destination.getAbsolutePath()}, null, new MediaScannerConnection.OnScanCompletedListener() { - @Override - public void onScanCompleted(String path, final Uri uri) { - // Runs on a binder thread - AndroidUtils.runOnUiThread(new Runnable() { - @Override - public void run() { - afterScan(uri); - } - }); - } - }); + // TODO +// MediaScannerConnection.scanFile(getAppContext(), new String[]{destination.getAbsolutePath()}, null, (path, uri) -> { +// Runs on a binder thread +// AndroidUtils.runOnUiThread(() -> afterScan(uri)); +// }); } private void afterScan(final Uri uri) { diff --git a/Clover/app/src/main/java/org/floens/chan/core/saver/ImageSaver.java b/Clover/app/src/main/java/org/floens/chan/core/saver/ImageSaver.java index ef99fb31..d145a032 100644 --- a/Clover/app/src/main/java/org/floens/chan/core/saver/ImageSaver.java +++ b/Clover/app/src/main/java/org/floens/chan/core/saver/ImageSaver.java @@ -27,6 +27,8 @@ import android.widget.Toast; import org.floens.chan.R; import org.floens.chan.core.model.PostImage; import org.floens.chan.core.settings.ChanSettings; +import org.floens.chan.core.storage.Storage; +import org.floens.chan.core.storage.StorageFile; import org.floens.chan.ui.activity.StartActivity; import org.floens.chan.ui.helper.RuntimePermissionsHelper; import org.floens.chan.ui.service.SavingNotification; @@ -37,11 +39,15 @@ import java.util.concurrent.ExecutorService; import java.util.concurrent.Executors; import java.util.regex.Pattern; +import javax.inject.Inject; +import javax.inject.Singleton; + import de.greenrobot.event.EventBus; import static org.floens.chan.utils.AndroidUtils.getAppContext; import static org.floens.chan.utils.AndroidUtils.getString; +@Singleton public class ImageSaver implements ImageSaveTask.ImageSaveTaskCallback { private static final String TAG = "ImageSaver"; private static final int MAX_RENAME_TRIES = 500; @@ -49,18 +55,18 @@ public class ImageSaver implements ImageSaveTask.ImageSaveTaskCallback { private static final int MAX_NAME_LENGTH = 50; private static final Pattern REPEATED_UNDERSCORES_PATTERN = Pattern.compile("_+"); private static final Pattern SAFE_CHARACTERS_PATTERN = Pattern.compile("[^a-zA-Z0-9._]"); - private static final ImageSaver instance = new ImageSaver(); private NotificationManager notificationManager; private ExecutorService executor = Executors.newSingleThreadExecutor(); private int doneTasks = 0; private int totalTasks = 0; private Toast toast; - public static ImageSaver getInstance() { - return instance; - } + private Storage storage; + + @Inject + public ImageSaver(Storage storage) { + this.storage = storage; - private ImageSaver() { EventBus.getDefault().register(this); notificationManager = (NotificationManager) getAppContext().getSystemService(Context.NOTIFICATION_SERVICE); } @@ -68,8 +74,13 @@ public class ImageSaver implements ImageSaveTask.ImageSaveTaskCallback { public void startDownloadTask(Context context, final ImageSaveTask task) { PostImage postImage = task.getPostImage(); String name = ChanSettings.saveOriginalFilename.get() ? postImage.originalName : postImage.filename; - String fileName = filterName(name + "." + postImage.extension); - task.setDestination(findUnusedFileName(new File(getSaveLocation(task), fileName), false)); +// String fileName = filterName(name + "." + postImage.extension); + + // TODO + StorageFile file = storage.obtainStorageFileForName(name + "." + postImage.extension); + task.setDestination(file); + +// task.setDestination(findUnusedFileName(new File(getSaveLocation(task), fileName), false)); // task.setMakeBitmap(true); task.setShowToast(true); @@ -163,9 +174,9 @@ public class ImageSaver implements ImageSaveTask.ImageSaveTaskCallback { for (ImageSaveTask task : tasks) { PostImage postImage = task.getPostImage(); String fileName = filterName(postImage.originalName + "." + postImage.extension); - task.setDestination(new File(getSaveLocation(task) + File.separator + subFolder + File.separator + fileName)); - - startTask(task); + // TODO +// task.setDestination(new File(getSaveLocation(task) + File.separator + subFolder + File.separator + fileName)); +// startTask(task); } updateNotification(); } @@ -194,7 +205,7 @@ public class ImageSaver implements ImageSaveTask.ImageSaveTaskCallback { NotificationCompat.Builder builder = new NotificationCompat.Builder(getAppContext()); builder.setSmallIcon(R.drawable.ic_stat_notify); builder.setContentTitle(getString(R.string.image_save_saved)); - String savedAs = getAppContext().getString(R.string.image_save_as, task.getDestination().getName()); + String savedAs = getAppContext().getString(R.string.image_save_as, task.getDestination().name()); builder.setContentText(savedAs); builder.setPriority(NotificationCompat.PRIORITY_HIGH); builder.setStyle(new NotificationCompat.BigPictureStyle() @@ -210,7 +221,7 @@ public class ImageSaver implements ImageSaveTask.ImageSaveTaskCallback { } String text = success ? - getAppContext().getString(R.string.image_save_as, task.getDestination().getName()) : + getAppContext().getString(R.string.image_save_as, task.getDestination().name()) : getString(R.string.image_save_failed); toast = Toast.makeText(getAppContext(), text, Toast.LENGTH_LONG); toast.show(); diff --git a/Clover/app/src/main/java/org/floens/chan/core/settings/ChanSettings.java b/Clover/app/src/main/java/org/floens/chan/core/settings/ChanSettings.java index 2dd373e9..acf07d7c 100644 --- a/Clover/app/src/main/java/org/floens/chan/core/settings/ChanSettings.java +++ b/Clover/app/src/main/java/org/floens/chan/core/settings/ChanSettings.java @@ -115,6 +115,7 @@ public class ChanSettings { public static final BooleanSetting developer; public static final StringSetting saveLocation; + public static final StringSetting saveLocationTreeUri; public static final BooleanSetting saveOriginalFilename; public static final BooleanSetting shareUrl; public static final BooleanSetting enableReplyFab; @@ -197,6 +198,7 @@ public class ChanSettings { saveLocation = new StringSetting(p, "preference_image_save_location", Environment.getExternalStorageDirectory() + File.separator + "Clover"); saveLocation.addCallback((setting, value) -> EventBus.getDefault().post(new SettingChanged<>(saveLocation))); + saveLocationTreeUri = new StringSetting(p, "preference_image_save_tree_uri", ""); saveOriginalFilename = new BooleanSetting(p, "preference_image_save_original", false); shareUrl = new BooleanSetting(p, "preference_image_share_url", false); enableReplyFab = new BooleanSetting(p, "preference_enable_reply_fab", true); diff --git a/Clover/app/src/main/java/org/floens/chan/core/storage/BaseStorageImpl.java b/Clover/app/src/main/java/org/floens/chan/core/storage/BaseStorageImpl.java new file mode 100644 index 00000000..cc9b509a --- /dev/null +++ b/Clover/app/src/main/java/org/floens/chan/core/storage/BaseStorageImpl.java @@ -0,0 +1,38 @@ +package org.floens.chan.core.storage; + +import android.content.Context; +import android.content.Intent; +import android.net.Uri; + +public class BaseStorageImpl implements StorageImpl { + protected Context applicationContext; + + public BaseStorageImpl(Context applicationContext) { + this.applicationContext = applicationContext; + } + + @Override + public boolean supportsExternalStorage() { + return false; + } + + @Override + public Intent getOpenTreeIntent() { + throw new UnsupportedOperationException(); + } + + @Override + public void handleOpenTreeIntent(Uri uri) { + throw new UnsupportedOperationException(); + } + + @Override + public StorageFile obtainStorageFileForName(String name) { + throw new UnsupportedOperationException(); + } + + @Override + public String currentStorageName() { + throw new UnsupportedOperationException(); + } +} diff --git a/Clover/app/src/main/java/org/floens/chan/core/storage/LollipopStorageImpl.java b/Clover/app/src/main/java/org/floens/chan/core/storage/LollipopStorageImpl.java new file mode 100644 index 00000000..3d4ab143 --- /dev/null +++ b/Clover/app/src/main/java/org/floens/chan/core/storage/LollipopStorageImpl.java @@ -0,0 +1,106 @@ +package org.floens.chan.core.storage; + +import android.content.ContentResolver; +import android.content.Context; +import android.content.Intent; +import android.database.Cursor; +import android.net.Uri; +import android.os.Build; +import android.provider.DocumentsContract; +import android.support.annotation.RequiresApi; + +import org.floens.chan.core.settings.ChanSettings; +import org.floens.chan.utils.IOUtils; +import org.floens.chan.utils.Logger; + +import java.io.FileNotFoundException; + +@RequiresApi(Build.VERSION_CODES.LOLLIPOP) +public class LollipopStorageImpl extends BaseStorageImpl { + private static final String TAG = "LollipopStorageImpl"; + + public LollipopStorageImpl(Context applicationContext) { + super(applicationContext); + } + + @Override + public boolean supportsExternalStorage() { + return true; + } + + @Override + public Intent getOpenTreeIntent() { + return new Intent(Intent.ACTION_OPEN_DOCUMENT_TREE); + } + + @Override + public void handleOpenTreeIntent(Uri uri) { + String documentId = DocumentsContract.getTreeDocumentId(uri); + Uri treeDocumentUri = DocumentsContract.buildDocumentUriUsingTree(uri, documentId); + + ChanSettings.saveLocationTreeUri.set(treeDocumentUri.toString()); + } + + @Override + public StorageFile obtainStorageFileForName(String name) { + String uriString = ChanSettings.saveLocationTreeUri.get(); + if (uriString.isEmpty()) { + return null; + } + + Uri treeUri = Uri.parse(uriString); + + ContentResolver contentResolver = applicationContext.getContentResolver(); + + String documentId = DocumentsContract.getTreeDocumentId(treeUri); + Uri treeDocumentUri = DocumentsContract.buildDocumentUriUsingTree(treeUri, documentId); + + Uri docUri; + try { + docUri = DocumentsContract.createDocument(contentResolver, treeDocumentUri, + "text", name); + } catch (FileNotFoundException e) { + Logger.e(TAG, "obtainStorageFileForName createDocument", e); + return null; + } + + return StorageFile.fromUri(contentResolver, docUri); + } + + @Override + public String currentStorageName() { + String uriString = ChanSettings.saveLocationTreeUri.get(); + if (uriString.isEmpty()) { + return null; + } + + Uri treeUri = Uri.parse(uriString); + return queryTreeName(treeUri); + } + + private String queryTreeName(Uri uri) { + ContentResolver contentResolver = applicationContext.getContentResolver(); + + Cursor c = null; + String name = null; + try { + c = contentResolver.query(uri, new String[]{ + DocumentsContract.Document.COLUMN_DISPLAY_NAME, + DocumentsContract.Document.COLUMN_MIME_TYPE + }, null, null, null); + + if (c != null && c.moveToNext()) { + name = c.getString(0); + // mime = c.getString(1); + } + + return name; + } catch (Exception e) { + Logger.e(TAG, "queryTreeName", e); + } finally { + IOUtils.closeQuietly(c); + } + + return null; + } +} diff --git a/Clover/app/src/main/java/org/floens/chan/core/storage/Storage.java b/Clover/app/src/main/java/org/floens/chan/core/storage/Storage.java index 846f7569..c7fe9c66 100644 --- a/Clover/app/src/main/java/org/floens/chan/core/storage/Storage.java +++ b/Clover/app/src/main/java/org/floens/chan/core/storage/Storage.java @@ -1,66 +1,58 @@ package org.floens.chan.core.storage; -import android.annotation.TargetApi; import android.content.Context; import android.content.Intent; +import android.net.Uri; import android.os.Build; -import android.os.storage.StorageManager; -import android.os.storage.StorageVolume; - -import java.util.Objects; +import javax.inject.Inject; +import javax.inject.Singleton; + +/** + * Abstraction of the storage APIs available in Android. + *
+ * This is used primarily for saving images, especially on removable storage. + *
+ * First, a good read: + * https://commonsware.com/blog/2017/11/13/storage-situation-internal-storage.html + * https://commonsware.com/blog/2017/11/14/storage-situation-external-storage.html + * https://commonsware.com/blog/2017/11/15/storage-situation-removable-storage.html + *
+ * The Android Storage Access Framework can be used from Android 5.0 and higher. Since Android 5.0 + * it has support for granting permissions for a directory, which we want to save our files to. + *
+ * Otherwise a fallback is provided for only saving on the primary volume with the older APIs.
+ */
+@Singleton
public class Storage {
- private static final Storage instance;
+ private StorageImpl impl;
- static {
- if (Build.VERSION.SDK_INT < Build.VERSION_CODES.N) {
- instance = new Storage(new BaseStorageImpl());
+ @Inject
+ public Storage(Context applicationContext) {
+ if (Build.VERSION.SDK_INT < Build.VERSION_CODES.LOLLIPOP) {
+ impl = new BaseStorageImpl(applicationContext);
} else {
- instance = new Storage(new NougatStorageImpl());
+ impl = new LollipopStorageImpl(applicationContext);
}
-
- }
-
- public static Storage getInstance() {
- return instance;
}
- private StorageImpl impl;
-
- public Storage(StorageImpl impl) {
- this.impl = impl;
+ public boolean supportsExternalStorage() {
+ return impl.supportsExternalStorage();
}
- public Intent requestExternalPermission(Context applicationContext) {
- return impl.requestExternalPermission(applicationContext);
+ public Intent getOpenTreeIntent() {
+ return impl.getOpenTreeIntent();
}
- public interface StorageImpl {
- Intent requestExternalPermission(Context applicationContext);
+ public void handleOpenTreeIntent(Uri uri) {
+ impl.handleOpenTreeIntent(uri);
}
- public static class BaseStorageImpl implements StorageImpl {
- @Override
- public Intent requestExternalPermission(Context applicationContext) {
- throw new UnsupportedOperationException();
- }
+ public StorageFile obtainStorageFileForName(String name) {
+ return impl.obtainStorageFileForName(name);
}
- @TargetApi(Build.VERSION_CODES.N)
- public static class NougatStorageImpl extends BaseStorageImpl {
- @Override
- public Intent requestExternalPermission(Context applicationContext) {
- StorageManager sm = (StorageManager)
- applicationContext.getSystemService(Context.STORAGE_SERVICE);
- Objects.requireNonNull(sm);
- for (StorageVolume storageVolume : sm.getStorageVolumes()) {
- if (!storageVolume.isPrimary()) {
- Intent accessIntent = storageVolume.createAccessIntent(null);
- return accessIntent;
- }
- }
-
- return null;
- }
+ public String currentStorageName() {
+ return impl.currentStorageName();
}
}
diff --git a/Clover/app/src/main/java/org/floens/chan/core/storage/StorageFile.java b/Clover/app/src/main/java/org/floens/chan/core/storage/StorageFile.java
new file mode 100644
index 00000000..da06db48
--- /dev/null
+++ b/Clover/app/src/main/java/org/floens/chan/core/storage/StorageFile.java
@@ -0,0 +1,93 @@
+package org.floens.chan.core.storage;
+
+import android.content.ContentResolver;
+import android.net.Uri;
+
+import org.floens.chan.utils.IOUtils;
+
+import java.io.BufferedInputStream;
+import java.io.BufferedOutputStream;
+import java.io.File;
+import java.io.FileInputStream;
+import java.io.FileOutputStream;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.OutputStream;
+
+public class StorageFile {
+ private final ContentResolver contentResolver;
+ private final Uri uriOpenableByContentResolvers;
+ private final File file;
+
+ public static StorageFile fromFile(File file) {
+ return new StorageFile(file);
+ }
+
+ public static StorageFile fromUri(ContentResolver contentResolver, Uri uriOpenableByContentResolvers) {
+ return new StorageFile(contentResolver, uriOpenableByContentResolvers);
+ }
+
+ private StorageFile(ContentResolver contentResolver, Uri uriOpenableByContentResolvers) {
+ this.contentResolver = contentResolver;
+ this.uriOpenableByContentResolvers = uriOpenableByContentResolvers;
+ this.file = null;
+ }
+
+ private StorageFile(File file) {
+ this.contentResolver = null;
+ this.uriOpenableByContentResolvers = null;
+ this.file = file;
+ }
+
+ public InputStream inputStream() throws IOException {
+ if (file != null) {
+ return new FileInputStream(file);
+ } else {
+ return contentResolver.openInputStream(uriOpenableByContentResolvers);
+ }
+ }
+
+ public OutputStream outputStream() throws IOException {
+ if (file != null) {
+ File parent = file.getParentFile();
+ if (!parent.mkdirs() && !parent.isDirectory()) {
+ throw new IOException("Could not create parent directory");
+ }
+
+ if (file.isDirectory()) {
+ throw new IOException("Destination not a file");
+ }
+
+ return new FileOutputStream(file);
+ } else {
+ return contentResolver.openOutputStream(uriOpenableByContentResolvers);
+ }
+ }
+
+ public String name() {
+ return "dummy name";
+ }
+
+ public boolean exists() {
+ // TODO
+ return false;
+ }
+
+ public boolean delete() {
+ // TODO
+ return true;
+ }
+
+ public void copyFrom(File source) throws IOException {
+ InputStream is = null;
+ OutputStream os = null;
+ try {
+ is = new BufferedInputStream(new FileInputStream(source));
+ os = new BufferedOutputStream(outputStream());
+ IOUtils.copy(is, os);
+ } finally {
+ IOUtils.closeQuietly(is);
+ IOUtils.closeQuietly(os);
+ }
+ }
+}
diff --git a/Clover/app/src/main/java/org/floens/chan/core/storage/StorageImpl.java b/Clover/app/src/main/java/org/floens/chan/core/storage/StorageImpl.java
new file mode 100644
index 00000000..8bf3e711
--- /dev/null
+++ b/Clover/app/src/main/java/org/floens/chan/core/storage/StorageImpl.java
@@ -0,0 +1,16 @@
+package org.floens.chan.core.storage;
+
+import android.content.Intent;
+import android.net.Uri;
+
+public interface StorageImpl {
+ boolean supportsExternalStorage();
+
+ Intent getOpenTreeIntent();
+
+ void handleOpenTreeIntent(Uri uri);
+
+ StorageFile obtainStorageFileForName(String name);
+
+ String currentStorageName();
+}
diff --git a/Clover/app/src/main/java/org/floens/chan/ui/activity/StartActivity.java b/Clover/app/src/main/java/org/floens/chan/ui/activity/StartActivity.java
index 03dd2ea5..3f7fc470 100644
--- a/Clover/app/src/main/java/org/floens/chan/ui/activity/StartActivity.java
+++ b/Clover/app/src/main/java/org/floens/chan/ui/activity/StartActivity.java
@@ -51,6 +51,7 @@ import org.floens.chan.ui.controller.BrowseController;
import org.floens.chan.ui.controller.DoubleNavigationController;
import org.floens.chan.ui.controller.DrawerController;
import org.floens.chan.ui.controller.SplitNavigationController;
+import org.floens.chan.ui.controller.StorageSetupController;
import org.floens.chan.ui.controller.StyledToolbarNavigationController;
import org.floens.chan.ui.controller.ThreadSlideController;
import org.floens.chan.ui.controller.ViewThreadController;
@@ -164,6 +165,8 @@ public class StartActivity extends AppCompatActivity implements NfcAdapter.Creat
} else {
browseController.loadWithDefaultBoard();
}
+
+ mainNavigationController.pushController(new StorageSetupController(this), false);
}
private boolean restoreFromUrl() {
@@ -487,6 +490,8 @@ public class StartActivity extends AppCompatActivity implements NfcAdapter.Creat
super.onActivityResult(requestCode, resultCode, data);
imagePickDelegate.onActivityResult(requestCode, resultCode, data);
+
+ drawerController.onActivityResult(requestCode, resultCode, data);
}
private Controller stackTop() {
diff --git a/Clover/app/src/main/java/org/floens/chan/ui/controller/AlbumDownloadController.java b/Clover/app/src/main/java/org/floens/chan/ui/controller/AlbumDownloadController.java
index 1450f8fa..d007e8ab 100644
--- a/Clover/app/src/main/java/org/floens/chan/ui/controller/AlbumDownloadController.java
+++ b/Clover/app/src/main/java/org/floens/chan/ui/controller/AlbumDownloadController.java
@@ -46,6 +46,9 @@ import org.floens.chan.utils.RecyclerUtils;
import java.util.ArrayList;
import java.util.List;
+import javax.inject.Inject;
+
+import static org.floens.chan.Chan.inject;
import static org.floens.chan.ui.theme.ThemeHelper.theme;
import static org.floens.chan.utils.AndroidUtils.dp;
@@ -59,6 +62,8 @@ public class AlbumDownloadController extends Controller implements View.OnClickL
private boolean allChecked = true;
private AlbumAdapter adapter;
+
+ @Inject
private ImageSaver imageSaver;
public AlbumDownloadController(Context context) {
@@ -69,7 +74,7 @@ public class AlbumDownloadController extends Controller implements View.OnClickL
public void onCreate() {
super.onCreate();
- imageSaver = ImageSaver.getInstance();
+ inject(this);
view = inflateRes(R.layout.controller_album_download);
diff --git a/Clover/app/src/main/java/org/floens/chan/ui/controller/ImageViewerController.java b/Clover/app/src/main/java/org/floens/chan/ui/controller/ImageViewerController.java
index 7a05c547..d2324495 100644
--- a/Clover/app/src/main/java/org/floens/chan/ui/controller/ImageViewerController.java
+++ b/Clover/app/src/main/java/org/floens/chan/ui/controller/ImageViewerController.java
@@ -92,6 +92,9 @@ public class ImageViewerController extends Controller implements ImageViewerPres
@Inject
ImageLoader imageLoader;
+ @Inject
+ ImageSaver imageSaver;
+
private int statusBarColorPrevious;
private AnimatorSet startAnimation;
private AnimatorSet endAnimation;
@@ -226,7 +229,7 @@ public class ImageViewerController extends Controller implements ImageViewerPres
File.separator +
presenter.getLoadable().boardCode);
}
- ImageSaver.getInstance().startDownloadTask(context, task);
+ imageSaver.startDownloadTask(context, task);
}
}
diff --git a/Clover/app/src/main/java/org/floens/chan/ui/controller/SaveLocationController.java b/Clover/app/src/main/java/org/floens/chan/ui/controller/SaveLocationController.java
index bd1acd51..70e48c4e 100644
--- a/Clover/app/src/main/java/org/floens/chan/ui/controller/SaveLocationController.java
+++ b/Clover/app/src/main/java/org/floens/chan/ui/controller/SaveLocationController.java
@@ -18,9 +18,7 @@
package org.floens.chan.ui.controller;
import android.Manifest;
-import android.app.Activity;
import android.content.Context;
-import android.content.Intent;
import android.support.design.widget.FloatingActionButton;
import android.view.View;
@@ -28,7 +26,6 @@ import org.floens.chan.R;
import org.floens.chan.controller.Controller;
import org.floens.chan.core.saver.FileWatcher;
import org.floens.chan.core.settings.ChanSettings;
-import org.floens.chan.core.storage.Storage;
import org.floens.chan.ui.activity.StartActivity;
import org.floens.chan.ui.adapter.FilesAdapter;
import org.floens.chan.ui.helper.RuntimePermissionsHelper;
@@ -77,18 +74,6 @@ public class SaveLocationController extends Controller implements FileWatcher.Fi
} else {
requestPermission();
}
-
- test();
- }
-
- private void test() {
- Storage storage = Storage.getInstance();
- Intent intent = storage.requestExternalPermission(context.getApplicationContext());
- if (intent != null) {
- if (intent.resolveActivity(context.getPackageManager()) != null) {
- ((Activity) context).startActivityForResult(intent, 10024);
- }
- }
}
@Override
diff --git a/Clover/app/src/main/java/org/floens/chan/ui/controller/StorageSetupController.java b/Clover/app/src/main/java/org/floens/chan/ui/controller/StorageSetupController.java
new file mode 100644
index 00000000..e867ad7c
--- /dev/null
+++ b/Clover/app/src/main/java/org/floens/chan/ui/controller/StorageSetupController.java
@@ -0,0 +1,79 @@
+package org.floens.chan.ui.controller;
+
+import android.app.Activity;
+import android.content.Context;
+import android.content.Intent;
+import android.view.View;
+import android.widget.Button;
+import android.widget.TextView;
+
+import org.floens.chan.R;
+import org.floens.chan.controller.Controller;
+import org.floens.chan.core.storage.Storage;
+
+import javax.inject.Inject;
+
+import static org.floens.chan.Chan.inject;
+
+public class StorageSetupController extends Controller implements View.OnClickListener {
+ private static final int OPEN_TREE_INTENT_RESULT_ID = 101;
+
+ private static final String TAG = "StorageSetupController";
+
+ @Inject
+ private Storage storage;
+
+ private TextView text;
+ private Button button;
+
+ public StorageSetupController(Context context) {
+ super(context);
+ }
+
+ @Override
+ public void onCreate() {
+ super.onCreate();
+
+ inject(this);
+
+ // Navigation
+ navigation.setTitle(R.string.storage_setup_screen);
+
+ // View inflation
+ view = inflateRes(R.layout.controller_storage_setup);
+
+ // View binding
+ text = view.findViewById(R.id.text);
+ button = view.findViewById(R.id.button);
+
+ // View setup
+ button.setOnClickListener(this);
+
+ updateName();
+ }
+
+ @Override
+ public void onClick(View v) {
+ if (v == button) {
+ requestTree();
+ }
+ }
+
+ private void requestTree() {
+ Intent i = storage.getOpenTreeIntent();
+ ((Activity) context).startActivityForResult(i, OPEN_TREE_INTENT_RESULT_ID);
+ updateName();
+ }
+
+ private void updateName() {
+ text.setText(storage.currentStorageName());
+ }
+
+ @Override
+ public void onActivityResult(int requestCode, int resultCode, Intent data) {
+ if (requestCode == OPEN_TREE_INTENT_RESULT_ID && resultCode == Activity.RESULT_OK) {
+ storage.handleOpenTreeIntent(data.getData());
+ updateName();
+ }
+ }
+}
diff --git a/Clover/app/src/main/java/org/floens/chan/utils/ImageDecoder.java b/Clover/app/src/main/java/org/floens/chan/utils/ImageDecoder.java
index be6bf4f6..0c7138ad 100644
--- a/Clover/app/src/main/java/org/floens/chan/utils/ImageDecoder.java
+++ b/Clover/app/src/main/java/org/floens/chan/utils/ImageDecoder.java
@@ -25,6 +25,7 @@ import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.IOException;
+import java.io.InputStream;
/**
* Simple ImageDecoder. Taken from Volley ImageRequest.
@@ -55,26 +56,27 @@ public class ImageDecoder {
if (!file.exists())
return null;
- FileInputStream fis;
-
try {
- fis = new FileInputStream(file);
+ InputStream fis = new FileInputStream(file);
+ return decodeFile(fis, maxWidth, maxHeight);
} catch (FileNotFoundException e) {
e.printStackTrace();
return null;
}
+ }
+ public static Bitmap decodeFile(InputStream is, int maxWidth, int maxHeight) {
ByteArrayOutputStream baos = new ByteArrayOutputStream();
Bitmap bitmap = null;
try {
- IOUtils.copy(fis, baos);
+ IOUtils.copy(is, baos);
bitmap = decode(baos.toByteArray(), maxWidth, maxHeight);
} catch (IOException | OutOfMemoryError e) {
e.printStackTrace();
} finally {
- IOUtils.closeQuietly(fis);
+ IOUtils.closeQuietly(is);
IOUtils.closeQuietly(baos);
}
diff --git a/Clover/app/src/main/res/layout/controller_storage_setup.xml b/Clover/app/src/main/res/layout/controller_storage_setup.xml
new file mode 100644
index 00000000..257d22a4
--- /dev/null
+++ b/Clover/app/src/main/res/layout/controller_storage_setup.xml
@@ -0,0 +1,25 @@
+
+