From 263a540151fe12ccfd37b98f7abb761d677da8d2 Mon Sep 17 00:00:00 2001 From: Anton Chekulaev Date: Tue, 8 Sep 2020 15:47:26 +1000 Subject: [PATCH 1/7] Remove condition and notNecessary flags from the Permissions util. --- .../securesms/CreateProfileActivity.java | 1 - .../thoughtcrime/securesms/DeviceActivity.java | 1 - .../securesms/MediaOverviewActivity.java | 1 - .../securesms/MediaPreviewActivity.java | 1 - .../securesms/VerifyIdentityActivity.java | 1 - .../securesms/WebRtcCallActivity.java | 1 - .../conversation/ConversationActivity.java | 2 -- .../loki/activities/SettingsActivity.kt | 1 - .../securesms/mediasend/MediaSendActivity.java | 1 - .../securesms/mms/AttachmentManager.java | 6 ------ .../securesms/permissions/Permissions.java | 18 +----------------- .../preferences/ChatsPreferenceFragment.java | 2 -- .../registration/WelcomeActivity.java | 1 - .../securesms/util/CommunicationActions.java | 3 +-- 14 files changed, 2 insertions(+), 38 deletions(-) diff --git a/src/org/thoughtcrime/securesms/CreateProfileActivity.java b/src/org/thoughtcrime/securesms/CreateProfileActivity.java index 1c9af4f4c6..9be993d218 100644 --- a/src/org/thoughtcrime/securesms/CreateProfileActivity.java +++ b/src/org/thoughtcrime/securesms/CreateProfileActivity.java @@ -215,7 +215,6 @@ public class CreateProfileActivity extends BaseActionBarActivity implements Inje this.avatar.setOnClickListener(view -> Permissions.with(this) .request(Manifest.permission.CAMERA, Manifest.permission.WRITE_EXTERNAL_STORAGE) - .ifNecessary() .onAnyResult(this::startAvatarSelection) .execute()); diff --git a/src/org/thoughtcrime/securesms/DeviceActivity.java b/src/org/thoughtcrime/securesms/DeviceActivity.java index 5b68a6b478..c3c750c58b 100644 --- a/src/org/thoughtcrime/securesms/DeviceActivity.java +++ b/src/org/thoughtcrime/securesms/DeviceActivity.java @@ -98,7 +98,6 @@ public class DeviceActivity extends PassphraseRequiredActionBarActivity public void onClick(View v) { Permissions.with(this) .request(Manifest.permission.CAMERA) - .ifNecessary() .withPermanentDenialDialog(getString(R.string.DeviceActivity_signal_needs_the_camera_permission_in_order_to_scan_a_qr_code)) .onAllGranted(() -> { getSupportFragmentManager().beginTransaction() diff --git a/src/org/thoughtcrime/securesms/MediaOverviewActivity.java b/src/org/thoughtcrime/securesms/MediaOverviewActivity.java index 2b3f12012c..54b80a60c1 100644 --- a/src/org/thoughtcrime/securesms/MediaOverviewActivity.java +++ b/src/org/thoughtcrime/securesms/MediaOverviewActivity.java @@ -330,7 +330,6 @@ public class MediaOverviewActivity extends PassphraseRequiredActionBarActivity { SaveAttachmentTask.showWarningDialog(context, (dialogInterface, which) -> { Permissions.with(this) .request(android.Manifest.permission.WRITE_EXTERNAL_STORAGE, Manifest.permission.READ_EXTERNAL_STORAGE) - .ifNecessary() .withPermanentDenialDialog(getString(R.string.MediaPreviewActivity_signal_needs_the_storage_permission_in_order_to_write_to_external_storage_but_it_has_been_permanently_denied)) .onAnyDenied(() -> Toast.makeText(getContext(), R.string.MediaPreviewActivity_unable_to_write_to_external_storage_without_permission, Toast.LENGTH_LONG).show()) .onAllGranted(() -> { diff --git a/src/org/thoughtcrime/securesms/MediaPreviewActivity.java b/src/org/thoughtcrime/securesms/MediaPreviewActivity.java index 4593382bbe..36117ea86a 100644 --- a/src/org/thoughtcrime/securesms/MediaPreviewActivity.java +++ b/src/org/thoughtcrime/securesms/MediaPreviewActivity.java @@ -346,7 +346,6 @@ public class MediaPreviewActivity extends PassphraseRequiredActionBarActivity im SaveAttachmentTask.showWarningDialog(this, (dialogInterface, i) -> { Permissions.with(this) .request(Manifest.permission.WRITE_EXTERNAL_STORAGE, Manifest.permission.READ_EXTERNAL_STORAGE) - .ifNecessary() .withPermanentDenialDialog(getString(R.string.MediaPreviewActivity_signal_needs_the_storage_permission_in_order_to_write_to_external_storage_but_it_has_been_permanently_denied)) .onAnyDenied(() -> Toast.makeText(this, R.string.MediaPreviewActivity_unable_to_write_to_external_storage_without_permission, Toast.LENGTH_LONG).show()) .onAllGranted(() -> { diff --git a/src/org/thoughtcrime/securesms/VerifyIdentityActivity.java b/src/org/thoughtcrime/securesms/VerifyIdentityActivity.java index f78de0ac53..981a76ca50 100644 --- a/src/org/thoughtcrime/securesms/VerifyIdentityActivity.java +++ b/src/org/thoughtcrime/securesms/VerifyIdentityActivity.java @@ -172,7 +172,6 @@ public class VerifyIdentityActivity extends PassphraseRequiredActionBarActivity public void onClick(View v) { Permissions.with(this) .request(Manifest.permission.CAMERA) - .ifNecessary() .withPermanentDenialDialog(getString(R.string.VerifyIdentityActivity_signal_needs_the_camera_permission_in_order_to_scan_a_qr_code_but_it_has_been_permanently_denied)) .onAllGranted(() -> { FragmentTransaction transaction = getSupportFragmentManager().beginTransaction(); diff --git a/src/org/thoughtcrime/securesms/WebRtcCallActivity.java b/src/org/thoughtcrime/securesms/WebRtcCallActivity.java index 311698578c..e17e4f631a 100644 --- a/src/org/thoughtcrime/securesms/WebRtcCallActivity.java +++ b/src/org/thoughtcrime/securesms/WebRtcCallActivity.java @@ -164,7 +164,6 @@ public class WebRtcCallActivity extends Activity { if (event != null) { Permissions.with(this) .request(Manifest.permission.RECORD_AUDIO, Manifest.permission.CAMERA) - .ifNecessary() .withRationaleDialog(getString(R.string.WebRtcCallActivity_to_answer_the_call_from_s_give_signal_access_to_your_microphone, event.getRecipient().toShortString()), R.drawable.ic_mic_white_48dp, R.drawable.ic_videocam_white_48dp) .withPermanentDenialDialog(getString(R.string.WebRtcCallActivity_signal_requires_microphone_and_camera_permissions_in_order_to_make_or_receive_calls)) diff --git a/src/org/thoughtcrime/securesms/conversation/ConversationActivity.java b/src/org/thoughtcrime/securesms/conversation/ConversationActivity.java index 02a68f3e2c..347e281742 100644 --- a/src/org/thoughtcrime/securesms/conversation/ConversationActivity.java +++ b/src/org/thoughtcrime/securesms/conversation/ConversationActivity.java @@ -2551,7 +2551,6 @@ public class ConversationActivity extends PassphraseRequiredActionBarActivity public void onRecorderPermissionRequired() { Permissions.with(this) .request(Manifest.permission.RECORD_AUDIO) - .ifNecessary() .withRationaleDialog(getString(R.string.ConversationActivity_to_send_audio_messages_allow_signal_access_to_your_microphone), R.drawable.ic_baseline_mic_48) .withPermanentDenialDialog(getString(R.string.ConversationActivity_signal_requires_the_microphone_permission_in_order_to_send_audio_messages)) .execute(); @@ -2752,7 +2751,6 @@ public class ConversationActivity extends PassphraseRequiredActionBarActivity public void onClick(View v) { Permissions.with(ConversationActivity.this) .request(Manifest.permission.CAMERA) - .ifNecessary() .withRationaleDialog(getString(R.string.ConversationActivity_to_capture_photos_and_video_allow_signal_access_to_the_camera), R.drawable.ic_baseline_photo_camera_48) .withPermanentDenialDialog(getString(R.string.ConversationActivity_signal_needs_the_camera_permission_to_take_photos_or_video)) .onAllGranted(() -> { diff --git a/src/org/thoughtcrime/securesms/loki/activities/SettingsActivity.kt b/src/org/thoughtcrime/securesms/loki/activities/SettingsActivity.kt index c61ddc94a3..45092b0c06 100644 --- a/src/org/thoughtcrime/securesms/loki/activities/SettingsActivity.kt +++ b/src/org/thoughtcrime/securesms/loki/activities/SettingsActivity.kt @@ -256,7 +256,6 @@ class SettingsActivity : PassphraseRequiredActionBarActivity() { // Ask for an optional camera permission. Permissions.with(this) .request(Manifest.permission.CAMERA) - .ifNecessary() .onAnyResult { tempFile = AvatarSelection.startAvatarSelection(this, false, true) } diff --git a/src/org/thoughtcrime/securesms/mediasend/MediaSendActivity.java b/src/org/thoughtcrime/securesms/mediasend/MediaSendActivity.java index 41324d4e61..adfc09a060 100644 --- a/src/org/thoughtcrime/securesms/mediasend/MediaSendActivity.java +++ b/src/org/thoughtcrime/securesms/mediasend/MediaSendActivity.java @@ -375,7 +375,6 @@ public class MediaSendActivity extends PassphraseRequiredActionBarActivity imple private void navigateToCamera() { Permissions.with(this) .request(Manifest.permission.CAMERA) - .ifNecessary() .withRationaleDialog(getString(R.string.ConversationActivity_to_capture_photos_and_video_allow_signal_access_to_the_camera), R.drawable.ic_baseline_photo_camera_48) .withPermanentDenialDialog(getString(R.string.ConversationActivity_signal_needs_the_camera_permission_to_take_photos_or_video)) .onAllGranted(() -> { diff --git a/src/org/thoughtcrime/securesms/mms/AttachmentManager.java b/src/org/thoughtcrime/securesms/mms/AttachmentManager.java index 53e4ca54cc..3ae608ee8c 100644 --- a/src/org/thoughtcrime/securesms/mms/AttachmentManager.java +++ b/src/org/thoughtcrime/securesms/mms/AttachmentManager.java @@ -373,7 +373,6 @@ public class AttachmentManager { public static void selectDocument(Activity activity, int requestCode) { Permissions.with(activity) .request(Manifest.permission.WRITE_EXTERNAL_STORAGE) - .ifNecessary() .withPermanentDenialDialog(activity.getString(R.string.AttachmentManager_signal_requires_the_external_storage_permission_in_order_to_attach_photos_videos_or_audio)) .onAllGranted(() -> selectMediaType(activity, "*/*", null, requestCode)) .execute(); @@ -382,7 +381,6 @@ public class AttachmentManager { public static void selectGallery(Activity activity, int requestCode, @NonNull Recipient recipient, @NonNull String body, @NonNull TransportOption transport) { Permissions.with(activity) .request(Manifest.permission.WRITE_EXTERNAL_STORAGE) - .ifNecessary() .withPermanentDenialDialog(activity.getString(R.string.AttachmentManager_signal_requires_the_external_storage_permission_in_order_to_attach_photos_videos_or_audio)) .onAllGranted(() -> selectMediaType(activity, "image/*", new String[] {"image/*", "video/*"}, requestCode)) .onAllGranted(() -> activity.startActivityForResult(MediaSendActivity.buildGalleryIntent(activity, recipient, body, transport), requestCode)) @@ -392,7 +390,6 @@ public class AttachmentManager { public static void selectAudio(Activity activity, int requestCode) { Permissions.with(activity) .request(Manifest.permission.WRITE_EXTERNAL_STORAGE) - .ifNecessary() .withPermanentDenialDialog(activity.getString(R.string.AttachmentManager_signal_requires_the_external_storage_permission_in_order_to_attach_photos_videos_or_audio)) .onAllGranted(() -> selectMediaType(activity, "audio/*", null, requestCode)) .execute(); @@ -401,7 +398,6 @@ public class AttachmentManager { public static void selectContactInfo(Activity activity, int requestCode) { Permissions.with(activity) .request(Manifest.permission.WRITE_CONTACTS) - .ifNecessary() .withPermanentDenialDialog(activity.getString(R.string.AttachmentManager_signal_requires_contacts_permission_in_order_to_attach_contact_information)) .onAllGranted(() -> { Intent intent = new Intent(Intent.ACTION_PICK, ContactsContract.Contacts.CONTENT_URI); @@ -414,7 +410,6 @@ public class AttachmentManager { /* Loki - Enable again once we have location sharing Permissions.with(activity) .request(Manifest.permission.ACCESS_FINE_LOCATION, Manifest.permission.ACCESS_COARSE_LOCATION) - .ifNecessary() .withPermanentDenialDialog(activity.getString(R.string.AttachmentManager_signal_requires_location_information_in_order_to_attach_a_location)) .onAllGranted(() -> { try { @@ -444,7 +439,6 @@ public class AttachmentManager { public void capturePhoto(Activity activity, int requestCode) { Permissions.with(activity) .request(Manifest.permission.CAMERA) - .ifNecessary() .withPermanentDenialDialog(activity.getString(R.string.AttachmentManager_signal_requires_the_camera_permission_in_order_to_take_photos_but_it_has_been_permanently_denied)) .onAllGranted(() -> { try { diff --git a/src/org/thoughtcrime/securesms/permissions/Permissions.java b/src/org/thoughtcrime/securesms/permissions/Permissions.java index f647da6db4..91eec1e011 100644 --- a/src/org/thoughtcrime/securesms/permissions/Permissions.java +++ b/src/org/thoughtcrime/securesms/permissions/Permissions.java @@ -1,6 +1,5 @@ package org.thoughtcrime.securesms.permissions; - import android.app.Activity; import android.app.AlertDialog; import android.content.Context; @@ -63,10 +62,6 @@ public class Permissions { private @DrawableRes int[] rationalDialogHeader; private String rationaleDialogMessage; - private boolean ifNecesary; - - private boolean condition = true; - PermissionsBuilder(PermissionObject permissionObject) { this.permissionObject = permissionObject; } @@ -76,17 +71,6 @@ public class Permissions { return this; } - public PermissionsBuilder ifNecessary() { - this.ifNecesary = true; - return this; - } - - public PermissionsBuilder ifNecessary(boolean condition) { - this.ifNecesary = true; - this.condition = condition; - return this; - } - public PermissionsBuilder withRationaleDialog(@NonNull String message, @NonNull @DrawableRes int... headers) { this.rationalDialogHeader = headers; this.rationaleDialogMessage = message; @@ -137,7 +121,7 @@ public class Permissions { PermissionsRequest request = new PermissionsRequest(allGrantedListener, anyDeniedListener, anyPermanentlyDeniedListener, anyResultListener, someGrantedListener, someDeniedListener, somePermanentlyDeniedListener); - if (ifNecesary && (permissionObject.hasAll(requestedPermissions) || !condition)) { + if (permissionObject.hasAll(requestedPermissions)) { executePreGrantedPermissionsRequest(request); } else if (rationaleDialogMessage != null && rationalDialogHeader != null) { executePermissionsRequestWithRationale(request); diff --git a/src/org/thoughtcrime/securesms/preferences/ChatsPreferenceFragment.java b/src/org/thoughtcrime/securesms/preferences/ChatsPreferenceFragment.java index 877da0506d..97c14b4279 100644 --- a/src/org/thoughtcrime/securesms/preferences/ChatsPreferenceFragment.java +++ b/src/org/thoughtcrime/securesms/preferences/ChatsPreferenceFragment.java @@ -139,7 +139,6 @@ public class ChatsPreferenceFragment extends ListSummaryPreferenceFragment { public boolean onPreferenceClick(Preference preference) { Permissions.with(ChatsPreferenceFragment.this) .request(Manifest.permission.WRITE_EXTERNAL_STORAGE) - .ifNecessary() .onAllGranted(() -> { if (!((SwitchPreferenceCompat)preference).isChecked()) { BackupDialog.showEnableBackupDialog(getActivity(), (SwitchPreferenceCompat)preference); @@ -160,7 +159,6 @@ public class ChatsPreferenceFragment extends ListSummaryPreferenceFragment { public boolean onPreferenceClick(Preference preference) { Permissions.with(ChatsPreferenceFragment.this) .request(Manifest.permission.WRITE_EXTERNAL_STORAGE) - .ifNecessary() .onAllGranted(() -> { Log.i(TAG, "Queing backup..."); ApplicationContext.getInstance(getContext()) diff --git a/src/org/thoughtcrime/securesms/registration/WelcomeActivity.java b/src/org/thoughtcrime/securesms/registration/WelcomeActivity.java index e9f8f2e026..d8d7809606 100644 --- a/src/org/thoughtcrime/securesms/registration/WelcomeActivity.java +++ b/src/org/thoughtcrime/securesms/registration/WelcomeActivity.java @@ -55,7 +55,6 @@ public class WelcomeActivity extends BaseActionBarActivity { private void onContinueClicked() { Permissions.with(this) .request(Manifest.permission.WRITE_EXTERNAL_STORAGE, Manifest.permission.READ_EXTERNAL_STORAGE) - .ifNecessary() .withRationaleDialog(getString(R.string.activity_landing_permission_dialog_message), R.drawable.ic_baseline_folder_48) .onAnyResult(() -> { Intent nextIntent = getIntent().getParcelableExtra("next_intent"); diff --git a/src/org/thoughtcrime/securesms/util/CommunicationActions.java b/src/org/thoughtcrime/securesms/util/CommunicationActions.java index 2c06f714b5..db66b955cf 100644 --- a/src/org/thoughtcrime/securesms/util/CommunicationActions.java +++ b/src/org/thoughtcrime/securesms/util/CommunicationActions.java @@ -35,8 +35,7 @@ public class CommunicationActions { Permissions.with(activity) .request(Manifest.permission.RECORD_AUDIO, Manifest.permission.CAMERA) - .ifNecessary() - .withRationaleDialog(activity.getString(R.string.ConversationActivity_to_call_s_signal_needs_access_to_your_microphone_and_camera, recipient.toShortString()), + .withRationaleDialog(activity.getString(R.string.ConversationActivity_to_call_s_signal_needs_access_to_your_microphone_and_camera), R.drawable.ic_mic_white_48dp, R.drawable.ic_videocam_white_48dp) .withPermanentDenialDialog(activity.getString(R.string.ConversationActivity_signal_needs_the_microphone_and_camera_permissions_in_order_to_call_s, recipient.toShortString())) From 45b41219e1451d1bb21693fbcb1b3fe6000dd73c Mon Sep 17 00:00:00 2001 From: Anton Chekulaev Date: Wed, 9 Sep 2020 17:12:06 +1000 Subject: [PATCH 2/7] Media repository no longer uses deprecated API. --- res/drawable-hdpi/ic_folder_white_48dp.png | Bin 245 -> 0 bytes res/drawable-mdpi/ic_folder_white_48dp.png | Bin 181 -> 0 bytes res/drawable-xhdpi/ic_folder_white_48dp.png | Bin 325 -> 0 bytes res/drawable-xxhdpi/ic_folder_white_48dp.png | Bin 499 -> 0 bytes res/drawable-xxxhdpi/ic_folder_white_48dp.png | Bin 681 -> 0 bytes res/drawable/ic_baseline_folder_24.xml | 10 +++++ res/layout/mediapicker_folder_item.xml | 3 +- .../securesms/mediasend/MediaFolder.java | 8 +--- .../mediasend/MediaPickerFolderAdapter.java | 1 - .../securesms/mediasend/MediaRepository.java | 42 +++--------------- .../mediasend/MediaSendActivity.java | 4 +- .../securesms/mms/AttachmentManager.java | 39 +++++++++------- 12 files changed, 44 insertions(+), 63 deletions(-) delete mode 100644 res/drawable-hdpi/ic_folder_white_48dp.png delete mode 100644 res/drawable-mdpi/ic_folder_white_48dp.png delete mode 100644 res/drawable-xhdpi/ic_folder_white_48dp.png delete mode 100644 res/drawable-xxhdpi/ic_folder_white_48dp.png delete mode 100644 res/drawable-xxxhdpi/ic_folder_white_48dp.png create mode 100644 res/drawable/ic_baseline_folder_24.xml diff --git a/res/drawable-hdpi/ic_folder_white_48dp.png b/res/drawable-hdpi/ic_folder_white_48dp.png deleted file mode 100644 index b93d5a1e4a48fb9cddfa530aacfc84e5ea4ad9c9..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 245 zcmeAS@N?(olHy`uVBq!ia0vp^9w5xY0wn)GsXhaw_ISEDhGg7(d&7~dDL|z4q4_1- z7ebF0dKZ*BYIUwt;W*;>)jV%O+J|}OmjAiz^vX--1bb;NmdKI;Vst0GcUV@&Et; diff --git a/res/drawable-mdpi/ic_folder_white_48dp.png b/res/drawable-mdpi/ic_folder_white_48dp.png deleted file mode 100644 index 71a5a137c463dfd97dfad592c86b7eac773664c5..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 181 zcmeAS@N?(olHy`uVBq!ia0vp^1|ZDA0wn)(8}b0D8c!F;kch)?uX*!1I0(1~u3k7j z(@|)CtD?lCSzab=p|%h2vhJCDWr_9aDM8CMGG^>pz2)3<9-DOK6&2^5E;t7@iOv_Z za^ku1A-hLl9mAHDjh3bd#2aH97>pYj7@1i1Fnhmp-(Sr7N?SnU?z8GoH}30QFuL9E fEIT*+)p6OLF7v?Cmm9c&?qKkA^>bP0l+XkK)+RzI diff --git a/res/drawable-xhdpi/ic_folder_white_48dp.png b/res/drawable-xhdpi/ic_folder_white_48dp.png deleted file mode 100644 index a1afbe9daf5a5e3945f33915f8817be26b539148..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 325 zcmeAS@N?(olHy`uVBq!ia0vp^2_VeD0wg^q?%&M7z{un2;uunK>+P+xzK0zI93I~B z(C26sy3FL&q}$ba<$&VE(+|=Gi?KQ4VXb{{b%I@HcvNQkRwH8&_$~OogvaO4 zw%oRd*KGoKY|H)oF+L(JmZ@K7yH~M)rTJtXSd}W-{epFln3^$y#Qw2YO4`4lFD^P$~Ceo|M5hK8FSd#^MHkMkW@H6#Nv%Xop z`uOoFkL`HenK;g;u9pWO^ufBT^1bLU+-`^-9uCPcnl{j2kn()Z}QE;mo-HoefB z*Zy-}Zisf^y_D#y9#hyORXX0WRn2=FCLL&}+VQJlp9+WovC=X(&#pQ9T;}=5T@Ln_ z%3|%(!~^O#+c0lg!?_^UmUYV;M``9)?_)VHd~Imh)$}_jz~Q9ef`8{2zM8XMxzBWA zQW*O$P7VPDhX#gF?l0V62IDHR7v>-lMy3$m3oh<%mmE)$H! z3ef=JHkk37tjQO7zM9#Woi)Uuzt~P=K~v+>>CH@0wZ4Xr8TVd)ajK2!K0n)wB{^B2 z0ylRb_+K`4?xmfVnjIP5uHVdAbHnER!(F>C`4?UNaCobI)c!Y5a+}SgrcZvf`T2hK wZz{`oW;_VLU%AZk^pY1z(_z6pN$M>>-_mtyZ3drtfRV}I>FVdQ&MBb@0M_rzSO5S3 diff --git a/res/drawable-xxxhdpi/ic_folder_white_48dp.png b/res/drawable-xxxhdpi/ic_folder_white_48dp.png deleted file mode 100644 index 862a359c659632d51853ffdd9fed00ea359e0d26..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 681 zcmeAS@N?(olHy`uVBq!ia0vp^2SAvE1xWt5x}=AJfvLdL#WAGf*4w+WUZRCE$3E`V zQjT>Lxbac%MCul%qh`W!o?LgekJoN#VZJvdc57o#^S5{2w@zP<6-aqC^WuD?`R9e^ zy`TBezF|1DYTUllOX!bez1HgR53{E~nDR6I zvsS3eiNkM}1nn1oQg?4zRK4(%ux&-MUrZh6J=^snx#`a7Kqbf6hUYuuru{Daf8SeW z1@{Mw>w)z`Pa>5ZQyauPS*9}HQxOVbyWy#_f@g#Hzvn8uZ=N#np1E@l!~5JCKlYnv z<}h5nemH6w%ZjaY8yU79oB3Yv0DHCHlmrEbue}*w4N=hA3smF8LF;*ngxP73*WwP-|dd;wV_mTw}I~fk~l(;R?%-Bo{^&hlYD> zKe9j!2F4Yf8}ebSf&@4#1I|hTu|OKq;6gcY7T7e9As_B`FmV8F3lM(LF32jtz*r^y zU^$q+8c{b(VF1}lnHv9C%m20d7=N5j7eEH?x z|MRaCUffPvZc~<9Que_{`A~`2=6Qj~;+{`=++%#IO3bSA6)P-J0O`C1_v5v{7>Z3f Q;tUe?boFyt=akR{005H`djJ3c diff --git a/res/drawable/ic_baseline_folder_24.xml b/res/drawable/ic_baseline_folder_24.xml new file mode 100644 index 0000000000..dc6b080235 --- /dev/null +++ b/res/drawable/ic_baseline_folder_24.xml @@ -0,0 +1,10 @@ + + + diff --git a/res/layout/mediapicker_folder_item.xml b/res/layout/mediapicker_folder_item.xml index 9d1d729ebe..b52ace6f4a 100644 --- a/res/layout/mediapicker_folder_item.xml +++ b/res/layout/mediapicker_folder_item.xml @@ -33,7 +33,8 @@ android:layout_width="20dp" android:layout_height="20dp" android:layout_marginEnd="6dp" - android:src="@drawable/ic_folder_white_48dp"/> + android:tint="@android:color/white" + android:src="@drawable/ic_baseline_folder_24"/> - * The functionality of this class should be refactored to use - * MediaStore. */ class MediaRepository { @@ -82,30 +76,17 @@ class MediaRepository { } } - String cameraBucketId = imageFolders.getCameraBucketId() != null ? imageFolders.getCameraBucketId() : videoFolders.getCameraBucketId(); - FolderData cameraFolder = cameraBucketId != null ? folders.remove(cameraBucketId) : null; - List mediaFolders = Stream.of(folders.values()).map(folder -> new MediaFolder(folder.getThumbnail(), + List mediaFolders = Stream.of(folders.values()).map(folder -> new MediaFolder(folder.getThumbnail(), folder.getTitle(), folder.getCount(), - folder.getBucketId(), - MediaFolder.FolderType.NORMAL)) + folder.getBucketId())) .sorted((o1, o2) -> o1.getTitle().toLowerCase().compareTo(o2.getTitle().toLowerCase())) .toList(); Uri allMediaThumbnail = imageFolders.getThumbnailTimestamp() > videoFolders.getThumbnailTimestamp() ? imageFolders.getThumbnail() : videoFolders.getThumbnail(); - if (allMediaThumbnail != null) { int allMediaCount = Stream.of(mediaFolders).reduce(0, (count, folder) -> count + folder.getItemCount()); - - if (cameraFolder != null) { - allMediaCount += cameraFolder.getCount(); - } - - mediaFolders.add(0, new MediaFolder(allMediaThumbnail, context.getString(R.string.MediaRepository_all_media), allMediaCount, Media.ALL_MEDIA_BUCKET_ID, MediaFolder.FolderType.NORMAL)); - } - - if (cameraFolder != null) { - mediaFolders.add(0, new MediaFolder(cameraFolder.getThumbnail(), cameraFolder.getTitle(), cameraFolder.getCount(), cameraFolder.getBucketId(), MediaFolder.FolderType.CAMERA)); + mediaFolders.add(0, new MediaFolder(allMediaThumbnail, context.getString(R.string.MediaRepository_all_media), allMediaCount, Media.ALL_MEDIA_BUCKET_ID)); } return mediaFolders; @@ -113,8 +94,6 @@ class MediaRepository { @WorkerThread private @NonNull FolderResult getFolders(@NonNull Context context, @NonNull Uri contentUri) { - String cameraPath = Environment.getExternalStoragePublicDirectory(Environment.DIRECTORY_DCIM).getAbsolutePath() + File.separator + "Camera"; - String cameraBucketId = null; Uri globalThumbnail = null; long thumbnailTimestamp = 0; Map folders = new HashMap<>(); @@ -135,10 +114,6 @@ class MediaRepository { folder.incrementCount(); folders.put(bucketId, folder); - if (cameraBucketId == null && path.startsWith(cameraPath)) { - cameraBucketId = bucketId; - } - if (timestamp > thumbnailTimestamp) { globalThumbnail = thumbnail; thumbnailTimestamp = timestamp; @@ -146,7 +121,7 @@ class MediaRepository { } } - return new FolderResult(cameraBucketId, globalThumbnail, thumbnailTimestamp, folders); + return new FolderResult(globalThumbnail, thumbnailTimestamp, folders); } @WorkerThread @@ -284,26 +259,19 @@ class MediaRepository { } private static class FolderResult { - private final String cameraBucketId; private final Uri thumbnail; private final long thumbnailTimestamp; private final Map folderData; - private FolderResult(@Nullable String cameraBucketId, - @Nullable Uri thumbnail, + private FolderResult(@Nullable Uri thumbnail, long thumbnailTimestamp, @NonNull Map folderData) { - this.cameraBucketId = cameraBucketId; this.thumbnail = thumbnail; this.thumbnailTimestamp = thumbnailTimestamp; this.folderData = folderData; } - @Nullable String getCameraBucketId() { - return cameraBucketId; - } - @Nullable Uri getThumbnail() { return thumbnail; } diff --git a/src/org/thoughtcrime/securesms/mediasend/MediaSendActivity.java b/src/org/thoughtcrime/securesms/mediasend/MediaSendActivity.java index adfc09a060..ff1792ec1a 100644 --- a/src/org/thoughtcrime/securesms/mediasend/MediaSendActivity.java +++ b/src/org/thoughtcrime/securesms/mediasend/MediaSendActivity.java @@ -1,6 +1,8 @@ package org.thoughtcrime.securesms.mediasend; import android.Manifest; + +import androidx.lifecycle.ViewModelProvider; import androidx.lifecycle.ViewModelProviders; import android.content.Context; import android.content.Intent; @@ -133,7 +135,7 @@ public class MediaSendActivity extends PassphraseRequiredActionBarActivity imple countButtonText = findViewById(R.id.mediasend_count_button_text); cameraButton = findViewById(R.id.mediasend_camera_button); - viewModel = ViewModelProviders.of(this, new MediaSendViewModel.Factory(getApplication(), new MediaRepository())).get(MediaSendViewModel.class); + viewModel = new ViewModelProvider(this, new MediaSendViewModel.Factory(getApplication(), new MediaRepository())).get(MediaSendViewModel.class); recipient = Recipient.from(this, Address.fromSerialized(getIntent().getStringExtra(KEY_ADDRESS)), true); transport = getIntent().getParcelableExtra(KEY_TRANSPORT); diff --git a/src/org/thoughtcrime/securesms/mms/AttachmentManager.java b/src/org/thoughtcrime/securesms/mms/AttachmentManager.java index 3ae608ee8c..f6f0cbbc23 100644 --- a/src/org/thoughtcrime/securesms/mms/AttachmentManager.java +++ b/src/org/thoughtcrime/securesms/mms/AttachmentManager.java @@ -371,28 +371,34 @@ public class AttachmentManager { } public static void selectDocument(Activity activity, int requestCode) { - Permissions.with(activity) - .request(Manifest.permission.WRITE_EXTERNAL_STORAGE) - .withPermanentDenialDialog(activity.getString(R.string.AttachmentManager_signal_requires_the_external_storage_permission_in_order_to_attach_photos_videos_or_audio)) - .onAllGranted(() -> selectMediaType(activity, "*/*", null, requestCode)) - .execute(); +// Permissions.with(activity) +// .request(Manifest.permission.WRITE_EXTERNAL_STORAGE) +// .withPermanentDenialDialog(activity.getString(R.string.AttachmentManager_signal_requires_the_external_storage_permission_in_order_to_attach_photos_videos_or_audio)) +// .onAllGranted(() -> selectMediaType(activity, "*/*", null, requestCode)) +// .execute(); + + selectMediaType(activity, "*/*", null, requestCode); } public static void selectGallery(Activity activity, int requestCode, @NonNull Recipient recipient, @NonNull String body, @NonNull TransportOption transport) { - Permissions.with(activity) - .request(Manifest.permission.WRITE_EXTERNAL_STORAGE) - .withPermanentDenialDialog(activity.getString(R.string.AttachmentManager_signal_requires_the_external_storage_permission_in_order_to_attach_photos_videos_or_audio)) - .onAllGranted(() -> selectMediaType(activity, "image/*", new String[] {"image/*", "video/*"}, requestCode)) - .onAllGranted(() -> activity.startActivityForResult(MediaSendActivity.buildGalleryIntent(activity, recipient, body, transport), requestCode)) - .execute(); +// Permissions.with(activity) +// .request(Manifest.permission.WRITE_EXTERNAL_STORAGE) +// .withPermanentDenialDialog(activity.getString(R.string.AttachmentManager_signal_requires_the_external_storage_permission_in_order_to_attach_photos_videos_or_audio)) +//// .onAllGranted(() -> selectMediaType(activity, "image/*", new String[] {"image/*", "video/*"}, requestCode)) +// .onAllGranted(() -> activity.startActivityForResult(MediaSendActivity.buildGalleryIntent(activity, recipient, body, transport), requestCode)) +// .execute(); + + activity.startActivityForResult(MediaSendActivity.buildGalleryIntent(activity, recipient, body, transport), requestCode); } public static void selectAudio(Activity activity, int requestCode) { - Permissions.with(activity) - .request(Manifest.permission.WRITE_EXTERNAL_STORAGE) - .withPermanentDenialDialog(activity.getString(R.string.AttachmentManager_signal_requires_the_external_storage_permission_in_order_to_attach_photos_videos_or_audio)) - .onAllGranted(() -> selectMediaType(activity, "audio/*", null, requestCode)) - .execute(); +// Permissions.with(activity) +//// .request(Manifest.permission.WRITE_EXTERNAL_STORAGE) +//// .withPermanentDenialDialog(activity.getString(R.string.AttachmentManager_signal_requires_the_external_storage_permission_in_order_to_attach_photos_videos_or_audio)) +//// .onAllGranted(() -> selectMediaType(activity, "audio/*", null, requestCode)) +//// .execute(); + + selectMediaType(activity, "audio/*", null, requestCode); } public static void selectContactInfo(Activity activity, int requestCode) { @@ -463,6 +469,7 @@ public class AttachmentManager { } private static void selectMediaType(Activity activity, @NonNull String type, @Nullable String[] extraMimeType, int requestCode) { + //TODO Constrain media file size to match the Loki protocol limit. final Intent intent = new Intent(); intent.setType(type); From 51032d43c4a98ac674b21809d5f6cc75e88beb3f Mon Sep 17 00:00:00 2001 From: Anton Chekulaev Date: Wed, 9 Sep 2020 18:09:07 +1000 Subject: [PATCH 3/7] Cleanup --- .../securesms/mediasend/MediaRepository.java | 7 ++--- .../securesms/mms/AttachmentManager.java | 26 +++++-------------- 2 files changed, 10 insertions(+), 23 deletions(-) diff --git a/src/org/thoughtcrime/securesms/mediasend/MediaRepository.java b/src/org/thoughtcrime/securesms/mediasend/MediaRepository.java index ce33ff723a..ffdfecbac5 100644 --- a/src/org/thoughtcrime/securesms/mediasend/MediaRepository.java +++ b/src/org/thoughtcrime/securesms/mediasend/MediaRepository.java @@ -138,7 +138,8 @@ class MediaRepository { } @WorkerThread - private @NonNull List getMediaInBucket(@NonNull Context context, @NonNull String bucketId, @NonNull Uri contentUri, boolean hasOrienation) { + private @NonNull List getMediaInBucket(@NonNull Context context, @NonNull String bucketId, @NonNull Uri contentUri, boolean hasOrientation) { + //TODO Constrain media file size to match the Loki protocol limit. List media = new LinkedList<>(); String selection = Images.Media.BUCKET_ID + " = ? AND " + Images.Media.DATA + " NOT NULL"; String[] selectionArgs = new String[] { bucketId }; @@ -146,7 +147,7 @@ class MediaRepository { String[] projection; - if (hasOrienation) { + if (hasOrientation) { projection = new String[]{Images.Media._ID, Images.Media.MIME_TYPE, Images.Media.DATE_TAKEN, Images.Media.ORIENTATION, Images.Media.WIDTH, Images.Media.HEIGHT, Images.Media.SIZE}; } else { projection = new String[]{Images.Media._ID, Images.Media.MIME_TYPE, Images.Media.DATE_TAKEN, Images.Media.WIDTH, Images.Media.HEIGHT, Images.Media.SIZE}; @@ -162,7 +163,7 @@ class MediaRepository { Uri uri = Uri.withAppendedPath(contentUri, cursor.getString(cursor.getColumnIndexOrThrow(Images.Media._ID))); String mimetype = cursor.getString(cursor.getColumnIndexOrThrow(Images.Media.MIME_TYPE)); long dateTaken = cursor.getLong(cursor.getColumnIndexOrThrow(Images.Media.DATE_TAKEN)); - int orientation = hasOrienation ? cursor.getInt(cursor.getColumnIndexOrThrow(Images.Media.ORIENTATION)) : 0; + int orientation = hasOrientation ? cursor.getInt(cursor.getColumnIndexOrThrow(Images.Media.ORIENTATION)) : 0; int width = cursor.getInt(cursor.getColumnIndexOrThrow(getWidthColumn(orientation))); int height = cursor.getInt(cursor.getColumnIndexOrThrow(getHeightColumn(orientation))); long size = cursor.getLong(cursor.getColumnIndexOrThrow(Images.Media.SIZE)); diff --git a/src/org/thoughtcrime/securesms/mms/AttachmentManager.java b/src/org/thoughtcrime/securesms/mms/AttachmentManager.java index f6f0cbbc23..60c6628203 100644 --- a/src/org/thoughtcrime/securesms/mms/AttachmentManager.java +++ b/src/org/thoughtcrime/securesms/mms/AttachmentManager.java @@ -371,33 +371,19 @@ public class AttachmentManager { } public static void selectDocument(Activity activity, int requestCode) { -// Permissions.with(activity) -// .request(Manifest.permission.WRITE_EXTERNAL_STORAGE) -// .withPermanentDenialDialog(activity.getString(R.string.AttachmentManager_signal_requires_the_external_storage_permission_in_order_to_attach_photos_videos_or_audio)) -// .onAllGranted(() -> selectMediaType(activity, "*/*", null, requestCode)) -// .execute(); - selectMediaType(activity, "*/*", null, requestCode); } public static void selectGallery(Activity activity, int requestCode, @NonNull Recipient recipient, @NonNull String body, @NonNull TransportOption transport) { -// Permissions.with(activity) -// .request(Manifest.permission.WRITE_EXTERNAL_STORAGE) -// .withPermanentDenialDialog(activity.getString(R.string.AttachmentManager_signal_requires_the_external_storage_permission_in_order_to_attach_photos_videos_or_audio)) -//// .onAllGranted(() -> selectMediaType(activity, "image/*", new String[] {"image/*", "video/*"}, requestCode)) -// .onAllGranted(() -> activity.startActivityForResult(MediaSendActivity.buildGalleryIntent(activity, recipient, body, transport), requestCode)) -// .execute(); - - activity.startActivityForResult(MediaSendActivity.buildGalleryIntent(activity, recipient, body, transport), requestCode); + Permissions.with(activity) + .request(Manifest.permission.WRITE_EXTERNAL_STORAGE) + .withPermanentDenialDialog(activity.getString(R.string.AttachmentManager_signal_requires_the_external_storage_permission_in_order_to_attach_photos_videos_or_audio)) +// .onAllGranted(() -> selectMediaType(activity, "image/*", new String[] {"image/*", "video/*"}, requestCode)) + .onAllGranted(() -> activity.startActivityForResult(MediaSendActivity.buildGalleryIntent(activity, recipient, body, transport), requestCode)) + .execute(); } public static void selectAudio(Activity activity, int requestCode) { -// Permissions.with(activity) -//// .request(Manifest.permission.WRITE_EXTERNAL_STORAGE) -//// .withPermanentDenialDialog(activity.getString(R.string.AttachmentManager_signal_requires_the_external_storage_permission_in_order_to_attach_photos_videos_or_audio)) -//// .onAllGranted(() -> selectMediaType(activity, "audio/*", null, requestCode)) -//// .execute(); - selectMediaType(activity, "audio/*", null, requestCode); } From 4e909ff3b96d4af6337a07f484ad2ef88d1d1dad Mon Sep 17 00:00:00 2001 From: Anton Chekulaev Date: Wed, 9 Sep 2020 18:21:35 +1000 Subject: [PATCH 4/7] Write external storage permission replaced with the read one. --- src/org/thoughtcrime/securesms/CreateProfileActivity.java | 8 ++++---- .../securesms/database/loaders/RecentPhotosLoader.java | 2 +- src/org/thoughtcrime/securesms/mms/AttachmentManager.java | 2 +- 3 files changed, 6 insertions(+), 6 deletions(-) diff --git a/src/org/thoughtcrime/securesms/CreateProfileActivity.java b/src/org/thoughtcrime/securesms/CreateProfileActivity.java index 9be993d218..57836e6bd6 100644 --- a/src/org/thoughtcrime/securesms/CreateProfileActivity.java +++ b/src/org/thoughtcrime/securesms/CreateProfileActivity.java @@ -213,10 +213,10 @@ public class CreateProfileActivity extends BaseActionBarActivity implements Inje this.reveal = ViewUtil.findById(this, R.id.reveal); this.nextIntent = getIntent().getParcelableExtra(NEXT_INTENT); - this.avatar.setOnClickListener(view -> Permissions.with(this) - .request(Manifest.permission.CAMERA, Manifest.permission.WRITE_EXTERNAL_STORAGE) - .onAnyResult(this::startAvatarSelection) - .execute()); +// this.avatar.setOnClickListener(view -> Permissions.with(this) +// .request(Manifest.permission.CAMERA, Manifest.permission.WRITE_EXTERNAL_STORAGE) +// .onAnyResult(this::startAvatarSelection) +// .execute()); this.name.getInput().addTextChangedListener(new TextWatcher() { @Override diff --git a/src/org/thoughtcrime/securesms/database/loaders/RecentPhotosLoader.java b/src/org/thoughtcrime/securesms/database/loaders/RecentPhotosLoader.java index 7ed0184e8e..21ed07ac66 100644 --- a/src/org/thoughtcrime/securesms/database/loaders/RecentPhotosLoader.java +++ b/src/org/thoughtcrime/securesms/database/loaders/RecentPhotosLoader.java @@ -35,7 +35,7 @@ public class RecentPhotosLoader extends CursorLoader { @Override public Cursor loadInBackground() { - if (Permissions.hasAll(context, Manifest.permission.WRITE_EXTERNAL_STORAGE)) { + if (Permissions.hasAll(context, Manifest.permission.READ_EXTERNAL_STORAGE)) { return context.getContentResolver().query(MediaStore.Images.Media.EXTERNAL_CONTENT_URI, PROJECTION, null, null, MediaStore.Images.ImageColumns.DATE_MODIFIED + " DESC"); diff --git a/src/org/thoughtcrime/securesms/mms/AttachmentManager.java b/src/org/thoughtcrime/securesms/mms/AttachmentManager.java index 60c6628203..16c0d44ed0 100644 --- a/src/org/thoughtcrime/securesms/mms/AttachmentManager.java +++ b/src/org/thoughtcrime/securesms/mms/AttachmentManager.java @@ -376,7 +376,7 @@ public class AttachmentManager { public static void selectGallery(Activity activity, int requestCode, @NonNull Recipient recipient, @NonNull String body, @NonNull TransportOption transport) { Permissions.with(activity) - .request(Manifest.permission.WRITE_EXTERNAL_STORAGE) + .request(Manifest.permission.READ_EXTERNAL_STORAGE) .withPermanentDenialDialog(activity.getString(R.string.AttachmentManager_signal_requires_the_external_storage_permission_in_order_to_attach_photos_videos_or_audio)) // .onAllGranted(() -> selectMediaType(activity, "image/*", new String[] {"image/*", "video/*"}, requestCode)) .onAllGranted(() -> activity.startActivityForResult(MediaSendActivity.buildGalleryIntent(activity, recipient, body, transport), requestCode)) From 7319ac46824cdc3edab655ebf51ccaff235cbe8c Mon Sep 17 00:00:00 2001 From: Anton Chekulaev Date: Thu, 10 Sep 2020 16:32:13 +1000 Subject: [PATCH 5/7] Attachment save task uses MediaStore API now. --- .../securesms/MediaOverviewActivity.java | 69 +++--- .../securesms/MediaPreviewActivity.java | 33 +-- .../conversation/ConversationFragment.java | 40 +-- .../securesms/permissions/Permissions.java | 23 +- .../securesms/util/SaveAttachmentTask.java | 234 ------------------ .../securesms/util/SaveAttachmentTask.kt | 196 +++++++++++++++ .../util/task/ProgressDialogAsyncTask.java | 8 +- 7 files changed, 299 insertions(+), 304 deletions(-) delete mode 100644 src/org/thoughtcrime/securesms/util/SaveAttachmentTask.java create mode 100644 src/org/thoughtcrime/securesms/util/SaveAttachmentTask.kt diff --git a/src/org/thoughtcrime/securesms/MediaOverviewActivity.java b/src/org/thoughtcrime/securesms/MediaOverviewActivity.java index 54b80a60c1..3bb2b6eb77 100644 --- a/src/org/thoughtcrime/securesms/MediaOverviewActivity.java +++ b/src/org/thoughtcrime/securesms/MediaOverviewActivity.java @@ -16,7 +16,6 @@ */ package org.thoughtcrime.securesms; -import android.Manifest; import android.annotation.SuppressLint; import android.content.Context; import android.content.Intent; @@ -324,46 +323,48 @@ public class MediaOverviewActivity extends PassphraseRequiredActionBarActivity { } @SuppressWarnings("CodeBlock2Expr") - @SuppressLint({"InlinedApi","StaticFieldLeak"}) + @SuppressLint({"InlinedApi", "StaticFieldLeak"}) private void handleSaveMedia(@NonNull Collection mediaRecords) { final Context context = getContext(); + SaveAttachmentTask.showWarningDialog(context, (dialogInterface, which) -> { Permissions.with(this) - .request(android.Manifest.permission.WRITE_EXTERNAL_STORAGE, Manifest.permission.READ_EXTERNAL_STORAGE) - .withPermanentDenialDialog(getString(R.string.MediaPreviewActivity_signal_needs_the_storage_permission_in_order_to_write_to_external_storage_but_it_has_been_permanently_denied)) - .onAnyDenied(() -> Toast.makeText(getContext(), R.string.MediaPreviewActivity_unable_to_write_to_external_storage_without_permission, Toast.LENGTH_LONG).show()) - .onAllGranted(() -> { - new ProgressDialogAsyncTask>(context, - R.string.MediaOverviewActivity_collecting_attachments, - R.string.please_wait) { - @Override - protected List doInBackground(Void... params) { - List attachments = new LinkedList<>(); + .request(android.Manifest.permission.WRITE_EXTERNAL_STORAGE) + .maxSdkVersion(Build.VERSION_CODES.P) + .withPermanentDenialDialog(getString(R.string.MediaPreviewActivity_signal_needs_the_storage_permission_in_order_to_write_to_external_storage_but_it_has_been_permanently_denied)) + .onAnyDenied(() -> Toast.makeText(getContext(), R.string.MediaPreviewActivity_unable_to_write_to_external_storage_without_permission, Toast.LENGTH_LONG).show()) + .onAllGranted(() -> { + new ProgressDialogAsyncTask>( + context, + R.string.MediaOverviewActivity_collecting_attachments, + R.string.please_wait) { + @Override + protected List doInBackground(Void... params) { + List attachments = new LinkedList<>(); - for (MediaDatabase.MediaRecord mediaRecord : mediaRecords) { - if (mediaRecord.getAttachment().getDataUri() != null) { - attachments.add(new SaveAttachmentTask.Attachment(mediaRecord.getAttachment().getDataUri(), - mediaRecord.getContentType(), - mediaRecord.getDate(), - mediaRecord.getAttachment().getFileName())); - } - } + for (MediaDatabase.MediaRecord mediaRecord : mediaRecords) { + if (mediaRecord.getAttachment().getDataUri() != null) { + attachments.add(new SaveAttachmentTask.Attachment(mediaRecord.getAttachment().getDataUri(), + mediaRecord.getContentType(), + mediaRecord.getDate(), + mediaRecord.getAttachment().getFileName())); + } + } - return attachments; - } + return attachments; + } - @Override - protected void onPostExecute(List attachments) { - super.onPostExecute(attachments); - SaveAttachmentTask saveTask = new SaveAttachmentTask(context, - attachments.size()); - saveTask.executeOnExecutor(THREAD_POOL_EXECUTOR, - attachments.toArray(new SaveAttachmentTask.Attachment[attachments.size()])); - actionMode.finish(); - } - }.execute(); - }) - .execute(); + @Override + protected void onPostExecute(List attachments) { + super.onPostExecute(attachments); + SaveAttachmentTask saveTask = new SaveAttachmentTask(context, attachments.size()); + saveTask.executeOnExecutor(THREAD_POOL_EXECUTOR, + attachments.toArray(new SaveAttachmentTask.Attachment[attachments.size()])); + actionMode.finish(); + } + }.execute(); + }) + .execute(); }, mediaRecords.size()); } diff --git a/src/org/thoughtcrime/securesms/MediaPreviewActivity.java b/src/org/thoughtcrime/securesms/MediaPreviewActivity.java index 36117ea86a..429b39c00e 100644 --- a/src/org/thoughtcrime/securesms/MediaPreviewActivity.java +++ b/src/org/thoughtcrime/securesms/MediaPreviewActivity.java @@ -16,17 +16,16 @@ */ package org.thoughtcrime.securesms; -import android.Manifest; import android.annotation.SuppressLint; import android.annotation.TargetApi; -import androidx.appcompat.widget.Toolbar; import androidx.lifecycle.ViewModelProviders; import android.content.Context; import android.content.Intent; import android.database.Cursor; import android.net.Uri; import android.os.AsyncTask; +import android.os.Build; import android.os.Build.VERSION; import android.os.Build.VERSION_CODES; import android.os.Bundle; @@ -341,21 +340,23 @@ public class MediaPreviewActivity extends PassphraseRequiredActionBarActivity im @SuppressLint("InlinedApi") private void saveToDisk() { MediaItem mediaItem = getCurrentMediaItem(); + if (mediaItem == null) return; - if (mediaItem != null) { - SaveAttachmentTask.showWarningDialog(this, (dialogInterface, i) -> { - Permissions.with(this) - .request(Manifest.permission.WRITE_EXTERNAL_STORAGE, Manifest.permission.READ_EXTERNAL_STORAGE) - .withPermanentDenialDialog(getString(R.string.MediaPreviewActivity_signal_needs_the_storage_permission_in_order_to_write_to_external_storage_but_it_has_been_permanently_denied)) - .onAnyDenied(() -> Toast.makeText(this, R.string.MediaPreviewActivity_unable_to_write_to_external_storage_without_permission, Toast.LENGTH_LONG).show()) - .onAllGranted(() -> { - SaveAttachmentTask saveTask = new SaveAttachmentTask(MediaPreviewActivity.this); - long saveDate = (mediaItem.date > 0) ? mediaItem.date : System.currentTimeMillis(); - saveTask.executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR, new Attachment(mediaItem.uri, mediaItem.type, saveDate, null)); - }) - .execute(); - }); - } + SaveAttachmentTask.showWarningDialog(this, (dialogInterface, i) -> { + Permissions.with(this) + .request(android.Manifest.permission.WRITE_EXTERNAL_STORAGE) + .maxSdkVersion(Build.VERSION_CODES.P) + .withPermanentDenialDialog(getString(R.string.MediaPreviewActivity_signal_needs_the_storage_permission_in_order_to_write_to_external_storage_but_it_has_been_permanently_denied)) + .onAnyDenied(() -> Toast.makeText(this, R.string.MediaPreviewActivity_unable_to_write_to_external_storage_without_permission, Toast.LENGTH_LONG).show()) + .onAllGranted(() -> { + SaveAttachmentTask saveTask = new SaveAttachmentTask(MediaPreviewActivity.this); + long saveDate = (mediaItem.date > 0) ? mediaItem.date : System.currentTimeMillis(); + saveTask.executeOnExecutor( + AsyncTask.THREAD_POOL_EXECUTOR, + new Attachment(mediaItem.uri, mediaItem.type, saveDate, null)); + }) + .execute(); + }); } @SuppressLint("StaticFieldLeak") diff --git a/src/org/thoughtcrime/securesms/conversation/ConversationFragment.java b/src/org/thoughtcrime/securesms/conversation/ConversationFragment.java index eeb1c3cc0a..8ee693adff 100644 --- a/src/org/thoughtcrime/securesms/conversation/ConversationFragment.java +++ b/src/org/thoughtcrime/securesms/conversation/ConversationFragment.java @@ -85,6 +85,7 @@ import org.thoughtcrime.securesms.mms.GlideApp; import org.thoughtcrime.securesms.mms.OutgoingMediaMessage; import org.thoughtcrime.securesms.mms.PartAuthority; import org.thoughtcrime.securesms.mms.Slide; +import org.thoughtcrime.securesms.permissions.Permissions; import org.thoughtcrime.securesms.profiles.UnknownSenderView; import org.thoughtcrime.securesms.recipients.Recipient; import org.thoughtcrime.securesms.sms.MessageSender; @@ -658,23 +659,30 @@ public class ConversationFragment extends Fragment } private void handleSaveAttachment(final MediaMmsMessageRecord message) { - SaveAttachmentTask.showWarningDialog(getActivity(), new DialogInterface.OnClickListener() { - public void onClick(DialogInterface dialog, int which) { - List attachments = Stream.of(message.getSlideDeck().getSlides()) - .filter(s -> s.getUri() != null && (s.hasImage() || s.hasVideo() || s.hasAudio() || s.hasDocument())) - .map(s -> new SaveAttachmentTask.Attachment(s.getUri(), s.getContentType(), message.getDateReceived(), s.getFileName().orNull())) - .toList(); - if (!Util.isEmpty(attachments)) { - SaveAttachmentTask saveTask = new SaveAttachmentTask(getActivity()); - saveTask.executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR, attachments.toArray(new SaveAttachmentTask.Attachment[0])); - return; - } + SaveAttachmentTask.showWarningDialog(getActivity(), (dialog, which) -> { + Permissions.with(this) + .request(android.Manifest.permission.WRITE_EXTERNAL_STORAGE) + .maxSdkVersion(Build.VERSION_CODES.P) + .withPermanentDenialDialog(getString(R.string.MediaPreviewActivity_signal_needs_the_storage_permission_in_order_to_write_to_external_storage_but_it_has_been_permanently_denied)) + .onAnyDenied(() -> Toast.makeText(getContext(), R.string.MediaPreviewActivity_unable_to_write_to_external_storage_without_permission, Toast.LENGTH_LONG).show()) + .onAllGranted(() -> { + List attachments = + Stream.of(message.getSlideDeck().getSlides()) + .filter(s -> s.getUri() != null && (s.hasImage() || s.hasVideo() || s.hasAudio() || s.hasDocument())) + .map(s -> new SaveAttachmentTask.Attachment(s.getUri(), s.getContentType(), message.getDateReceived(), s.getFileName().orNull())) + .toList(); + if (!Util.isEmpty(attachments)) { + SaveAttachmentTask saveTask = new SaveAttachmentTask(getActivity()); + saveTask.executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR, attachments.toArray(new SaveAttachmentTask.Attachment[0])); + return; + } - Log.w(TAG, "No slide with attachable media found, failing nicely."); - Toast.makeText(getActivity(), - getResources().getQuantityString(R.plurals.ConversationFragment_error_while_saving_attachments_to_sd_card, 1), - Toast.LENGTH_LONG).show(); - } + Log.w(TAG, "No slide with attachable media found, failing nicely."); + Toast.makeText(getActivity(), + getResources().getQuantityString(R.plurals.ConversationFragment_error_while_saving_attachments_to_sd_card, 1), + Toast.LENGTH_LONG).show(); + }) + .execute(); }); } diff --git a/src/org/thoughtcrime/securesms/permissions/Permissions.java b/src/org/thoughtcrime/securesms/permissions/Permissions.java index 91eec1e011..41cce6f8ef 100644 --- a/src/org/thoughtcrime/securesms/permissions/Permissions.java +++ b/src/org/thoughtcrime/securesms/permissions/Permissions.java @@ -62,6 +62,9 @@ public class Permissions { private @DrawableRes int[] rationalDialogHeader; private String rationaleDialogMessage; + private int minSdkVersion = 0; + private int maxSdkVersion = Integer.MAX_VALUE; + PermissionsBuilder(PermissionObject permissionObject) { this.permissionObject = permissionObject; } @@ -117,11 +120,29 @@ public class Permissions { return this; } + /** + * Min Android SDK version to request the permissions for (inclusive). + */ + public PermissionsBuilder minSdkVersion(int minSdkVersion) { + this.minSdkVersion = minSdkVersion; + return this; + } + + /** + * Max Android SDK version to request the permissions for (inclusive). + */ + public PermissionsBuilder maxSdkVersion(int maxSdkVersion) { + this.maxSdkVersion = maxSdkVersion; + return this; + } + public void execute() { PermissionsRequest request = new PermissionsRequest(allGrantedListener, anyDeniedListener, anyPermanentlyDeniedListener, anyResultListener, someGrantedListener, someDeniedListener, somePermanentlyDeniedListener); - if (permissionObject.hasAll(requestedPermissions)) { + boolean targetSdk = Build.VERSION.SDK_INT >= minSdkVersion && Build.VERSION.SDK_INT <= maxSdkVersion; + + if (!targetSdk || permissionObject.hasAll(requestedPermissions)) { executePreGrantedPermissionsRequest(request); } else if (rationaleDialogMessage != null && rationalDialogHeader != null) { executePermissionsRequestWithRationale(request); diff --git a/src/org/thoughtcrime/securesms/util/SaveAttachmentTask.java b/src/org/thoughtcrime/securesms/util/SaveAttachmentTask.java deleted file mode 100644 index c4daadf36e..0000000000 --- a/src/org/thoughtcrime/securesms/util/SaveAttachmentTask.java +++ /dev/null @@ -1,234 +0,0 @@ -package org.thoughtcrime.securesms.util; - -import android.content.Context; -import android.content.DialogInterface.OnClickListener; -import android.media.MediaScannerConnection; -import android.net.Uri; -import androidx.annotation.NonNull; -import androidx.annotation.Nullable; -import androidx.appcompat.app.AlertDialog; -import android.text.TextUtils; -import android.webkit.MimeTypeMap; -import android.widget.Toast; - -import network.loki.messenger.R; -import org.thoughtcrime.securesms.database.NoExternalStorageException; -import org.thoughtcrime.securesms.logging.Log; -import org.thoughtcrime.securesms.mms.PartAuthority; -import org.thoughtcrime.securesms.util.task.ProgressDialogAsyncTask; -import org.whispersystems.libsignal.util.Pair; - -import java.io.File; -import java.io.FileOutputStream; -import java.io.IOException; -import java.io.InputStream; -import java.io.OutputStream; -import java.lang.ref.WeakReference; -import java.text.SimpleDateFormat; - -public class SaveAttachmentTask extends ProgressDialogAsyncTask> { - private static final String TAG = SaveAttachmentTask.class.getSimpleName(); - - static final int SUCCESS = 0; - private static final int FAILURE = 1; - private static final int WRITE_ACCESS_FAILURE = 2; - - private final WeakReference contextReference; - - private final int attachmentCount; - - public SaveAttachmentTask(Context context) { - this(context, 1); - } - - public SaveAttachmentTask(Context context, int count) { - super(context, - context.getResources().getQuantityString(R.plurals.ConversationFragment_saving_n_attachments, count, count), - context.getResources().getQuantityString(R.plurals.ConversationFragment_saving_n_attachments_to_sd_card, count, count)); - this.contextReference = new WeakReference<>(context); - this.attachmentCount = count; - } - - @Override - protected Pair doInBackground(SaveAttachmentTask.Attachment... attachments) { - if (attachments == null || attachments.length == 0) { - throw new AssertionError("must pass in at least one attachment"); - } - - try { - Context context = contextReference.get(); - String directory = null; - - if (context == null) { - return new Pair<>(FAILURE, null); - } - - for (Attachment attachment : attachments) { - if (attachment != null) { - directory = saveAttachment(context, attachment); - if (directory == null) return new Pair<>(FAILURE, null); - } - } - - if (attachments.length > 1) return new Pair<>(SUCCESS, null); - else return new Pair<>(SUCCESS, directory); - } catch (NoExternalStorageException|IOException ioe) { - Log.w(TAG, ioe); - return new Pair<>(FAILURE, null); - } - } - - private @Nullable String saveAttachment(Context context, Attachment attachment) - throws NoExternalStorageException, IOException - { - String contentType = MediaUtil.getCorrectedMimeType(attachment.contentType); - String fileName = attachment.fileName; - - if (fileName == null) fileName = generateOutputFileName(contentType, attachment.date); - fileName = sanitizeOutputFileName(fileName); - - File outputDirectory = createOutputDirectoryFromContentType(contentType); - File mediaFile = createOutputFile(outputDirectory, fileName); - InputStream inputStream = PartAuthority.getAttachmentStream(context, attachment.uri); - - if (inputStream == null) { - return null; - } - - OutputStream outputStream = new FileOutputStream(mediaFile); - Util.copy(inputStream, outputStream); - - MediaScannerConnection.scanFile(context, new String[]{mediaFile.getAbsolutePath()}, - new String[]{contentType}, null); - - return outputDirectory.getName(); - } - - private File createOutputDirectoryFromContentType(@NonNull String contentType) - throws NoExternalStorageException - { - File outputDirectory; - - if (contentType.startsWith("video/")) { - outputDirectory = ExternalStorageUtil.getVideoDir(getContext()); - } else if (contentType.startsWith("audio/")) { - outputDirectory = ExternalStorageUtil.getAudioDir(getContext()); - } else if (contentType.startsWith("image/")) { - outputDirectory = ExternalStorageUtil.getImageDir(getContext()); - } else { - outputDirectory = ExternalStorageUtil.getDownloadDir(getContext()); - } - - if (!outputDirectory.mkdirs()) Log.w(TAG, "mkdirs() returned false, attempting to continue"); - return outputDirectory; - } - - private String generateOutputFileName(@NonNull String contentType, long timestamp) { - MimeTypeMap mimeTypeMap = MimeTypeMap.getSingleton(); - String extension = mimeTypeMap.getExtensionFromMimeType(contentType); - SimpleDateFormat dateFormatter = new SimpleDateFormat("yyyy-MM-dd-HHmmss"); - String base = "signal-" + dateFormatter.format(timestamp); - - if (extension == null) extension = "attach"; - - return base + "." + extension; - } - - private String sanitizeOutputFileName(@NonNull String fileName) { - return new File(fileName).getName(); - } - - private File createOutputFile(@NonNull File outputDirectory, @NonNull String fileName) - throws IOException - { - String[] fileParts = getFileNameParts(fileName); - String base = fileParts[0]; - String extension = fileParts[1]; - - File outputFile = new File(outputDirectory, base + "." + extension); - - int i = 0; - while (outputFile.exists()) { - outputFile = new File(outputDirectory, base + "-" + (++i) + "." + extension); - } - - if (outputFile.isHidden()) { - throw new IOException("Specified name would not be visible"); - } - - return outputFile; - } - - private String[] getFileNameParts(String fileName) { - String[] result = new String[2]; - String[] tokens = fileName.split("\\.(?=[^\\.]+$)"); - - result[0] = tokens[0]; - - if (tokens.length > 1) result[1] = tokens[1]; - else result[1] = ""; - - return result; - } - - @Override - protected void onPostExecute(final Pair result) { - super.onPostExecute(result); - final Context context = contextReference.get(); - if (context == null) return; - - switch (result.first()) { - case FAILURE: - Toast.makeText(context, - context.getResources().getQuantityText(R.plurals.ConversationFragment_error_while_saving_attachments_to_sd_card, - attachmentCount), - Toast.LENGTH_LONG).show(); - break; - case SUCCESS: - String message = !TextUtils.isEmpty(result.second()) ? context.getResources().getString(R.string.SaveAttachmentTask_saved_to, result.second()) - : context.getResources().getString(R.string.SaveAttachmentTask_saved); - Toast.makeText(context, message, Toast.LENGTH_LONG).show(); - break; - case WRITE_ACCESS_FAILURE: - Toast.makeText(context, R.string.ConversationFragment_unable_to_write_to_sd_card_exclamation, - Toast.LENGTH_LONG).show(); - break; - } - } - - public static class Attachment { - public Uri uri; - public String fileName; - public String contentType; - public long date; - - public Attachment(@NonNull Uri uri, @NonNull String contentType, - long date, @Nullable String fileName) - { - if (uri == null || contentType == null || date < 0) { - throw new AssertionError("uri, content type, and date must all be specified"); - } - this.uri = uri; - this.fileName = fileName; - this.contentType = contentType; - this.date = date; - } - } - - public static void showWarningDialog(Context context, OnClickListener onAcceptListener) { - showWarningDialog(context, onAcceptListener, 1); - } - - public static void showWarningDialog(Context context, OnClickListener onAcceptListener, int count) { - AlertDialog.Builder builder = new AlertDialog.Builder(context); - builder.setTitle(R.string.ConversationFragment_save_to_sd_card); - builder.setIconAttribute(R.attr.dialog_alert_icon); - builder.setCancelable(true); - builder.setMessage(context.getResources().getQuantityString(R.plurals.ConversationFragment_saving_n_media_to_storage_warning, - count, count)); - builder.setPositiveButton(R.string.yes, onAcceptListener); - builder.setNegativeButton(R.string.no, null); - builder.show(); - } -} - diff --git a/src/org/thoughtcrime/securesms/util/SaveAttachmentTask.kt b/src/org/thoughtcrime/securesms/util/SaveAttachmentTask.kt new file mode 100644 index 0000000000..6e4938dd8c --- /dev/null +++ b/src/org/thoughtcrime/securesms/util/SaveAttachmentTask.kt @@ -0,0 +1,196 @@ +package org.thoughtcrime.securesms.util + +import android.content.ContentValues +import android.content.Context +import android.content.DialogInterface.OnClickListener +import android.net.Uri +import android.os.Build +import android.provider.MediaStore +import android.text.TextUtils +import android.webkit.MimeTypeMap +import android.widget.Toast +import androidx.appcompat.app.AlertDialog +import network.loki.messenger.R +import org.thoughtcrime.securesms.logging.Log +import org.thoughtcrime.securesms.mms.PartAuthority +import org.thoughtcrime.securesms.util.task.ProgressDialogAsyncTask +import java.io.File +import java.io.IOException +import java.lang.ref.WeakReference +import java.text.SimpleDateFormat + +/** + * Saves attachment files to an external storage using [MediaStore] API. + */ +class SaveAttachmentTask : ProgressDialogAsyncTask> { + + companion object { + @JvmStatic + private val TAG = SaveAttachmentTask::class.simpleName + + private const val RESULT_SUCCESS = 0 + private const val RESULT_FAILURE = 1 + + @JvmStatic + @JvmOverloads + fun showWarningDialog(context: Context, onAcceptListener: OnClickListener, count: Int = 1) { + val builder = AlertDialog.Builder(context) + builder.setTitle(R.string.ConversationFragment_save_to_sd_card) + builder.setIconAttribute(R.attr.dialog_alert_icon) + builder.setCancelable(true) + builder.setMessage(context.resources.getQuantityString( + R.plurals.ConversationFragment_saving_n_media_to_storage_warning, + count, + count)) + builder.setPositiveButton(R.string.yes, onAcceptListener) + builder.setNegativeButton(R.string.no, null) + builder.show() + } + } + + private val contextReference: WeakReference + private val attachmentCount: Int + + @JvmOverloads + constructor(context: Context, count: Int = 1): super(context, + context.resources.getQuantityString(R.plurals.ConversationFragment_saving_n_attachments, count, count), + context.resources.getQuantityString(R.plurals.ConversationFragment_saving_n_attachments_to_sd_card, count, count)) { + this.contextReference = WeakReference(context) + this.attachmentCount = count + } + + override fun doInBackground(vararg attachments: Attachment?): Pair { + if (attachments.isEmpty()) { + throw IllegalArgumentException("Must pass in at least one attachment") + } + + try { + val context = contextReference.get() + var directory: String? = null + + if (context == null) { + return Pair(RESULT_FAILURE, null) + } + + for (attachment in attachments) { + if (attachment != null) { + directory = saveAttachment(context, attachment) + if (directory == null) return Pair(RESULT_FAILURE, null) + } + } + + return if (attachments.size > 1) + Pair(RESULT_SUCCESS, null) + else + Pair(RESULT_SUCCESS, directory) + } catch (e: IOException) { + Log.w(TAG, e) + return Pair(RESULT_FAILURE, null) + } + } + + @Throws(IOException::class) + private fun saveAttachment(context: Context, attachment: Attachment): String? { + val resolver = context.contentResolver + + val contentType = MediaUtil.getCorrectedMimeType(attachment.contentType)!! + val fileName = attachment.fileName + ?: sanitizeOutputFileName(generateOutputFileName(contentType, attachment.date)) + + val mediaRecord = ContentValues() + val mediaVolume = if (Build.VERSION.SDK_INT <= Build.VERSION_CODES.P) { + MediaStore.VOLUME_EXTERNAL + } else { + MediaStore.VOLUME_EXTERNAL_PRIMARY + } + val collectionUri: Uri + + when { + contentType.startsWith("video/") -> { + collectionUri = MediaStore.Video.Media.getContentUri(mediaVolume) + mediaRecord.put(MediaStore.Video.Media.DISPLAY_NAME, fileName) + mediaRecord.put(MediaStore.Video.Media.MIME_TYPE, contentType) + // Add the date meta data to ensure the image is added at the front of the gallery + mediaRecord.put(MediaStore.Video.Media.DATE_ADDED, System.currentTimeMillis()) + mediaRecord.put(MediaStore.Video.Media.DATE_TAKEN, System.currentTimeMillis()) + + } + contentType.startsWith("audio/") -> { + collectionUri = MediaStore.Audio.Media.getContentUri(mediaVolume) + mediaRecord.put(MediaStore.Audio.Media.DISPLAY_NAME, fileName) + mediaRecord.put(MediaStore.Audio.Media.MIME_TYPE, contentType) + mediaRecord.put(MediaStore.Audio.Media.DATE_ADDED, System.currentTimeMillis()) + mediaRecord.put(MediaStore.Audio.Media.DATE_TAKEN, System.currentTimeMillis()) + + } + contentType.startsWith("image/") -> { + collectionUri = MediaStore.Images.Media.getContentUri(mediaVolume) + mediaRecord.put(MediaStore.Images.Media.TITLE, fileName) + mediaRecord.put(MediaStore.Images.Media.DISPLAY_NAME, fileName) + mediaRecord.put(MediaStore.Images.Media.MIME_TYPE, contentType) + mediaRecord.put(MediaStore.Images.Media.DATE_ADDED, System.currentTimeMillis()) + mediaRecord.put(MediaStore.Images.Media.DATE_TAKEN, System.currentTimeMillis()) + + } + else -> { + mediaRecord.put(MediaStore.Files.FileColumns.DISPLAY_NAME, fileName) + collectionUri = MediaStore.Files.getContentUri(mediaVolume) + } + } + + val mediaFileUri = resolver.insert(collectionUri, mediaRecord) + if (mediaFileUri == null) return null + + val inputStream = PartAuthority.getAttachmentStream(context, attachment.uri) + if (inputStream == null) return null + + inputStream.use { + resolver.openOutputStream(mediaFileUri).use { + Util.copy(inputStream, it) + } + } + + return mediaFileUri.toString() + } + + private fun generateOutputFileName(contentType: String, timestamp: Long): String { + val mimeTypeMap = MimeTypeMap.getSingleton() + val extension = mimeTypeMap.getExtensionFromMimeType(contentType) ?: "attach" + val dateFormatter = SimpleDateFormat("yyyy-MM-dd-HHmmss") + val base = "signal-${dateFormatter.format(timestamp)}" + + return "${base}.${extension}"; + } + + private fun sanitizeOutputFileName(fileName: String): String { + return File(fileName).name + } + + override fun onPostExecute(result: Pair) { + super.onPostExecute(result) + val context = contextReference.get() + if (context == null) return + + when (result.first) { + RESULT_FAILURE -> { + val message = context.resources.getQuantityText( + R.plurals.ConversationFragment_error_while_saving_attachments_to_sd_card, + attachmentCount) + Toast.makeText(context, message, Toast.LENGTH_LONG).show() + } + + RESULT_SUCCESS -> { + val message = if (!TextUtils.isEmpty(result.second)) { + context.resources.getString(R.string.SaveAttachmentTask_saved_to, result.second) + } else { + context.resources.getString(R.string.SaveAttachmentTask_saved) + } + Toast.makeText(context, message, Toast.LENGTH_LONG).show() + } + + else -> throw IllegalStateException("Unexpected result value: " + result.first) + } + } + + data class Attachment(val uri: Uri, val contentType: String, val date: Long, val fileName: String?) +} \ No newline at end of file diff --git a/src/org/thoughtcrime/securesms/util/task/ProgressDialogAsyncTask.java b/src/org/thoughtcrime/securesms/util/task/ProgressDialogAsyncTask.java index e862d5d4f5..4c343bcf00 100644 --- a/src/org/thoughtcrime/securesms/util/task/ProgressDialogAsyncTask.java +++ b/src/org/thoughtcrime/securesms/util/task/ProgressDialogAsyncTask.java @@ -4,6 +4,8 @@ import android.app.ProgressDialog; import android.content.Context; import android.os.AsyncTask; +import androidx.annotation.NonNull; + import java.lang.ref.WeakReference; public abstract class ProgressDialogAsyncTask extends AsyncTask { @@ -13,14 +15,14 @@ public abstract class ProgressDialogAsyncTask extends private final String title; private final String message; - public ProgressDialogAsyncTask(Context context, String title, String message) { + public ProgressDialogAsyncTask(@NonNull Context context, @NonNull String title, @NonNull String message) { super(); this.contextReference = new WeakReference<>(context); this.title = title; this.message = message; } - public ProgressDialogAsyncTask(Context context, int title, int message) { + public ProgressDialogAsyncTask(@NonNull Context context, int title, int message) { this(context, context.getString(title), context.getString(message)); } @@ -35,7 +37,7 @@ public abstract class ProgressDialogAsyncTask extends if (progress != null) progress.dismiss(); } - protected Context getContext() { + protected @NonNull Context getContext() { return contextReference.get(); } } From f20ebc31f301a95cb6c202300d9b7c63bdf3fa9c Mon Sep 17 00:00:00 2001 From: Anton Chekulaev Date: Fri, 11 Sep 2020 12:11:46 +1000 Subject: [PATCH 6/7] Replace some "write" permissions with with "read" ones. Notes for backup related classes. --- src/org/thoughtcrime/securesms/RegistrationActivity.java | 2 +- .../securesms/components/AttachmentTypeSelector.java | 2 +- src/org/thoughtcrime/securesms/jobs/LocalBackupJob.java | 1 + src/org/thoughtcrime/securesms/util/BackupUtil.java | 1 + 4 files changed, 4 insertions(+), 2 deletions(-) diff --git a/src/org/thoughtcrime/securesms/RegistrationActivity.java b/src/org/thoughtcrime/securesms/RegistrationActivity.java index c96e433013..fdabe5d229 100644 --- a/src/org/thoughtcrime/securesms/RegistrationActivity.java +++ b/src/org/thoughtcrime/securesms/RegistrationActivity.java @@ -268,7 +268,7 @@ public class RegistrationActivity extends BaseActionBarActivity implements Verif @SuppressLint("StaticFieldLeak") private void initializeBackupDetection() { - if (!Permissions.hasAll(this, Manifest.permission.WRITE_EXTERNAL_STORAGE)) { + if (!Permissions.hasAll(this, Manifest.permission.READ_EXTERNAL_STORAGE)) { Log.i(TAG, "Skipping backup detection. We don't have the permission."); return; } diff --git a/src/org/thoughtcrime/securesms/components/AttachmentTypeSelector.java b/src/org/thoughtcrime/securesms/components/AttachmentTypeSelector.java index 86dfa8b6ec..67d6405d3f 100644 --- a/src/org/thoughtcrime/securesms/components/AttachmentTypeSelector.java +++ b/src/org/thoughtcrime/securesms/components/AttachmentTypeSelector.java @@ -110,7 +110,7 @@ public class AttachmentTypeSelector extends PopupWindow { public void show(@NonNull Activity activity, final @NonNull View anchor) { updateHeight(); - if (Permissions.hasAll(activity, Manifest.permission.WRITE_EXTERNAL_STORAGE)) { + if (Permissions.hasAll(activity, Manifest.permission.READ_EXTERNAL_STORAGE)) { recentRail.setVisibility(View.VISIBLE); loaderManager.restartLoader(1, null, recentRail); } else { diff --git a/src/org/thoughtcrime/securesms/jobs/LocalBackupJob.java b/src/org/thoughtcrime/securesms/jobs/LocalBackupJob.java index 4110699e67..ba19d8e2a5 100644 --- a/src/org/thoughtcrime/securesms/jobs/LocalBackupJob.java +++ b/src/org/thoughtcrime/securesms/jobs/LocalBackupJob.java @@ -26,6 +26,7 @@ import java.util.Locale; import network.loki.messenger.R; +//TODO AC: Needs to be refactored to use Storage Access Framework or Media Store API. public class LocalBackupJob extends BaseJob { public static final String KEY = "LocalBackupJob"; diff --git a/src/org/thoughtcrime/securesms/util/BackupUtil.java b/src/org/thoughtcrime/securesms/util/BackupUtil.java index d60660da4f..5df025a3bc 100644 --- a/src/org/thoughtcrime/securesms/util/BackupUtil.java +++ b/src/org/thoughtcrime/securesms/util/BackupUtil.java @@ -16,6 +16,7 @@ import java.util.Arrays; import java.util.Calendar; import java.util.Locale; +//TODO AC: Needs to be refactored to use Storage Access Framework or Media Store API. public class BackupUtil { private static final String TAG = BackupUtil.class.getSimpleName(); From 8b98f85470c55c2322644af13eef6dfe759a8153 Mon Sep 17 00:00:00 2001 From: Anton Chekulaev Date: Fri, 11 Sep 2020 12:22:29 +1000 Subject: [PATCH 7/7] Uncomment accidently commented code. --- src/org/thoughtcrime/securesms/CreateProfileActivity.java | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/org/thoughtcrime/securesms/CreateProfileActivity.java b/src/org/thoughtcrime/securesms/CreateProfileActivity.java index 57836e6bd6..9be993d218 100644 --- a/src/org/thoughtcrime/securesms/CreateProfileActivity.java +++ b/src/org/thoughtcrime/securesms/CreateProfileActivity.java @@ -213,10 +213,10 @@ public class CreateProfileActivity extends BaseActionBarActivity implements Inje this.reveal = ViewUtil.findById(this, R.id.reveal); this.nextIntent = getIntent().getParcelableExtra(NEXT_INTENT); -// this.avatar.setOnClickListener(view -> Permissions.with(this) -// .request(Manifest.permission.CAMERA, Manifest.permission.WRITE_EXTERNAL_STORAGE) -// .onAnyResult(this::startAvatarSelection) -// .execute()); + this.avatar.setOnClickListener(view -> Permissions.with(this) + .request(Manifest.permission.CAMERA, Manifest.permission.WRITE_EXTERNAL_STORAGE) + .onAnyResult(this::startAvatarSelection) + .execute()); this.name.getInput().addTextChangedListener(new TextWatcher() { @Override