Skip to content

Commit

Permalink
feat(capture-sdk): Use new photo-picker.
Browse files Browse the repository at this point in the history
Now we are using new photo picker, for devices that doesn't have backdrop version, we use the same approach with list of apps
PM-198
  • Loading branch information
jackkray committed Jul 10, 2024
1 parent fa05e8f commit 8ebdbe0
Show file tree
Hide file tree
Showing 7 changed files with 162 additions and 27 deletions.
11 changes: 11 additions & 0 deletions capture-sdk/sdk/src/main/AndroidManifest.xml
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
package="net.gini.android.capture">

<uses-permission android:name="android.permission.VIBRATE" />
Expand All @@ -18,6 +19,16 @@
<meta-data
android:name="com.google.mlkit.vision.DEPENDENCIES"
android:value="barcode,ocr" />
<!-- Trigger Google Play services to install the backported photo picker module. -->
<service android:name="com.google.android.gms.metadata.ModuleDependencies"
android:enabled="false"
android:exported="false"
tools:ignore="MissingClass">
<intent-filter>
<action android:name="com.google.android.gms.metadata.MODULE_DEPENDENCIES" />
</intent-filter>
<meta-data android:name="photopicker_activity:0:required" android:value="" />
</service>
</application>

</manifest>
Original file line number Diff line number Diff line change
Expand Up @@ -456,6 +456,8 @@ private void setFileChooserFragmentResultListener() {
public void handleFileChooserResult(@NonNull FileChooserResult result) {
if (result instanceof FileChooserResult.FilesSelected) {
importDocumentFromIntent(((FileChooserResult.FilesSelected) result).getDataIntent());
} else if(result instanceof FileChooserResult.FilesSelectedUri) {
importDocumentFromUriList(((FileChooserResult.FilesSelectedUri) result).getList());
} else if (result instanceof FileChooserResult.Error) {
final GiniCaptureError error = ((FileChooserResult.Error) result).getError();
final String message = "Document import failed: " + error.getMessage();
Expand Down Expand Up @@ -1113,6 +1115,13 @@ private void importDocumentFromIntent(@NonNull final Intent data) {
}
}

private void importDocumentFromUriList(List<Uri> uriList) {
if (mFragment.getActivity() == null)
return;

handleMultiPageDocumentAndCallListener(mFragment.getActivity(), new Intent(Intent.ACTION_PICK), uriList);
}

private boolean isImage(@NonNull final Intent data, @NonNull final Activity activity) {
return IntentHelper.hasMimeTypeWithPrefix(data, activity, MimeType.IMAGE_PREFIX.asString());
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ import android.app.Dialog
import android.content.Context
import android.content.Intent
import android.content.pm.ResolveInfo
import android.net.Uri
import android.os.Build
import android.os.Bundle
import android.os.Parcelable
Expand All @@ -15,7 +16,9 @@ import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
import androidx.activity.result.ActivityResultLauncher
import androidx.activity.result.PickVisualMediaRequest
import androidx.activity.result.contract.ActivityResultContracts
import androidx.core.content.ContextCompat
import androidx.fragment.app.setFragmentResult
import androidx.navigation.fragment.findNavController
import androidx.recyclerview.widget.GridLayoutManager
Expand All @@ -30,6 +33,7 @@ import net.gini.android.capture.R
import net.gini.android.capture.databinding.GcFragmentFileChooserBinding
import net.gini.android.capture.internal.fileimport.providerchooser.ProvidersAdapter
import net.gini.android.capture.internal.fileimport.providerchooser.ProvidersAppItem
import net.gini.android.capture.internal.fileimport.providerchooser.ProvidersAppWrapperItem
import net.gini.android.capture.internal.fileimport.providerchooser.ProvidersItem
import net.gini.android.capture.internal.fileimport.providerchooser.ProvidersSectionItem
import net.gini.android.capture.internal.fileimport.providerchooser.ProvidersSeparatorItem
Expand All @@ -52,6 +56,7 @@ class FileChooserFragment : BottomSheetDialogFragment() {
private var docImportEnabledFileTypes: DocumentImportEnabledFileTypes? = null
private var binding: GcFragmentFileChooserBinding by autoCleared()
private var chooseFileLauncher: ActivityResultLauncher<Intent>? = null
private var pickMedia: ActivityResultLauncher<PickVisualMediaRequest>? = null

override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
Expand Down Expand Up @@ -139,6 +144,32 @@ class FileChooserFragment : BottomSheetDialogFragment() {
})
}
}

val photoPickType =
if (FeatureConfiguration.isMultiPageEnabled())
ActivityResultContracts.PickMultipleVisualMedia()
else
ActivityResultContracts.PickVisualMedia()


pickMedia = registerForActivityResult(photoPickType) { uri ->
findNavController().popBackStack()
if (uri != null) {
val lst = when(uri){
is Uri -> listOf(uri)
is List<*> -> uri.filterIsInstance<Uri>().takeIf { it.size == uri.size }
?: throw IllegalArgumentException("List contains non-Uri elements")
else -> throw IllegalArgumentException("uri is neither Uri nor List<Uri>")
}
setFragmentResult(REQUEST_KEY, Bundle().apply {
putParcelable(RESULT_KEY, FileChooserResult.FilesSelectedUri(lst))
})
} else {
setFragmentResult(REQUEST_KEY, Bundle().apply {
putParcelable(RESULT_KEY, FileChooserResult.Cancelled)
})
}
}
}

