Blocks
Extension Full Code
package com.ry.imagepicker;
import android.app.Activity;
import android.content.ClipData;
import android.content.ContentResolver;
import android.content.Intent;
import android.database.Cursor;
import android.net.Uri;
import android.os.Build;
import android.provider.OpenableColumns;
import android.webkit.MimeTypeMap;
import com.google.appinventor.components.annotations.DesignerComponent;
import com.google.appinventor.components.annotations.SimpleObject;
import com.google.appinventor.components.annotations.SimpleEvent;
import com.google.appinventor.components.annotations.SimpleFunction;
import com.google.appinventor.components.annotations.SimpleProperty;
import com.google.appinventor.components.annotations.UsesPermissions;
import com.google.appinventor.components.annotations.UsesLibraries;
import com.google.appinventor.components.common.ComponentCategory;
import com.google.appinventor.components.runtime.AndroidNonvisibleComponent;
import com.google.appinventor.components.runtime.ComponentContainer;
import com.google.appinventor.components.runtime.EventDispatcher;
import com.google.appinventor.components.runtime.Form;
import com.google.appinventor.components.runtime.ActivityResultListener;
import com.google.appinventor.components.runtime.util.YailList;
import java.util.ArrayList;
import java.util.List;
@DesignerComponent(version=4,description="Image/File Picker extension using Storage Access Framework. Supports single and multiple selection with metadata.",category=ComponentCategory.EXTENSION,nonVisible=true,iconName="aiwebres/icon.png")
@SimpleObject(external=true)
@UsesPermissions(permissionNames="android.permission.READ_EXTERNAL_STORAGE,android.permission.WRITE_EXTERNAL_STORAGE")
@UsesLibraries(libraries="")
public class RYimagepicker extends AndroidNonvisibleComponent implements ActivityResultListener{
private final Activity activity;
private final Form form;
private static final int REQ_SINGLE=9001;
private static final int REQ_MULTI=9002;
private String mimeFilter="image/*";
private boolean allowMultiple=true;
private boolean persistUriPermission=true;
public RYimagepicker(ComponentContainer container){
super(container.$form());
this.activity=container.$context();
this.form=container.$form();
form.registerForActivityResult(this);
}
@SimpleProperty(description="Current MIME filter used for picking, e.g., image/*, video/*, */*.")
public String MimeFilter(){return mimeFilter;}
@SimpleProperty public void MimeFilter(String mime){this.mimeFilter=(mime==null||mime.isEmpty())?"*/*":mime;}
@SimpleProperty(description="If true, MultiplePick will request multiple selection capability where supported.")
public boolean AllowMultiple(){return allowMultiple;}
@SimpleProperty public void AllowMultiple(boolean value){this.allowMultiple=value;}
@SimpleProperty(description="If true, persisted URI permission is requested for selected items.")
public boolean PersistUriPermission(){return persistUriPermission;}
@SimpleProperty public void PersistUriPermission(boolean value){this.persistUriPermission=value;}
@SimpleFunction(description="Open a system picker for a single file (defaults to images). Uses MimeFilter property.")
public void SinglePick(){
Intent intent=new Intent(Intent.ACTION_OPEN_DOCUMENT);
intent.addCategory(Intent.CATEGORY_OPENABLE);
intent.setType(mimeFilter==null?"*/*":mimeFilter);
if(persistUriPermission){intent.addFlags(Intent.FLAG_GRANT_PERSISTABLE_URI_PERMISSION);}
intent.addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION);
form.startActivityForResult(intent,REQ_SINGLE);
}
@SimpleFunction(description="Open a system picker for multiple files.")
public void MultiplePick(int maxCount,String mime){
Intent intent=new Intent(Intent.ACTION_OPEN_DOCUMENT);
intent.addCategory(Intent.CATEGORY_OPENABLE);
String type=(mime==null||mime.isEmpty())?(mimeFilter==null?"*/*":mimeFilter):mime;
intent.setType(type);
if(allowMultiple){intent.putExtra(Intent.EXTRA_ALLOW_MULTIPLE,true);}
if(persistUriPermission){intent.addFlags(Intent.FLAG_GRANT_PERSISTABLE_URI_PERMISSION);}
intent.addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION);
form.startActivityForResult(intent,REQ_MULTI);
}
@Override public void resultReturned(int requestCode,int resultCode,Intent data){
if(resultCode!=Activity.RESULT_OK||data==null)return;
if(requestCode==REQ_SINGLE){handleSingleResult(data);}
else if(requestCode==REQ_MULTI){handleMultiResult(data);}
}
private void handleSingleResult(Intent data){
Uri uri=data.getData(); if(uri==null)return;
takePersistablePermissionIfPossible(uri,data);
String name=queryDisplayName(uri);
String mime=queryMime(uri);
long size=querySize(uri);
String pseudoPath=toPseudoPath(uri);
AfterSinglePick(uri.toString(),pseudoPath,name,mime,size);
}
private void handleMultiResult(Intent data){
List<String>uris=new ArrayList<>();
List<String>paths=new ArrayList<>();
List<String>names=new ArrayList<>();
List<String>mimes=new ArrayList<>();
List<Long>sizes=new ArrayList<>();
ClipData clip=data.getClipData();
if(clip!=null&&clip.getItemCount()>0){
int count=clip.getItemCount();
for(int i=0;i<count;i++){
Uri uri=clip.getItemAt(i).getUri();
if(uri!=null){collectForUri(data,uri,uris,paths,names,mimes,sizes);}
}
}else{
Uri uri=data.getData();
if(uri!=null){collectForUri(data,uri,uris,paths,names,mimes,sizes);}
}
YailList yUris=YailList.makeList(uris);
YailList yPaths=YailList.makeList(paths);
YailList yNames=YailList.makeList(names);
YailList yMimes=YailList.makeList(mimes);
YailList ySizes=YailList.makeList(sizes);
int total=uris.size();
AfterMultiplePick(yUris,yPaths,yNames,yMimes,ySizes,total);
}
private void collectForUri(Intent data,Uri uri,List<String>uris,List<String>paths,List<String>names,List<String>mimes,List<Long>sizes){
takePersistablePermissionIfPossible(uri,data);
uris.add(uri.toString());
paths.add(toPseudoPath(uri));
names.add(queryDisplayName(uri));
mimes.add(queryMime(uri));
sizes.add(querySize(uri));
}
private String queryDisplayName(Uri uri){
String name=null; Cursor c=null;
try{
c=activity.getContentResolver().query(uri,new String[]{OpenableColumns.DISPLAY_NAME},null,null,null);
if(c!=null&&c.moveToFirst()){
int idx=c.getColumnIndex(OpenableColumns.DISPLAY_NAME);
if(idx!=-1&&!c.isNull(idx)) name=c.getString(idx);
}
}catch(Exception ignored){} finally{ if(c!=null)c.close(); }
if(name==null||name.isEmpty()) name=uri.getLastPathSegment();
return name==null?"":name;
}
private long querySize(Uri uri){
long size=0L; Cursor c=null;
try{
c=activity.getContentResolver().query(uri,new String[]{OpenableColumns.SIZE},null,null,null);
if(c!=null&&c.moveToFirst()){
int idx=c.getColumnIndex(OpenableColumns.SIZE);
if(idx!=-1&&!c.isNull(idx)) size=c.getLong(idx);
}
}catch(Exception ignored){} finally{ if(c!=null)c.close(); }
return size;
}
private String queryMime(Uri uri){
ContentResolver cr=activity.getContentResolver();
String mime=cr.getType(uri);
if(mime==null){
String ext=MimeTypeMap.getFileExtensionFromUrl(uri.toString());
if(ext!=null){
String guess=MimeTypeMap.getSingleton().getMimeTypeFromExtension(ext.toLowerCase());
if(guess!=null) mime=guess;
}
}
return mime==null?"*/*":mime;
}
private String toPseudoPath(Uri uri){ return uri.toString(); }
private void takePersistablePermissionIfPossible(Uri uri,Intent data){
if(!persistUriPermission) return;
int flags=data.getFlags();
boolean canPersist=(flags & Intent.FLAG_GRANT_PERSISTABLE_URI_PERMISSION)!=0;
if(canPersist){
int takeFlags=flags & (Intent.FLAG_GRANT_READ_URI_PERMISSION|Intent.FLAG_GRANT_WRITE_URI_PERMISSION|Intent.FLAG_GRANT_PERSISTABLE_URI_PERMISSION);
try{ activity.getContentResolver().takePersistableUriPermission(uri,takeFlags); }catch(Exception ignored){}
}
}
@SimpleEvent(description="Fires after a single item was picked.")
public void AfterSinglePick(String uri,String path,String name,String mimeType,long sizeBytes){
EventDispatcher.dispatchEvent(this,"AfterSinglePick",uri,path,name,mimeType,sizeBytes);
}
@SimpleEvent(description="Fires after multiple items were picked. Lists align by index.")
public void AfterMultiplePick(YailList uris,YailList paths,YailList names,YailList mimeTypes,YailList sizes,int totalFiles){
EventDispatcher.dispatchEvent(this,"AfterMultiplePick",uris,paths,names,mimeTypes,sizes,totalFiles);
}
}
