1
0
mirror of https://github.com/KokaKiwi/BarInsta synced 2024-11-14 02:37:30 +00:00

Migrate File usage to DocumentFile

This commit is contained in:
Ammar Githam 2021-04-03 19:53:01 +09:00
parent 91c70b778a
commit 7c0acdbd6e
16 changed files with 569 additions and 313 deletions

View File

@ -14,6 +14,7 @@ import java.text.SimpleDateFormat;
import java.util.UUID; import java.util.UUID;
import awais.instagrabber.utils.Constants; import awais.instagrabber.utils.Constants;
import awais.instagrabber.utils.DownloadUtils;
import awais.instagrabber.utils.LocaleUtils; import awais.instagrabber.utils.LocaleUtils;
import awais.instagrabber.utils.SettingsHelper; import awais.instagrabber.utils.SettingsHelper;
import awais.instagrabber.utils.TextUtils; import awais.instagrabber.utils.TextUtils;
@ -86,5 +87,6 @@ public final class InstaGrabberApplication extends Application {
if (TextUtils.isEmpty(settingsHelper.getString(Constants.DEVICE_UUID))) { if (TextUtils.isEmpty(settingsHelper.getString(Constants.DEVICE_UUID))) {
settingsHelper.putString(Constants.DEVICE_UUID, UUID.randomUUID().toString()); settingsHelper.putString(Constants.DEVICE_UUID, UUID.randomUUID().toString());
} }
DownloadUtils.init(this);
} }
} }

View File