private fun getGridSpanCount(): Int =
Expand All @@ -151,15 +182,18 @@ class FileChooserFragment : BottomSheetDialogFragment() {

private fun populateFileProviders() {
val providerItems: MutableList<ProvidersItem> = ArrayList()
var imageProviderItems: List<ProvidersItem> = ArrayList()
var imageProviderItems: List<ProvidersItem> = arrayListOf()
var pdfProviderItems: List<ProvidersItem> = ArrayList()
if (shouldShowImageProviders()) {
val imagePickerResolveInfos = queryImagePickers(requireContext())
val imageProviderResolveInfos = queryImageProviders(requireContext())
imageProviderItems = getImageProviderItems(
imagePickerResolveInfos,
imageProviderResolveInfos
)

imageProviderItems =
if (ActivityResultContracts.PickVisualMedia.isPhotoPickerAvailable(requireContext())) {
getPhotoPickerProvider()
} else {
getImageProviderItems(imagePickerResolveInfos, imageProviderResolveInfos)
}
}
if (shouldShowPdfProviders()) {
val pdfProviderResolveInfos = queryPdfProviders(requireContext())
Expand All @@ -172,15 +206,31 @@ class FileChooserFragment : BottomSheetDialogFragment() {
providerItems.addAll(pdfProviderItems)
(binding.gcFileProviders.layoutManager as GridLayoutManager).spanSizeLookup =
ProvidersSpanSizeLookup(providerItems, getGridSpanCount())
binding.gcFileProviders.adapter = ProvidersAdapter(requireContext(), providerItems) { item -> launchApp(item) }
binding.gcFileProviders.adapter = ProvidersAdapter(requireContext(), providerItems) {
item -> launchApp(item)
}
}

private fun launchApp(item: ProvidersAppItem) {
item.intent.setClassName(
item.resolveInfo.activityInfo.packageName,
item.resolveInfo.activityInfo.name
)
chooseFileLauncher?.launch(item.intent)
private fun launchApp(item: ProvidersItem) {
when (item) {
is ProvidersAppItem -> {
item.intent.setClassName(
item.resolveInfo.activityInfo.packageName,
item.resolveInfo.activityInfo.name
)
chooseFileLauncher?.launch(item.intent)
}

is ProvidersAppWrapperItem -> {
pickMedia?.launch(
PickVisualMediaRequest(
ActivityResultContracts.PickVisualMedia.SingleMimeType(
MimeType.IMAGE_WILDCARD.asString()
)
)
)
}
}
}

private fun getImageProviderItems(
Expand All @@ -204,6 +254,23 @@ class FileChooserFragment : BottomSheetDialogFragment() {
}
}

private fun getPhotoPickerProvider(): List<ProvidersItem> {
val providerList =
ContextCompat.getDrawable(requireContext(), (R.drawable.gc_photo_tip_align))
?.let { image ->
listOf(
ProvidersSectionItem(getString(R.string.gc_file_chooser_fotos_section_header)),
ProvidersAppWrapperItem(
image,
getString(R.string.gc_file_chooser_fotos_section_header)
)
)
} ?: run {
emptyList()
}
return providerList
}

private fun getPdfProviderItems(pdfProviderResolveInfos: List<ResolveInfo>): List<ProvidersItem> =
mutableListOf<ProvidersItem>().apply {
if (pdfProviderResolveInfos.isNotEmpty()) {
Expand Down Expand Up @@ -311,6 +378,7 @@ class FileChooserFragment : BottomSheetDialogFragment() {
@Parcelize
sealed class FileChooserResult : Parcelable {
data class FilesSelected(val dataIntent: Intent) : FileChooserResult()
data class FilesSelectedUri(val list: List<Uri>) : FileChooserResult()
data class Error(val error: GiniCaptureError) : FileChooserResult()
object Cancelled : FileChooserResult()
}
Original file line number Diff line number Diff line change
Expand Up @@ -40,12 +40,14 @@ public int getItemViewType(final int position) {

@Override
public ProvidersItemViewHolder onCreateViewHolder(final ViewGroup parent,
final int viewType) {
final int viewType) {
switch (ProvidersItem.FileProviderItemType.fromOrdinal(viewType)) {
case SECTION:
return createSectionItemViewHolder(parent);
case APP:
return createAppItemViewHolder(parent);
return createAppItemViewHolder(parent, ProvidersItem.FileProviderItemType.APP);
case APP_WRAPPER_PHOTO_PICKER:
return createAppItemViewHolder(parent, ProvidersItem.FileProviderItemType.APP_WRAPPER_PHOTO_PICKER);
case SEPARATOR:
return createSeparatorItemViewHolder(parent);
default:
Expand All @@ -62,10 +64,12 @@ private ProvidersItemViewHolder createSectionItemViewHolder(
}

@NonNull
private ProvidersItemViewHolder createAppItemViewHolder(@NonNull final ViewGroup parent) {
private ProvidersItemViewHolder createAppItemViewHolder(
@NonNull final ViewGroup parent,
@NonNull ProvidersItem.FileProviderItemType providerItemType) {
final View itemView = LayoutInflater.from(parent.getContext())
.inflate(R.layout.gc_item_file_provider_app, parent, false);
return new ProvidersAppItemViewHolder(itemView);
return new ProvidersAppItemViewHolder(itemView, providerItemType);
}

private ProvidersItemViewHolder createSeparatorItemViewHolder(@NonNull final ViewGroup parent) {
Expand All @@ -83,6 +87,8 @@ public void onBindViewHolder(final ProvidersItemViewHolder holder, final int pos
case APP:
bindAppItemViewHolder((ProvidersAppItemViewHolder) holder, position);
break;
case APP_WRAPPER_PHOTO_PICKER:
bindAppWrapperItemViewHolder((ProvidersAppItemViewHolder) holder, position);
case SEPARATOR:
break;
default:
Expand All @@ -101,13 +107,22 @@ private void bindAppItemViewHolder(@NonNull final ProvidersAppItemViewHolder hol
final ProvidersAppItem item = (ProvidersAppItem) mItems.get(position);
holder.icon.setImageDrawable(item.getResolveInfo().loadIcon(mContext.getPackageManager()));
holder.label.setText(item.getResolveInfo().loadLabel(mContext.getPackageManager()));
holder.itemView.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(final View view) {
final ProvidersAppItem item =
(ProvidersAppItem) mItems.get(holder.getAdapterPosition());
mItemSelectedListener.onItemSelected(item);
}
holder.itemView.setOnClickListener(view -> {
final ProvidersAppItem item1 =
(ProvidersAppItem) mItems.get(holder.getAdapterPosition());
mItemSelectedListener.onItemSelected(item1);
});
}

private void bindAppWrapperItemViewHolder(@NonNull final ProvidersAppItemViewHolder holder,
final int position) {
final ProvidersAppWrapperItem item = (ProvidersAppWrapperItem) mItems.get(position);
holder.icon.setImageDrawable(item.getDrawableIcon());
holder.label.setText(item.getText());
holder.itemView.setOnClickListener(view -> {
final ProvidersAppWrapperItem item1 =
(ProvidersAppWrapperItem) mItems.get(holder.getAdapterPosition());
mItemSelectedListener.onItemSelected(item1);
});
}

Expand All @@ -123,8 +138,8 @@ private static class ProvidersAppItemViewHolder extends ProvidersItemViewHolder
@NonNull
final TextView label;

ProvidersAppItemViewHolder(@NonNull final View itemView) {
super(itemView, ProvidersItem.FileProviderItemType.APP);
ProvidersAppItemViewHolder(@NonNull final View itemView, @NonNull ProvidersItem.FileProviderItemType providerItemType) {
super(itemView, providerItemType);
icon = itemView.findViewById(R.id.gc_app_icon);
label = itemView.findViewById(R.id.gc_app_label);
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -9,5 +9,5 @@
*/
public interface ProvidersAppItemSelectedListener {

void onItemSelected(@NonNull final ProvidersAppItem item);
void onItemSelected(@NonNull final ProvidersItem item);
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
package net.gini.android.capture.internal.fileimport.providerchooser;

import android.graphics.drawable.Drawable;

import androidx.annotation.NonNull;

/**
* Internal use only.
*
* @suppress
*/
public class ProvidersAppWrapperItem extends ProvidersItem {

private final Drawable mIcon;
private final String mText;

public ProvidersAppWrapperItem(@NonNull final Drawable icon, @NonNull final String text) {
super(FileProviderItemType.APP_WRAPPER_PHOTO_PICKER);
mIcon = icon;
mText = text;
}

public Drawable getDrawableIcon() {
return mIcon;
}

public String getText() {
return mText;
}

}
Original file line number Diff line number Diff line change
Expand Up @@ -53,7 +53,8 @@ FileProviderItemType getType() {
enum FileProviderItemType {
SECTION,
SEPARATOR,
APP;
APP,
APP_WRAPPER_PHOTO_PICKER;

static FileProviderItemType fromOrdinal(final int ordinal) {
if (ordinal >= values().length) {
Expand Down

0 comments on commit 8ebdbe0

Please sign in to comment.