mirror of
https://github.com/oxen-io/session-android.git
synced 2024-12-25 17:27:45 +00:00
Merge pull request #324 from metaphore/external-file-access-fix
External Files Access Fix
This commit is contained in:
commit
4dd89ba03f
@ -8,4 +8,6 @@
|
|||||||
<external-path name="external_video" path="Movies"/>
|
<external-path name="external_video" path="Movies"/>
|
||||||
<external-path name="external_audio" path="Music"/>
|
<external-path name="external_audio" path="Music"/>
|
||||||
<external-path name="external_download" path="Download"/>
|
<external-path name="external_download" path="Download"/>
|
||||||
|
|
||||||
|
<cache-path name="internal_cache" path="." />
|
||||||
</paths>
|
</paths>
|
@ -76,6 +76,7 @@ import org.whispersystems.signalservice.api.util.PhoneNumberFormatter;
|
|||||||
import org.whispersystems.signalservice.internal.push.LockedException;
|
import org.whispersystems.signalservice.internal.push.LockedException;
|
||||||
|
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
|
import java.lang.ref.WeakReference;
|
||||||
import java.util.Collections;
|
import java.util.Collections;
|
||||||
import java.util.LinkedList;
|
import java.util.LinkedList;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
@ -278,7 +279,7 @@ public class RegistrationActivity extends BaseActionBarActivity implements Verif
|
|||||||
@Override
|
@Override
|
||||||
protected @Nullable BackupUtil.BackupInfo doInBackground(Void... voids) {
|
protected @Nullable BackupUtil.BackupInfo doInBackground(Void... voids) {
|
||||||
try {
|
try {
|
||||||
return BackupUtil.getLatestBackup();
|
return BackupUtil.getLatestBackup(RegistrationActivity.this);
|
||||||
} catch (NoExternalStorageException e) {
|
} catch (NoExternalStorageException e) {
|
||||||
Log.w(TAG, e);
|
Log.w(TAG, e);
|
||||||
return null;
|
return null;
|
||||||
|
@ -4,9 +4,11 @@ import android.Manifest;
|
|||||||
import android.app.Activity;
|
import android.app.Activity;
|
||||||
import android.content.Context;
|
import android.content.Context;
|
||||||
import android.content.Intent;
|
import android.content.Intent;
|
||||||
|
import android.content.pm.PackageManager;
|
||||||
import android.net.Uri;
|
import android.net.Uri;
|
||||||
import android.os.Build;
|
import android.os.Build;
|
||||||
import android.provider.MediaStore;
|
import android.provider.MediaStore;
|
||||||
|
|
||||||
import androidx.annotation.Nullable;
|
import androidx.annotation.Nullable;
|
||||||
import androidx.annotation.StringRes;
|
import androidx.annotation.StringRes;
|
||||||
import androidx.core.content.ContextCompat;
|
import androidx.core.content.ContextCompat;
|
||||||
@ -14,9 +16,9 @@ import androidx.core.content.ContextCompat;
|
|||||||
import com.theartofdev.edmodo.cropper.CropImage;
|
import com.theartofdev.edmodo.cropper.CropImage;
|
||||||
import com.theartofdev.edmodo.cropper.CropImageView;
|
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.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.FileProviderUtil;
|
||||||
import org.thoughtcrime.securesms.util.IntentUtils;
|
import org.thoughtcrime.securesms.util.IntentUtils;
|
||||||
|
|
||||||
@ -25,18 +27,20 @@ import java.io.IOException;
|
|||||||
import java.util.LinkedList;
|
import java.util.LinkedList;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
|
||||||
|
import network.loki.messenger.R;
|
||||||
|
|
||||||
import static android.provider.MediaStore.EXTRA_OUTPUT;
|
import static android.provider.MediaStore.EXTRA_OUTPUT;
|
||||||
|
|
||||||
public final class AvatarSelection {
|
public final class AvatarSelection {
|
||||||
|
|
||||||
private static final String TAG = AvatarSelection.class.getSimpleName();
|
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() {
|
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}
|
* Returns result on {@link #REQUEST_CODE_CROP_IMAGE}
|
||||||
*/
|
*/
|
||||||
@ -63,16 +67,14 @@ public final class AvatarSelection {
|
|||||||
* @return Temporary capture file if created.
|
* @return Temporary capture file if created.
|
||||||
*/
|
*/
|
||||||
public static File startAvatarSelection(Activity activity, boolean includeClear, boolean attemptToIncludeCamera) {
|
public static File startAvatarSelection(Activity activity, boolean includeClear, boolean attemptToIncludeCamera) {
|
||||||
File captureFile = null;
|
File captureFile = null;
|
||||||
|
boolean hasCameraPermission = ContextCompat
|
||||||
if (attemptToIncludeCamera) {
|
.checkSelfPermission(activity, Manifest.permission.CAMERA) == PackageManager.PERMISSION_GRANTED;
|
||||||
if (Permissions.hasAll(activity, Manifest.permission.CAMERA)) {
|
if (attemptToIncludeCamera && hasCameraPermission) {
|
||||||
try {
|
try {
|
||||||
captureFile = File.createTempFile("capture", "jpg", activity.getExternalCacheDir());
|
captureFile = File.createTempFile("avatar-capture", ".jpg", ExternalStorageUtil.getImageDir(activity));
|
||||||
} catch (IOException e) {
|
} catch (IOException | NoExternalStorageException e) {
|
||||||
Log.w(TAG, e);
|
Log.e("Cannot reserve a temporary avatar capture file.", e);
|
||||||
captureFile = null;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -83,8 +85,7 @@ public final class AvatarSelection {
|
|||||||
|
|
||||||
private static Intent createAvatarSelectionIntent(Context context, @Nullable File tempCaptureFile, boolean includeClear) {
|
private static Intent createAvatarSelectionIntent(Context context, @Nullable File tempCaptureFile, boolean includeClear) {
|
||||||
List<Intent> extraIntents = new LinkedList<>();
|
List<Intent> extraIntents = new LinkedList<>();
|
||||||
Intent galleryIntent = new Intent(Intent.ACTION_PICK);
|
Intent galleryIntent = new Intent(Intent.ACTION_PICK);
|
||||||
|
|
||||||
galleryIntent.setDataAndType(android.provider.MediaStore.Images.Media.INTERNAL_CONTENT_URI, "image/*");
|
galleryIntent.setDataAndType(android.provider.MediaStore.Images.Media.INTERNAL_CONTENT_URI, "image/*");
|
||||||
|
|
||||||
if (!IntentUtils.isResolvable(context, galleryIntent)) {
|
if (!IntentUtils.isResolvable(context, galleryIntent)) {
|
||||||
@ -93,12 +94,11 @@ public final class AvatarSelection {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (tempCaptureFile != null) {
|
if (tempCaptureFile != null) {
|
||||||
|
Uri uri = FileProviderUtil.getUriFor(context, tempCaptureFile);
|
||||||
Intent cameraIntent = new Intent(MediaStore.ACTION_IMAGE_CAPTURE);
|
Intent cameraIntent = new Intent(MediaStore.ACTION_IMAGE_CAPTURE);
|
||||||
|
cameraIntent.putExtra(EXTRA_OUTPUT, uri);
|
||||||
if (cameraIntent.resolveActivity(context.getPackageManager()) != null) {
|
cameraIntent.setFlags(Intent.FLAG_GRANT_WRITE_URI_PERMISSION);
|
||||||
cameraIntent.putExtra(EXTRA_OUTPUT, FileProviderUtil.getUriFor(context, tempCaptureFile));
|
extraIntents.add(cameraIntent);
|
||||||
extraIntents.add(cameraIntent);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if (includeClear) {
|
if (includeClear) {
|
||||||
|
@ -77,7 +77,7 @@ public class BackupDialog {
|
|||||||
.setPositiveButton(R.string.BackupDialog_delete_backups_statement, (dialog, which) -> {
|
.setPositiveButton(R.string.BackupDialog_delete_backups_statement, (dialog, which) -> {
|
||||||
BackupPassphrase.set(context, null);
|
BackupPassphrase.set(context, null);
|
||||||
TextSecurePreferences.setBackupEnabled(context, false);
|
TextSecurePreferences.setBackupEnabled(context, false);
|
||||||
BackupUtil.deleteAllBackups();
|
BackupUtil.deleteAllBackups(context);
|
||||||
preference.setChecked(false);
|
preference.setChecked(false);
|
||||||
})
|
})
|
||||||
.create()
|
.create()
|
||||||
|
@ -54,7 +54,7 @@ import org.thoughtcrime.securesms.util.BitmapUtil;
|
|||||||
import org.thoughtcrime.securesms.util.JsonUtils;
|
import org.thoughtcrime.securesms.util.JsonUtils;
|
||||||
import org.thoughtcrime.securesms.util.MediaUtil;
|
import org.thoughtcrime.securesms.util.MediaUtil;
|
||||||
import org.thoughtcrime.securesms.util.MediaUtil.ThumbnailData;
|
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.util.Util;
|
||||||
import org.thoughtcrime.securesms.video.EncryptedMediaDataSource;
|
import org.thoughtcrime.securesms.video.EncryptedMediaDataSource;
|
||||||
|
|
||||||
@ -473,7 +473,7 @@ public class AttachmentDatabase extends Database {
|
|||||||
SQLiteDatabase database = databaseHelper.getWritableDatabase();
|
SQLiteDatabase database = databaseHelper.getWritableDatabase();
|
||||||
|
|
||||||
ContentValues contentValues = new ContentValues(1);
|
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());
|
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(DIGEST, attachment.getDigest());
|
||||||
contentValues.put(CONTENT_DISPOSITION, attachment.getKey());
|
contentValues.put(CONTENT_DISPOSITION, attachment.getKey());
|
||||||
contentValues.put(NAME, attachment.getRelay());
|
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(SIZE, attachment.getSize());
|
||||||
contentValues.put(FAST_PREFLIGHT_ID, attachment.getFastPreflightId());
|
contentValues.put(FAST_PREFLIGHT_ID, attachment.getFastPreflightId());
|
||||||
contentValues.put(VOICE_NOTE, attachment.isVoiceNote() ? 1 : 0);
|
contentValues.put(VOICE_NOTE, attachment.isVoiceNote() ? 1 : 0);
|
||||||
|
@ -16,7 +16,7 @@ import org.thoughtcrime.securesms.notifications.NotificationChannels;
|
|||||||
import org.thoughtcrime.securesms.permissions.Permissions;
|
import org.thoughtcrime.securesms.permissions.Permissions;
|
||||||
import org.thoughtcrime.securesms.service.GenericForegroundService;
|
import org.thoughtcrime.securesms.service.GenericForegroundService;
|
||||||
import org.thoughtcrime.securesms.util.BackupUtil;
|
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.File;
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
@ -71,7 +71,7 @@ public class LocalBackupJob extends BaseJob {
|
|||||||
|
|
||||||
try {
|
try {
|
||||||
String backupPassword = BackupPassphrase.get(context);
|
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 timestamp = new SimpleDateFormat("yyyy-MM-dd-HH-mm-ss", Locale.US).format(new Date());
|
||||||
String fileName = String.format("session-%s.backup", timestamp);
|
String fileName = String.format("session-%s.backup", timestamp);
|
||||||
File backupFile = new File(backupDirectory, fileName);
|
File backupFile = new File(backupDirectory, fileName);
|
||||||
@ -84,7 +84,7 @@ public class LocalBackupJob extends BaseJob {
|
|||||||
throw new IOException("Backup password is null");
|
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,
|
FullBackupExporter.export(context,
|
||||||
AttachmentSecretProvider.getInstance(context).getOrCreateAttachmentSecret(),
|
AttachmentSecretProvider.getInstance(context).getOrCreateAttachmentSecret(),
|
||||||
@ -97,7 +97,7 @@ public class LocalBackupJob extends BaseJob {
|
|||||||
throw new IOException("Renaming temporary backup file failed!");
|
throw new IOException("Renaming temporary backup file failed!");
|
||||||
}
|
}
|
||||||
|
|
||||||
BackupUtil.deleteOldBackups();
|
BackupUtil.deleteOldBackups(context);
|
||||||
} finally {
|
} finally {
|
||||||
GenericForegroundService.stopForegroundTask(context);
|
GenericForegroundService.stopForegroundTask(context);
|
||||||
}
|
}
|
||||||
|
@ -96,11 +96,11 @@ public class RetrieveProfileAvatarJob extends BaseJob implements InjectableType
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
File downloadDestination = File.createTempFile("avatar", "jpg", context.getCacheDir());
|
File downloadDestination = File.createTempFile("avatar", ".jpg", context.getCacheDir());
|
||||||
|
|
||||||
try {
|
try {
|
||||||
InputStream avatarStream = receiver.retrieveProfileAvatar(profileAvatar, downloadDestination, profileKey, MAX_PROFILE_SIZE_BYTES);
|
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));
|
Util.copy(avatarStream, new FileOutputStream(decryptDestination));
|
||||||
decryptDestination.renameTo(AvatarHelper.getAvatarFile(context, recipient.getAddress()));
|
decryptDestination.renameTo(AvatarHelper.getAvatarFile(context, recipient.getAddress()));
|
||||||
|
@ -1,5 +1,6 @@
|
|||||||
package org.thoughtcrime.securesms.loki.activities
|
package org.thoughtcrime.securesms.loki.activities
|
||||||
|
|
||||||
|
import android.Manifest
|
||||||
import android.app.Activity
|
import android.app.Activity
|
||||||
import android.content.ClipData
|
import android.content.ClipData
|
||||||
import android.content.ClipboardManager
|
import android.content.ClipboardManager
|
||||||
@ -38,6 +39,7 @@ import org.thoughtcrime.securesms.loki.utilities.fadeOut
|
|||||||
import org.thoughtcrime.securesms.loki.utilities.push
|
import org.thoughtcrime.securesms.loki.utilities.push
|
||||||
import org.thoughtcrime.securesms.mms.GlideApp
|
import org.thoughtcrime.securesms.mms.GlideApp
|
||||||
import org.thoughtcrime.securesms.mms.GlideRequests
|
import org.thoughtcrime.securesms.mms.GlideRequests
|
||||||
|
import org.thoughtcrime.securesms.permissions.Permissions
|
||||||
import org.thoughtcrime.securesms.profiles.AvatarHelper
|
import org.thoughtcrime.securesms.profiles.AvatarHelper
|
||||||
import org.thoughtcrime.securesms.profiles.ProfileMediaConstraints
|
import org.thoughtcrime.securesms.profiles.ProfileMediaConstraints
|
||||||
import org.thoughtcrime.securesms.util.BitmapDecodingException
|
import org.thoughtcrime.securesms.util.BitmapDecodingException
|
||||||
@ -148,6 +150,11 @@ class SettingsActivity : PassphraseRequiredActionBarActivity() {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
override fun onRequestPermissionsResult(requestCode: Int, permissions: Array<out String>, grantResults: IntArray) {
|
||||||
|
super.onRequestPermissionsResult(requestCode, permissions, grantResults)
|
||||||
|
Permissions.onRequestPermissionsResult(this, requestCode, permissions, grantResults)
|
||||||
|
}
|
||||||
// endregion
|
// endregion
|
||||||
|
|
||||||
// region Updating
|
// region Updating
|
||||||
@ -241,7 +248,14 @@ class SettingsActivity : PassphraseRequiredActionBarActivity() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private fun showEditProfilePictureUI() {
|
private fun showEditProfilePictureUI() {
|
||||||
tempFile = AvatarSelection.startAvatarSelection(this, false, true)
|
// Ask for an optional camera permission.
|
||||||
|
Permissions.with(this)
|
||||||
|
.request(Manifest.permission.CAMERA)
|
||||||
|
.ifNecessary()
|
||||||
|
.onAnyResult {
|
||||||
|
tempFile = AvatarSelection.startAvatarSelection(this, false, true)
|
||||||
|
}
|
||||||
|
.execute()
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun copyPublicKey() {
|
private fun copyPublicKey() {
|
||||||
|
@ -33,6 +33,11 @@ import java.util.Map;
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* Handles the retrieval of media present on the user's device.
|
* Handles the retrieval of media present on the user's device.
|
||||||
|
* @deprecated Usage of this class is unsafe on Android API 30 and up,
|
||||||
|
* the public external directory is no longer exposed to the apps.
|
||||||
|
* <p><b>
|
||||||
|
* The functionality of this class should be refactored to use
|
||||||
|
* <a href="https://developer.android.com/reference/android/provider/MediaStore">MediaStore</a>.
|
||||||
*/
|
*/
|
||||||
class MediaRepository {
|
class MediaRepository {
|
||||||
|
|
||||||
|
@ -46,6 +46,7 @@ import org.thoughtcrime.securesms.components.RemovableEditableMediaView;
|
|||||||
import org.thoughtcrime.securesms.components.ThumbnailView;
|
import org.thoughtcrime.securesms.components.ThumbnailView;
|
||||||
import org.thoughtcrime.securesms.components.location.SignalMapView;
|
import org.thoughtcrime.securesms.components.location.SignalMapView;
|
||||||
import org.thoughtcrime.securesms.components.location.SignalPlace;
|
import org.thoughtcrime.securesms.components.location.SignalPlace;
|
||||||
|
import org.thoughtcrime.securesms.database.NoExternalStorageException;
|
||||||
import org.thoughtcrime.securesms.giph.ui.GiphyActivity;
|
import org.thoughtcrime.securesms.giph.ui.GiphyActivity;
|
||||||
import org.thoughtcrime.securesms.logging.Log;
|
import org.thoughtcrime.securesms.logging.Log;
|
||||||
import org.thoughtcrime.securesms.mediasend.MediaSendActivity;
|
import org.thoughtcrime.securesms.mediasend.MediaSendActivity;
|
||||||
@ -54,6 +55,8 @@ import org.thoughtcrime.securesms.providers.BlobProvider;
|
|||||||
import org.thoughtcrime.securesms.providers.DeprecatedPersistentBlobProvider;
|
import org.thoughtcrime.securesms.providers.DeprecatedPersistentBlobProvider;
|
||||||
import org.thoughtcrime.securesms.recipients.Recipient;
|
import org.thoughtcrime.securesms.recipients.Recipient;
|
||||||
import org.thoughtcrime.securesms.util.BitmapUtil;
|
import org.thoughtcrime.securesms.util.BitmapUtil;
|
||||||
|
import org.thoughtcrime.securesms.util.ExternalStorageUtil;
|
||||||
|
import org.thoughtcrime.securesms.util.FileProviderUtil;
|
||||||
import org.thoughtcrime.securesms.util.MediaUtil;
|
import org.thoughtcrime.securesms.util.MediaUtil;
|
||||||
import org.thoughtcrime.securesms.util.ThemeUtil;
|
import org.thoughtcrime.securesms.util.ThemeUtil;
|
||||||
import org.thoughtcrime.securesms.util.Util;
|
import org.thoughtcrime.securesms.util.Util;
|
||||||
@ -65,6 +68,7 @@ import org.thoughtcrime.securesms.util.concurrent.SettableFuture;
|
|||||||
import org.thoughtcrime.securesms.util.views.Stub;
|
import org.thoughtcrime.securesms.util.views.Stub;
|
||||||
import org.whispersystems.libsignal.util.guava.Optional;
|
import org.whispersystems.libsignal.util.guava.Optional;
|
||||||
|
|
||||||
|
import java.io.File;
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
import java.util.Iterator;
|
import java.util.Iterator;
|
||||||
import java.util.LinkedList;
|
import java.util.LinkedList;
|
||||||
@ -73,6 +77,8 @@ import java.util.concurrent.ExecutionException;
|
|||||||
|
|
||||||
import network.loki.messenger.R;
|
import network.loki.messenger.R;
|
||||||
|
|
||||||
|
import static android.provider.MediaStore.EXTRA_OUTPUT;
|
||||||
|
|
||||||
|
|
||||||
public class AttachmentManager {
|
public class AttachmentManager {
|
||||||
|
|
||||||
@ -112,7 +118,6 @@ public class AttachmentManager {
|
|||||||
thumbnail.setOnClickListener(new ThumbnailClickListener());
|
thumbnail.setOnClickListener(new ThumbnailClickListener());
|
||||||
documentView.getBackground().setColorFilter(ThemeUtil.getThemedColor(context, R.attr.conversation_item_bubble_background), PorterDuff.Mode.MULTIPLY);
|
documentView.getBackground().setColorFilter(ThemeUtil.getThemedColor(context, R.attr.conversation_item_bubble_background), PorterDuff.Mode.MULTIPLY);
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public void clear(@NonNull GlideRequests glideRequests, boolean animate) {
|
public void clear(@NonNull GlideRequests glideRequests, boolean animate) {
|
||||||
@ -438,25 +443,29 @@ public class AttachmentManager {
|
|||||||
|
|
||||||
public void capturePhoto(Activity activity, int requestCode) {
|
public void capturePhoto(Activity activity, int requestCode) {
|
||||||
Permissions.with(activity)
|
Permissions.with(activity)
|
||||||
.request(Manifest.permission.CAMERA)
|
.request(Manifest.permission.CAMERA)
|
||||||
.ifNecessary()
|
.ifNecessary()
|
||||||
.withPermanentDenialDialog(activity.getString(R.string.AttachmentManager_signal_requires_the_camera_permission_in_order_to_take_photos_but_it_has_been_permanently_denied))
|
.withPermanentDenialDialog(activity.getString(R.string.AttachmentManager_signal_requires_the_camera_permission_in_order_to_take_photos_but_it_has_been_permanently_denied))
|
||||||
.onAllGranted(() -> {
|
.onAllGranted(() -> {
|
||||||
try {
|
try {
|
||||||
Intent captureIntent = new Intent(MediaStore.ACTION_IMAGE_CAPTURE);
|
File captureFile = File.createTempFile(
|
||||||
if (captureIntent.resolveActivity(activity.getPackageManager()) != null) {
|
"conversation-capture",
|
||||||
if (captureUri == null) {
|
".jpg",
|
||||||
captureUri = DeprecatedPersistentBlobProvider.getInstance(context).createForExternal(context, MediaUtil.IMAGE_JPEG);
|
ExternalStorageUtil.getImageDir(activity));
|
||||||
}
|
Uri captureUri = FileProviderUtil.getUriFor(context, captureFile);
|
||||||
Log.d(TAG, "captureUri path is " + captureUri.getPath());
|
Intent captureIntent = new Intent(MediaStore.ACTION_IMAGE_CAPTURE);
|
||||||
captureIntent.putExtra(MediaStore.EXTRA_OUTPUT, captureUri);
|
captureIntent.putExtra(EXTRA_OUTPUT, captureUri);
|
||||||
activity.startActivityForResult(captureIntent, requestCode);
|
captureIntent.setFlags(Intent.FLAG_GRANT_WRITE_URI_PERMISSION);
|
||||||
}
|
if (captureIntent.resolveActivity(activity.getPackageManager()) != null) {
|
||||||
} catch (IOException ioe) {
|
Log.d(TAG, "captureUri path is " + captureUri.getPath());
|
||||||
Log.w(TAG, ioe);
|
this.captureUri = captureUri;
|
||||||
}
|
activity.startActivityForResult(captureIntent, requestCode);
|
||||||
})
|
}
|
||||||
.execute();
|
} catch (IOException | NoExternalStorageException e) {
|
||||||
|
throw new RuntimeException("Error creating image capture intent.", e);
|
||||||
|
}
|
||||||
|
})
|
||||||
|
.execute();
|
||||||
}
|
}
|
||||||
|
|
||||||
private static void selectMediaType(Activity activity, @NonNull String type, @Nullable String[] extraMimeType, int requestCode) {
|
private static void selectMediaType(Activity activity, @NonNull String type, @Nullable String[] extraMimeType, int requestCode) {
|
||||||
|
@ -7,7 +7,7 @@ import androidx.annotation.NonNull;
|
|||||||
import androidx.annotation.Nullable;
|
import androidx.annotation.Nullable;
|
||||||
|
|
||||||
import org.thoughtcrime.securesms.attachments.Attachment;
|
import org.thoughtcrime.securesms.attachments.Attachment;
|
||||||
import org.thoughtcrime.securesms.util.StorageUtil;
|
import org.thoughtcrime.securesms.util.ExternalStorageUtil;
|
||||||
|
|
||||||
public class DocumentSlide extends Slide {
|
public class DocumentSlide extends Slide {
|
||||||
|
|
||||||
@ -19,7 +19,7 @@ public class DocumentSlide extends Slide {
|
|||||||
@NonNull String contentType, long size,
|
@NonNull String contentType, long size,
|
||||||
@Nullable String fileName)
|
@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
|
@Override
|
||||||
|
@ -171,7 +171,7 @@ public class Permissions {
|
|||||||
}
|
}
|
||||||
|
|
||||||
for (String permission : requestedPermissions) {
|
for (String permission : requestedPermissions) {
|
||||||
request.addMapping(permission, permissionObject.shouldShouldPermissionRationale(permission));
|
request.addMapping(permission, permissionObject.shouldShowPermissionRationale(permission));
|
||||||
}
|
}
|
||||||
|
|
||||||
permissionObject.requestPermissions(requestCode, requestedPermissions);
|
permissionObject.requestPermissions(requestCode, requestedPermissions);
|
||||||
@ -240,7 +240,7 @@ public class Permissions {
|
|||||||
|
|
||||||
for (int i=0;i<permissions.length;i++) {
|
for (int i=0;i<permissions.length;i++) {
|
||||||
if (grantResults[i] != PackageManager.PERMISSION_GRANTED) {
|
if (grantResults[i] != PackageManager.PERMISSION_GRANTED) {
|
||||||
shouldShowRationaleDialog[i] = context.shouldShouldPermissionRationale(permissions[i]);
|
shouldShowRationaleDialog[i] = context.shouldShowPermissionRationale(permissions[i]);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -259,7 +259,7 @@ public class Permissions {
|
|||||||
private abstract static class PermissionObject {
|
private abstract static class PermissionObject {
|
||||||
|
|
||||||
abstract Context getContext();
|
abstract Context getContext();
|
||||||
abstract boolean shouldShouldPermissionRationale(String permission);
|
abstract boolean shouldShowPermissionRationale(String permission);
|
||||||
abstract boolean hasAll(String... permissions);
|
abstract boolean hasAll(String... permissions);
|
||||||
abstract void requestPermissions(int requestCode, String... permissions);
|
abstract void requestPermissions(int requestCode, String... permissions);
|
||||||
|
|
||||||
@ -287,7 +287,7 @@ public class Permissions {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public boolean shouldShouldPermissionRationale(String permission) {
|
public boolean shouldShowPermissionRationale(String permission) {
|
||||||
return ActivityCompat.shouldShowRequestPermissionRationale(activity, permission);
|
return ActivityCompat.shouldShowRequestPermissionRationale(activity, permission);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -316,7 +316,7 @@ public class Permissions {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public boolean shouldShouldPermissionRationale(String permission) {
|
public boolean shouldShowPermissionRationale(String permission) {
|
||||||
return fragment.shouldShowRequestPermissionRationale(permission);
|
return fragment.shouldShowRequestPermissionRationale(permission);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -22,7 +22,7 @@ public class BackupUtil {
|
|||||||
|
|
||||||
public static @NonNull String getLastBackupTime(@NonNull Context context, @NonNull Locale locale) {
|
public static @NonNull String getLastBackupTime(@NonNull Context context, @NonNull Locale locale) {
|
||||||
try {
|
try {
|
||||||
BackupInfo backup = getLatestBackup();
|
BackupInfo backup = getLatestBackup(context);
|
||||||
|
|
||||||
if (backup == null) return context.getString(R.string.BackupUtil_never);
|
if (backup == null) return context.getString(R.string.BackupUtil_never);
|
||||||
else return DateUtils.getExtendedRelativeTimeSpanString(context, locale, backup.getTimestamp());
|
else return DateUtils.getExtendedRelativeTimeSpanString(context, locale, backup.getTimestamp());
|
||||||
@ -32,8 +32,8 @@ public class BackupUtil {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public static @Nullable BackupInfo getLatestBackup() throws NoExternalStorageException {
|
public static @Nullable BackupInfo getLatestBackup(Context context) throws NoExternalStorageException {
|
||||||
File backupDirectory = StorageUtil.getBackupDirectory();
|
File backupDirectory = ExternalStorageUtil.getBackupDir(context);
|
||||||
File[] backups = backupDirectory.listFiles();
|
File[] backups = backupDirectory.listFiles();
|
||||||
BackupInfo latestBackup = null;
|
BackupInfo latestBackup = null;
|
||||||
|
|
||||||
@ -49,9 +49,9 @@ public class BackupUtil {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@SuppressWarnings("ResultOfMethodCallIgnored")
|
@SuppressWarnings("ResultOfMethodCallIgnored")
|
||||||
public static void deleteAllBackups() {
|
public static void deleteAllBackups(Context context) {
|
||||||
try {
|
try {
|
||||||
File backupDirectory = StorageUtil.getBackupDirectory();
|
File backupDirectory = ExternalStorageUtil.getBackupDir(context);
|
||||||
File[] backups = backupDirectory.listFiles();
|
File[] backups = backupDirectory.listFiles();
|
||||||
|
|
||||||
for (File backup : backups) {
|
for (File backup : backups) {
|
||||||
@ -62,9 +62,9 @@ public class BackupUtil {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public static void deleteOldBackups() {
|
public static void deleteOldBackups(Context context) {
|
||||||
try {
|
try {
|
||||||
File backupDirectory = StorageUtil.getBackupDirectory();
|
File backupDirectory = ExternalStorageUtil.getBackupDir(context);
|
||||||
File[] backups = backupDirectory.listFiles();
|
File[] backups = backupDirectory.listFiles();
|
||||||
|
|
||||||
if (backups != null && backups.length > 2) {
|
if (backups != null && backups.length > 2) {
|
||||||
|
66
src/org/thoughtcrime/securesms/util/ExternalStorageUtil.java
Normal file
66
src/org/thoughtcrime/securesms/util/ExternalStorageUtil.java
Normal file
@ -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;
|
||||||
|
}
|
||||||
|
}
|
@ -1,18 +1,16 @@
|
|||||||
package org.thoughtcrime.securesms.util;
|
package org.thoughtcrime.securesms.util;
|
||||||
|
|
||||||
|
|
||||||
import android.content.Context;
|
import android.content.Context;
|
||||||
import android.content.Intent;
|
import android.content.Intent;
|
||||||
import android.content.pm.ResolveInfo;
|
import android.content.pm.PackageManager;
|
||||||
import androidx.annotation.NonNull;
|
|
||||||
|
|
||||||
import java.util.List;
|
import androidx.annotation.NonNull;
|
||||||
|
|
||||||
public class IntentUtils {
|
public class IntentUtils {
|
||||||
|
|
||||||
public static boolean isResolvable(@NonNull Context context, @NonNull Intent intent) {
|
public static boolean isResolvable(@NonNull Context context, @NonNull Intent intent) {
|
||||||
List<ResolveInfo> resolveInfoList = context.getPackageManager().queryIntentActivities(intent, 0);
|
return context.getPackageManager()
|
||||||
return resolveInfoList != null && resolveInfoList.size() > 1;
|
.queryIntentActivities(intent, PackageManager.MATCH_DEFAULT_ONLY).size() > 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -59,10 +59,6 @@ public class SaveAttachmentTask extends ProgressDialogAsyncTask<SaveAttachmentTa
|
|||||||
Context context = contextReference.get();
|
Context context = contextReference.get();
|
||||||
String directory = null;
|
String directory = null;
|
||||||
|
|
||||||
if (!StorageUtil.canWriteInSessionStorageDir()) {
|
|
||||||
return new Pair<>(WRITE_ACCESS_FAILURE, null);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (context == null) {
|
if (context == null) {
|
||||||
return new Pair<>(FAILURE, null);
|
return new Pair<>(FAILURE, null);
|
||||||
}
|
}
|
||||||
@ -114,13 +110,13 @@ public class SaveAttachmentTask extends ProgressDialogAsyncTask<SaveAttachmentTa
|
|||||||
File outputDirectory;
|
File outputDirectory;
|
||||||
|
|
||||||
if (contentType.startsWith("video/")) {
|
if (contentType.startsWith("video/")) {
|
||||||
outputDirectory = StorageUtil.getVideoDir();
|
outputDirectory = ExternalStorageUtil.getVideoDir(getContext());
|
||||||
} else if (contentType.startsWith("audio/")) {
|
} else if (contentType.startsWith("audio/")) {
|
||||||
outputDirectory = StorageUtil.getAudioDir();
|
outputDirectory = ExternalStorageUtil.getAudioDir(getContext());
|
||||||
} else if (contentType.startsWith("image/")) {
|
} else if (contentType.startsWith("image/")) {
|
||||||
outputDirectory = StorageUtil.getImageDir();
|
outputDirectory = ExternalStorageUtil.getImageDir(getContext());
|
||||||
} else {
|
} else {
|
||||||
outputDirectory = StorageUtil.getDownloadDir();
|
outputDirectory = ExternalStorageUtil.getDownloadDir(getContext());
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!outputDirectory.mkdirs()) Log.w(TAG, "mkdirs() returned false, attempting to continue");
|
if (!outputDirectory.mkdirs()) Log.w(TAG, "mkdirs() returned false, attempting to continue");
|
||||||
|
@ -1,86 +0,0 @@
|
|||||||
package org.thoughtcrime.securesms.util;
|
|
||||||
|
|
||||||
import android.content.Context;
|
|
||||||
import android.os.Environment;
|
|
||||||
import androidx.annotation.Nullable;
|
|
||||||
|
|
||||||
import org.thoughtcrime.securesms.database.NoExternalStorageException;
|
|
||||||
|
|
||||||
import java.io.File;
|
|
||||||
|
|
||||||
public class StorageUtil {
|
|
||||||
|
|
||||||
public static File getBackupDirectory() throws NoExternalStorageException {
|
|
||||||
File storage = Environment.getExternalStorageDirectory();
|
|
||||||
|
|
||||||
if (!storage.canWrite()) {
|
|
||||||
throw new NoExternalStorageException();
|
|
||||||
}
|
|
||||||
|
|
||||||
File session = new File(storage, "Session");
|
|
||||||
File backups = new File(session, "Backups");
|
|
||||||
|
|
||||||
if (!backups.exists()) {
|
|
||||||
if (!backups.mkdirs()) {
|
|
||||||
throw new NoExternalStorageException("Unable to create backup directory...");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return backups;
|
|
||||||
}
|
|
||||||
|
|
||||||
public static File getBackupCacheDirectory(Context context) {
|
|
||||||
return context.getExternalCacheDir();
|
|
||||||
}
|
|
||||||
|
|
||||||
private static File getSessionStorageDir() throws NoExternalStorageException {
|
|
||||||
final File storage = Environment.getExternalStorageDirectory();
|
|
||||||
|
|
||||||
if (!storage.canWrite()) {
|
|
||||||
throw new NoExternalStorageException();
|
|
||||||
}
|
|
||||||
|
|
||||||
return storage;
|
|
||||||
}
|
|
||||||
|
|
||||||
public static boolean canWriteInSessionStorageDir() {
|
|
||||||
File storage;
|
|
||||||
|
|
||||||
try {
|
|
||||||
storage = getSessionStorageDir();
|
|
||||||
} catch (NoExternalStorageException e) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
return storage.canWrite();
|
|
||||||
}
|
|
||||||
|
|
||||||
public static File getLegacyBackupDirectory() throws NoExternalStorageException {
|
|
||||||
return getSessionStorageDir();
|
|
||||||
}
|
|
||||||
|
|
||||||
public static File getVideoDir() throws NoExternalStorageException {
|
|
||||||
return new File(getSessionStorageDir(), Environment.DIRECTORY_MOVIES);
|
|
||||||
}
|
|
||||||
|
|
||||||
public static File getAudioDir() throws NoExternalStorageException {
|
|
||||||
return new File(getSessionStorageDir(), Environment.DIRECTORY_MUSIC);
|
|
||||||
}
|
|
||||||
|
|
||||||
public static File getImageDir() throws NoExternalStorageException {
|
|
||||||
return new File(getSessionStorageDir(), Environment.DIRECTORY_PICTURES);
|
|
||||||
}
|
|
||||||
|
|
||||||
public static File getDownloadDir() throws NoExternalStorageException {
|
|
||||||
return new File(getSessionStorageDir(), Environment.DIRECTORY_DOWNLOADS);
|
|
||||||
}
|
|
||||||
|
|
||||||
public static @Nullable String getCleanFileName(@Nullable String fileName) {
|
|
||||||
if (fileName == null) return null;
|
|
||||||
|
|
||||||
fileName = fileName.replace('\u202D', '\uFFFD');
|
|
||||||
fileName = fileName.replace('\u202E', '\uFFFD');
|
|
||||||
|
|
||||||
return fileName;
|
|
||||||
}
|
|
||||||
}
|
|
Loading…
x
Reference in New Issue
Block a user