@ -5,12 +5,9 @@ import android.content.Context;
import android.content.Intent; import android.content.Intent;
import android.content.res.Configuration; import android.content.res.Configuration;
import android.hardware.display.DisplayManager; import android.hardware.display.DisplayManager;
import android.media.MediaScannerConnection;
import android.net.Uri;
import android.os.Bundle; import android.os.Bundle;
import android.util.Log; import android.util.Log;
import android.view.LayoutInflater; import android.view.LayoutInflater;
import android.webkit.MimeTypeMap;
import androidx.annotation.NonNull; import androidx.annotation.NonNull;
import androidx.annotation.Nullable; import androidx.annotation.Nullable;
@ -21,11 +18,13 @@ import androidx.camera.core.ImageCaptureException;
import androidx.camera.core.Preview; import androidx.camera.core.Preview;
import androidx.camera.lifecycle.ProcessCameraProvider; import androidx.camera.lifecycle.ProcessCameraProvider;
import androidx.core.content.ContextCompat; import androidx.core.content.ContextCompat;
import androidx.documentfile.provider.DocumentFile;
import com.google.common.io.Files;
import com.google.common.util.concurrent.ListenableFuture; import com.google.common.util.concurrent.ListenableFuture;
import java.io.File; import java.io.FileNotFoundException;
import java.io.IOException;
import java.io.OutputStream;
import java.text.SimpleDateFormat; import java.text.SimpleDateFormat;
import java.util.Locale; import java.util.Locale;
import java.util.concurrent.ExecutionException; import java.util.concurrent.ExecutionException;
@ -45,7 +44,7 @@ public class CameraActivity extends BaseLanguageActivity {
private ActivityCameraBinding binding; private ActivityCameraBinding binding;
private ImageCapture imageCapture; private ImageCapture imageCapture;
private File outputDirectory; private DocumentFile outputDirectory;
private ExecutorService cameraExecutor; private ExecutorService cameraExecutor;
private int displayId = -1; private int displayId = -1;
@ -113,7 +112,13 @@ public class CameraActivity extends BaseLanguageActivity {
} }
private void updateUi() { private void updateUi() {
binding.cameraCaptureButton.setOnClickListener(v -> takePhoto()); binding.cameraCaptureButton.setOnClickListener(v -> {
try {
takePhoto();
} catch (FileNotFoundException e) {
Log.e(TAG, "updateUi: ", e);
}
});
// Disable the button until the camera is set up // Disable the button until the camera is set up
binding.switchCamera.setEnabled(false); binding.switchCamera.setEnabled(false);
// Listener for button used to switch cameras. Only called if the button is enabled // Listener for button used to switch cameras. Only called if the button is enabled
@ -200,37 +205,44 @@ public class CameraActivity extends BaseLanguageActivity {
preview.setSurfaceProvider(binding.viewFinder.getSurfaceProvider()); preview.setSurfaceProvider(binding.viewFinder.getSurfaceProvider());
} }
private void takePhoto() { private void takePhoto() throws FileNotFoundException {
if (imageCapture == null) return; if (imageCapture == null) return;
final File photoFile = new File(outputDirectory, SIMPLE_DATE_FORMAT.format(System.currentTimeMillis()) + ".jpg"); final String extension = "jpg";
final ImageCapture.OutputFileOptions outputFileOptions = new ImageCapture.OutputFileOptions.Builder(photoFile).build(); final String fileName = SIMPLE_DATE_FORMAT.format(System.currentTimeMillis()) + "." + extension;
// final File photoFile = new File(outputDirectory, fileName);
final String mimeType = Utils.mimeTypeMap.getMimeTypeFromExtension(extension);
final DocumentFile photoFile = outputDirectory.createFile(mimeType, fileName);
final OutputStream outputStream = getContentResolver().openOutputStream(photoFile.getUri());
final ImageCapture.OutputFileOptions outputFileOptions = new ImageCapture.OutputFileOptions.Builder(outputStream).build();
imageCapture.takePicture( imageCapture.takePicture(
outputFileOptions, outputFileOptions,
cameraExecutor, cameraExecutor,
new ImageCapture.OnImageSavedCallback() { new ImageCapture.OnImageSavedCallback() {
@Override @Override
public void onImageSaved(@NonNull final ImageCapture.OutputFileResults outputFileResults) { public void onImageSaved(@NonNull final ImageCapture.OutputFileResults outputFileResults) {
final Uri uri = Uri.fromFile(photoFile); if (outputStream != null) {
//noinspection UnstableApiUsage try { outputStream.close(); } catch (IOException ignored) {}
final String mimeType = MimeTypeMap.getSingleton() }
.getMimeTypeFromExtension(Files.getFileExtension(photoFile.getName())); // final Uri uri = Uri.fromFile(photoFile);
MediaScannerConnection.scanFile( // final String mimeType = MimeTypeMap.getSingleton()
CameraActivity.this, // .getMimeTypeFromExtension(Files.getFileExtension(photoFile.getName()));
new String[]{photoFile.getAbsolutePath()}, sendBroadcast(new Intent(Intent.ACTION_MEDIA_SCANNER_SCAN_FILE, photoFile.getUri()));
new String[]{mimeType}, Utils.scanDocumentFile(CameraActivity.this, photoFile, (path, uri1) -> {
(path, uri1) -> { Log.d(TAG, "onImageSaved: scan complete");
Log.d(TAG, "onImageSaved: scan complete"); final Intent intent = new Intent();
final Intent intent = new Intent(); intent.setData(uri1);
intent.setData(uri1); setResult(Activity.RESULT_OK, intent);
setResult(Activity.RESULT_OK, intent); finish();
finish(); });
}); Log.d(TAG, "onImageSaved: " + photoFile.getUri());
Log.d(TAG, "onImageSaved: " + uri);
} }
@Override @Override
public void onError(@NonNull final ImageCaptureException exception) { public void onError(@NonNull final ImageCaptureException exception) {
Log.e(TAG, "onError: ", exception); Log.e(TAG, "onError: ", exception);
if (outputStream != null) {
try { outputStream.close(); } catch (IOException ignored) {}
}
} }
} }
); );

View File

@ -102,7 +102,7 @@ public class FeedGridItemViewHolder extends RecyclerView.ViewHolder {
binding.typeIcon.setVisibility(View.VISIBLE); binding.typeIcon.setVisibility(View.VISIBLE);
binding.typeIcon.setImageResource(typeIconRes); binding.typeIcon.setImageResource(typeIconRes);
} }
final DownloadedCheckerAsyncTask task = new DownloadedCheckerAsyncTask(result -> { final DownloadedCheckerAsyncTask task = new DownloadedCheckerAsyncTask(itemView.getContext(), result -> {
final List<Boolean> checkList = result.get(media.getPk()); final List<Boolean> checkList = result.get(media.getPk());
if (checkList == null || checkList.isEmpty()) { if (checkList == null || checkList.isEmpty()) {
return; return;

View File

@ -1,7 +1,9 @@
package awais.instagrabber.asyncs; package awais.instagrabber.asyncs;
import android.content.Context;
import android.os.AsyncTask; import android.os.AsyncTask;
import java.lang.ref.WeakReference;
import java.util.HashMap; import java.util.HashMap;
import java.util.List; import java.util.List;
import java.util.Map; import java.util.Map;
@ -12,9 +14,12 @@ import awais.instagrabber.utils.DownloadUtils;
public final class DownloadedCheckerAsyncTask extends AsyncTask<Media, Void, Map<String, List<Boolean>>> { public final class DownloadedCheckerAsyncTask extends AsyncTask<Media, Void, Map<String, List<Boolean>>> {
private static final String TAG = "DownloadedCheckerAsyncTask"; private static final String TAG = "DownloadedCheckerAsyncTask";
private final WeakReference<Context> context;
private final OnCheckResultListener listener; private final OnCheckResultListener listener;
public DownloadedCheckerAsyncTask(final OnCheckResultListener listener) { public DownloadedCheckerAsyncTask(final Context context,
final OnCheckResultListener listener) {
this.context = new WeakReference<>(context);
this.listener = listener; this.listener = listener;
} }
@ -25,7 +30,9 @@ public final class DownloadedCheckerAsyncTask extends AsyncTask<Media, Void, Map
} }
final Map<String, List<Boolean>> map = new HashMap<>(); final Map<String, List<Boolean>> map = new HashMap<>();
for (final Media media : feedModels) { for (final Media media : feedModels) {
map.put(media.getPk(), DownloadUtils.checkDownloaded(media)); final Context context = this.context.get();
if (context == null) return map;
map.put(media.getPk(), DownloadUtils.checkDownloaded(context, media));
} }
return map; return map;
} }

View File

@ -3,7 +3,6 @@ package awais.instagrabber.dialogs;
import android.app.Dialog; import android.app.Dialog;
import android.content.Context; import android.content.Context;
import android.content.Intent; import android.content.Intent;
import android.net.Uri;
import android.os.Build; import android.os.Build;
import android.os.Bundle; import android.os.Bundle;
import android.provider.DocumentsContract; import android.provider.DocumentsContract;
@ -214,7 +213,7 @@ public class CreateBackupDialogFragment extends DialogFragment {
// Optionally, specify a URI for the directory that should be opened in // Optionally, specify a URI for the directory that should be opened in
// the system file picker when your app creates the document. // the system file picker when your app creates the document.
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) { if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
intent.putExtra(DocumentsContract.EXTRA_INITIAL_URI, Uri.fromFile(DownloadUtils.getDownloadDir())); intent.putExtra(DocumentsContract.EXTRA_INITIAL_URI, DownloadUtils.getDownloadDir().getUri());
} }
startActivityForResult(intent, CREATE_FILE_REQUEST_CODE); startActivityForResult(intent, CREATE_FILE_REQUEST_CODE);

View File

@ -7,7 +7,6 @@ import android.graphics.Color;
import android.graphics.drawable.Animatable; import android.graphics.drawable.Animatable;
import android.graphics.drawable.ColorDrawable; import android.graphics.drawable.ColorDrawable;
import android.os.Bundle; import android.os.Bundle;
import android.os.Environment;
import android.view.LayoutInflater; import android.view.LayoutInflater;
import android.view.View; import android.view.View;
import android.view.ViewGroup; import android.view.ViewGroup;
@ -17,6 +16,7 @@ import android.widget.Toast;
import androidx.annotation.NonNull; import androidx.annotation.NonNull;
import androidx.annotation.Nullable; import androidx.annotation.Nullable;
import androidx.core.content.ContextCompat; import androidx.core.content.ContextCompat;
import androidx.documentfile.provider.DocumentFile;
import androidx.fragment.app.DialogFragment; import androidx.fragment.app.DialogFragment;
import com.facebook.drawee.backends.pipeline.Fresco; import com.facebook.drawee.backends.pipeline.Fresco;
@ -24,15 +24,13 @@ import com.facebook.drawee.controller.BaseControllerListener;
import com.facebook.drawee.interfaces.DraweeController; import com.facebook.drawee.interfaces.DraweeController;
import com.facebook.imagepipeline.image.ImageInfo; import com.facebook.imagepipeline.image.ImageInfo;
import java.io.File;
import awais.instagrabber.R;
import awais.instagrabber.databinding.DialogProfilepicBinding; import awais.instagrabber.databinding.DialogProfilepicBinding;
import awais.instagrabber.repositories.responses.User; import awais.instagrabber.repositories.responses.User;
import awais.instagrabber.utils.Constants; import awais.instagrabber.utils.Constants;
import awais.instagrabber.utils.CookieUtils; import awais.instagrabber.utils.CookieUtils;
import awais.instagrabber.utils.DownloadUtils; import awais.instagrabber.utils.DownloadUtils;
import awais.instagrabber.utils.TextUtils; import awais.instagrabber.utils.TextUtils;
import awais.instagrabber.utils.Utils;
import awais.instagrabber.webservices.ServiceCallback; import awais.instagrabber.webservices.ServiceCallback;
import awais.instagrabber.webservices.UserService; import awais.instagrabber.webservices.UserService;
@ -182,14 +180,18 @@ public class ProfilePicDialogFragment extends DialogFragment {
private void downloadProfilePicture() { private void downloadProfilePicture() {
if (url == null) return; if (url == null) return;
final File dir = new File(Environment.getExternalStorageDirectory(), "Download"); // final File dir = new File(Environment.getExternalStorageDirectory(), "Download");
final Context context = getContext(); final Context context = getContext();
if (context == null) return; if (context == null) return;
if (dir.exists() || dir.mkdirs()) { // if (dir.exists() || dir.mkdirs()) {
final File saveFile = new File(dir, name + '_' + System.currentTimeMillis() + ".jpg"); //
DownloadUtils.download(context, url, saveFile.getAbsolutePath()); // }
return; final String fileName = name + '_' + System.currentTimeMillis() + ".jpg";
} // final File saveFile = new File(dir, fileName);
Toast.makeText(context, R.string.downloader_error_creating_folder, Toast.LENGTH_SHORT).show(); final DocumentFile downloadDir = DownloadUtils.getDownloadDir();
final DocumentFile saveFile = downloadDir.createFile(Utils.mimeTypeMap.getMimeTypeFromExtension("jpg"), fileName);
DownloadUtils.download(context, url, saveFile);
// return;
// Toast.makeText(context, R.string.downloader_error_creating_folder, Toast.LENGTH_SHORT).show();
} }
} }

View File

@ -1,22 +1,43 @@
package awais.instagrabber.fragments.settings; package awais.instagrabber.fragments.settings;
import android.content.Context; import android.content.Context;
import android.content.Intent;
import android.net.Uri;
import android.util.Log;
import android.view.View;
import androidx.annotation.NonNull; import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import androidx.appcompat.widget.AppCompatButton;
import androidx.appcompat.widget.AppCompatTextView;
import androidx.documentfile.provider.DocumentFile;
import androidx.preference.Preference; import androidx.preference.Preference;
import androidx.preference.PreferenceScreen; import androidx.preference.PreferenceScreen;
import androidx.preference.PreferenceViewHolder;
import androidx.preference.SwitchPreferenceCompat; import androidx.preference.SwitchPreferenceCompat;
import com.google.android.material.switchmaterial.SwitchMaterial;
import awais.instagrabber.R; import awais.instagrabber.R;
import awais.instagrabber.utils.Constants; import awais.instagrabber.utils.Constants;
import awais.instagrabber.utils.TextUtils;
import static android.app.Activity.RESULT_OK;
import static awais.instagrabber.utils.Constants.FOLDER_PATH;
import static awais.instagrabber.utils.Constants.FOLDER_SAVE_TO;
import static awais.instagrabber.utils.Utils.settingsHelper;
public class DownloadsPreferencesFragment extends BasePreferencesFragment { public class DownloadsPreferencesFragment extends BasePreferencesFragment {
private static final String TAG = DownloadsPreferencesFragment.class.getSimpleName();
private static final int SELECT_DIR_REQUEST_CODE = 1;
private SaveToCustomFolderPreference.ResultCallback resultCallback;
@Override @Override
void setupPreferenceScreen(final PreferenceScreen screen) { void setupPreferenceScreen(final PreferenceScreen screen) {
final Context context = getContext(); final Context context = getContext();
if (context == null) return; if (context == null) return;
screen.addPreference(getDownloadUserFolderPreference(context)); screen.addPreference(getDownloadUserFolderPreference(context));
// screen.addPreference(getSaveToCustomFolderPreference(context)); screen.addPreference(getSaveToCustomFolderPreference(context));
} }
private Preference getDownloadUserFolderPreference(@NonNull final Context context) { private Preference getDownloadUserFolderPreference(@NonNull final Context context) {
@ -27,63 +48,89 @@ public class DownloadsPreferencesFragment extends BasePreferencesFragment {
return preference; return preference;
} }
// private Preference getSaveToCustomFolderPreference(@NonNull final Context context) { private Preference getSaveToCustomFolderPreference(@NonNull final Context context) {
// return new SaveToCustomFolderPreference(context, (resultCallback) -> new DirectoryChooser() return new SaveToCustomFolderPreference(context, (resultCallback) -> {
// .setInitialDirectory(settingsHelper.getString(FOLDER_PATH)) // Choose a directory using the system's file picker.
// .setInteractionListener(file -> { final Intent intent = new Intent(Intent.ACTION_OPEN_DOCUMENT_TREE);
// settingsHelper.putString(FOLDER_PATH, file.getAbsolutePath()); startActivityForResult(intent, SELECT_DIR_REQUEST_CODE);
// resultCallback.onResult(file.getAbsolutePath()); this.resultCallback = resultCallback;
// })
// .show(getParentFragmentManager(), null));
// }
// public static class SaveToCustomFolderPreference extends Preference { // new DirectoryChooser()
// private AppCompatTextView customPathTextView; // .setInitialDirectory(settingsHelper.getString(FOLDER_PATH))
// private final OnSelectFolderButtonClickListener onSelectFolderButtonClickListener; // .setInteractionListener(file -> {
// private final String key; // settingsHelper.putString(FOLDER_PATH, file.getAbsolutePath());
// // resultCallback.onResult(file.getAbsolutePath());
// public SaveToCustomFolderPreference(final Context context, final OnSelectFolderButtonClickListener onSelectFolderButtonClickListener) { // })
// super(context); // .show(getParentFragmentManager(), null);
// this.onSelectFolderButtonClickListener = onSelectFolderButtonClickListener; });
// key = Constants.FOLDER_SAVE_TO; }
// setLayoutResource(R.layout.pref_custom_folder);
// setKey(key); @Override
// setTitle(R.string.save_to_folder); public void onActivityResult(final int requestCode, final int resultCode, @Nullable final Intent data) {
// setIconSpaceReserved(false); if (data == null || data.getData() == null) return;
// } if (resultCode != RESULT_OK || requestCode != SELECT_DIR_REQUEST_CODE) return;
// final Context context = getContext();
// @Override if (context == null) return;
// public void onBindViewHolder(final PreferenceViewHolder holder) { final Uri dirUri = data.getData();
// super.onBindViewHolder(holder); Log.d(TAG, "onActivityResult: " + dirUri);
// final SwitchMaterial cbSaveTo = (SwitchMaterial) holder.findViewById(R.id.cbSaveTo); final int takeFlags = data.getFlags() & (Intent.FLAG_GRANT_READ_URI_PERMISSION | Intent.FLAG_GRANT_WRITE_URI_PERMISSION);
// final View buttonContainer = holder.findViewById(R.id.button_container); context.getContentResolver().takePersistableUriPermission(dirUri, takeFlags);
// customPathTextView = (AppCompatTextView) holder.findViewById(R.id.custom_path); final DocumentFile root = DocumentFile.fromTreeUri(context, dirUri);
// cbSaveTo.setOnCheckedChangeListener((buttonView, isChecked) -> { settingsHelper.putString(FOLDER_PATH, data.getData().toString());
// settingsHelper.putBoolean(FOLDER_SAVE_TO, isChecked); if (resultCallback != null) {
// buttonContainer.setVisibility(isChecked ? View.VISIBLE : View.GONE); resultCallback.onResult(root.getName());
// final String customPath = settingsHelper.getString(FOLDER_PATH); resultCallback = null;
// customPathTextView.setText(customPath); }
// }); // Log.d(TAG, "onActivityResult: " + root);
// final boolean savedToEnabled = settingsHelper.getBoolean(key); }
// holder.itemView.setOnClickListener(v -> cbSaveTo.toggle());
// cbSaveTo.setChecked(savedToEnabled); public static class SaveToCustomFolderPreference extends Preference {
// buttonContainer.setVisibility(savedToEnabled ? View.VISIBLE : View.GONE); private AppCompatTextView customPathTextView;
// final AppCompatButton btnSaveTo = (AppCompatButton) holder.findViewById(R.id.btnSaveTo); private final OnSelectFolderButtonClickListener onSelectFolderButtonClickListener;
// btnSaveTo.setOnClickListener(v -> { private final String key;
// if (onSelectFolderButtonClickListener == null) return;
// onSelectFolderButtonClickListener.onClick(result -> { public SaveToCustomFolderPreference(final Context context, final OnSelectFolderButtonClickListener onSelectFolderButtonClickListener) {
// if (TextUtils.isEmpty(result)) return; super(context);
// customPathTextView.setText(result); this.onSelectFolderButtonClickListener = onSelectFolderButtonClickListener;
// }); key = FOLDER_SAVE_TO;
// }); setLayoutResource(R.layout.pref_custom_folder);
// } setKey(key);
// setTitle(R.string.save_to_folder);
// public interface ResultCallback { setIconSpaceReserved(false);
// void onResult(String result); }
// }
// @Override
// public interface OnSelectFolderButtonClickListener { public void onBindViewHolder(final PreferenceViewHolder holder) {
// void onClick(ResultCallback resultCallback); super.onBindViewHolder(holder);
// } final SwitchMaterial cbSaveTo = (SwitchMaterial) holder.findViewById(R.id.cbSaveTo);
// } final View buttonContainer = holder.findViewById(R.id.button_container);
customPathTextView = (AppCompatTextView) holder.findViewById(R.id.custom_path);
cbSaveTo.setOnCheckedChangeListener((buttonView, isChecked) -> {
settingsHelper.putBoolean(FOLDER_SAVE_TO, isChecked);
buttonContainer.setVisibility(isChecked ? View.VISIBLE : View.GONE);
final String customPath = settingsHelper.getString(FOLDER_PATH);
customPathTextView.setText(customPath);
});
final boolean savedToEnabled = settingsHelper.getBoolean(key);
holder.itemView.setOnClickListener(v -> cbSaveTo.toggle());
cbSaveTo.setChecked(savedToEnabled);
buttonContainer.setVisibility(savedToEnabled ? View.VISIBLE : View.GONE);
final AppCompatButton btnSaveTo = (AppCompatButton) holder.findViewById(R.id.btnSaveTo);
btnSaveTo.setOnClickListener(v -> {
if (onSelectFolderButtonClickListener == null) return;
onSelectFolderButtonClickListener.onClick(result -> {
if (TextUtils.isEmpty(result)) return;
customPathTextView.setText(result);
});
});
}
public interface ResultCallback {
void onResult(String result);
}
public interface OnSelectFolderButtonClickListener {
void onClick(ResultCallback resultCallback);
}
}
} }

View File

@ -4,13 +4,14 @@ import android.app.IntentService;
import android.app.PendingIntent; import android.app.PendingIntent;
import android.content.Context; import android.content.Context;
import android.content.Intent; import android.content.Intent;
import android.net.Uri;
import android.util.Log; import android.util.Log;
import androidx.annotation.NonNull; import androidx.annotation.NonNull;
import androidx.annotation.Nullable; import androidx.annotation.Nullable;
import androidx.core.app.NotificationManagerCompat; import androidx.core.app.NotificationManagerCompat;
import androidx.documentfile.provider.DocumentFile;
import java.io.File;
import java.util.Random; import java.util.Random;
import awais.instagrabber.utils.TextUtils; import awais.instagrabber.utils.TextUtils;
@ -39,7 +40,10 @@ public class DeleteImageIntentService extends IntentService {
if (intent != null && Intent.ACTION_DELETE.equals(intent.getAction()) && intent.hasExtra(EXTRA_IMAGE_PATH)) { if (intent != null && Intent.ACTION_DELETE.equals(intent.getAction()) && intent.hasExtra(EXTRA_IMAGE_PATH)) {
final String path = intent.getStringExtra(EXTRA_IMAGE_PATH); final String path = intent.getStringExtra(EXTRA_IMAGE_PATH);
if (TextUtils.isEmpty(path)) return; if (TextUtils.isEmpty(path)) return;
final File file = new File(path); // final File file = new File(path);
final Uri parse = Uri.parse(path);
if (parse == null) return;
final DocumentFile file = DocumentFile.fromSingleUri(getApplicationContext(), parse);
boolean deleted; boolean deleted;
if (file.exists()) { if (file.exists()) {
deleted = file.delete(); deleted = file.delete();
@ -58,11 +62,11 @@ public class DeleteImageIntentService extends IntentService {
@NonNull @NonNull
public static PendingIntent pendingIntent(@NonNull final Context context, public static PendingIntent pendingIntent(@NonNull final Context context,
@NonNull final String imagePath, @NonNull final DocumentFile imagePath,
final int notificationId) { final int notificationId) {
final Intent intent = new Intent(context, DeleteImageIntentService.class); final Intent intent = new Intent(context, DeleteImageIntentService.class);
intent.setAction(Intent.ACTION_DELETE); intent.setAction(Intent.ACTION_DELETE);
intent.putExtra(EXTRA_IMAGE_PATH, imagePath); intent.putExtra(EXTRA_IMAGE_PATH, imagePath.getUri().toString());
intent.putExtra(EXTRA_NOTIFICATION_ID, notificationId); intent.putExtra(EXTRA_NOTIFICATION_ID, notificationId);
return PendingIntent.getService(context, random.nextInt(), intent, PendingIntent.FLAG_UPDATE_CURRENT); return PendingIntent.getService(context, random.nextInt(), intent, PendingIntent.FLAG_UPDATE_CURRENT);
} }

View File

@ -11,14 +11,13 @@ import android.util.LruCache;
import androidx.annotation.NonNull; import androidx.annotation.NonNull;
import androidx.annotation.Nullable; import androidx.annotation.Nullable;
import androidx.core.util.Pair; import androidx.core.util.Pair;
import androidx.documentfile.provider.DocumentFile;
import com.google.common.util.concurrent.FutureCallback; import com.google.common.util.concurrent.FutureCallback;
import com.google.common.util.concurrent.Futures; import com.google.common.util.concurrent.Futures;
import com.google.common.util.concurrent.ListenableFuture; import com.google.common.util.concurrent.ListenableFuture;
import java.io.File;
import java.io.FileNotFoundException; import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException; import java.io.IOException;
import java.io.InputStream; import java.io.InputStream;
import java.io.OutputStream; import java.io.OutputStream;
@ -247,12 +246,14 @@ public final class BitmapUtils {
return null; return null;
} }
public static File convertToJpegAndSaveToFile(@NonNull final Bitmap bitmap, @Nullable final File file) throws IOException { public static DocumentFile convertToJpegAndSaveToFile(@NonNull final ContentResolver contentResolver,
File tempFile = file; @NonNull final Bitmap bitmap,
@Nullable final DocumentFile file) throws IOException {
DocumentFile tempFile = file;
if (file == null) { if (file == null) {
tempFile = DownloadUtils.getTempFile(); tempFile = DownloadUtils.getTempFile(null, "jpg");
} }
try (OutputStream output = new FileOutputStream(tempFile)) { try (OutputStream output = contentResolver.openOutputStream(tempFile.getUri())) {
final boolean compressResult = bitmap.compress(Bitmap.CompressFormat.JPEG, 100, output); final boolean compressResult = bitmap.compress(Bitmap.CompressFormat.JPEG, 100, output);
if (!compressResult) { if (!compressResult) {
throw new RuntimeException("Compression failed!"); throw new RuntimeException("Compression failed!");

View File

@ -1,9 +1,11 @@
package awais.instagrabber.utils; package awais.instagrabber.utils;
import android.Manifest; import android.Manifest;
import android.content.ContentResolver;
import android.content.Context; import android.content.Context;
import android.content.DialogInterface; import android.content.DialogInterface;
import android.os.Environment; import android.net.Uri;
import android.provider.DocumentsContract;
import android.util.Log; import android.util.Log;
import android.webkit.MimeTypeMap; import android.webkit.MimeTypeMap;
import android.widget.Toast; import android.widget.Toast;
@ -11,6 +13,8 @@ import android.widget.Toast;
import androidx.annotation.NonNull; import androidx.annotation.NonNull;
import androidx.annotation.Nullable; import androidx.annotation.Nullable;
import androidx.appcompat.app.AlertDialog; import androidx.appcompat.app.AlertDialog;
import androidx.core.util.Pair;
import androidx.documentfile.provider.DocumentFile;
import androidx.work.Constraints; import androidx.work.Constraints;
import androidx.work.Data; import androidx.work.Data;
import androidx.work.NetworkType; import androidx.work.NetworkType;
@ -21,9 +25,9 @@ import androidx.work.WorkRequest;
import com.google.gson.Gson; import com.google.gson.Gson;
import java.io.BufferedWriter; import java.io.BufferedWriter;
import java.io.File;
import java.io.FileWriter;
import java.io.IOException; import java.io.IOException;
import java.io.OutputStreamWriter;
import java.util.ArrayList;
import java.util.Collections; import java.util.Collections;
import java.util.HashMap; import java.util.HashMap;
import java.util.LinkedList; import java.util.LinkedList;
@ -41,31 +45,50 @@ import awais.instagrabber.repositories.responses.User;
import awais.instagrabber.repositories.responses.VideoVersion; import awais.instagrabber.repositories.responses.VideoVersion;
import awais.instagrabber.workers.DownloadWorker; import awais.instagrabber.workers.DownloadWorker;
import static awais.instagrabber.utils.Constants.FOLDER_PATH;
public final class DownloadUtils { public final class DownloadUtils {
private static final String TAG = DownloadUtils.class.getSimpleName(); private static final String TAG = DownloadUtils.class.getSimpleName();
public static final String WRITE_PERMISSION = Manifest.permission.WRITE_EXTERNAL_STORAGE; public static final String WRITE_PERMISSION = Manifest.permission.WRITE_EXTERNAL_STORAGE;
public static final String[] PERMS = new String[]{Manifest.permission.WRITE_EXTERNAL_STORAGE}; public static final String[] PERMS = new String[]{Manifest.permission.WRITE_EXTERNAL_STORAGE};
public static final String DIR_BARINSTA = "Barinsta"; private static final String DIR_BARINSTA = "Barinsta";
public static final String DIR_DOWNLOADS = "Downloads"; private static final String DIR_DOWNLOADS = "Downloads";
public static final String DIR_CAMERA = "Camera"; private static final String DIR_CAMERA = "Camera";
public static final String DIR_EDIT = "Edit"; private static final String DIR_EDIT = "Edit";
private static final String TEMP_DIR = "Temp";
public static File getDownloadDir(final String... dirs) { private static DocumentFile root;
final File parent = new File(Environment.getExternalStorageDirectory(), Environment.DIRECTORY_DOWNLOADS);
File subDir = new File(parent, DIR_BARINSTA); public static void init(@NonNull final Context context) {
// if (!Utils.settingsHelper.getBoolean(FOLDER_SAVE_TO)) return;
final String customPath = Utils.settingsHelper.getString(FOLDER_PATH);
if (TextUtils.isEmpty(customPath)) return;
// dir = new File(customPath);
root = DocumentFile.fromTreeUri(context, Uri.parse(customPath));
Log.d(TAG, "init: " + root);
// final File parent = Environment.getExternalStoragePublicDirectory(Environment.DIRECTORY_DOWNLOADS);
// final DocumentFile documentFile = DocumentFile.fromFile(parent);
// Log.d(TAG, "init: " + documentFile);
}
public static DocumentFile getDownloadDir(final String... dirs) {
// final File parent = new File(Environment.getExternalStorageDirectory(), Environment.DIRECTORY_DOWNLOADS);
// File subDir = new File(parent, DIR_BARINSTA);
DocumentFile subDir = root;
if (dirs != null) { if (dirs != null) {
for (final String dir : dirs) { for (final String dir : dirs) {
subDir = new File(subDir, dir); final DocumentFile subDirFile = subDir.findFile(dir);
//noinspection ResultOfMethodCallIgnored if (subDirFile == null) {
subDir.mkdirs(); subDir = subDir.createDirectory(dir);
}
} }
} }
return subDir; return subDir;
} }
@NonNull @NonNull
public static File getDownloadDir() { public static DocumentFile getDownloadDir() {
// final File parent = new File(Environment.getExternalStorageDirectory(), Environment.DIRECTORY_DOWNLOADS); // final File parent = new File(Environment.getExternalStorageDirectory(), Environment.DIRECTORY_DOWNLOADS);
// final File dir = new File(new File(parent, "barinsta"), "downloads"); // final File dir = new File(new File(parent, "barinsta"), "downloads");
// if (!dir.exists()) { // if (!dir.exists()) {
@ -83,37 +106,63 @@ public final class DownloadUtils {
return getDownloadDir(DIR_DOWNLOADS); return getDownloadDir(DIR_DOWNLOADS);
} }
public static File getCameraDir() { public static DocumentFile getCameraDir() {
return getDownloadDir(DIR_CAMERA); return getDownloadDir(DIR_CAMERA);
} }
public static File getImageEditDir(final String sessionId) { public static DocumentFile getImageEditDir(final String sessionId) {
return getDownloadDir(DIR_EDIT, sessionId); return getDownloadDir(DIR_EDIT, sessionId);
} }
@Nullable // @Nullable
private static File getDownloadDir(@NonNull final Context context, @Nullable final String username) { // private static DocumentFile getDownloadDir(@NonNull final Context context, @Nullable final String username) {
return getDownloadDir(context, username, false); // return getDownloadDir(context, username, false);
} // }
@Nullable @Nullable
private static File getDownloadDir(final Context context, private static DocumentFile getDownloadDir(final Context context,
@Nullable final String username, @Nullable final String username) {
final boolean skipCreateDir) { final List<String> userFolderPaths = getSubPathForUserFolder(username);
File dir = getDownloadDir(); DocumentFile dir = root;
for (final String dirName : userFolderPaths) {
if (Utils.settingsHelper.getBoolean(Constants.DOWNLOAD_USER_FOLDER) && !TextUtils.isEmpty(username)) { final DocumentFile file = dir.findFile(dirName);
final String finaleUsername = username.startsWith("@") ? username.substring(1) : username; if (file != null) {
dir = new File(dir, finaleUsername); dir = file;
continue;
}
dir = dir.createDirectory(dirName);
if (dir == null) break;
} }
// final String joined = android.text.TextUtils.join("/", userFolderPaths);
if (context != null && !skipCreateDir && !dir.exists() && !dir.mkdirs()) { // final Uri userFolderUri = DocumentsContract.buildDocumentUriUsingTree(root.getUri(), joined);
// final DocumentFile userFolder = DocumentFile.fromSingleUri(context, userFolderUri);
if (context != null && (dir == null || !dir.exists())) {
Toast.makeText(context, R.string.error_creating_folders, Toast.LENGTH_SHORT).show(); Toast.makeText(context, R.string.error_creating_folders, Toast.LENGTH_SHORT).show();
return null; return null;
} }
return dir; return dir;
} }
private static List<String> getSubPathForUserFolder(final String username) {
final List<String> list = new ArrayList<>();
if (!Utils.settingsHelper.getBoolean(Constants.DOWNLOAD_USER_FOLDER) || TextUtils.isEmpty(username)) {
list.add(DIR_DOWNLOADS);
return list;
}
final String finalUsername = username.startsWith("@") ? username.substring(1) : username;
list.add(DIR_DOWNLOADS);
list.add(finalUsername);
return list;
}
private static DocumentFile getTempDir() {
DocumentFile file = root.findFile(TEMP_DIR);
if (file == null) {
file = root.createDirectory(TEMP_DIR);
}
return file;
}
// public static void dmDownload(@NonNull final Context context, // public static void dmDownload(@NonNull final Context context,
// @Nullable final String username, // @Nullable final String username,
// final String modelId, // final String modelId,
@ -126,59 +175,71 @@ public final class DownloadUtils {
// } // }
// } // }
private static void dmDownloadImpl(@NonNull final Context context, // private static void dmDownloadImpl(@NonNull final Context context,
@Nullable final String username, // @Nullable final String username,
final String modelId, // final String modelId,
final String url) { // final String url) {
final File dir = getDownloadDir(context, username); // final DocumentFile dir = getDownloadDir(context, username);
if (dir.exists() || dir.mkdirs()) { // if (dir != null && dir.exists()) {
download(context, // download(context, url, getDownloadSavePaths(dir, modelId, url));
url, // return;
getDownloadSaveFile(dir, modelId, url).getAbsolutePath()); // }
return; // Toast.makeText(context, R.string.error_creating_folders, Toast.LENGTH_SHORT).show();
} // }
Toast.makeText(context, R.string.error_creating_folders, Toast.LENGTH_SHORT).show();
}
@NonNull @NonNull
private static File getDownloadSaveFile(final File finalDir, private static Pair<List<String>, String> getDownloadSavePaths(final List<String> paths,
final String postId, final String postId,
final String displayUrl) { final String displayUrl) {
return getDownloadSaveFile(finalDir, postId, "", displayUrl); return getDownloadSavePaths(paths, postId, "", displayUrl);
} }
private static File getDownloadChildSaveFile(final File downloadDir, private static Pair<List<String>, String> getDownloadChildSaveFile(final List<String> paths,
final String postId, final String postId,
final int childPosition, final int childPosition,
final String url) { final String url) {
final String sliderPostfix = "_slide_" + childPosition; final String sliderPostfix = "_slide_" + childPosition;
return getDownloadSaveFile(downloadDir, postId, sliderPostfix, url); return getDownloadSavePaths(paths, postId, sliderPostfix, url);
} }
@NonNull @Nullable
private static File getDownloadSaveFile(final File finalDir, private static Pair<List<String>, String> getDownloadSavePaths(final List<String> paths,
final String postId, final String postId,
final String sliderPostfix, final String sliderPostfix,
final String displayUrl) { final String displayUrl) {
final String fileName = postId + sliderPostfix + getFileExtensionFromUrl(displayUrl); if (paths == null) return null;
return new File(finalDir, fileName); final String extension = getFileExtensionFromUrl(displayUrl);
final String fileName = postId + sliderPostfix + extension;
// return new File(finalDir, fileName);
// DocumentFile file = finalDir.findFile(fileName);
// if (file == null) {
final String mimeType = Utils.mimeTypeMap.getMimeTypeFromExtension(extension.startsWith(".") ? extension.substring(1) : extension);
// file = finalDir.createFile(mimeType, fileName);
// }
paths.add(fileName);
return new Pair<>(paths, mimeType);
} }
@NonNull public static DocumentFile getTempFile() {
public static File getTempFile() {
return getTempFile(null, null); return getTempFile(null, null);
} }
public static File getTempFile(final String fileName, final String extension) { public static DocumentFile getTempFile(final String fileName, final String extension) {
final File dir = getDownloadDir(); final DocumentFile dir = getTempDir();
String name = fileName; String name = fileName;
if (TextUtils.isEmpty(name)) { if (TextUtils.isEmpty(name)) {
name = UUID.randomUUID().toString(); name = UUID.randomUUID().toString();
} }
String mimeType = "application/octet-stream";
if (!TextUtils.isEmpty(extension)) { if (!TextUtils.isEmpty(extension)) {
name += "." + extension; name += "." + extension;
mimeType = Utils.mimeTypeMap.getMimeTypeFromExtension(extension);
} }
return new File(dir, name); DocumentFile file = dir.findFile(name);
if (file == null) {
file = dir.createFile(mimeType, name);
}
return file;
} }
/** /**
@ -221,20 +282,21 @@ public final class DownloadUtils {
return ""; return "";
} }
public static List<Boolean> checkDownloaded(@NonNull final Media media) { public static List<Boolean> checkDownloaded(@NonNull final Context context,
@NonNull final Media media) {
final List<Boolean> checkList = new LinkedList<>(); final List<Boolean> checkList = new LinkedList<>();
final User user = media.getUser(); final User user = media.getUser();
String username = "username"; String username = "username";
if (user != null) { if (user != null) {
username = user.getUsername(); username = user.getUsername();
} }
final File downloadDir = getDownloadDir(null, "@" + username, true); final List<String> userFolderPaths = getSubPathForUserFolder(username);
switch (media.getMediaType()) { switch (media.getMediaType()) {
case MEDIA_TYPE_IMAGE: case MEDIA_TYPE_IMAGE:
case MEDIA_TYPE_VIDEO: { case MEDIA_TYPE_VIDEO: {
final String url = ResponseBodyUtils.getImageUrl(media); final String url = ResponseBodyUtils.getImageUrl(media);
final File file = getDownloadSaveFile(downloadDir, media.getCode(), url); final Pair<List<String>, String> pair = getDownloadSavePaths(userFolderPaths, media.getCode(), url);
checkList.add(file.exists()); checkList.add(checkPathExists(context, pair.first));
break; break;
} }
case MEDIA_TYPE_SLIDER: case MEDIA_TYPE_SLIDER:
@ -243,8 +305,8 @@ public final class DownloadUtils {
final Media child = sliderItems.get(i); final Media child = sliderItems.get(i);
if (child == null) continue; if (child == null) continue;
final String url = ResponseBodyUtils.getImageUrl(child); final String url = ResponseBodyUtils.getImageUrl(child);
final File file = getDownloadChildSaveFile(downloadDir, media.getCode(), i + 1, url); final Pair<List<String>, String> pair = getDownloadChildSaveFile(userFolderPaths, media.getCode(), i + 1, url);
checkList.add(file.exists()); checkList.add(checkPathExists(context, pair.first));
} }
break; break;
default: default:
@ -252,6 +314,14 @@ public final class DownloadUtils {
return checkList; return checkList;
} }
private static boolean checkPathExists(@NonNull final Context context,
@NonNull final List<String> paths) {
final String joined = android.text.TextUtils.join("/", paths);
final Uri userFolderUri = DocumentsContract.buildDocumentUriUsingTree(root.getUri(), joined);
final DocumentFile userFolder = DocumentFile.fromSingleUri(context, userFolderUri);
return userFolder != null && userFolder.exists();
}
public static void showDownloadDialog(@NonNull Context context, public static void showDownloadDialog(@NonNull Context context,
@NonNull final Media feedModel, @NonNull final Media feedModel,
final int childPosition) { final int childPosition) {
@ -286,15 +356,20 @@ public final class DownloadUtils {
public static void download(@NonNull final Context context, public static void download(@NonNull final Context context,
@NonNull final StoryModel storyModel) { @NonNull final StoryModel storyModel) {
final File downloadDir = getDownloadDir(context, "@" + storyModel.getUsername()); final DocumentFile downloadDir = getDownloadDir(context, "@" + storyModel.getUsername());
final String url = storyModel.getItemType() == MediaItemType.MEDIA_TYPE_VIDEO final String url = storyModel.getItemType() == MediaItemType.MEDIA_TYPE_VIDEO
? storyModel.getVideoUrl() ? storyModel.getVideoUrl()
: storyModel.getStoryUrl(); : storyModel.getStoryUrl();
final File saveFile = new File(downloadDir, final String extension = DownloadUtils.getFileExtensionFromUrl(url);
storyModel.getStoryMediaId() final String fileName = storyModel.getStoryMediaId() + "_" + storyModel.getTimestamp() + extension;
+ "_" + storyModel.getTimestamp() DocumentFile saveFile = downloadDir.findFile(fileName);
+ DownloadUtils.getFileExtensionFromUrl(url)); if (saveFile == null) {
download(context, url, saveFile.getAbsolutePath()); saveFile = downloadDir.createFile(
Utils.mimeTypeMap.getMimeTypeFromExtension(extension.startsWith(".") ? extension.substring(1) : extension),
fileName);
}
// final File saveFile = new File(downloadDir, fileName);
download(context, url, saveFile);
} }
public static void download(@NonNull final Context context, public static void download(@NonNull final Context context,
@ -316,17 +391,19 @@ public final class DownloadUtils {
private static void download(@NonNull final Context context, private static void download(@NonNull final Context context,
@NonNull final List<Media> feedModels, @NonNull final List<Media> feedModels,
final int childPositionIfSingle) { final int childPositionIfSingle) {
final Map<String, String> map = new HashMap<>(); final Map<String, DocumentFile> map = new HashMap<>();
for (final Media media : feedModels) { for (final Media media : feedModels) {
final User mediaUser = media.getUser(); final User mediaUser = media.getUser();
final File downloadDir = getDownloadDir(context, mediaUser == null ? "" : "@" + mediaUser.getUsername()); final List<String> userFolderPaths = getSubPathForUserFolder(mediaUser == null ? "" : "@" + mediaUser.getUsername());
if (downloadDir == null) return; // final DocumentFile downloadDir = getDownloadDir(context, mediaUser == null ? "" : "@" + mediaUser.getUsername());
switch (media.getMediaType()) { switch (media.getMediaType()) {
case MEDIA_TYPE_IMAGE: case MEDIA_TYPE_IMAGE:
case MEDIA_TYPE_VIDEO: { case MEDIA_TYPE_VIDEO: {
final String url = getUrlOfType(media); final String url = getUrlOfType(media);
final File file = getDownloadSaveFile(downloadDir, media.getCode(), url); final Pair<List<String>, String> pair = getDownloadSavePaths(userFolderPaths, media.getCode(), url);
map.put(url, file.getAbsolutePath()); final DocumentFile file = createFile(pair);
if (file == null) continue;
map.put(url, file);
break; break;
} }
case MEDIA_TYPE_VOICE: { case MEDIA_TYPE_VOICE: {
@ -335,28 +412,48 @@ public final class DownloadUtils {
if (mediaUser != null) { if (mediaUser != null) {
fileName = mediaUser.getUsername() + "_" + fileName; fileName = mediaUser.getUsername() + "_" + fileName;
} }
final File file = getDownloadSaveFile(downloadDir, fileName, url); final Pair<List<String>, String> pair = getDownloadSavePaths(userFolderPaths, fileName, url);
map.put(url, file.getAbsolutePath()); final DocumentFile file = createFile(pair);
if (file == null) continue;
map.put(url, file);
break; break;
} }
case MEDIA_TYPE_SLIDER: case MEDIA_TYPE_SLIDER:
final List<Media> sliderItems = media.getCarouselMedia(); final List<Media> sliderItems = media.getCarouselMedia();
for (int i = 0; i < sliderItems.size(); i++) { for (int i = 0; i < sliderItems.size(); i++) {
if (childPositionIfSingle >= 0 && feedModels.size() == 1 && i != childPositionIfSingle) { if (childPositionIfSingle >= 0 && feedModels.size() == 1 && i != childPositionIfSingle) continue;
continue;
}
final Media child = sliderItems.get(i); final Media child = sliderItems.get(i);
final String url = getUrlOfType(child); final String url = getUrlOfType(child);
final File file = getDownloadChildSaveFile(downloadDir, media.getCode(), i + 1, url); final Pair<List<String>, String> pair = getDownloadChildSaveFile(userFolderPaths, media.getCode(), i + 1, url);
map.put(url, file.getAbsolutePath()); final DocumentFile file = createFile(pair);
if (file == null) continue;
map.put(url, file);
} }
break; break;
default: default:
} }
} }
if (map.isEmpty()) return;
download(context, map); download(context, map);
} }
private static DocumentFile createFile(@NonNull final Pair<List<String>, String> pair) {
if (pair.first == null || pair.second == null) return null;
DocumentFile dir = root;
final List<String> first = pair.first;
for (int i = 0; i < first.size(); i++) {
final String name = first.get(i);
final DocumentFile file = dir.findFile(name);
if (file != null) {
dir = file;
continue;
}
dir = i == first.size() - 1 ? dir.createFile(pair.second, name) : dir.createDirectory(name);
if (dir == null) break;
}
return dir;
}
@Nullable @Nullable
private static String getUrlOfType(@NonNull final Media media) { private static String getUrlOfType(@NonNull final Media media) {
switch (media.getMediaType()) { switch (media.getMediaType()) {
@ -388,12 +485,13 @@ public final class DownloadUtils {
public static void download(final Context context, public static void download(final Context context,
final String url, final String url,
final String filePath) { final DocumentFile filePath) {
if (context == null || url == null || filePath == null) return; if (context == null || url == null || filePath == null) return;
download(context, Collections.singletonMap(url, filePath)); download(context, Collections.singletonMap(url, filePath));
} }
private static void download(final Context context, final Map<String, String> urlFilePathMap) { private static void download(final Context context, final Map<String, DocumentFile> urlFilePathMap) {
if (context == null) return;
final Constraints constraints = new Constraints.Builder() final Constraints constraints = new Constraints.Builder()
.setRequiredNetworkType(NetworkType.CONNECTED) .setRequiredNetworkType(NetworkType.CONNECTED)
.build(); .build();
@ -401,19 +499,25 @@ public final class DownloadUtils {
.setUrlToFilePathMap(urlFilePathMap) .setUrlToFilePathMap(urlFilePathMap)
.build(); .build();
final String requestJson = new Gson().toJson(request); final String requestJson = new Gson().toJson(request);
final File tempFile = getTempFile(); final DocumentFile tempFile = getTempFile(null, "json");
try (BufferedWriter writer = new BufferedWriter(new FileWriter(tempFile))) { if (tempFile == null) {
Log.e(TAG, "download: temp file is null");
return;
}
final Uri uri = tempFile.getUri();
final ContentResolver contentResolver = context.getContentResolver();
if (contentResolver == null) return;
try (BufferedWriter writer = new BufferedWriter(new OutputStreamWriter(contentResolver.openOutputStream(uri)))) {
writer.write(requestJson); writer.write(requestJson);
} catch (IOException e) { } catch (IOException e) {
Log.e(TAG, "download: Error writing request to file", e); Log.e(TAG, "download: Error writing request to file", e);
//noinspection ResultOfMethodCallIgnored
tempFile.delete(); tempFile.delete();
return; return;
} }
final WorkRequest downloadWorkRequest = new OneTimeWorkRequest.Builder(DownloadWorker.class) final WorkRequest downloadWorkRequest = new OneTimeWorkRequest.Builder(DownloadWorker.class)
.setInputData( .setInputData(
new Data.Builder() new Data.Builder()
.putString(DownloadWorker.KEY_DOWNLOAD_REQUEST_JSON, tempFile.getAbsolutePath()) .putString(DownloadWorker.KEY_DOWNLOAD_REQUEST_JSON, tempFile.getUri().toString())
.build() .build()
) )
.setConstraints(constraints) .setConstraints(constraints)

View File

@ -6,11 +6,10 @@ import android.net.Uri;
import androidx.annotation.NonNull; import androidx.annotation.NonNull;
import androidx.annotation.Nullable; import androidx.annotation.Nullable;
import androidx.documentfile.provider.DocumentFile;
import org.json.JSONObject; import org.json.JSONObject;
import java.io.File;
import java.io.FileInputStream;
import java.io.IOException; import java.io.IOException;
import java.io.InputStream; import java.io.InputStream;
import java.util.Map; import java.util.Map;
@ -45,7 +44,7 @@ public final class MediaUploader {
listener.onFailure(new RuntimeException("Bitmap result was null")); listener.onFailure(new RuntimeException("Bitmap result was null"));
return; return;
} }
uploadPhoto(bitmap, listener); uploadPhoto(contentResolver, bitmap, listener);
} }
@Override @Override
@ -55,13 +54,14 @@ public final class MediaUploader {
}); });
} }
private static void uploadPhoto(@NonNull final Bitmap bitmap, private static void uploadPhoto(@NonNull final ContentResolver contentResolver,
@NonNull final Bitmap bitmap,
@NonNull final OnMediaUploadCompleteListener listener) { @NonNull final OnMediaUploadCompleteListener listener) {
appExecutors.tasksThread().submit(() -> { appExecutors.tasksThread().submit(() -> {
final File file; final DocumentFile file;
final long byteLength; final long byteLength;
try { try {
file = BitmapUtils.convertToJpegAndSaveToFile(bitmap, null); file = BitmapUtils.convertToJpegAndSaveToFile(contentResolver, bitmap, null);
byteLength = file.length(); byteLength = file.length();
} catch (Exception e) { } catch (Exception e) {
listener.onFailure(e); listener.onFailure(e);
@ -71,12 +71,11 @@ public final class MediaUploader {
final Map<String, String> headers = MediaUploadHelper.getUploadPhotoHeaders(options); final Map<String, String> headers = MediaUploadHelper.getUploadPhotoHeaders(options);
final String url = HOST + "/rupload_igphoto/" + options.getName() + "/"; final String url = HOST + "/rupload_igphoto/" + options.getName() + "/";
appExecutors.networkIO().execute(() -> { appExecutors.networkIO().execute(() -> {
try (FileInputStream input = new FileInputStream(file)) { try (InputStream input = contentResolver.openInputStream(file.getUri())) {
upload(input, url, headers, listener); upload(input, url, headers, listener);
} catch (IOException e) { } catch (IOException e) {
listener.onFailure(e); listener.onFailure(e);
} finally { } finally {
//noinspection ResultOfMethodCallIgnored
file.delete(); file.delete();
} }
}); });

View File

@ -15,8 +15,11 @@ import android.media.MediaScannerConnection;
import android.media.MediaScannerConnection.OnScanCompletedListener; import android.media.MediaScannerConnection.OnScanCompletedListener;
import android.net.Uri; import android.net.Uri;
import android.os.Build; import android.os.Build;
import android.os.Environment;
import android.os.Handler; import android.os.Handler;
import android.os.storage.StorageManager;
import android.provider.Browser; import android.provider.Browser;
import android.provider.DocumentsContract;
import android.util.DisplayMetrics; import android.util.DisplayMetrics;
import android.util.Log; import android.util.Log;
import android.util.Pair; import android.util.Pair;
@ -35,6 +38,7 @@ import androidx.annotation.Nullable;
import androidx.appcompat.app.ActionBar; import androidx.appcompat.app.ActionBar;
import androidx.appcompat.app.AppCompatActivity; import androidx.appcompat.app.AppCompatActivity;
import androidx.core.content.ContextCompat; import androidx.core.content.ContextCompat;
import androidx.documentfile.provider.DocumentFile;
import androidx.vectordrawable.graphics.drawable.AnimatedVectorDrawableCompat; import androidx.vectordrawable.graphics.drawable.AnimatedVectorDrawableCompat;
import com.google.android.exoplayer2.database.ExoDatabaseProvider; import com.google.android.exoplayer2.database.ExoDatabaseProvider;
@ -46,6 +50,8 @@ import org.json.JSONObject;
import java.io.File; import java.io.File;
import java.lang.reflect.Field; import java.lang.reflect.Field;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.text.SimpleDateFormat; import java.text.SimpleDateFormat;
import java.util.HashMap; import java.util.HashMap;
import java.util.Map; import java.util.Map;
@ -71,6 +77,7 @@ public final class Utils {
public static Handler applicationHandler; public static Handler applicationHandler;
public static String cacheDir; public static String cacheDir;
private static int defaultStatusBarColor; private static int defaultStatusBarColor;
private static Object[] volumes;
public static int convertDpToPx(final float dp) { public static int convertDpToPx(final float dp) {
return Math.round((dp * displayMetrics.densityDpi) / DisplayMetrics.DENSITY_DEFAULT); return Math.round((dp * displayMetrics.densityDpi) / DisplayMetrics.DENSITY_DEFAULT);
@ -367,4 +374,51 @@ public final class Utils {
if (window == null) return; if (window == null) return;
window.clearFlags(WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON); window.clearFlags(WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON);
} }
public static void scanDocumentFile(@NonNull final Context context,
@NonNull final DocumentFile documentFile,
@NonNull final OnScanCompletedListener callback) {
if (!documentFile.isFile()) return;
File file = null;
try {
file = getDocumentFileRealPath(context, documentFile);
} catch (NoSuchMethodException | InvocationTargetException | IllegalAccessException e) {
Log.e(TAG, "scanDocumentFile: ", e);
}
if (file == null) return;
MediaScannerConnection.scanFile(context,
new String[]{file.getAbsolutePath()},
new String[]{documentFile.getType()},
callback);
}
private static File getDocumentFileRealPath(Context context, DocumentFile documentFile)
throws NoSuchMethodException, InvocationTargetException, IllegalAccessException {
final String docId = DocumentsContract.getDocumentId(documentFile.getUri());
final String[] split = docId.split(":");
final String type = split[0];
if (type.equalsIgnoreCase("primary")) {
return new File(Environment.getExternalStorageDirectory(), split[1]);
} else {
if (volumes == null) {
StorageManager sm = (StorageManager) context.getSystemService(Context.STORAGE_SERVICE);
Method getVolumeListMethod = sm.getClass().getMethod("getVolumeList");
volumes = (Object[]) getVolumeListMethod.invoke(sm);
}
for (Object volume : volumes) {
Method getUuidMethod = volume.getClass().getMethod("getUuid");
String uuid = (String) getUuidMethod.invoke(volume);
if (uuid != null && uuid.equalsIgnoreCase(type)) {
Method getPathMethod = volume.getClass().getMethod("getPath");
String path = (String) getPathMethod.invoke(volume);
return new File(path, split[1]);
}
}
}
return null;
}
} }

View File

@ -1,14 +1,16 @@
package awais.instagrabber.utils; package awais.instagrabber.utils;
import android.app.Application;
import android.media.MediaRecorder; import android.media.MediaRecorder;
import android.os.Handler; import android.os.Handler;
import android.os.Message; import android.os.Message;
import android.os.ParcelFileDescriptor;
import android.util.Log; import android.util.Log;
import android.webkit.MimeTypeMap; import android.webkit.MimeTypeMap;
import androidx.annotation.NonNull; import androidx.annotation.NonNull;
import androidx.documentfile.provider.DocumentFile;
import java.io.File;
import java.text.SimpleDateFormat; import java.text.SimpleDateFormat;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.Date; import java.util.Date;
@ -27,20 +29,20 @@ public class VoiceRecorder {
private static final SimpleDateFormat SIMPLE_DATE_FORMAT = new SimpleDateFormat(FILE_FORMAT, Locale.US); private static final SimpleDateFormat SIMPLE_DATE_FORMAT = new SimpleDateFormat(FILE_FORMAT, Locale.US);
private final List<Float> waveform = new ArrayList<>(); private final List<Float> waveform = new ArrayList<>();
private final File recordingsDir; private final DocumentFile recordingsDir;
private final VoiceRecorderCallback callback; private final VoiceRecorderCallback callback;
private MediaRecorder recorder; private MediaRecorder recorder;
private File audioTempFile; private DocumentFile audioTempFile;
private MaxAmpHandler maxAmpHandler; private MaxAmpHandler maxAmpHandler;
private boolean stopped; private boolean stopped;
public VoiceRecorder(@NonNull final File recordingsDir, final VoiceRecorderCallback callback) { public VoiceRecorder(@NonNull final DocumentFile recordingsDir, final VoiceRecorderCallback callback) {
this.recordingsDir = recordingsDir; this.recordingsDir = recordingsDir;
this.callback = callback; this.callback = callback;
} }
public void startRecording() { public void startRecording(final Application application) {
stopped = false; stopped = false;
try { try {
recorder = new MediaRecorder(); recorder = new MediaRecorder();
@ -48,7 +50,8 @@ public class VoiceRecorder {
recorder.setOutputFormat(MediaRecorder.OutputFormat.MPEG_4); recorder.setOutputFormat(MediaRecorder.OutputFormat.MPEG_4);
deleteTempAudioFile(); deleteTempAudioFile();
audioTempFile = getAudioRecordFile(); audioTempFile = getAudioRecordFile();
recorder.setOutputFile(audioTempFile.getAbsolutePath()); final ParcelFileDescriptor parcelFileDescriptor = application.getContentResolver().openFileDescriptor(audioTempFile.getUri(), "rwt");
recorder.setOutputFile(parcelFileDescriptor.getFileDescriptor());
recorder.setAudioEncoder(MediaRecorder.AudioEncoder.HE_AAC); recorder.setAudioEncoder(MediaRecorder.AudioEncoder.HE_AAC);
recorder.setAudioEncodingBitRate(AUDIO_BIT_RATE); recorder.setAudioEncodingBitRate(AUDIO_BIT_RATE);
recorder.setAudioSamplingRate(AUDIO_SAMPLE_RATE); recorder.setAudioSamplingRate(AUDIO_SAMPLE_RATE);
@ -140,9 +143,9 @@ public class VoiceRecorder {
// } // }
@NonNull @NonNull
private File getAudioRecordFile() { private DocumentFile getAudioRecordFile() {
final String name = String.format("%s-%s.%s", FILE_PREFIX, SIMPLE_DATE_FORMAT.format(new Date()), EXTENSION); final String name = String.format("%s-%s.%s", FILE_PREFIX, SIMPLE_DATE_FORMAT.format(new Date()), EXTENSION);
return new File(recordingsDir, name); return recordingsDir.createFile(MIME_TYPE, name);
} }
private void deleteTempAudioFile() { private void deleteTempAudioFile() {
@ -160,11 +163,11 @@ public class VoiceRecorder {
public static class VoiceRecordingResult { public static class VoiceRecordingResult {
private final String mimeType; private final String mimeType;
private final File file; private final DocumentFile file;
private final List<Float> waveform; private final List<Float> waveform;
private final int samplingFreq = 10; private final int samplingFreq = 10;
public VoiceRecordingResult(final String mimeType, final File file, final List<Float> waveform) { public VoiceRecordingResult(final String mimeType, final DocumentFile file, final List<Float> waveform) {
this.mimeType = mimeType; this.mimeType = mimeType;
this.file = file; this.file = file;
this.waveform = waveform; this.waveform = waveform;
@ -174,7 +177,7 @@ public class VoiceRecorder {
return mimeType; return mimeType;
} }
public File getFile() { public DocumentFile getFile() {
return file; return file;
} }

View File

@ -2,18 +2,17 @@ package awais.instagrabber.viewmodels;
import android.app.Application; import android.app.Application;
import android.content.ContentResolver; import android.content.ContentResolver;
import android.media.MediaScannerConnection;
import android.net.Uri; import android.net.Uri;
import android.util.Log; import android.util.Log;
import androidx.annotation.NonNull; import androidx.annotation.NonNull;
import androidx.annotation.Nullable; import androidx.annotation.Nullable;
import androidx.documentfile.provider.DocumentFile;
import androidx.lifecycle.AndroidViewModel; import androidx.lifecycle.AndroidViewModel;
import androidx.lifecycle.LiveData; import androidx.lifecycle.LiveData;
import androidx.lifecycle.MutableLiveData; import androidx.lifecycle.MutableLiveData;
import androidx.lifecycle.Transformations; import androidx.lifecycle.Transformations;
import java.io.File;
import java.util.List; import java.util.List;
import java.util.Map; import java.util.Map;
import java.util.Objects; import java.util.Objects;
@ -38,6 +37,7 @@ import awais.instagrabber.utils.DownloadUtils;
import awais.instagrabber.utils.MediaController; import awais.instagrabber.utils.MediaController;
import awais.instagrabber.utils.MediaUtils; import awais.instagrabber.utils.MediaUtils;
import awais.instagrabber.utils.TextUtils; import awais.instagrabber.utils.TextUtils;
import awais.instagrabber.utils.Utils;
import awais.instagrabber.utils.VoiceRecorder; import awais.instagrabber.utils.VoiceRecorder;
import static awais.instagrabber.utils.Utils.settingsHelper; import static awais.instagrabber.utils.Utils.settingsHelper;
@ -47,7 +47,7 @@ public class DirectThreadViewModel extends AndroidViewModel {
// private static final String ERROR_INVALID_THREAD = "Invalid thread"; // private static final String ERROR_INVALID_THREAD = "Invalid thread";
private final ContentResolver contentResolver; private final ContentResolver contentResolver;
private final File recordingsDir; private final DocumentFile recordingsDir;
private final Application application; private final Application application;
private final long viewerId; private final long viewerId;
private final String threadId; private final String threadId;
@ -166,37 +166,32 @@ public class DirectThreadViewModel extends AndroidViewModel {
@Override @Override
public void onComplete(final VoiceRecorder.VoiceRecordingResult result) { public void onComplete(final VoiceRecorder.VoiceRecordingResult result) {
Log.d(TAG, "onComplete: recording complete. Scanning file..."); Log.d(TAG, "onComplete: recording complete. Scanning file...");
MediaScannerConnection.scanFile( Utils.scanDocumentFile(application, result.getFile(), (path, uri) -> {
application, if (uri == null) {
new String[]{result.getFile().getAbsolutePath()}, final String msg = "Scan failed!";
new String[]{result.getMimeType()}, Log.e(TAG, msg);
(path, uri) -> { data.postValue(Resource.error(msg, null));
if (uri == null) { return;
final String msg = "Scan failed!"; }
Log.e(TAG, msg); Log.d(TAG, "onComplete: scan complete");
data.postValue(Resource.error(msg, null)); MediaUtils.getVoiceInfo(contentResolver, uri, new MediaUtils.OnInfoLoadListener<MediaUtils.VideoInfo>() {
return; @Override
} public void onLoad(@Nullable final MediaUtils.VideoInfo videoInfo) {
Log.d(TAG, "onComplete: scan complete"); if (videoInfo == null) return;
MediaUtils.getVoiceInfo(contentResolver, uri, new MediaUtils.OnInfoLoadListener<MediaUtils.VideoInfo>() { threadManager.sendVoice(data,
@Override uri,
public void onLoad(@Nullable final MediaUtils.VideoInfo videoInfo) { result.getWaveform(),
if (videoInfo == null) return; result.getSamplingFreq(),
threadManager.sendVoice(data, videoInfo == null ? 0 : videoInfo.duration,
uri, videoInfo == null ? 0 : videoInfo.size);
result.getWaveform(),
result.getSamplingFreq(),
videoInfo == null ? 0 : videoInfo.duration,
videoInfo == null ? 0 : videoInfo.size);
}
@Override
public void onFailure(final Throwable t) {
data.postValue(Resource.error(t.getMessage(), null));
}
});
} }
);
@Override
public void onFailure(final Throwable t) {
data.postValue(Resource.error(t.getMessage(), null));
}
});
});
} }
@Override @Override
@ -204,7 +199,7 @@ public class DirectThreadViewModel extends AndroidViewModel {
} }
}); });
voiceRecorder.startRecording(); voiceRecorder.startRecording(application);
return data; return data;
} }

View File

@ -5,6 +5,7 @@ import android.graphics.RectF;
import android.net.Uri; import android.net.Uri;
import androidx.annotation.NonNull; import androidx.annotation.NonNull;
import androidx.documentfile.provider.DocumentFile;
import androidx.lifecycle.AndroidViewModel; import androidx.lifecycle.AndroidViewModel;
import androidx.lifecycle.LiveData; import androidx.lifecycle.LiveData;
import androidx.lifecycle.MutableLiveData; import androidx.lifecycle.MutableLiveData;
@ -26,6 +27,7 @@ import awais.instagrabber.models.SavedImageEditState;
import awais.instagrabber.utils.AppExecutors; import awais.instagrabber.utils.AppExecutors;
import awais.instagrabber.utils.DownloadUtils; import awais.instagrabber.utils.DownloadUtils;
import awais.instagrabber.utils.SerializablePair; import awais.instagrabber.utils.SerializablePair;
import awais.instagrabber.utils.Utils;
import jp.co.cyberagent.android.gpuimage.GPUImage; import jp.co.cyberagent.android.gpuimage.GPUImage;
import jp.co.cyberagent.android.gpuimage.filter.GPUImageFilter; import jp.co.cyberagent.android.gpuimage.filter.GPUImageFilter;
import jp.co.cyberagent.android.gpuimage.filter.GPUImageFilterGroup; import jp.co.cyberagent.android.gpuimage.filter.GPUImageFilterGroup;
@ -35,6 +37,7 @@ public class ImageEditViewModel extends AndroidViewModel {
private static final String RESULT = "result"; private static final String RESULT = "result";
private static final String FILE_FORMAT = "yyyyMMddHHmmssSSS"; private static final String FILE_FORMAT = "yyyyMMddHHmmssSSS";
private static final SimpleDateFormat SIMPLE_DATE_FORMAT = new SimpleDateFormat(FILE_FORMAT, Locale.US); private static final SimpleDateFormat SIMPLE_DATE_FORMAT = new SimpleDateFormat(FILE_FORMAT, Locale.US);
private static final String MIME_TYPE = Utils.mimeTypeMap.getMimeTypeFromExtension("jpg");
private Uri originalUri; private Uri originalUri;
private SavedImageEditState savedImageEditState; private SavedImageEditState savedImageEditState;
@ -48,18 +51,18 @@ public class ImageEditViewModel extends AndroidViewModel {
private final MutableLiveData<Boolean> isCropped = new MutableLiveData<>(false); private final MutableLiveData<Boolean> isCropped = new MutableLiveData<>(false);
private final MutableLiveData<Boolean> isTuned = new MutableLiveData<>(false); private final MutableLiveData<Boolean> isTuned = new MutableLiveData<>(false);
private final MutableLiveData<Boolean> isFiltered = new MutableLiveData<>(false); private final MutableLiveData<Boolean> isFiltered = new MutableLiveData<>(false);
private final File outputDir; private final DocumentFile outputDir;
private List<Filter<? extends GPUImageFilter>> tuningFilters; private List<Filter<? extends GPUImageFilter>> tuningFilters;
private Filter<? extends GPUImageFilter> appliedFilter; private Filter<? extends GPUImageFilter> appliedFilter;
private final File destinationFile; private final DocumentFile destinationFile;
public ImageEditViewModel(final Application application) { public ImageEditViewModel(final Application application) {
super(application); super(application);
sessionId = SIMPLE_DATE_FORMAT.format(new Date()); sessionId = SIMPLE_DATE_FORMAT.format(new Date());
outputDir = DownloadUtils.getImageEditDir(sessionId); outputDir = DownloadUtils.getImageEditDir(sessionId);
destinationFile = new File(outputDir, RESULT + ".jpg"); destinationFile = outputDir.createFile(MIME_TYPE, RESULT + ".jpg");
destinationUri = Uri.fromFile(destinationFile); destinationUri = destinationFile.getUri();
cropDestinationUri = Uri.fromFile(new File(outputDir, CROP + ".jpg")); cropDestinationUri = outputDir.createFile(MIME_TYPE, CROP + ".jpg").getUri();
} }
public String getSessionId() { public String getSessionId() {
@ -159,16 +162,15 @@ public class ImageEditViewModel extends AndroidViewModel {
delete(outputDir); delete(outputDir);
} }
private void delete(@NonNull final File file) { private void delete(@NonNull final DocumentFile file) {
if (file.isDirectory()) { if (file.isDirectory()) {
final File[] files = file.listFiles(); final DocumentFile[] files = file.listFiles();
if (files != null) { if (files != null) {
for (File f : files) { for (DocumentFile f : files) {
delete(f); delete(f);
} }
} }
} }
//noinspection ResultOfMethodCallIgnored
file.delete(); file.delete();
} }
@ -206,9 +208,9 @@ public class ImageEditViewModel extends AndroidViewModel {
return new SerializablePair<>(type, propertyValueMap); return new SerializablePair<>(type, propertyValueMap);
} }
public File getDestinationFile() { // public File getDestinationFile() {
return destinationFile; // return destinationFile;
} // }
public enum Tab { public enum Tab {
RESULT, RESULT,

View File

@ -8,7 +8,6 @@ import android.content.Intent;
import android.graphics.Bitmap; import android.graphics.Bitmap;
import android.graphics.BitmapFactory; import android.graphics.BitmapFactory;
import android.media.MediaMetadataRetriever; import android.media.MediaMetadataRetriever;
import android.media.MediaScannerConnection;
import android.net.Uri; import android.net.Uri;
import android.os.Build; import android.os.Build;
import android.os.Handler; import android.os.Handler;
@ -18,7 +17,7 @@ import android.util.Log;
import androidx.annotation.NonNull; import androidx.annotation.NonNull;
import androidx.core.app.NotificationCompat; import androidx.core.app.NotificationCompat;
import androidx.core.app.NotificationManagerCompat; import androidx.core.app.NotificationManagerCompat;
import androidx.core.content.FileProvider; import androidx.documentfile.provider.DocumentFile;
import androidx.work.Data; import androidx.work.Data;
import androidx.work.ForegroundInfo; import androidx.work.ForegroundInfo;
import androidx.work.Worker; import androidx.work.Worker;
@ -30,10 +29,8 @@ import com.google.gson.JsonSyntaxException;
import org.apache.commons.imaging.formats.jpeg.iptc.JpegIptcRewriter; import org.apache.commons.imaging.formats.jpeg.iptc.JpegIptcRewriter;
import java.io.BufferedInputStream; import java.io.BufferedInputStream;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.InputStream; import java.io.InputStream;
import java.io.OutputStream;
import java.net.URL; import java.net.URL;
import java.net.URLConnection; import java.net.URLConnection;
import java.util.Collection; import java.util.Collection;
@ -44,6 +41,7 @@ import java.util.Map;
import java.util.Scanner; import java.util.Scanner;
import java.util.Set; import java.util.Set;
import java.util.concurrent.ExecutionException; import java.util.concurrent.ExecutionException;
import java.util.stream.Collectors;
import awais.instagrabber.BuildConfig; import awais.instagrabber.BuildConfig;
import awais.instagrabber.R; import awais.instagrabber.R;
@ -52,10 +50,11 @@ import awais.instagrabber.utils.Constants;
import awais.instagrabber.utils.DownloadUtils; import awais.instagrabber.utils.DownloadUtils;
import awais.instagrabber.utils.TextUtils; import awais.instagrabber.utils.TextUtils;
import awais.instagrabber.utils.Utils; import awais.instagrabber.utils.Utils;
//import awaisomereport.LogCollector;
import static awais.instagrabber.utils.Constants.DOWNLOAD_CHANNEL_ID; import static awais.instagrabber.utils.Constants.DOWNLOAD_CHANNEL_ID;
import static awais.instagrabber.utils.Constants.NOTIF_GROUP_NAME; import static awais.instagrabber.utils.Constants.NOTIF_GROUP_NAME;
//import awaisomereport.LogCollector;
//import static awais.instagrabber.utils.Utils.logCollector; //import static awais.instagrabber.utils.Utils.logCollector;
public class DownloadWorker extends Worker { public class DownloadWorker extends Worker {
@ -85,8 +84,21 @@ public class DownloadWorker extends Worker {
.build()); .build());
} }
final String downloadRequestString; final String downloadRequestString;
final File requestFile = new File(downloadRequestFilePath); // final File requestFile = new File(downloadRequestFilePath);
try (Scanner scanner = new Scanner(requestFile)) { final Uri requestFile = Uri.parse(downloadRequestFilePath);
if (requestFile == null) {
return Result.failure(new Data.Builder()
.putString("error", "requestFile is null")
.build());
}
final Context context = getApplicationContext();
final ContentResolver contentResolver = context.getContentResolver();
if (contentResolver == null) {
return Result.failure(new Data.Builder()
.putString("error", "contentResolver is null")
.build());
}
try (Scanner scanner = new Scanner(contentResolver.openInputStream(requestFile))) {
downloadRequestString = scanner.useDelimiter("\\A").next(); downloadRequestString = scanner.useDelimiter("\\A").next();
} catch (Exception e) { } catch (Exception e) {
Log.e(TAG, "doWork: ", e); Log.e(TAG, "doWork: ", e);
@ -116,7 +128,7 @@ public class DownloadWorker extends Worker {
final Map<String, String> urlToFilePathMap = downloadRequest.getUrlToFilePathMap(); final Map<String, String> urlToFilePathMap = downloadRequest.getUrlToFilePathMap();
download(urlToFilePathMap); download(urlToFilePathMap);
new Handler(Looper.getMainLooper()).postDelayed(() -> showSummary(urlToFilePathMap), 500); new Handler(Looper.getMainLooper()).postDelayed(() -> showSummary(urlToFilePathMap), 500);
final boolean deleted = requestFile.delete(); final boolean deleted = DocumentFile.fromSingleUri(context, requestFile).delete();
if (!deleted) { if (!deleted) {
Log.w(TAG, "doWork: requestFile not deleted!"); Log.w(TAG, "doWork: requestFile not deleted!");
} }
@ -131,7 +143,9 @@ public class DownloadWorker extends Worker {
for (final Map.Entry<String, String> urlAndFilePath : entries) { for (final Map.Entry<String, String> urlAndFilePath : entries) {
final String url = urlAndFilePath.getKey(); final String url = urlAndFilePath.getKey();
updateDownloadProgress(notificationId, count, total, 0); updateDownloadProgress(notificationId, count, total, 0);
download(notificationId, count, total, url, urlAndFilePath.getValue()); final String uriString = urlAndFilePath.getValue();
final DocumentFile file = DocumentFile.fromSingleUri(getApplicationContext(), Uri.parse(uriString));
download(notificationId, count, total, url, file);
count++; count++;
} }
} }
@ -144,17 +158,24 @@ public class DownloadWorker extends Worker {
final int position, final int position,
final int total, final int total,
final String url, final String url,
final String filePath) { final DocumentFile filePath) {
final boolean isJpg = filePath.endsWith("jpg"); final Context context = getApplicationContext();
if (context == null) return;
final ContentResolver contentResolver = context.getContentResolver();
if (contentResolver == null) return;
final String filePathType = filePath.getType();
if (filePathType == null) return;
// final String extension = Utils.mimeTypeMap.getExtensionFromMimeType(filePathType);
final boolean isJpg = filePathType.startsWith("image"); // extension.endsWith("jpg");
// using temp file approach to remove IPTC so that download progress can be reported // using temp file approach to remove IPTC so that download progress can be reported
final File outFile = isJpg ? DownloadUtils.getTempFile() : new File(filePath); final DocumentFile outFile = isJpg ? DownloadUtils.getTempFile(null, "jpg") : filePath;
try { try {
final URLConnection urlConnection = new URL(url).openConnection(); final URLConnection urlConnection = new URL(url).openConnection();
final long fileSize = Build.VERSION.SDK_INT >= 24 ? urlConnection.getContentLengthLong() : final long fileSize = Build.VERSION.SDK_INT >= 24 ? urlConnection.getContentLengthLong() :
urlConnection.getContentLength(); urlConnection.getContentLength();
float totalRead = 0; float totalRead = 0;
try (final BufferedInputStream bis = new BufferedInputStream(urlConnection.getInputStream()); try (final BufferedInputStream bis = new BufferedInputStream(urlConnection.getInputStream());
final FileOutputStream fos = new FileOutputStream(outFile)) { final OutputStream fos = contentResolver.openOutputStream(outFile.getUri())) {
final byte[] buffer = new byte[0x2000]; final byte[] buffer = new byte[0x2000];
int count; int count;
while ((count = bis.read(buffer, 0, 0x2000)) != -1) { while ((count = bis.read(buffer, 0, 0x2000)) != -1) {
@ -167,18 +188,17 @@ public class DownloadWorker extends Worker {
} }
fos.flush(); fos.flush();
} catch (final Exception e) { } catch (final Exception e) {
Log.e(TAG, "Error while writing data from url: " + url + " to file: " + outFile.getAbsolutePath(), e); Log.e(TAG, "Error while writing data from url: " + url + " to file: " + outFile.getName(), e);
} }
if (isJpg) { if (isJpg) {
final File finalFile = new File(filePath); try (final InputStream fis = contentResolver.openInputStream(outFile.getUri());
try (FileInputStream fis = new FileInputStream(outFile); final OutputStream fos = contentResolver.openOutputStream(filePath.getUri())) {
FileOutputStream fos = new FileOutputStream(finalFile)) {
final JpegIptcRewriter jpegIptcRewriter = new JpegIptcRewriter(); final JpegIptcRewriter jpegIptcRewriter = new JpegIptcRewriter();
jpegIptcRewriter.removeIPTC(fis, fos); jpegIptcRewriter.removeIPTC(fis, fos);
} catch (Exception e) { } catch (Exception e) {
Log.e(TAG, "Error while removing iptc: url: " + url Log.e(TAG, "Error while removing iptc: url: " + url
+ ", tempFile: " + outFile.getAbsolutePath() + ", tempFile: " + outFile
+ ", finalFile: " + finalFile.getAbsolutePath(), e); + ", finalFile: " + filePath, e);
} }
final boolean deleted = outFile.delete(); final boolean deleted = outFile.delete();
if (!deleted) { if (!deleted) {
@ -243,57 +263,56 @@ public class DownloadWorker extends Worker {
private void showSummary(final Map<String, String> urlToFilePathMap) { private void showSummary(final Map<String, String> urlToFilePathMap) {
final Context context = getApplicationContext(); final Context context = getApplicationContext();
final Collection<String> filePaths = urlToFilePathMap.values(); final Collection<DocumentFile> filePaths = urlToFilePathMap.values()
.stream()
.map(s -> DocumentFile.fromSingleUri(context, Uri.parse(s)))
.collect(Collectors.toList());
final List<NotificationCompat.Builder> notifications = new LinkedList<>(); final List<NotificationCompat.Builder> notifications = new LinkedList<>();
final List<Integer> notificationIds = new LinkedList<>(); final List<Integer> notificationIds = new LinkedList<>();
int count = 1; int count = 1;
for (final String filePath : filePaths) { for (final DocumentFile filePath : filePaths) {
final File file = new File(filePath); // final File file = new File(filePath);
context.sendBroadcast(new Intent(Intent.ACTION_MEDIA_SCANNER_SCAN_FILE, Uri.fromFile(file))); context.sendBroadcast(new Intent(Intent.ACTION_MEDIA_SCANNER_SCAN_FILE, filePath.getUri()));
MediaScannerConnection.scanFile(context, new String[]{file.getAbsolutePath()}, null, null); Utils.scanDocumentFile(context, filePath, (path, uri) -> {});
final Uri uri = FileProvider.getUriForFile(context, BuildConfig.APPLICATION_ID + ".provider", file); // final Uri uri = FileProvider.getUriForFile(context, BuildConfig.APPLICATION_ID + ".provider", file);
final ContentResolver contentResolver = context.getContentResolver(); final ContentResolver contentResolver = context.getContentResolver();
Bitmap bitmap = null; Bitmap bitmap = null;
final String mimeType = Utils.getMimeType(uri, contentResolver); final String mimeType = filePath.getType(); // Utils.getMimeType(uri, contentResolver);
if (!TextUtils.isEmpty(mimeType)) { if (!TextUtils.isEmpty(mimeType)) {
if (mimeType.startsWith("image")) { if (mimeType.startsWith("image")) {
try (final InputStream inputStream = contentResolver.openInputStream(uri)) { try (final InputStream inputStream = contentResolver.openInputStream(filePath.getUri())) {
bitmap = BitmapFactory.decodeStream(inputStream); bitmap = BitmapFactory.decodeStream(inputStream);
} catch (final Exception e) { } catch (final Exception e) {
// if (logCollector != null)
// logCollector.appendException(e, LogCollector.LogFile.ASYNC_DOWNLOADER, "onPostExecute::bitmap_1");
if (BuildConfig.DEBUG) Log.e(TAG, "", e); if (BuildConfig.DEBUG) Log.e(TAG, "", e);
} }
} else if (mimeType.startsWith("video")) { } else if (mimeType.startsWith("video")) {
final MediaMetadataRetriever retriever = new MediaMetadataRetriever(); final MediaMetadataRetriever retriever = new MediaMetadataRetriever();
try { try {
try { try {
retriever.setDataSource(context, uri); retriever.setDataSource(context, filePath.getUri());
} catch (final Exception e) { } catch (final Exception e) {
retriever.setDataSource(file.getAbsolutePath()); // retriever.setDataSource(file.getAbsolutePath());
Log.e(TAG, "showSummary: ", e);
} }
bitmap = retriever.getFrameAtTime(); bitmap = retriever.getFrameAtTime();
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q) if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q)
try { try {
retriever.close(); retriever.close();
} catch (final Exception e) { } catch (final Exception e) {
// if (logCollector != null) Log.e(TAG, "showSummary: ", e);
// logCollector.appendException(e, LogCollector.LogFile.ASYNC_DOWNLOADER, "onPostExecute::bitmap_2");
} }
} catch (final Exception e) { } catch (final Exception e) {
if (BuildConfig.DEBUG) Log.e(TAG, "", e); Log.e(TAG, "", e);
// if (logCollector != null)
// logCollector.appendException(e, LogCollector.LogFile.ASYNC_DOWNLOADER, "onPostExecute::bitmap_3");
} }
} }
} }
final String downloadComplete = context.getString(R.string.downloader_complete); final String downloadComplete = context.getString(R.string.downloader_complete);
final Intent intent = new Intent(Intent.ACTION_VIEW, uri) final Intent intent = new Intent(Intent.ACTION_VIEW, filePath.getUri())
.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK .addFlags(Intent.FLAG_ACTIVITY_NEW_TASK
| Intent.FLAG_FROM_BACKGROUND | Intent.FLAG_FROM_BACKGROUND
| Intent.FLAG_GRANT_READ_URI_PERMISSION | Intent.FLAG_GRANT_READ_URI_PERMISSION
| Intent.FLAG_GRANT_WRITE_URI_PERMISSION) | Intent.FLAG_GRANT_WRITE_URI_PERMISSION)
.putExtra(Intent.EXTRA_STREAM, uri); .putExtra(Intent.EXTRA_STREAM, filePath.getUri());
final PendingIntent pendingIntent = PendingIntent.getActivity( final PendingIntent pendingIntent = PendingIntent.getActivity(
context, context,
DOWNLOAD_NOTIFICATION_INTENT_REQUEST_CODE, DOWNLOAD_NOTIFICATION_INTENT_REQUEST_CODE,
@ -357,16 +376,22 @@ public class DownloadWorker extends Worker {
public static class Builder { public static class Builder {
private Map<String, String> urlToFilePathMap; private Map<String, String> urlToFilePathMap;
public Builder setUrlToFilePathMap(final Map<String, String> urlToFilePathMap) { public Builder setUrlToFilePathMap(final Map<String, DocumentFile> urlToFilePathMap) {
this.urlToFilePathMap = urlToFilePathMap; if (urlToFilePathMap == null) {
return this;
}
this.urlToFilePathMap = urlToFilePathMap.entrySet()
.stream()
.collect(Collectors.toMap(Map.Entry::getKey,
o -> o.getValue().getUri().toString()));
return this; return this;
} }
public Builder addUrl(@NonNull final String url, @NonNull final String filePath) { public Builder addUrl(@NonNull final String url, @NonNull final DocumentFile filePath) {
if (urlToFilePathMap == null) { if (urlToFilePathMap == null) {
urlToFilePathMap = new HashMap<>(); urlToFilePathMap = new HashMap<>();
} }
urlToFilePathMap.put(url, filePath); urlToFilePathMap.put(url, filePath.getUri().toString());
return this; return this;
} }