diff --git a/res/xml/file_provider_paths.xml b/res/xml/file_provider_paths.xml
index c913a472c9..c05fbc994e 100644
--- a/res/xml/file_provider_paths.xml
+++ b/res/xml/file_provider_paths.xml
@@ -8,4 +8,6 @@
+
+
\ No newline at end of file
diff --git a/src/org/thoughtcrime/securesms/RegistrationActivity.java b/src/org/thoughtcrime/securesms/RegistrationActivity.java
index 29ca7d34d9..c96e433013 100644
--- a/src/org/thoughtcrime/securesms/RegistrationActivity.java
+++ b/src/org/thoughtcrime/securesms/RegistrationActivity.java
@@ -76,6 +76,7 @@ import org.whispersystems.signalservice.api.util.PhoneNumberFormatter;
import org.whispersystems.signalservice.internal.push.LockedException;
import java.io.IOException;
+import java.lang.ref.WeakReference;
import java.util.Collections;
import java.util.LinkedList;
import java.util.List;
@@ -278,7 +279,7 @@ public class RegistrationActivity extends BaseActionBarActivity implements Verif
@Override
protected @Nullable BackupUtil.BackupInfo doInBackground(Void... voids) {
try {
- return BackupUtil.getLatestBackup();
+ return BackupUtil.getLatestBackup(RegistrationActivity.this);
} catch (NoExternalStorageException e) {
Log.w(TAG, e);
return null;
diff --git a/src/org/thoughtcrime/securesms/avatar/AvatarSelection.java b/src/org/thoughtcrime/securesms/avatar/AvatarSelection.java
index b54c9e6cc4..5f1e26fc6f 100644
--- a/src/org/thoughtcrime/securesms/avatar/AvatarSelection.java
+++ b/src/org/thoughtcrime/securesms/avatar/AvatarSelection.java
@@ -6,17 +6,22 @@ import android.content.Context;
import android.content.Intent;
import android.net.Uri;
import android.os.Build;
+import android.os.Environment;
import android.provider.MediaStore;
import androidx.annotation.Nullable;
import androidx.annotation.StringRes;
+import androidx.core.app.ShareCompat;
import androidx.core.content.ContextCompat;
import com.theartofdev.edmodo.cropper.CropImage;
import com.theartofdev.edmodo.cropper.CropImageView;
import network.loki.messenger.R;
+
+import org.thoughtcrime.securesms.database.NoExternalStorageException;
import org.thoughtcrime.securesms.logging.Log;
import org.thoughtcrime.securesms.permissions.Permissions;
+import org.thoughtcrime.securesms.util.ExternalStorageUtil;
import org.thoughtcrime.securesms.util.FileProviderUtil;
import org.thoughtcrime.securesms.util.IntentUtils;
@@ -31,12 +36,12 @@ public final class AvatarSelection {
private static final String TAG = AvatarSelection.class.getSimpleName();
+ public static final int REQUEST_CODE_CROP_IMAGE = CropImage.CROP_IMAGE_ACTIVITY_REQUEST_CODE;
+ public static final int REQUEST_CODE_AVATAR = REQUEST_CODE_CROP_IMAGE + 1;
+
private AvatarSelection() {
}
- public static final int REQUEST_CODE_CROP_IMAGE = CropImage.CROP_IMAGE_ACTIVITY_REQUEST_CODE;
- public static final int REQUEST_CODE_AVATAR = REQUEST_CODE_CROP_IMAGE + 1;
-
/**
* Returns result on {@link #REQUEST_CODE_CROP_IMAGE}
*/
@@ -63,16 +68,12 @@ public final class AvatarSelection {
* @return Temporary capture file if created.
*/
public static File startAvatarSelection(Activity activity, boolean includeClear, boolean attemptToIncludeCamera) {
- File captureFile = null;
-
+ File captureFile = null;
if (attemptToIncludeCamera) {
- if (Permissions.hasAll(activity, Manifest.permission.CAMERA)) {
- try {
- captureFile = File.createTempFile("capture", "jpg", activity.getExternalCacheDir());
- } catch (IOException e) {
- Log.w(TAG, e);
- captureFile = null;
- }
+ try {
+ captureFile = File.createTempFile("avatar-capture", ".jpg", ExternalStorageUtil.getImageDir(activity));
+ } catch (IOException | NoExternalStorageException e) {
+ Log.e("Cannot reserve a temporary avatar capture file.", e);
}
}
@@ -84,7 +85,6 @@ public final class AvatarSelection {
private static Intent createAvatarSelectionIntent(Context context, @Nullable File tempCaptureFile, boolean includeClear) {
List extraIntents = new LinkedList<>();
Intent galleryIntent = new Intent(Intent.ACTION_PICK);
-
galleryIntent.setDataAndType(android.provider.MediaStore.Images.Media.INTERNAL_CONTENT_URI, "image/*");
if (!IntentUtils.isResolvable(context, galleryIntent)) {
@@ -93,12 +93,11 @@ public final class AvatarSelection {
}
if (tempCaptureFile != null) {
+ Uri uri = FileProviderUtil.getUriFor(context, tempCaptureFile);
Intent cameraIntent = new Intent(MediaStore.ACTION_IMAGE_CAPTURE);
-
- if (cameraIntent.resolveActivity(context.getPackageManager()) != null) {
- cameraIntent.putExtra(EXTRA_OUTPUT, FileProviderUtil.getUriFor(context, tempCaptureFile));
- extraIntents.add(cameraIntent);
- }
+ cameraIntent.putExtra(EXTRA_OUTPUT, uri);
+ cameraIntent.setFlags(Intent.FLAG_GRANT_WRITE_URI_PERMISSION);
+ extraIntents.add(cameraIntent);
}
if (includeClear) {
@@ -113,4 +112,4 @@ public final class AvatarSelection {
return chooserIntent;
}
-}
+}
\ No newline at end of file
diff --git a/src/org/thoughtcrime/securesms/backup/BackupDialog.java b/src/org/thoughtcrime/securesms/backup/BackupDialog.java
index 11ca52a688..1da08039f8 100644
--- a/src/org/thoughtcrime/securesms/backup/BackupDialog.java
+++ b/src/org/thoughtcrime/securesms/backup/BackupDialog.java
@@ -77,7 +77,7 @@ public class BackupDialog {
.setPositiveButton(R.string.BackupDialog_delete_backups_statement, (dialog, which) -> {
BackupPassphrase.set(context, null);
TextSecurePreferences.setBackupEnabled(context, false);
- BackupUtil.deleteAllBackups();
+ BackupUtil.deleteAllBackups(context);
preference.setChecked(false);
})
.create()
diff --git a/src/org/thoughtcrime/securesms/database/AttachmentDatabase.java b/src/org/thoughtcrime/securesms/database/AttachmentDatabase.java
index 8f6f663fe6..63bdd4ca70 100644
--- a/src/org/thoughtcrime/securesms/database/AttachmentDatabase.java
+++ b/src/org/thoughtcrime/securesms/database/AttachmentDatabase.java
@@ -54,7 +54,7 @@ import org.thoughtcrime.securesms.util.BitmapUtil;
import org.thoughtcrime.securesms.util.JsonUtils;
import org.thoughtcrime.securesms.util.MediaUtil;
import org.thoughtcrime.securesms.util.MediaUtil.ThumbnailData;
-import org.thoughtcrime.securesms.util.StorageUtil;
+import org.thoughtcrime.securesms.util.ExternalStorageUtil;
import org.thoughtcrime.securesms.util.Util;
import org.thoughtcrime.securesms.video.EncryptedMediaDataSource;
@@ -473,7 +473,7 @@ public class AttachmentDatabase extends Database {
SQLiteDatabase database = databaseHelper.getWritableDatabase();
ContentValues contentValues = new ContentValues(1);
- contentValues.put(FILE_NAME, StorageUtil.getCleanFileName(fileName));
+ contentValues.put(FILE_NAME, ExternalStorageUtil.getCleanFileName(fileName));
database.update(TABLE_NAME, contentValues, PART_ID_WHERE, attachmentId.toStrings());
}
@@ -724,7 +724,7 @@ public class AttachmentDatabase extends Database {
contentValues.put(DIGEST, attachment.getDigest());
contentValues.put(CONTENT_DISPOSITION, attachment.getKey());
contentValues.put(NAME, attachment.getRelay());
- contentValues.put(FILE_NAME, StorageUtil.getCleanFileName(attachment.getFileName()));
+ contentValues.put(FILE_NAME, ExternalStorageUtil.getCleanFileName(attachment.getFileName()));
contentValues.put(SIZE, attachment.getSize());
contentValues.put(FAST_PREFLIGHT_ID, attachment.getFastPreflightId());
contentValues.put(VOICE_NOTE, attachment.isVoiceNote() ? 1 : 0);
diff --git a/src/org/thoughtcrime/securesms/jobs/LocalBackupJob.java b/src/org/thoughtcrime/securesms/jobs/LocalBackupJob.java
index dc0d8b1f4b..4110699e67 100644
--- a/src/org/thoughtcrime/securesms/jobs/LocalBackupJob.java
+++ b/src/org/thoughtcrime/securesms/jobs/LocalBackupJob.java
@@ -16,7 +16,7 @@ import org.thoughtcrime.securesms.notifications.NotificationChannels;
import org.thoughtcrime.securesms.permissions.Permissions;
import org.thoughtcrime.securesms.service.GenericForegroundService;
import org.thoughtcrime.securesms.util.BackupUtil;
-import org.thoughtcrime.securesms.util.StorageUtil;
+import org.thoughtcrime.securesms.util.ExternalStorageUtil;
import java.io.File;
import java.io.IOException;
@@ -71,7 +71,7 @@ public class LocalBackupJob extends BaseJob {
try {
String backupPassword = BackupPassphrase.get(context);
- File backupDirectory = StorageUtil.getBackupDirectory();
+ File backupDirectory = ExternalStorageUtil.getBackupDir(context);
String timestamp = new SimpleDateFormat("yyyy-MM-dd-HH-mm-ss", Locale.US).format(new Date());
String fileName = String.format("session-%s.backup", timestamp);
File backupFile = new File(backupDirectory, fileName);
@@ -84,7 +84,7 @@ public class LocalBackupJob extends BaseJob {
throw new IOException("Backup password is null");
}
- File tempFile = File.createTempFile("backup", "tmp", StorageUtil.getBackupCacheDirectory(context));
+ File tempFile = File.createTempFile("backup", "tmp", ExternalStorageUtil.getCacheDir(context));
FullBackupExporter.export(context,
AttachmentSecretProvider.getInstance(context).getOrCreateAttachmentSecret(),
@@ -97,7 +97,7 @@ public class LocalBackupJob extends BaseJob {
throw new IOException("Renaming temporary backup file failed!");
}
- BackupUtil.deleteOldBackups();
+ BackupUtil.deleteOldBackups(context);
} finally {
GenericForegroundService.stopForegroundTask(context);
}
diff --git a/src/org/thoughtcrime/securesms/jobs/RetrieveProfileAvatarJob.java b/src/org/thoughtcrime/securesms/jobs/RetrieveProfileAvatarJob.java
index 48bc091e97..168a6630a3 100644
--- a/src/org/thoughtcrime/securesms/jobs/RetrieveProfileAvatarJob.java
+++ b/src/org/thoughtcrime/securesms/jobs/RetrieveProfileAvatarJob.java
@@ -96,11 +96,11 @@ public class RetrieveProfileAvatarJob extends BaseJob implements InjectableType
return;
}
- File downloadDestination = File.createTempFile("avatar", "jpg", context.getCacheDir());
+ File downloadDestination = File.createTempFile("avatar", ".jpg", context.getCacheDir());
try {
InputStream avatarStream = receiver.retrieveProfileAvatar(profileAvatar, downloadDestination, profileKey, MAX_PROFILE_SIZE_BYTES);
- File decryptDestination = File.createTempFile("avatar", "jpg", context.getCacheDir());
+ File decryptDestination = File.createTempFile("avatar", ".jpg", context.getCacheDir());
Util.copy(avatarStream, new FileOutputStream(decryptDestination));
decryptDestination.renameTo(AvatarHelper.getAvatarFile(context, recipient.getAddress()));
diff --git a/src/org/thoughtcrime/securesms/mms/DocumentSlide.java b/src/org/thoughtcrime/securesms/mms/DocumentSlide.java
index 24d6ceb133..968e8dd441 100644
--- a/src/org/thoughtcrime/securesms/mms/DocumentSlide.java
+++ b/src/org/thoughtcrime/securesms/mms/DocumentSlide.java
@@ -7,7 +7,7 @@ import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import org.thoughtcrime.securesms.attachments.Attachment;
-import org.thoughtcrime.securesms.util.StorageUtil;
+import org.thoughtcrime.securesms.util.ExternalStorageUtil;
public class DocumentSlide extends Slide {
@@ -19,7 +19,7 @@ public class DocumentSlide extends Slide {
@NonNull String contentType, long size,
@Nullable String fileName)
{
- super(context, constructAttachmentFromUri(context, uri, contentType, size, 0, 0, true, StorageUtil.getCleanFileName(fileName), null, null, false, false));
+ super(context, constructAttachmentFromUri(context, uri, contentType, size, 0, 0, true, ExternalStorageUtil.getCleanFileName(fileName), null, null, false, false));
}
@Override
diff --git a/src/org/thoughtcrime/securesms/util/BackupUtil.java b/src/org/thoughtcrime/securesms/util/BackupUtil.java
index d3b4f4a112..d60660da4f 100644
--- a/src/org/thoughtcrime/securesms/util/BackupUtil.java
+++ b/src/org/thoughtcrime/securesms/util/BackupUtil.java
@@ -22,7 +22,7 @@ public class BackupUtil {
public static @NonNull String getLastBackupTime(@NonNull Context context, @NonNull Locale locale) {
try {
- BackupInfo backup = getLatestBackup();
+ BackupInfo backup = getLatestBackup(context);
if (backup == null) return context.getString(R.string.BackupUtil_never);
else return DateUtils.getExtendedRelativeTimeSpanString(context, locale, backup.getTimestamp());
@@ -32,8 +32,8 @@ public class BackupUtil {
}
}
- public static @Nullable BackupInfo getLatestBackup() throws NoExternalStorageException {
- File backupDirectory = StorageUtil.getBackupDirectory();
+ public static @Nullable BackupInfo getLatestBackup(Context context) throws NoExternalStorageException {
+ File backupDirectory = ExternalStorageUtil.getBackupDir(context);
File[] backups = backupDirectory.listFiles();
BackupInfo latestBackup = null;
@@ -49,9 +49,9 @@ public class BackupUtil {
}
@SuppressWarnings("ResultOfMethodCallIgnored")
- public static void deleteAllBackups() {
+ public static void deleteAllBackups(Context context) {
try {
- File backupDirectory = StorageUtil.getBackupDirectory();
+ File backupDirectory = ExternalStorageUtil.getBackupDir(context);
File[] backups = backupDirectory.listFiles();
for (File backup : backups) {
@@ -62,9 +62,9 @@ public class BackupUtil {
}
}
- public static void deleteOldBackups() {
+ public static void deleteOldBackups(Context context) {
try {
- File backupDirectory = StorageUtil.getBackupDirectory();
+ File backupDirectory = ExternalStorageUtil.getBackupDir(context);
File[] backups = backupDirectory.listFiles();
if (backups != null && backups.length > 2) {
diff --git a/src/org/thoughtcrime/securesms/util/ExternalStorageUtil.java b/src/org/thoughtcrime/securesms/util/ExternalStorageUtil.java
new file mode 100644
index 0000000000..6c701b4501
--- /dev/null
+++ b/src/org/thoughtcrime/securesms/util/ExternalStorageUtil.java
@@ -0,0 +1,66 @@
+package org.thoughtcrime.securesms.util;
+
+import android.content.Context;
+import android.os.Environment;
+
+import androidx.annotation.NonNull;
+import androidx.annotation.Nullable;
+
+import org.thoughtcrime.securesms.database.NoExternalStorageException;
+
+import java.io.File;
+
+public class ExternalStorageUtil {
+
+ public static final String DIRECTORY_BACKUPS = "Backups";
+
+ /** @see Context#getExternalFilesDir(String) */
+ @NonNull
+ public static File getDir(Context context, @Nullable String type) throws NoExternalStorageException {
+ final File dir = context.getExternalFilesDir(type);
+ if (dir == null) {
+ throw new NoExternalStorageException("External storage dir is currently unavailable: " + type);
+ }
+ return dir;
+ }
+
+ @NonNull
+ public static File getBackupDir(Context context) throws NoExternalStorageException {
+ return getDir(context, DIRECTORY_BACKUPS);
+ }
+
+ @NonNull
+ public static File getVideoDir(Context context) throws NoExternalStorageException {
+ return getDir(context, Environment.DIRECTORY_MOVIES);
+ }
+
+ @NonNull
+ public static File getAudioDir(Context context) throws NoExternalStorageException {
+ return getDir(context, Environment.DIRECTORY_MUSIC);
+ }
+
+ @NonNull
+ public static File getImageDir(Context context) throws NoExternalStorageException {
+ return getDir(context, Environment.DIRECTORY_PICTURES);
+ }
+
+ @NonNull
+ public static File getDownloadDir(Context context) throws NoExternalStorageException {
+ return getDir(context, Environment.DIRECTORY_DOWNLOADS);
+ }
+
+ @Nullable
+ public static File getCacheDir(Context context) {
+ return context.getExternalCacheDir();
+ }
+
+ @Nullable
+ public static String getCleanFileName(@Nullable String fileName) {
+ if (fileName == null) return null;
+
+ fileName = fileName.replace('\u202D', '\uFFFD');
+ fileName = fileName.replace('\u202E', '\uFFFD');
+
+ return fileName;
+ }
+}
diff --git a/src/org/thoughtcrime/securesms/util/IntentUtils.java b/src/org/thoughtcrime/securesms/util/IntentUtils.java
index 0eb110ebdc..c01f42c2c2 100644
--- a/src/org/thoughtcrime/securesms/util/IntentUtils.java
+++ b/src/org/thoughtcrime/securesms/util/IntentUtils.java
@@ -1,18 +1,16 @@
package org.thoughtcrime.securesms.util;
-
import android.content.Context;
import android.content.Intent;
-import android.content.pm.ResolveInfo;
-import androidx.annotation.NonNull;
+import android.content.pm.PackageManager;
-import java.util.List;
+import androidx.annotation.NonNull;
public class IntentUtils {
public static boolean isResolvable(@NonNull Context context, @NonNull Intent intent) {
- List resolveInfoList = context.getPackageManager().queryIntentActivities(intent, 0);
- return resolveInfoList != null && resolveInfoList.size() > 1;
+ return context.getPackageManager()
+ .queryIntentActivities(intent, PackageManager.MATCH_DEFAULT_ONLY).size() > 0;
}
}
diff --git a/src/org/thoughtcrime/securesms/util/SaveAttachmentTask.java b/src/org/thoughtcrime/securesms/util/SaveAttachmentTask.java
index f1afec8de8..c4daadf36e 100644
--- a/src/org/thoughtcrime/securesms/util/SaveAttachmentTask.java
+++ b/src/org/thoughtcrime/securesms/util/SaveAttachmentTask.java
@@ -59,10 +59,6 @@ public class SaveAttachmentTask extends ProgressDialogAsyncTask(WRITE_ACCESS_FAILURE, null);
- }
-
if (context == null) {
return new Pair<>(FAILURE, null);
}
@@ -114,13 +110,13 @@ public class SaveAttachmentTask extends ProgressDialogAsyncTask