diff options
author | Niedermann IT-Dienstleistungen <stefan-niedermann@users.noreply.github.com> | 2021-04-19 13:34:29 +0300 |
---|---|---|
committer | GitHub <noreply@github.com> | 2021-04-19 13:34:29 +0300 |
commit | 8098ebe8c07fbb456de22abd17895d8acc3d1092 (patch) | |
tree | cec281b63a4eb3b9a0153c80b90e499950ac7a4b | |
parent | b84b800169e8c599956b5acd9afbf2b9e6f1e8c1 (diff) |
Raise targetSdk 30 (#943)
- Raises `targetSdk` to `30`
- Implements the [necessary `<queries />` to the files app](https://github.com/nextcloud/Android-SingleSignOn/issues/277) since it is not yet merged in the corresponding [SSO pull request](https://github.com/nextcloud/Android-SingleSignOn/pull/282)
- Discards usage of `requestLegacyStorage="true"`
- Replaces the preview of downloaded files in the attachment picker with a direct access to the native system file picker because
- `MediaStore` is only able to access files if
> The files reside in one of the following well-defined media collections: MediaStore.Images, MediaStore.Video, MediaStore.Audio
([Source](https://developer.android.com/training/data-storage/shared/media#request-permissions))
... which is not true for downloaded content.
- Also the Storage Access Framework [is not able to list content from downloads on Android 11+](https://developer.android.com/training/data-storage/shared/documents-files).
6 files changed, 53 insertions, 146 deletions
diff --git a/app/build.gradle b/app/build.gradle index c8d4c6e2f..25d0ca332 100644 --- a/app/build.gradle +++ b/app/build.gradle @@ -2,12 +2,12 @@ apply plugin: 'com.android.application' apply plugin: 'kotlin-android' android { - compileSdkVersion 29 + compileSdkVersion 30 buildToolsVersion "30.0.3" defaultConfig { applicationId "it.niedermann.nextcloud.deck" minSdkVersion 21 - targetSdkVersion 29 + targetSdkVersion 30 versionCode 1018000 versionName "1.18.0" vectorDrawables.useSupportLibrary true diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml index 329486c39..7e94eff17 100644 --- a/app/src/main/AndroidManifest.xml +++ b/app/src/main/AndroidManifest.xml @@ -13,6 +13,11 @@ <uses-sdk tools:overrideLibrary="androidx.camera.core, androidx.camera.camera2, androidx.camera.lifecycle, androidx.camera.view" /> + <queries> + <package android:name="com.nextcloud.client" /> + <package android:name="com.nextcloud.android.beta" /> + </queries> + <application android:name="it.niedermann.nextcloud.deck.DeckApplication" android:allowBackup="true" @@ -21,7 +26,6 @@ android:icon="@mipmap/ic_launcher" android:label="@string/app_name" android:networkSecurityConfig="@xml/network_security_config" - android:requestLegacyExternalStorage="true" android:roundIcon="@mipmap/ic_launcher_round" android:supportsRtl="true" android:theme="@style/AppTheme" @@ -35,7 +39,7 @@ android:grantUriPermissions="true"> <meta-data android:name="android.support.FILE_PROVIDER_PATHS" - android:resource="@xml/provider_paths"/> + android:resource="@xml/provider_paths" /> </provider> <activity diff --git a/app/src/main/java/it/niedermann/nextcloud/deck/ui/card/attachments/CardAttachmentsFragment.java b/app/src/main/java/it/niedermann/nextcloud/deck/ui/card/attachments/CardAttachmentsFragment.java index 76262309f..5eedb5086 100644 --- a/app/src/main/java/it/niedermann/nextcloud/deck/ui/card/attachments/CardAttachmentsFragment.java +++ b/app/src/main/java/it/niedermann/nextcloud/deck/ui/card/attachments/CardAttachmentsFragment.java @@ -58,7 +58,6 @@ import it.niedermann.nextcloud.deck.ui.card.EditCardViewModel; import it.niedermann.nextcloud.deck.ui.card.attachments.picker.AbstractPickerAdapter; import it.niedermann.nextcloud.deck.ui.card.attachments.picker.ContactAdapter; import it.niedermann.nextcloud.deck.ui.card.attachments.picker.FileAdapter; -import it.niedermann.nextcloud.deck.ui.card.attachments.picker.FileAdapterLegacy; import it.niedermann.nextcloud.deck.ui.card.attachments.picker.GalleryAdapter; import it.niedermann.nextcloud.deck.ui.card.attachments.picker.GalleryItemDecoration; import it.niedermann.nextcloud.deck.ui.card.attachments.previewdialog.PreviewDialog; @@ -139,6 +138,7 @@ public class CardAttachmentsFragment extends Fragment implements AttachmentDelet showContactPicker(); } else if (item.getItemId() == R.id.files) { showFilePicker(); + return false; } return true; }); @@ -235,13 +235,8 @@ public class CardAttachmentsFragment extends Fragment implements AttachmentDelet } final SharedPreferences sharedPreferences = PreferenceManager.getDefaultSharedPreferences(requireContext()); compressImagesOnUpload = sharedPreferences.getBoolean(getString(R.string.pref_key_compress_image_attachments), true); - return binding.getRoot(); - } - - @Override - public void onViewCreated(@NonNull View view, @Nullable Bundle savedInstanceState) { - super.onViewCreated(view, savedInstanceState); editViewModel.getBrandingColor().observe(getViewLifecycleOwner(), this::applyBrand); + return binding.getRoot(); } @Override @@ -303,28 +298,24 @@ public class CardAttachmentsFragment extends Fragment implements AttachmentDelet } private void showFilePicker() { - if (!(pickerAdapter instanceof FileAdapter) && !(pickerAdapter instanceof FileAdapterLegacy)) { + if (!(pickerAdapter instanceof FileAdapter)) { if (isPermissionRequestNeeded(READ_EXTERNAL_STORAGE)) { requestPermissions(new String[]{READ_EXTERNAL_STORAGE}, REQUEST_CODE_PICK_FILE_PERMISSION); } else { - unbindPickerAdapter(); -// if (SDK_INT >= Build.VERSION_CODES.Q) { -// // TODO Only usable with Scoped Storage -// pickerAdapter = new FileAdapter(requireContext(), uri -> onActivityResult(REQUEST_CODE_PICK_FILE, RESULT_OK, new Intent().setData(uri)), this::openNativeFilePicker); -// } else { - pickerAdapter = new FileAdapterLegacy((uri, pair) -> { - previewViewModel.prepareDialog(pair.first, pair.second); - PreviewDialog.newInstance().show(getChildFragmentManager(), PreviewDialog.class.getSimpleName()); - observeOnce(previewViewModel.getResult(), getViewLifecycleOwner(), (submitPositive) -> { - if (submitPositive) { - onActivityResult(REQUEST_CODE_PICK_FILE, RESULT_OK, new Intent().setData(uri)); - } - }); - }, this::openNativeFilePicker); -// } - removeGalleryItemDecoration(); - binding.pickerRecyclerView.setLayoutManager(new LinearLayoutManager(requireContext())); - binding.pickerRecyclerView.setAdapter(pickerAdapter); + openNativeFilePicker(); +// unbindPickerAdapter(); +// pickerAdapter = new FileAdapter(requireContext(), (uri, pair) -> { +// previewViewModel.prepareDialog(pair.first, pair.second); +// PreviewDialog.newInstance().show(getChildFragmentManager(), PreviewDialog.class.getSimpleName()); +// observeOnce(previewViewModel.getResult(), getViewLifecycleOwner(), (submitPositive) -> { +// if (submitPositive) { +// onActivityResult(REQUEST_CODE_PICK_FILE, RESULT_OK, new Intent().setData(uri)); +// } +// }); +// }, this::openNativeFilePicker); +// removeGalleryItemDecoration(); +// binding.pickerRecyclerView.setLayoutManager(new LinearLayoutManager(requireContext())); +// binding.pickerRecyclerView.setAdapter(pickerAdapter); } } } diff --git a/app/src/main/java/it/niedermann/nextcloud/deck/ui/card/attachments/picker/FileAdapter.java b/app/src/main/java/it/niedermann/nextcloud/deck/ui/card/attachments/picker/FileAdapter.java index aa96a0e69..1ada444b4 100644 --- a/app/src/main/java/it/niedermann/nextcloud/deck/ui/card/attachments/picker/FileAdapter.java +++ b/app/src/main/java/it/niedermann/nextcloud/deck/ui/card/attachments/picker/FileAdapter.java @@ -11,7 +11,6 @@ import android.view.LayoutInflater; import android.view.ViewGroup; import androidx.annotation.NonNull; -import androidx.annotation.RequiresApi; import androidx.recyclerview.widget.RecyclerView; import com.bumptech.glide.RequestBuilder; @@ -21,16 +20,15 @@ import java.util.function.BiConsumer; import it.niedermann.nextcloud.deck.databinding.ItemAttachmentDefaultBinding; import it.niedermann.nextcloud.deck.databinding.ItemPickerNativeBinding; -import static android.provider.MediaStore.Downloads.DATE_ADDED; -import static android.provider.MediaStore.Downloads.DATE_MODIFIED; -import static android.provider.MediaStore.Downloads.EXTERNAL_CONTENT_URI; -import static android.provider.MediaStore.Downloads.MIME_TYPE; -import static android.provider.MediaStore.Downloads.SIZE; -import static android.provider.MediaStore.Downloads.TITLE; -import static android.provider.MediaStore.Downloads._ID; -import static java.util.Objects.requireNonNull; +import static android.provider.BaseColumns._ID; +import static android.provider.MediaStore.Files.FileColumns.DATE_ADDED; +import static android.provider.MediaStore.Files.FileColumns.DATE_MODIFIED; +import static android.provider.MediaStore.Files.FileColumns.MEDIA_TYPE; +import static android.provider.MediaStore.Files.FileColumns.MEDIA_TYPE_IMAGE; +import static android.provider.MediaStore.Files.FileColumns.MIME_TYPE; +import static android.provider.MediaStore.Files.FileColumns.SIZE; +import static android.provider.MediaStore.Files.FileColumns.TITLE; -@RequiresApi(api = 29) public class FileAdapter extends AbstractCursorPickerAdapter<RecyclerView.ViewHolder> { private final int displayNameColumnIndex; @@ -38,8 +36,8 @@ public class FileAdapter extends AbstractCursorPickerAdapter<RecyclerView.ViewHo private final int modifiedColumnIndex; private final int mimeTypeColumnIndex; - private FileAdapter(@NonNull Context context, @NonNull BiConsumer<Uri, Pair<String, RequestBuilder<?>>> onSelect, @NonNull Runnable onSelectPicker) { - super(context, onSelect, onSelectPicker, _ID, requireNonNull(context.getContentResolver().query(EXTERNAL_CONTENT_URI, new String[]{_ID, TITLE, SIZE, DATE_MODIFIED, MIME_TYPE}, null, null, DATE_ADDED + " DESC"))); + public FileAdapter(@NonNull Context context, @NonNull BiConsumer<Uri, Pair<String, RequestBuilder<?>>> onSelect, @NonNull Runnable onSelectPicker) { + super(context, onSelect, onSelectPicker, _ID, context.getContentResolver().query(MediaStore.Files.getContentUri("external"), new String[]{_ID, TITLE, SIZE, DATE_MODIFIED, MIME_TYPE}, MEDIA_TYPE + " != ?", new String[]{String.valueOf(MEDIA_TYPE_IMAGE)}, DATE_ADDED + " DESC")); displayNameColumnIndex = cursor.getColumnIndex(TITLE); sizeColumnIndex = cursor.getColumnIndex(SIZE); modifiedColumnIndex = cursor.getColumnIndex(DATE_MODIFIED); diff --git a/app/src/main/java/it/niedermann/nextcloud/deck/ui/card/attachments/picker/FileAdapterLegacy.java b/app/src/main/java/it/niedermann/nextcloud/deck/ui/card/attachments/picker/FileAdapterLegacy.java deleted file mode 100644 index 1ac14361a..000000000 --- a/app/src/main/java/it/niedermann/nextcloud/deck/ui/card/attachments/picker/FileAdapterLegacy.java +++ /dev/null @@ -1,88 +0,0 @@ -package it.niedermann.nextcloud.deck.ui.card.attachments.picker; - -import android.net.Uri; -import android.os.Environment; -import android.util.Pair; -import android.view.LayoutInflater; -import android.view.ViewGroup; - -import androidx.annotation.NonNull; -import androidx.recyclerview.widget.RecyclerView; - -import com.bumptech.glide.RequestBuilder; - -import java.io.File; -import java.util.Arrays; -import java.util.List; -import java.util.function.BiConsumer; - -import it.niedermann.nextcloud.deck.databinding.ItemAttachmentDefaultBinding; -import it.niedermann.nextcloud.deck.databinding.ItemPickerNativeBinding; -import it.niedermann.nextcloud.deck.util.AttachmentUtil; - -import static java.util.Collections.reverseOrder; -import static java.util.Comparator.comparingLong; -import static java.util.Objects.requireNonNull; -import static java.util.stream.Collectors.toList; - -@Deprecated -public class FileAdapterLegacy extends AbstractPickerAdapter<RecyclerView.ViewHolder> { - - @NonNull - private final List<File> files; - @NonNull - protected final BiConsumer<Uri, Pair<String, RequestBuilder<?>>> onSelect; - @NonNull - protected final Runnable openNativePicker; - - public FileAdapterLegacy(@NonNull BiConsumer<Uri, Pair<String, RequestBuilder<?>>> onSelect, @NonNull Runnable openNativePicker) { - // TODO run in separate thread? - this.onSelect = onSelect; - this.openNativePicker = openNativePicker; - this.files = Arrays.stream(requireNonNull(requireNonNull(Environment.getExternalStoragePublicDirectory(Environment.DIRECTORY_DOWNLOADS)).listFiles())) - .sorted(reverseOrder(comparingLong(File::lastModified))) - .collect(toList()); - - } - - @NonNull - @Override - public RecyclerView.ViewHolder onCreateViewHolder(@NonNull ViewGroup parent, int viewType) { - switch (viewType) { - case VIEW_TYPE_ITEM_NATIVE: - return new FileNativeItemViewHolder(ItemPickerNativeBinding.inflate(LayoutInflater.from(parent.getContext()), parent, false)); - case VIEW_TYPE_ITEM: - return new FileItemViewHolder(ItemAttachmentDefaultBinding.inflate(LayoutInflater.from(parent.getContext()), parent, false)); - default: - throw new IllegalStateException("Unknown viewType " + viewType); - } - } - - @Override - public void onBindViewHolder(@NonNull RecyclerView.ViewHolder holder, int position) { - switch (getItemViewType(position)) { - case VIEW_TYPE_ITEM_NATIVE: { - ((FileNativeItemViewHolder) holder).bind(openNativePicker); - break; - } - case VIEW_TYPE_ITEM: { - final File file = files.get(position - 1); - if (file.isFile()) { - ((FileItemViewHolder) holder).bind(Uri.fromFile(file), file.getName(), AttachmentUtil.getMimeType(file.getAbsolutePath()), file.length(), file.lastModified(), onSelect); - } else { - ((FileItemViewHolder) holder).bindError(); - } - break; - } - } - } - - @Override - public int getItemCount() { - return files.size(); - } - - public void onDestroy() { - // Let GarbageCollection do this stuff... - } -} diff --git a/app/src/main/java/it/niedermann/nextcloud/deck/ui/card/attachments/picker/GalleryAdapter.java b/app/src/main/java/it/niedermann/nextcloud/deck/ui/card/attachments/picker/GalleryAdapter.java index 658eb1ee3..2bd03011f 100644 --- a/app/src/main/java/it/niedermann/nextcloud/deck/ui/card/attachments/picker/GalleryAdapter.java +++ b/app/src/main/java/it/niedermann/nextcloud/deck/ui/card/attachments/picker/GalleryAdapter.java @@ -69,23 +69,25 @@ public class GalleryAdapter extends AbstractCursorPickerAdapter<RecyclerView.Vie } case VIEW_TYPE_ITEM: { final long id = getItemId(position); - bindExecutor.execute(() -> { - try { - final Bitmap thumbnail; - if (SDK_INT >= Q) { - thumbnail = contentResolver.loadThumbnail(ContentUris.withAppendedId( - EXTERNAL_CONTENT_URI, id), new Size(512, 384), null); - } else { - thumbnail = MediaStore.Images.Thumbnails.getThumbnail( - contentResolver, id, - MediaStore.Images.Thumbnails.MINI_KIND, null); + if (!bindExecutor.isTerminated()) { + bindExecutor.execute(() -> { + try { + final Bitmap thumbnail; + if (SDK_INT >= Q) { + thumbnail = contentResolver.loadThumbnail(ContentUris.withAppendedId( + EXTERNAL_CONTENT_URI, id), new Size(512, 384), null); + } else { + thumbnail = MediaStore.Images.Thumbnails.getThumbnail( + contentResolver, id, + MediaStore.Images.Thumbnails.MINI_KIND, null); + } + new Handler(Looper.getMainLooper()).post(() -> ((GalleryItemViewHolder) holder).bind(ContentUris.withAppendedId( + EXTERNAL_CONTENT_URI, id), thumbnail, onSelect)); + } catch (IOException ignored) { + new Handler(Looper.getMainLooper()).post(((GalleryItemViewHolder) holder)::bindError); } - new Handler(Looper.getMainLooper()).post(() -> ((GalleryItemViewHolder) holder).bind(ContentUris.withAppendedId( - EXTERNAL_CONTENT_URI, id), thumbnail, onSelect)); - } catch (IOException ignored) { - new Handler(Looper.getMainLooper()).post(((GalleryItemViewHolder) holder)::bindError); - } - }); + }); + } } } } |