mirror of
https://codeberg.org/Bazsalanszky/Infinity-For-Lemmy.git
synced 2024-12-30 21:07:11 +01:00
Select download location is now available. Download images and gifs to locations set by users in ViewImageOrGifActivity is available.
This commit is contained in:
parent
60eb71fe11
commit
cefc21fd1b
@ -6,7 +6,7 @@
|
||||
<uses-permission android:name="android.permission.INTERNET" />
|
||||
<uses-permission android:name="android.permission.SET_WALLPAPER" />
|
||||
<uses-permission android:name="android.permission.SET_WALLPAPER_HINTS" />
|
||||
<uses-permission android:name="android.permission.VIBRATE"/>
|
||||
<uses-permission android:name="android.permission.VIBRATE" />
|
||||
<uses-permission
|
||||
android:name="android.permission.WRITE_EXTERNAL_STORAGE"
|
||||
android:maxSdkVersion="22" />
|
||||
@ -24,7 +24,13 @@
|
||||
android:theme="@style/AppTheme"
|
||||
android:usesCleartextTraffic="true"
|
||||
tools:replace="android:label">
|
||||
<activity android:name=".Activity.ViewRedditGalleryActivity"
|
||||
<service
|
||||
android:name=".Service.DownloadImageService"
|
||||
android:enabled="true"
|
||||
android:exported="false"></service>
|
||||
|
||||
<activity
|
||||
android:name=".Activity.ViewRedditGalleryActivity"
|
||||
android:configChanges="orientation|screenSize|layoutDirection"
|
||||
android:parentActivityName=".Activity.MainActivity"
|
||||
android:theme="@style/AppTheme.Draggable" />
|
||||
|
@ -6,7 +6,7 @@ import retrofit2.http.GET;
|
||||
import retrofit2.http.Streaming;
|
||||
import retrofit2.http.Url;
|
||||
|
||||
public interface DownloadVideo {
|
||||
public interface DownloadFile {
|
||||
@Streaming
|
||||
@GET()
|
||||
Call<ResponseBody> downloadFile(@Url String fileUrl);
|
@ -1,6 +1,5 @@
|
||||
package ml.docilealligator.infinityforreddit.Activity;
|
||||
|
||||
import android.Manifest;
|
||||
import android.content.Intent;
|
||||
import android.content.SharedPreferences;
|
||||
import android.content.pm.PackageManager;
|
||||
@ -22,8 +21,6 @@ import androidx.annotation.NonNull;
|
||||
import androidx.annotation.Nullable;
|
||||
import androidx.appcompat.app.ActionBar;
|
||||
import androidx.appcompat.app.AppCompatActivity;
|
||||
import androidx.core.app.ActivityCompat;
|
||||
import androidx.core.content.ContextCompat;
|
||||
import androidx.core.content.FileProvider;
|
||||
|
||||
import com.bumptech.glide.Glide;
|
||||
@ -52,8 +49,8 @@ import javax.inject.Named;
|
||||
|
||||
import butterknife.BindView;
|
||||
import butterknife.ButterKnife;
|
||||
import ml.docilealligator.infinityforreddit.AsyncTask.SaveBitmapImageToFileAsyncTask;
|
||||
import ml.docilealligator.infinityforreddit.AsyncTask.SaveGIFToFileAsyncTask;
|
||||
import ml.docilealligator.infinityforreddit.AsyncTask.SaveImageToFileAsyncTask;
|
||||
import ml.docilealligator.infinityforreddit.BottomSheetFragment.SetAsWallpaperBottomSheetFragment;
|
||||
import ml.docilealligator.infinityforreddit.BuildConfig;
|
||||
import ml.docilealligator.infinityforreddit.Font.ContentFontFamily;
|
||||
@ -66,6 +63,7 @@ import ml.docilealligator.infinityforreddit.Infinity;
|
||||
import ml.docilealligator.infinityforreddit.MediaDownloader;
|
||||
import ml.docilealligator.infinityforreddit.MediaDownloaderImpl;
|
||||
import ml.docilealligator.infinityforreddit.R;
|
||||
import ml.docilealligator.infinityforreddit.Service.DownloadImageService;
|
||||
import ml.docilealligator.infinityforreddit.SetAsWallpaperCallback;
|
||||
import ml.docilealligator.infinityforreddit.Utils.SharedPreferencesUtils;
|
||||
import ml.docilealligator.infinityforreddit.WallpaperSetter;
|
||||
@ -279,29 +277,16 @@ public class ViewImageOrGifActivity extends AppCompatActivity implements SetAsWa
|
||||
finish();
|
||||
return true;
|
||||
case R.id.action_download_view_image_or_gif_activity:
|
||||
if (isDownloading) {
|
||||
return false;
|
||||
}
|
||||
|
||||
isDownloading = true;
|
||||
|
||||
if (Build.VERSION.SDK_INT >= 23) {
|
||||
if (ContextCompat.checkSelfPermission(this,
|
||||
Manifest.permission.WRITE_EXTERNAL_STORAGE)
|
||||
!= PackageManager.PERMISSION_GRANTED) {
|
||||
|
||||
// Permission is not granted
|
||||
// No explanation needed; request the permission
|
||||
ActivityCompat.requestPermissions(this,
|
||||
new String[]{Manifest.permission.WRITE_EXTERNAL_STORAGE},
|
||||
PERMISSION_REQUEST_WRITE_EXTERNAL_STORAGE);
|
||||
} else {
|
||||
// Permission has already been granted
|
||||
mediaDownloader.download(mImageUrl, mImageFileName, this);
|
||||
}
|
||||
Intent intent = new Intent(this, DownloadImageService.class);
|
||||
intent.putExtra(DownloadImageService.EXTRA_URL, mImageUrl);
|
||||
intent.putExtra(DownloadImageService.EXTRA_IS_GIF, isGif);
|
||||
intent.putExtra(DownloadImageService.EXTRA_FILE_NAME, mImageFileName);
|
||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
|
||||
startForegroundService(intent);
|
||||
} else {
|
||||
mediaDownloader.download(mImageUrl, mImageFileName, this);
|
||||
startService(intent);
|
||||
}
|
||||
Toast.makeText(this, R.string.download_started, Toast.LENGTH_SHORT).show();
|
||||
|
||||
return true;
|
||||
case R.id.action_share_view_image_or_gif_activity:
|
||||
@ -343,8 +328,8 @@ public class ViewImageOrGifActivity extends AppCompatActivity implements SetAsWa
|
||||
public void onResourceReady(@NonNull Bitmap resource, @Nullable Transition<? super Bitmap> transition) {
|
||||
if (getExternalCacheDir() != null) {
|
||||
Toast.makeText(ViewImageOrGifActivity.this, R.string.save_image_first, Toast.LENGTH_SHORT).show();
|
||||
new SaveImageToFileAsyncTask(resource, getExternalCacheDir().getPath(), mImageFileName,
|
||||
new SaveImageToFileAsyncTask.SaveImageToFileAsyncTaskListener() {
|
||||
new SaveBitmapImageToFileAsyncTask(resource, getExternalCacheDir().getPath(), mImageFileName,
|
||||
new SaveBitmapImageToFileAsyncTask.SaveBitmapImageToFileAsyncTaskListener() {
|
||||
@Override
|
||||
public void saveSuccess(File imageFile) {
|
||||
Uri uri = FileProvider.getUriForFile(ViewImageOrGifActivity.this,
|
||||
|
@ -13,6 +13,7 @@ import ml.docilealligator.infinityforreddit.Activity.EditCommentActivity;
|
||||
import ml.docilealligator.infinityforreddit.Activity.EditMultiRedditActivity;
|
||||
import ml.docilealligator.infinityforreddit.Activity.EditPostActivity;
|
||||
import ml.docilealligator.infinityforreddit.Activity.FilteredThingActivity;
|
||||
import ml.docilealligator.infinityforreddit.Activity.InboxActivity;
|
||||
import ml.docilealligator.infinityforreddit.Activity.LinkResolverActivity;
|
||||
import ml.docilealligator.infinityforreddit.Activity.LoginActivity;
|
||||
import ml.docilealligator.infinityforreddit.Activity.MainActivity;
|
||||
@ -34,7 +35,6 @@ import ml.docilealligator.infinityforreddit.Activity.SubscribedThingListingActiv
|
||||
import ml.docilealligator.infinityforreddit.Activity.ThemePreviewActivity;
|
||||
import ml.docilealligator.infinityforreddit.Activity.ViewImageOrGifActivity;
|
||||
import ml.docilealligator.infinityforreddit.Activity.ViewImgurMediaActivity;
|
||||
import ml.docilealligator.infinityforreddit.Activity.InboxActivity;
|
||||
import ml.docilealligator.infinityforreddit.Activity.ViewMultiRedditDetailActivity;
|
||||
import ml.docilealligator.infinityforreddit.Activity.ViewPostDetailActivity;
|
||||
import ml.docilealligator.infinityforreddit.Activity.ViewPrivateMessagesActivity;
|
||||
@ -42,8 +42,8 @@ import ml.docilealligator.infinityforreddit.Activity.ViewRedditGalleryActivity;
|
||||
import ml.docilealligator.infinityforreddit.Activity.ViewSubredditDetailActivity;
|
||||
import ml.docilealligator.infinityforreddit.Activity.ViewUserDetailActivity;
|
||||
import ml.docilealligator.infinityforreddit.Activity.ViewVideoActivity;
|
||||
import ml.docilealligator.infinityforreddit.Fragment.CommentsListingFragment;
|
||||
import ml.docilealligator.infinityforreddit.BottomSheetFragment.FlairBottomSheetFragment;
|
||||
import ml.docilealligator.infinityforreddit.Fragment.CommentsListingFragment;
|
||||
import ml.docilealligator.infinityforreddit.Fragment.FollowedUsersListingFragment;
|
||||
import ml.docilealligator.infinityforreddit.Fragment.InboxFragment;
|
||||
import ml.docilealligator.infinityforreddit.Fragment.MultiRedditListingFragment;
|
||||
@ -54,10 +54,12 @@ import ml.docilealligator.infinityforreddit.Fragment.SubscribedSubredditsListing
|
||||
import ml.docilealligator.infinityforreddit.Fragment.UserListingFragment;
|
||||
import ml.docilealligator.infinityforreddit.Fragment.ViewImgurVideoFragment;
|
||||
import ml.docilealligator.infinityforreddit.Fragment.ViewRedditGalleryVideoFragment;
|
||||
import ml.docilealligator.infinityforreddit.Service.DownloadImageService;
|
||||
import ml.docilealligator.infinityforreddit.Service.DownloadVideoService;
|
||||
import ml.docilealligator.infinityforreddit.Service.SubmitPostService;
|
||||
import ml.docilealligator.infinityforreddit.Settings.AdvancedPreferenceFragment;
|
||||
import ml.docilealligator.infinityforreddit.Settings.CustomizeMainPageTabsFragment;
|
||||
import ml.docilealligator.infinityforreddit.Settings.DownloadLocationPreferenceFragment;
|
||||
import ml.docilealligator.infinityforreddit.Settings.GesturesAndButtonsPreferenceFragment;
|
||||
import ml.docilealligator.infinityforreddit.Settings.MainPreferenceFragment;
|
||||
import ml.docilealligator.infinityforreddit.Settings.NotificationPreferenceFragment;
|
||||
@ -188,4 +190,8 @@ public interface AppComponent {
|
||||
void inject(ViewRedditGalleryVideoFragment viewRedditGalleryVideoFragment);
|
||||
|
||||
void inject(CustomizeMainPageTabsFragment customizeMainPageTabsFragment);
|
||||
|
||||
void inject(DownloadImageService downloadImageService);
|
||||
|
||||
void inject(DownloadLocationPreferenceFragment downloadLocationPreferenceFragment);
|
||||
}
|
||||
|
@ -91,7 +91,7 @@ class AppModule {
|
||||
}
|
||||
|
||||
@Provides
|
||||
@Named("download_reddit_video")
|
||||
@Named("download_media")
|
||||
@Singleton
|
||||
Retrofit provideDownloadRedditVideoRetrofit() {
|
||||
return new Retrofit.Builder()
|
||||
|
@ -8,23 +8,23 @@ import java.io.FileOutputStream;
|
||||
import java.io.IOException;
|
||||
import java.io.OutputStream;
|
||||
|
||||
public class SaveImageToFileAsyncTask extends AsyncTask<Void, Void, Void> {
|
||||
public class SaveBitmapImageToFileAsyncTask extends AsyncTask<Void, Void, Void> {
|
||||
private Bitmap resource;
|
||||
private String cacheDirPath;
|
||||
private String fileName;
|
||||
private SaveImageToFileAsyncTaskListener saveImageToFileAsyncTaskListener;
|
||||
private SaveBitmapImageToFileAsyncTaskListener saveBitmapImageToFileAsyncTaskListener;
|
||||
private boolean saveSuccess = true;
|
||||
private File imageFile;
|
||||
|
||||
public SaveImageToFileAsyncTask(Bitmap resource, String cacheDirPath, String fileName,
|
||||
SaveImageToFileAsyncTaskListener saveImageToFileAsyncTaskListener) {
|
||||
public SaveBitmapImageToFileAsyncTask(Bitmap resource, String cacheDirPath, String fileName,
|
||||
SaveBitmapImageToFileAsyncTaskListener saveBitmapImageToFileAsyncTaskListener) {
|
||||
this.resource = resource;
|
||||
this.cacheDirPath = cacheDirPath;
|
||||
this.fileName = fileName;
|
||||
this.saveImageToFileAsyncTaskListener = saveImageToFileAsyncTaskListener;
|
||||
this.saveBitmapImageToFileAsyncTaskListener = saveBitmapImageToFileAsyncTaskListener;
|
||||
}
|
||||
|
||||
public interface SaveImageToFileAsyncTaskListener {
|
||||
public interface SaveBitmapImageToFileAsyncTaskListener {
|
||||
void saveSuccess(File imageFile);
|
||||
void saveFailed();
|
||||
}
|
||||
@ -47,9 +47,9 @@ public class SaveImageToFileAsyncTask extends AsyncTask<Void, Void, Void> {
|
||||
protected void onPostExecute(Void aVoid) {
|
||||
super.onPostExecute(aVoid);
|
||||
if (saveSuccess) {
|
||||
saveImageToFileAsyncTaskListener.saveSuccess(imageFile);
|
||||
saveBitmapImageToFileAsyncTaskListener.saveSuccess(imageFile);
|
||||
} else {
|
||||
saveImageToFileAsyncTaskListener.saveFailed();
|
||||
saveBitmapImageToFileAsyncTaskListener.saveFailed();
|
||||
}
|
||||
}
|
||||
}
|
@ -0,0 +1,9 @@
|
||||
package ml.docilealligator.infinityforreddit.Event;
|
||||
|
||||
public class DownloadImageOrGifEvent {
|
||||
public boolean isSuccessful;
|
||||
|
||||
public DownloadImageOrGifEvent(boolean isSuccessful) {
|
||||
this.isSuccessful = isSuccessful;
|
||||
}
|
||||
}
|
@ -41,7 +41,7 @@ import java.io.File;
|
||||
import butterknife.BindView;
|
||||
import butterknife.ButterKnife;
|
||||
import ml.docilealligator.infinityforreddit.Activity.ViewImgurMediaActivity;
|
||||
import ml.docilealligator.infinityforreddit.AsyncTask.SaveImageToFileAsyncTask;
|
||||
import ml.docilealligator.infinityforreddit.AsyncTask.SaveBitmapImageToFileAsyncTask;
|
||||
import ml.docilealligator.infinityforreddit.BottomSheetFragment.SetAsWallpaperBottomSheetFragment;
|
||||
import ml.docilealligator.infinityforreddit.BuildConfig;
|
||||
import ml.docilealligator.infinityforreddit.ImgurMedia;
|
||||
@ -183,9 +183,9 @@ public class ViewImgurImageFragment extends Fragment {
|
||||
public void onResourceReady(@NonNull Bitmap resource, @Nullable Transition<? super Bitmap> transition) {
|
||||
if (activity.getExternalCacheDir() != null) {
|
||||
Toast.makeText(activity, R.string.save_image_first, Toast.LENGTH_SHORT).show();
|
||||
new SaveImageToFileAsyncTask(resource, activity.getExternalCacheDir().getPath(),
|
||||
new SaveBitmapImageToFileAsyncTask(resource, activity.getExternalCacheDir().getPath(),
|
||||
imgurMedia.getFileName(),
|
||||
new SaveImageToFileAsyncTask.SaveImageToFileAsyncTaskListener() {
|
||||
new SaveBitmapImageToFileAsyncTask.SaveBitmapImageToFileAsyncTaskListener() {
|
||||
@Override
|
||||
public void saveSuccess(File imageFile) {
|
||||
Uri uri = FileProvider.getUriForFile(activity,
|
||||
|
@ -47,7 +47,7 @@ import butterknife.BindView;
|
||||
import butterknife.ButterKnife;
|
||||
import ml.docilealligator.infinityforreddit.Activity.ViewRedditGalleryActivity;
|
||||
import ml.docilealligator.infinityforreddit.AsyncTask.SaveGIFToFileAsyncTask;
|
||||
import ml.docilealligator.infinityforreddit.AsyncTask.SaveImageToFileAsyncTask;
|
||||
import ml.docilealligator.infinityforreddit.AsyncTask.SaveBitmapImageToFileAsyncTask;
|
||||
import ml.docilealligator.infinityforreddit.BottomSheetFragment.SetAsWallpaperBottomSheetFragment;
|
||||
import ml.docilealligator.infinityforreddit.BuildConfig;
|
||||
import ml.docilealligator.infinityforreddit.MediaDownloader;
|
||||
@ -277,9 +277,9 @@ public class ViewRedditGalleryImageOrGifFragment extends Fragment {
|
||||
public void onResourceReady(@NonNull Bitmap resource, @Nullable Transition<? super Bitmap> transition) {
|
||||
if (activity.getExternalCacheDir() != null) {
|
||||
Toast.makeText(activity, R.string.save_image_first, Toast.LENGTH_SHORT).show();
|
||||
new SaveImageToFileAsyncTask(resource, activity.getExternalCacheDir().getPath(),
|
||||
new SaveBitmapImageToFileAsyncTask(resource, activity.getExternalCacheDir().getPath(),
|
||||
media.fileName,
|
||||
new SaveImageToFileAsyncTask.SaveImageToFileAsyncTaskListener() {
|
||||
new SaveBitmapImageToFileAsyncTask.SaveBitmapImageToFileAsyncTaskListener() {
|
||||
@Override
|
||||
public void saveSuccess(File imageFile) {
|
||||
Uri uri = FileProvider.getUriForFile(activity,
|
||||
|
@ -7,6 +7,8 @@ import android.os.Build;
|
||||
import android.os.Environment;
|
||||
import android.widget.Toast;
|
||||
|
||||
import androidx.documentfile.provider.DocumentFile;
|
||||
|
||||
import java.io.File;
|
||||
|
||||
public class MediaDownloaderImpl implements MediaDownloader {
|
||||
@ -21,7 +23,10 @@ public class MediaDownloaderImpl implements MediaDownloader {
|
||||
|
||||
//Android Q support
|
||||
if (android.os.Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q) {
|
||||
request.setDestinationInExternalPublicDir(Environment.DIRECTORY_PICTURES, fileName);
|
||||
//ctx.getContentResolver().takePersistableUriPermission(Uri.parse("content://com.android.providers.downloads.documents/tree/downloads"), Intent.FLAG_GRANT_WRITE_URI_PERMISSION);
|
||||
request.setDestinationInExternalPublicDir(DocumentFile.fromTreeUri(ctx, Uri.parse("content://com.android.providers.downloads.documents/tree/downloads")).toString(), fileName);
|
||||
//request.setDestinationInExternalPublicDir(Environment.DIRECTORY_PICTURES, fileName);
|
||||
//request.setDestinationUri(Uri.parse(Paths.get("content://com.android.providers.downloads.documents/tree/downloads", fileName).toString()));
|
||||
} else {
|
||||
String path = Environment.getExternalStoragePublicDirectory(Environment.DIRECTORY_PICTURES).toString();
|
||||
File directory = new File(path + "/Infinity/");
|
||||
|
@ -15,9 +15,15 @@ public class NotificationUtils {
|
||||
public static final String CHANNEL_DOWNLOAD_REDDIT_VIDEO = "Download Reddit Video";
|
||||
public static final String CHANNEL_ID_DOWNLOAD_VIDEO = "download_video";
|
||||
public static final String CHANNEL_DOWNLOAD_VIDEO = "Download Video";
|
||||
public static final String CHANNEL_ID_DOWNLOAD_IMAGE = "download_image";
|
||||
public static final String CHANNEL_DOWNLOAD_IMAGE = "Download Image";
|
||||
public static final String CHANNEL_ID_DOWNLOAD_GIF = "download_gif";
|
||||
public static final String CHANNEL_DOWNLOAD_GIF = "Download Gif";
|
||||
public static final int SUBMIT_POST_SERVICE_NOTIFICATION_ID = 10000;
|
||||
public static final int DOWNLOAD_REDDIT_VIDEO_NOTIFICATION_ID = 20000;
|
||||
public static final int DOWNLOAD_VIDEO_NOTIFICATION_ID = 30000;
|
||||
public static final int DOWNLOAD_IMAGE_NOTIFICATION_ID = 40000;
|
||||
public static final int DOWNLOAD_GIF_NOTIFICATION_ID = 50000;
|
||||
|
||||
private static final int SUMMARY_BASE_ID_UNREAD_MESSAGE = 0;
|
||||
private static final int NOTIFICATION_BASE_ID_UNREAD_MESSAGE = 1;
|
||||
|
@ -0,0 +1,345 @@
|
||||
package ml.docilealligator.infinityforreddit.Service;
|
||||
|
||||
import android.app.Notification;
|
||||
import android.app.NotificationChannel;
|
||||
import android.app.NotificationManager;
|
||||
import android.app.PendingIntent;
|
||||
import android.app.Service;
|
||||
import android.content.ContentResolver;
|
||||
import android.content.ContentValues;
|
||||
import android.content.Context;
|
||||
import android.content.Intent;
|
||||
import android.content.SharedPreferences;
|
||||
import android.media.MediaScannerConnection;
|
||||
import android.net.Uri;
|
||||
import android.os.AsyncTask;
|
||||
import android.os.Build;
|
||||
import android.os.Environment;
|
||||
import android.os.IBinder;
|
||||
import android.provider.MediaStore;
|
||||
|
||||
import androidx.annotation.NonNull;
|
||||
import androidx.core.app.NotificationCompat;
|
||||
import androidx.core.app.NotificationManagerCompat;
|
||||
import androidx.documentfile.provider.DocumentFile;
|
||||
|
||||
import org.greenrobot.eventbus.EventBus;
|
||||
|
||||
import java.io.File;
|
||||
import java.io.FileOutputStream;
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.io.OutputStream;
|
||||
|
||||
import javax.inject.Inject;
|
||||
import javax.inject.Named;
|
||||
|
||||
import ml.docilealligator.infinityforreddit.API.DownloadFile;
|
||||
import ml.docilealligator.infinityforreddit.CustomTheme.CustomThemeWrapper;
|
||||
import ml.docilealligator.infinityforreddit.Event.DownloadImageOrGifEvent;
|
||||
import ml.docilealligator.infinityforreddit.Infinity;
|
||||
import ml.docilealligator.infinityforreddit.NotificationUtils;
|
||||
import ml.docilealligator.infinityforreddit.R;
|
||||
import ml.docilealligator.infinityforreddit.Utils.SharedPreferencesUtils;
|
||||
import okhttp3.ResponseBody;
|
||||
import retrofit2.Call;
|
||||
import retrofit2.Callback;
|
||||
import retrofit2.Response;
|
||||
import retrofit2.Retrofit;
|
||||
|
||||
import static android.os.Environment.getExternalStoragePublicDirectory;
|
||||
|
||||
public class DownloadImageService extends Service {
|
||||
public static final String EXTRA_URL = "EU";
|
||||
public static final String EXTRA_FILE_NAME = "EFN";
|
||||
public static final String EXTRA_IS_GIF = "EIG";
|
||||
|
||||
private static final int NO_ERROR = -1;
|
||||
private static final int ERROR_CANNOT_GET_DESTINATION_DIRECTORY = 0;
|
||||
private static final int ERROR_FILE_CANNOT_DOWNLOAD = 1;
|
||||
private static final int ERROR_FILE_CANNOT_SAVE = 2;
|
||||
@Inject
|
||||
@Named("download_media")
|
||||
Retrofit retrofit;
|
||||
@Inject
|
||||
@Named("default")
|
||||
SharedPreferences mSharedPreferences;
|
||||
@Inject
|
||||
CustomThemeWrapper mCustomThemeWrapper;
|
||||
private boolean isGif;
|
||||
|
||||
public DownloadImageService() {
|
||||
}
|
||||
|
||||
@Override
|
||||
public IBinder onBind(Intent intent) {
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int onStartCommand(Intent intent, int flags, int startId) {
|
||||
((Infinity) getApplication()).getAppComponent().inject(this);
|
||||
|
||||
String fileUrl = intent.getStringExtra(EXTRA_URL);
|
||||
String fileName;
|
||||
fileName = intent.getStringExtra(EXTRA_FILE_NAME);
|
||||
isGif = intent.getBooleanExtra(EXTRA_IS_GIF, false);
|
||||
|
||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
|
||||
NotificationChannel serviceChannel;
|
||||
serviceChannel = new NotificationChannel(
|
||||
isGif ? NotificationUtils.CHANNEL_ID_DOWNLOAD_GIF : NotificationUtils.CHANNEL_ID_DOWNLOAD_IMAGE,
|
||||
isGif ? NotificationUtils.CHANNEL_DOWNLOAD_GIF : NotificationUtils.CHANNEL_DOWNLOAD_IMAGE,
|
||||
NotificationManager.IMPORTANCE_LOW
|
||||
);
|
||||
|
||||
NotificationManagerCompat manager = NotificationManagerCompat.from(this);
|
||||
manager.createNotificationChannel(serviceChannel);
|
||||
}
|
||||
|
||||
startForeground(
|
||||
isGif ? NotificationUtils.DOWNLOAD_GIF_NOTIFICATION_ID : NotificationUtils.DOWNLOAD_IMAGE_NOTIFICATION_ID,
|
||||
createNotification(isGif ? R.string.downloading_gif : R.string.downloading_image, fileName, null)
|
||||
);
|
||||
|
||||
retrofit.create(DownloadFile.class).downloadFile(fileUrl).enqueue(new Callback<ResponseBody>() {
|
||||
@Override
|
||||
public void onResponse(@NonNull Call<ResponseBody> call, @NonNull Response<ResponseBody> response) {
|
||||
if (response.isSuccessful() && response.body() != null) {
|
||||
String destinationFileDirectory = isGif ? mSharedPreferences.getString(SharedPreferencesUtils.GIF_DOWNLOAD_LOCATION, "") :
|
||||
mSharedPreferences.getString(SharedPreferencesUtils.IMAGE_DOWNLOAD_LOCATION, "");
|
||||
String destinationFileUriString;
|
||||
boolean isDefaultDestination;
|
||||
if (destinationFileDirectory.equals("")) {
|
||||
if (android.os.Build.VERSION.SDK_INT < Build.VERSION_CODES.Q) {
|
||||
File directory = getExternalStoragePublicDirectory(Environment.DIRECTORY_PICTURES);
|
||||
if (directory != null) {
|
||||
String directoryPath = directory.getAbsolutePath() + "/Infinity/";
|
||||
File infinityDir = new File(directoryPath);
|
||||
if (!infinityDir.exists() && !infinityDir.mkdir()) {
|
||||
downloadFinished(null, fileName, ERROR_CANNOT_GET_DESTINATION_DIRECTORY);
|
||||
return;
|
||||
}
|
||||
destinationFileUriString = directoryPath + fileName;
|
||||
} else {
|
||||
downloadFinished(null, fileName, ERROR_CANNOT_GET_DESTINATION_DIRECTORY);
|
||||
return;
|
||||
}
|
||||
} else {
|
||||
destinationFileUriString = Environment.DIRECTORY_PICTURES + "/Infinity/";
|
||||
}
|
||||
isDefaultDestination = true;
|
||||
} else {
|
||||
isDefaultDestination = false;
|
||||
DocumentFile picFile = DocumentFile.fromTreeUri(DownloadImageService.this, Uri.parse(destinationFileDirectory)).createFile("image/*", fileName);
|
||||
if (picFile == null) {
|
||||
downloadFinished(null, fileName, ERROR_CANNOT_GET_DESTINATION_DIRECTORY);
|
||||
return;
|
||||
}
|
||||
destinationFileUriString = picFile.getUri().toString();
|
||||
}
|
||||
|
||||
new SaveImageOrGifAndCopyToExternalStorageAsyncTask(response.body(), isGif,
|
||||
isDefaultDestination, fileName, destinationFileUriString, getContentResolver(),
|
||||
new SaveImageOrGifAndCopyToExternalStorageAsyncTask.SaveImageOrGifAndCopyToExternalStorageAsyncTaskListener() {
|
||||
@Override
|
||||
public void finished(Uri destinationFileUri, int errorCode) {
|
||||
downloadFinished(destinationFileUri, fileName, errorCode);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void updateProgressNotification(int stringResId) {
|
||||
updateNotification(stringResId, fileName, null);
|
||||
}
|
||||
}).execute();
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onFailure(@NonNull Call<ResponseBody> call, @NonNull Throwable t) {
|
||||
downloadFinished(null, fileName, ERROR_FILE_CANNOT_DOWNLOAD);
|
||||
}
|
||||
});
|
||||
return super.onStartCommand(intent, flags, startId);
|
||||
}
|
||||
|
||||
private Notification createNotification(int stringResId, String fileName, PendingIntent pendingIntent) {
|
||||
NotificationCompat.Builder builder;
|
||||
builder = new NotificationCompat.Builder(this, isGif ? NotificationUtils.CHANNEL_ID_DOWNLOAD_GIF : NotificationUtils.CHANNEL_ID_DOWNLOAD_IMAGE);
|
||||
builder.setContentTitle(fileName).setContentText(getString(stringResId));
|
||||
if (pendingIntent != null) {
|
||||
builder.setContentIntent(pendingIntent);
|
||||
}
|
||||
return builder.setSmallIcon(R.drawable.ic_notification)
|
||||
.setColor(mCustomThemeWrapper.getColorPrimaryLightTheme())
|
||||
.build();
|
||||
}
|
||||
|
||||
private void updateNotification(int stringResId, String fileName, PendingIntent pendingIntent) {
|
||||
NotificationManager notificationManager = (NotificationManager) getSystemService(Context.NOTIFICATION_SERVICE);
|
||||
if (notificationManager != null) {
|
||||
notificationManager.notify(isGif ? NotificationUtils.DOWNLOAD_GIF_NOTIFICATION_ID : NotificationUtils.DOWNLOAD_IMAGE_NOTIFICATION_ID,
|
||||
createNotification(stringResId, fileName, pendingIntent));
|
||||
}
|
||||
}
|
||||
|
||||
private void downloadFinished(Uri destinationFileUri, String fileName, int errorCode) {
|
||||
if (errorCode != NO_ERROR) {
|
||||
switch (errorCode) {
|
||||
case ERROR_CANNOT_GET_DESTINATION_DIRECTORY:
|
||||
updateNotification(R.string.downloading_image_or_gif_failed_cannot_get_destination_directory, fileName, null);
|
||||
break;
|
||||
case ERROR_FILE_CANNOT_DOWNLOAD:
|
||||
updateNotification(isGif ? R.string.downloading_gif_failed_cannot_download_gif : R.string.downloading_image_failed_cannot_download_image, fileName, null);
|
||||
break;
|
||||
case ERROR_FILE_CANNOT_SAVE:
|
||||
updateNotification(isGif ? R.string.downloading_gif_failed_cannot_save_gif : R.string.downloading_image_failed_cannot_save_image, fileName, null);
|
||||
break;
|
||||
}
|
||||
EventBus.getDefault().post(new DownloadImageOrGifEvent(false));
|
||||
} else {
|
||||
MediaScannerConnection.scanFile(
|
||||
this, new String[]{destinationFileUri.toString()}, null,
|
||||
(path, uri) -> {
|
||||
Intent intent = new Intent();
|
||||
intent.setAction(android.content.Intent.ACTION_VIEW);
|
||||
intent.setDataAndType(destinationFileUri, "image/*");
|
||||
PendingIntent pendingIntent = PendingIntent.getActivity(this, 0, intent, PendingIntent.FLAG_CANCEL_CURRENT);
|
||||
updateNotification(isGif ? R.string.downloading_gif_finished : R.string.downloading_image_finished, fileName, pendingIntent);
|
||||
EventBus.getDefault().post(new DownloadImageOrGifEvent(true));
|
||||
}
|
||||
);
|
||||
}
|
||||
stopForeground(false);
|
||||
}
|
||||
|
||||
private static class SaveImageOrGifAndCopyToExternalStorageAsyncTask extends AsyncTask<Void, Integer, Void> {
|
||||
|
||||
private ResponseBody response;
|
||||
private boolean isGif;
|
||||
private boolean isDefaultDestination;
|
||||
private String destinationFileName;
|
||||
@NonNull
|
||||
private String destinationFileUriString;
|
||||
private ContentResolver contentResolver;
|
||||
private SaveImageOrGifAndCopyToExternalStorageAsyncTaskListener saveImageOrGifAndCopyToExternalStorageAsyncTaskListener;
|
||||
private int errorCode = NO_ERROR;
|
||||
|
||||
interface SaveImageOrGifAndCopyToExternalStorageAsyncTaskListener {
|
||||
void finished(Uri destinationFileUri, int errorCode);
|
||||
void updateProgressNotification(int stringResId);
|
||||
}
|
||||
|
||||
public SaveImageOrGifAndCopyToExternalStorageAsyncTask(ResponseBody response, boolean isGif,
|
||||
boolean isDefaultDestination,
|
||||
String destinationFileName,
|
||||
@NonNull String destinationFileUriString,
|
||||
ContentResolver contentResolver,
|
||||
SaveImageOrGifAndCopyToExternalStorageAsyncTaskListener saveImageOrGifAndCopyToExternalStorageAsyncTaskListener) {
|
||||
this.response = response;
|
||||
this.isGif = isGif;
|
||||
this.isDefaultDestination = isDefaultDestination;
|
||||
this.destinationFileName = destinationFileName;
|
||||
this.destinationFileUriString = destinationFileUriString;
|
||||
this.contentResolver = contentResolver;
|
||||
this.saveImageOrGifAndCopyToExternalStorageAsyncTaskListener = saveImageOrGifAndCopyToExternalStorageAsyncTaskListener;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onProgressUpdate(Integer... values) {
|
||||
super.onProgressUpdate(values);
|
||||
saveImageOrGifAndCopyToExternalStorageAsyncTaskListener.updateProgressNotification(values[0]);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected Void doInBackground(Void... voids) {
|
||||
publishProgress(isGif ? R.string.downloading_gif_save_gif : R.string.downloading_image_save_image);
|
||||
try {
|
||||
writeResponseBodyToDisk(response);
|
||||
} catch (IOException e) {
|
||||
errorCode = ERROR_FILE_CANNOT_SAVE;
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onPostExecute(Void aVoid) {
|
||||
super.onPostExecute(aVoid);
|
||||
saveImageOrGifAndCopyToExternalStorageAsyncTaskListener.finished(Uri.parse(destinationFileUriString), errorCode);
|
||||
}
|
||||
|
||||
private void writeResponseBodyToDisk(ResponseBody body) throws IOException {
|
||||
if (isDefaultDestination) {
|
||||
if (android.os.Build.VERSION.SDK_INT < Build.VERSION_CODES.Q) {
|
||||
InputStream inputStream = body.byteStream();
|
||||
OutputStream outputStream = new FileOutputStream(destinationFileUriString);
|
||||
byte[] fileReader = new byte[4096];
|
||||
|
||||
long fileSize = body.contentLength();
|
||||
long fileSizeDownloaded = 0;
|
||||
|
||||
while (true) {
|
||||
int read = inputStream.read(fileReader);
|
||||
|
||||
if (read == -1) {
|
||||
break;
|
||||
}
|
||||
|
||||
outputStream.write(fileReader, 0, read);
|
||||
|
||||
fileSizeDownloaded += read;
|
||||
}
|
||||
|
||||
outputStream.flush();
|
||||
} else {
|
||||
String relativeLocation = Environment.DIRECTORY_PICTURES + "/Infinity/";
|
||||
|
||||
ContentValues contentValues = new ContentValues();
|
||||
contentValues.put(MediaStore.MediaColumns.DISPLAY_NAME, destinationFileName);
|
||||
contentValues.put(MediaStore.MediaColumns.MIME_TYPE, "image/*");
|
||||
contentValues.put(MediaStore.MediaColumns.RELATIVE_PATH, relativeLocation);
|
||||
contentValues.put(MediaStore.Images.Media.IS_PENDING, 1);
|
||||
|
||||
final Uri contentUri = MediaStore.Images.Media.EXTERNAL_CONTENT_URI;
|
||||
Uri uri = contentResolver.insert(contentUri, contentValues);
|
||||
|
||||
if (uri == null) {
|
||||
throw new IOException("Failed to create new MediaStore record.");
|
||||
}
|
||||
|
||||
OutputStream stream = contentResolver.openOutputStream(uri);
|
||||
|
||||
if (stream == null) {
|
||||
throw new IOException("Failed to get output stream.");
|
||||
}
|
||||
|
||||
InputStream in = body.byteStream();
|
||||
byte[] buf = new byte[1024];
|
||||
int len;
|
||||
while ((len = in.read(buf)) > 0) {
|
||||
stream.write(buf, 0, len);
|
||||
}
|
||||
contentValues.clear();
|
||||
contentValues.put(MediaStore.Images.Media.IS_PENDING, 0);
|
||||
contentResolver.update(uri, contentValues, null, null);
|
||||
destinationFileUriString = uri.toString();
|
||||
}
|
||||
} else {
|
||||
try (OutputStream stream = contentResolver.openOutputStream(Uri.parse(destinationFileUriString))) {
|
||||
if (stream == null) {
|
||||
throw new IOException("Failed to get output stream.");
|
||||
}
|
||||
|
||||
InputStream in = body.byteStream();
|
||||
|
||||
byte[] buf = new byte[1024];
|
||||
int len;
|
||||
while ((len = in.read(buf)) > 0) {
|
||||
stream.write(buf, 0, len);
|
||||
}
|
||||
destinationFileUriString = destinationFileUriString.replaceAll("%3A", ":").replaceAll("%2F", "/");
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
@ -39,7 +39,7 @@ import java.nio.ByteBuffer;
|
||||
import javax.inject.Inject;
|
||||
import javax.inject.Named;
|
||||
|
||||
import ml.docilealligator.infinityforreddit.API.DownloadVideo;
|
||||
import ml.docilealligator.infinityforreddit.API.DownloadFile;
|
||||
import ml.docilealligator.infinityforreddit.CustomTheme.CustomThemeWrapper;
|
||||
import ml.docilealligator.infinityforreddit.Event.DownloadRedditVideoEvent;
|
||||
import ml.docilealligator.infinityforreddit.Infinity;
|
||||
@ -72,7 +72,7 @@ public class DownloadVideoService extends Service {
|
||||
private boolean isRedditVideo;
|
||||
|
||||
@Inject
|
||||
@Named("download_reddit_video")
|
||||
@Named("download_media")
|
||||
Retrofit retrofit;
|
||||
@Inject
|
||||
CustomThemeWrapper mCustomThemeWrapper;
|
||||
@ -132,19 +132,19 @@ public class DownloadVideoService extends Service {
|
||||
);
|
||||
}
|
||||
|
||||
DownloadVideo downloadVideo = retrofit.create(DownloadVideo.class);
|
||||
DownloadFile downloadFile = retrofit.create(DownloadFile.class);
|
||||
|
||||
File directory = getExternalCacheDir();
|
||||
String destinationFileName = fileName + ".mp4";
|
||||
if (directory != null) {
|
||||
String directoryPath = directory.getAbsolutePath() + "/";
|
||||
downloadVideo.downloadFile(videoUrl).enqueue(new Callback<ResponseBody>() {
|
||||
downloadFile.downloadFile(videoUrl).enqueue(new Callback<ResponseBody>() {
|
||||
@Override
|
||||
public void onResponse(@NonNull Call<ResponseBody> call, @NonNull Response<ResponseBody> videoResponse) {
|
||||
if (videoResponse.isSuccessful() && videoResponse.body() != null) {
|
||||
if (isRedditVideo) {
|
||||
updateNotification(R.string.downloading_reddit_video_audio_track, destinationFileName, null);
|
||||
downloadVideo.downloadFile(audioUrl).enqueue(new Callback<ResponseBody>() {
|
||||
downloadFile.downloadFile(audioUrl).enqueue(new Callback<ResponseBody>() {
|
||||
@Override
|
||||
public void onResponse(@NonNull Call<ResponseBody> call, @NonNull Response<ResponseBody> audioResponse) {
|
||||
if (audioResponse.isSuccessful() && audioResponse.body() != null) {
|
||||
@ -521,10 +521,12 @@ public class DownloadVideoService extends Service {
|
||||
|
||||
@RequiresApi(api = Build.VERSION_CODES.Q)
|
||||
private void copyFileQ(File src, String outputFileName) throws IOException {
|
||||
|
||||
String relativeLocation = Environment.DIRECTORY_MOVIES + "/Infinity/";
|
||||
|
||||
ContentValues contentValues = new ContentValues();
|
||||
contentValues.put(MediaStore.MediaColumns.DISPLAY_NAME, outputFileName);
|
||||
contentValues.put(MediaStore.MediaColumns.MIME_TYPE, "video/*");
|
||||
contentValues.put(MediaStore.MediaColumns.RELATIVE_PATH, relativeLocation);
|
||||
contentValues.put(MediaStore.Video.Media.IS_PENDING, 1);
|
||||
|
||||
|
@ -0,0 +1,115 @@
|
||||
package ml.docilealligator.infinityforreddit.Settings;
|
||||
|
||||
import android.app.Activity;
|
||||
import android.content.Context;
|
||||
import android.content.Intent;
|
||||
import android.content.SharedPreferences;
|
||||
import android.os.Bundle;
|
||||
|
||||
import androidx.annotation.NonNull;
|
||||
import androidx.annotation.Nullable;
|
||||
import androidx.preference.Preference;
|
||||
import androidx.preference.PreferenceFragmentCompat;
|
||||
|
||||
import javax.inject.Inject;
|
||||
import javax.inject.Named;
|
||||
|
||||
import ml.docilealligator.infinityforreddit.Infinity;
|
||||
import ml.docilealligator.infinityforreddit.R;
|
||||
import ml.docilealligator.infinityforreddit.Utils.SharedPreferencesUtils;
|
||||
|
||||
import static android.content.Intent.ACTION_OPEN_DOCUMENT_TREE;
|
||||
|
||||
public class DownloadLocationPreferenceFragment extends PreferenceFragmentCompat {
|
||||
private static final int IMAGE_DOWNLOAD_LOCATION_REQUEST_CODE = 10;
|
||||
private static final int GIF_DOWNLOAD_LOCATION_REQUEST_CODE = 11;
|
||||
private static final int VIDEO_DOWNLOAD_LOCATION_REQUEST_CODE = 12;
|
||||
|
||||
Preference imageDownloadLocationPreference;
|
||||
Preference gifDownloadLocationPreference;
|
||||
Preference videoDownloadLocationPreference;
|
||||
private Activity activity;
|
||||
@Inject
|
||||
@Named("default")
|
||||
SharedPreferences sharedPreferences;
|
||||
|
||||
@Override
|
||||
public void onCreatePreferences(Bundle savedInstanceState, String rootKey) {
|
||||
((Infinity) activity.getApplication()).getAppComponent().inject(this);
|
||||
setPreferencesFromResource(R.xml.download_location_preferences, rootKey);
|
||||
imageDownloadLocationPreference = findPreference(SharedPreferencesUtils.IMAGE_DOWNLOAD_LOCATION);
|
||||
gifDownloadLocationPreference = findPreference(SharedPreferencesUtils.GIF_DOWNLOAD_LOCATION);
|
||||
videoDownloadLocationPreference = findPreference(SharedPreferencesUtils.VIDEO_DOWNLOAD_LOCATION);
|
||||
|
||||
if (imageDownloadLocationPreference != null) {
|
||||
String downloadLocation = sharedPreferences.getString(SharedPreferencesUtils.IMAGE_DOWNLOAD_LOCATION, "");
|
||||
if (!downloadLocation.equals("")) {
|
||||
imageDownloadLocationPreference.setSummary(downloadLocation);
|
||||
}
|
||||
|
||||
imageDownloadLocationPreference.setOnPreferenceClickListener(preference -> {
|
||||
Intent intent = new Intent(ACTION_OPEN_DOCUMENT_TREE);
|
||||
intent.addFlags(Intent.FLAG_GRANT_WRITE_URI_PERMISSION);
|
||||
startActivityForResult(intent, IMAGE_DOWNLOAD_LOCATION_REQUEST_CODE);
|
||||
return true;
|
||||
});
|
||||
}
|
||||
|
||||
if (gifDownloadLocationPreference != null) {
|
||||
String downloadLocation = sharedPreferences.getString(SharedPreferencesUtils.GIF_DOWNLOAD_LOCATION, "");
|
||||
if (!downloadLocation.equals("")) {
|
||||
gifDownloadLocationPreference.setSummary(downloadLocation);
|
||||
}
|
||||
|
||||
gifDownloadLocationPreference.setOnPreferenceClickListener(preference -> {
|
||||
Intent intent = new Intent(ACTION_OPEN_DOCUMENT_TREE);
|
||||
intent.addFlags(Intent.FLAG_GRANT_WRITE_URI_PERMISSION);
|
||||
startActivityForResult(intent, GIF_DOWNLOAD_LOCATION_REQUEST_CODE);
|
||||
return true;
|
||||
});
|
||||
}
|
||||
|
||||
if (videoDownloadLocationPreference != null) {
|
||||
String downloadLocation = sharedPreferences.getString(SharedPreferencesUtils.VIDEO_DOWNLOAD_LOCATION, "");
|
||||
if (!downloadLocation.equals("")) {
|
||||
videoDownloadLocationPreference.setSummary(downloadLocation);
|
||||
}
|
||||
|
||||
videoDownloadLocationPreference.setOnPreferenceClickListener(preference -> {
|
||||
Intent intent = new Intent(ACTION_OPEN_DOCUMENT_TREE);
|
||||
intent.addFlags(Intent.FLAG_GRANT_WRITE_URI_PERMISSION);
|
||||
startActivityForResult(intent, VIDEO_DOWNLOAD_LOCATION_REQUEST_CODE);
|
||||
return true;
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onActivityResult(int requestCode, int resultCode, @Nullable Intent data) {
|
||||
super.onActivityResult(requestCode, resultCode, data);
|
||||
if (resultCode == Activity.RESULT_OK && data != null) {
|
||||
if (requestCode == IMAGE_DOWNLOAD_LOCATION_REQUEST_CODE) {
|
||||
sharedPreferences.edit().putString(SharedPreferencesUtils.IMAGE_DOWNLOAD_LOCATION, data.getDataString()).apply();
|
||||
if (imageDownloadLocationPreference != null) {
|
||||
imageDownloadLocationPreference.setSummary(data.getDataString());
|
||||
}
|
||||
} else if (requestCode == GIF_DOWNLOAD_LOCATION_REQUEST_CODE) {
|
||||
sharedPreferences.edit().putString(SharedPreferencesUtils.GIF_DOWNLOAD_LOCATION, data.getDataString()).apply();
|
||||
if (gifDownloadLocationPreference != null) {
|
||||
gifDownloadLocationPreference.setSummary(data.getDataString());
|
||||
}
|
||||
} else if (requestCode == VIDEO_DOWNLOAD_LOCATION_REQUEST_CODE) {
|
||||
sharedPreferences.edit().putString(SharedPreferencesUtils.VIDEO_DOWNLOAD_LOCATION, data.getDataString()).apply();
|
||||
if (videoDownloadLocationPreference != null) {
|
||||
videoDownloadLocationPreference.setSummary(data.getDataString());
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onAttach(@NonNull Context context) {
|
||||
super.onAttach(context);
|
||||
activity = (Activity) context;
|
||||
}
|
||||
}
|
@ -0,0 +1,238 @@
|
||||
package ml.docilealligator.infinityforreddit.Utils;
|
||||
|
||||
import android.annotation.TargetApi;
|
||||
import android.content.ContentUris;
|
||||
import android.content.Context;
|
||||
import android.database.Cursor;
|
||||
import android.net.Uri;
|
||||
import android.os.Build;
|
||||
import android.os.Environment;
|
||||
import android.provider.DocumentsContract;
|
||||
import android.provider.MediaStore;
|
||||
|
||||
import androidx.core.content.ContextCompat;
|
||||
|
||||
import java.io.File;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
public class GetPathUtils {
|
||||
|
||||
private static final String PATH_TREE = "tree";
|
||||
private static final String PRIMARY_TYPE = "primary";
|
||||
private static final String RAW_TYPE = "raw";
|
||||
|
||||
@TargetApi(Build.VERSION_CODES.KITKAT)
|
||||
public static String getFilePathFromUri(final Context context, final Uri uri) {
|
||||
|
||||
// DocumentProvider
|
||||
if (DocumentsContract.isDocumentUri(context, uri)) {
|
||||
// ExternalStorageProvider
|
||||
if (isExternalStorageDocument(uri)) {
|
||||
final String docId = DocumentsContract.getDocumentId(uri);
|
||||
//Timber.d("docId -> %s", docId);
|
||||
final String[] split = docId.split(":");
|
||||
final String type = split[0];
|
||||
|
||||
if (PRIMARY_TYPE.equalsIgnoreCase(type)) {
|
||||
return Environment.getExternalStorageDirectory() + "/" + split[1];
|
||||
} else {
|
||||
// TODO handle non-primary volumes
|
||||
StringBuilder path = new StringBuilder();
|
||||
String pathSegment[] = docId.split(":");
|
||||
return path.append(getRemovableStorageRootPath(context, pathSegment[0])).append(File.separator).append(pathSegment[1]).toString();
|
||||
}
|
||||
} else if (isDownloadsDocument(uri)) { // DownloadsProvider
|
||||
|
||||
final String id = DocumentsContract.getDocumentId(uri);
|
||||
|
||||
if (id.contains("raw:")) {
|
||||
return id.substring(id.indexOf(File.separator));
|
||||
} else {
|
||||
Uri contentUri = ContentUris.withAppendedId(
|
||||
Uri.parse("content://downloads/public_downloads"), Long.valueOf(id));
|
||||
|
||||
return getDataColumn(context, contentUri, null, null);
|
||||
}
|
||||
|
||||
} else if (isMediaDocument(uri)) { // MediaProvider
|
||||
final String docId = DocumentsContract.getDocumentId(uri);
|
||||
final String[] split = docId.split(":");
|
||||
final String type = split[0];
|
||||
|
||||
Uri contentUri = null;
|
||||
if ("image".equals(type)) {
|
||||
contentUri = MediaStore.Images.Media.EXTERNAL_CONTENT_URI;
|
||||
} else if ("video".equals(type)) {
|
||||
contentUri = MediaStore.Video.Media.EXTERNAL_CONTENT_URI;
|
||||
} else if ("audio".equals(type)) {
|
||||
contentUri = MediaStore.Audio.Media.EXTERNAL_CONTENT_URI;
|
||||
}
|
||||
|
||||
final String selection = "_id=?";
|
||||
final String[] selectionArgs = new String[]{
|
||||
split[1]
|
||||
};
|
||||
|
||||
return getDataColumn(context, contentUri, selection, selectionArgs);
|
||||
}
|
||||
} else if ("content".equalsIgnoreCase(uri.getScheme())) { // MediaStore (and general)
|
||||
return getDataColumn(context, uri, null, null);
|
||||
} else if ("file".equalsIgnoreCase(uri.getScheme())) { // File
|
||||
return uri.getPath();
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the value of the data column for this Uri. This is useful for
|
||||
* MediaStore Uris, and other file-based ContentProviders.
|
||||
*
|
||||
* @param context The context.
|
||||
* @param uri The Uri to query.
|
||||
* @param selection (Optional) Filter used in the query.
|
||||
* @param selectionArgs (Optional) Selection arguments used in the query.
|
||||
* @return The value of the _data column, which is typically a file path.
|
||||
*/
|
||||
public static String getDataColumn(Context context, Uri uri, String selection, String[] selectionArgs) {
|
||||
|
||||
Cursor cursor = null;
|
||||
final String column = MediaStore.MediaColumns.DATA;
|
||||
final String[] projection = {column};
|
||||
try {
|
||||
cursor = context.getContentResolver().query(uri, projection, selection, selectionArgs,
|
||||
null);
|
||||
if (cursor != null && cursor.moveToFirst()) {
|
||||
final int column_index = cursor.getColumnIndexOrThrow(column);
|
||||
return cursor.getString(column_index);
|
||||
}
|
||||
} finally {
|
||||
if (cursor != null)
|
||||
cursor.close();
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param uri The Uri to check.
|
||||
* @return Whether the Uri authority is ExternalStorageProvider.
|
||||
*/
|
||||
public static boolean isExternalStorageDocument(Uri uri) {
|
||||
return "com.android.externalstorage.documents".equals(uri.getAuthority());
|
||||
}
|
||||
|
||||
/**
|
||||
* @param uri The Uri to check.
|
||||
* @return Whether the Uri authority is DownloadsProvider.
|
||||
*/
|
||||
public static boolean isDownloadsDocument(Uri uri) {
|
||||
return "com.android.providers.downloads.documents".equals(uri.getAuthority());
|
||||
}
|
||||
|
||||
/**
|
||||
* @param uri The Uri to check.
|
||||
* @return Whether the Uri authority is MediaProvider.
|
||||
*/
|
||||
public static boolean isMediaDocument(Uri uri) {
|
||||
return "com.android.providers.media.documents".equals(uri.getAuthority());
|
||||
}
|
||||
|
||||
/**
|
||||
* @param uri
|
||||
* @return file path of Uri
|
||||
*/
|
||||
public static String getDirectoryPathFromUri(Context context, Uri uri) {
|
||||
|
||||
if ("file".equals(uri.getScheme())) {
|
||||
return uri.getPath();
|
||||
}
|
||||
|
||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT
|
||||
&& isTreeUri(uri)) {
|
||||
|
||||
String treeId = getTreeDocumentId(uri);
|
||||
|
||||
if (treeId != null) {
|
||||
String[] paths = treeId.split(":");
|
||||
String type = paths[0];
|
||||
String subPath = paths.length == 2 ? paths[1] : "";
|
||||
|
||||
if (RAW_TYPE.equalsIgnoreCase(type)) {
|
||||
return treeId.substring(treeId.indexOf(File.separator));
|
||||
} else if (PRIMARY_TYPE.equalsIgnoreCase(type)) {
|
||||
return Environment.getExternalStorageDirectory() + File.separator + subPath;
|
||||
} else {
|
||||
StringBuilder path = new StringBuilder();
|
||||
String[] pathSegment = treeId.split(":");
|
||||
if (pathSegment.length == 1) {
|
||||
path.append(getRemovableStorageRootPath(context, paths[0]));
|
||||
} else {
|
||||
String rootPath = getRemovableStorageRootPath(context, paths[0]);
|
||||
path.append(rootPath).append(File.separator).append(pathSegment[1]);
|
||||
}
|
||||
|
||||
return path.toString();
|
||||
}
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
private static String getRemovableStorageRootPath(Context context, String storageId) {
|
||||
StringBuilder rootPath = new StringBuilder();
|
||||
File[] externalFilesDirs = ContextCompat.getExternalFilesDirs(context, null);
|
||||
for (File fileDir : externalFilesDirs) {
|
||||
if (fileDir.getPath().contains(storageId)) {
|
||||
String[] pathSegment = fileDir.getPath().split(File.separator);
|
||||
for (String segment : pathSegment) {
|
||||
if (segment.equals(storageId)) {
|
||||
rootPath.append(storageId);
|
||||
break;
|
||||
}
|
||||
rootPath.append(segment).append(File.separator);
|
||||
}
|
||||
//rootPath.append(fileDir.getPath().split("/Android")[0]); // faster
|
||||
break;
|
||||
}
|
||||
}
|
||||
return rootPath.toString();
|
||||
}
|
||||
|
||||
@TargetApi(Build.VERSION_CODES.LOLLIPOP)
|
||||
public static List<String> getListRemovableStorage(Context context) {
|
||||
|
||||
List<String> paths = new ArrayList<>();
|
||||
|
||||
File[] externalFilesDirs = ContextCompat.getExternalFilesDirs(context, null);
|
||||
for (File fileDir : externalFilesDirs) {
|
||||
if (Environment.isExternalStorageRemovable(fileDir)) {
|
||||
String path = fileDir.getPath();
|
||||
if (path.contains("/Android")) {
|
||||
paths.add(path.substring(0, path.indexOf("/Android")));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return paths;
|
||||
}
|
||||
|
||||
//https://github.com/rcketscientist/DocumentActivity/blob/master/library/src/main/java/com/anthonymandra/framework/DocumentUtil.java#L56
|
||||
/**
|
||||
* Extract the via {@link DocumentsContract.Document#COLUMN_DOCUMENT_ID} from the given URI.
|
||||
* From {@link DocumentsContract} but return null instead of throw
|
||||
*/
|
||||
public static String getTreeDocumentId(Uri uri) {
|
||||
final List<String> paths = uri.getPathSegments();
|
||||
if (paths.size() >= 2 && PATH_TREE.equals(paths.get(0))) {
|
||||
return paths.get(1);
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
public static boolean isTreeUri(Uri uri) {
|
||||
final List<String> paths = uri.getPathSegments();
|
||||
return (paths.size() == 2 && PATH_TREE.equals(paths.get(0)));
|
||||
}
|
||||
}
|
@ -117,6 +117,9 @@ public class SharedPreferencesUtils {
|
||||
public static final String DELETE_FRONT_PAGE_SCROLLED_POSITIONS_IN_DATABASE = "delete_front_page_scrolled_positions_in_database";
|
||||
public static final String DELETE_ALL_LEGACY_SETTINGS = "delete_all_legacy_settings";
|
||||
public static final String RESET_ALL_SETTINGS = "reset_all_settings";
|
||||
public static final String IMAGE_DOWNLOAD_LOCATION = "image_download_location";
|
||||
public static final String GIF_DOWNLOAD_LOCATION = "gif_download_location";
|
||||
public static final String VIDEO_DOWNLOAD_LOCATION = "video_download_location";
|
||||
|
||||
public static final String MAIN_PAGE_TABS_SHARED_PREFERENCES_FILE = "ml.docilealligator.infinityforreddit.main_page_tabs";
|
||||
public static final String MAIN_PAGE_TAB_1_TITLE = "_main_page_tab_1_title";
|
||||
|
9
app/src/main/res/drawable-night/ic_download_24dp.xml
Normal file
9
app/src/main/res/drawable-night/ic_download_24dp.xml
Normal file
@ -0,0 +1,9 @@
|
||||
<vector xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:width="24dp"
|
||||
android:height="24dp"
|
||||
android:viewportWidth="24"
|
||||
android:viewportHeight="24">
|
||||
<path
|
||||
android:pathData="M13,5v6h1.17L12,13.17 9.83,11L11,11L11,5h2m2,-2L9,3v6L5,9l7,7 7,-7h-4L15,3zM19,18L5,18v2h14v-2z"
|
||||
android:fillColor="#FFFFFF"/>
|
||||
</vector>
|
9
app/src/main/res/drawable/ic_download_24dp.xml
Normal file
9
app/src/main/res/drawable/ic_download_24dp.xml
Normal file
@ -0,0 +1,9 @@
|
||||
<vector xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:width="24dp"
|
||||
android:height="24dp"
|
||||
android:viewportWidth="24"
|
||||
android:viewportHeight="24">
|
||||
<path
|
||||
android:pathData="M13,5v6h1.17L12,13.17 9.83,11L11,11L11,5h2m2,-2L9,3v6L5,9l7,7 7,-7h-4L15,3zM19,18L5,18v2h14v-2z"
|
||||
android:fillColor="#000000"/>
|
||||
</vector>
|
@ -160,7 +160,7 @@
|
||||
<item>ManropeBold</item>
|
||||
<item>Sriracha</item>
|
||||
</string-array>
|
||||
|
||||
|
||||
<string-array name="settings_tab_post_type">
|
||||
<item>Home</item>
|
||||
<item>Popular</item>
|
||||
@ -228,9 +228,19 @@
|
||||
<item>yyyy.M.d HH:mm</item>
|
||||
<item>yyyy.M.d hh:mm a</item>
|
||||
</string-array>
|
||||
|
||||
|
||||
<string-array name="settings_number_of_columns_in_post_feed">
|
||||
<item>1</item>
|
||||
<item>2</item>
|
||||
</string-array>
|
||||
<!-- Reply Preference -->
|
||||
<string-array name="reply_entries">
|
||||
<item>Reply</item>
|
||||
<item>Reply to all</item>
|
||||
</string-array>
|
||||
|
||||
<string-array name="reply_values">
|
||||
<item>reply</item>
|
||||
<item>reply_all</item>
|
||||
</string-array>
|
||||
</resources>
|
@ -471,6 +471,10 @@
|
||||
<string name="settings_tab_multi_reddit_name">MultiReddit Name (/user/yourusername/m/yourmultiredditname)</string>
|
||||
<string name="settings_tab_username">Username (Without u/ prefix)</string>
|
||||
<string name="no_developer_easter_egg">There\'s no developer options here</string>
|
||||
<string name="settings_download_location_title">Download Location</string>
|
||||
<string name="settings_image_download_location_title">Image Download Location</string>
|
||||
<string name="settings_gif_download_location_title">Gif Download Location</string>
|
||||
<string name="settings_video_download_location_title">Video Download Location</string>
|
||||
|
||||
<string name="no_link_available">Cannot get the link</string>
|
||||
|
||||
@ -840,6 +844,18 @@
|
||||
<string name="downloading_reddit_video_failed_cannot_save_mux_video">Download failed: cannot save the video to public directory</string>
|
||||
<string name="downloading_video">Downloading Video</string>
|
||||
|
||||
<string name="downloading_image">Downloading Image</string>
|
||||
<string name="downloading_gif">Downloading Gif</string>
|
||||
<string name="downloading_image_save_image">Saving Image</string>
|
||||
<string name="downloading_gif_save_gif">Saving Gif</string>
|
||||
<string name="downloading_image_finished">Downloaded</string>
|
||||
<string name="downloading_gif_finished">Downloaded</string>
|
||||
<string name="downloading_image_or_gif_failed_cannot_get_destination_directory">Download failed: cannot access destination directory</string>
|
||||
<string name="downloading_image_failed_cannot_download_image">Download image failed</string>
|
||||
<string name="downloading_image_failed_cannot_save_image">Download failed: cannot save image to destination directory</string>
|
||||
<string name="downloading_gif_failed_cannot_download_gif">Download gif failed</string>
|
||||
<string name="downloading_gif_failed_cannot_save_gif">Download failed: cannot save gif to destination directory</string>
|
||||
|
||||
<string name="wallpaper_set">Wallpaper set</string>
|
||||
<string name="error_set_wallpaper">Cannot set wallpaper</string>
|
||||
|
||||
@ -858,5 +874,19 @@
|
||||
<string name="block_user">Block User</string>
|
||||
<string name="block_user_success">Blocked</string>
|
||||
<string name="block_user_failed">Failed to block user</string>
|
||||
<!-- Preference Titles -->
|
||||
<string name="messages_header">Messages</string>
|
||||
<string name="sync_header">Sync</string>
|
||||
|
||||
<!-- Messages Preferences -->
|
||||
<string name="signature_title">Your signature</string>
|
||||
<string name="reply_title">Default reply action</string>
|
||||
|
||||
<!-- Sync Preferences -->
|
||||
<string name="sync_title">Sync email periodically</string>
|
||||
<string name="attachment_title">Download incoming attachments</string>
|
||||
<string name="attachment_summary_on">Automatically download attachments for incoming emails
|
||||
</string>
|
||||
<string name="attachment_summary_off">Only download attachments when manually requested</string>
|
||||
|
||||
</resources>
|
||||
|
13
app/src/main/res/xml/download_location_preferences.xml
Normal file
13
app/src/main/res/xml/download_location_preferences.xml
Normal file
@ -0,0 +1,13 @@
|
||||
<PreferenceScreen xmlns:app="http://schemas.android.com/apk/res-auto">
|
||||
<Preference
|
||||
app:key="image_download_location"
|
||||
app:title="@string/settings_image_download_location_title" />
|
||||
|
||||
<Preference
|
||||
app:key="gif_download_location"
|
||||
app:title="@string/settings_gif_download_location_title" />
|
||||
|
||||
<Preference
|
||||
app:key="video_download_location"
|
||||
app:title="@string/settings_video_download_location_title" />
|
||||
</PreferenceScreen>
|
@ -36,6 +36,11 @@
|
||||
app:title="@string/settings_lazy_mode_interval_title"
|
||||
app:useSimpleSummaryProvider="true" />
|
||||
|
||||
<Preference
|
||||
app:title="@string/settings_download_location_title"
|
||||
android:icon="@drawable/ic_download_24dp"
|
||||
app:fragment="ml.docilealligator.infinityforreddit.Settings.DownloadLocationPreferenceFragment" />
|
||||
|
||||
<SwitchPreference
|
||||
app:defaultValue="false"
|
||||
app:key="save_front_page_scrolled_position"
|
||||
|
Loading…
Reference in New Issue
Block a user