- Source code.
Source code is an adaptation of:
https://github.com/mit-cml/appinventor-sources/blob/master/appinventor/components/src/com/google/appinventor/components/runtime/ImagePicker.java
///////////////////////////////////////////////////////////////////
KIO4_ImagePickerY.java
///////////////////////////////////////////////////////////////////
// -*- Mode: java; c-basic-offset: 2; -*-
// Copyright 2009-2011 Google, All Rights reserved
// Copyright 2011-2012 MIT, All rights reserved
// Released under the Apache License, Version 2.0
// http://www.apache.org/licenses/LICENSE-2.0
// Modificado por Juan A. Villalpando.
// kio4.com
// Noviembre 2017. Agosto 2022.
package com.KIO4_ImagePickerY;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.util.Arrays;
import java.util.Comparator;
import android.app.Activity;
import android.content.ContentResolver;
import android.content.Intent;
import android.net.Uri;
import android.os.Environment;
import android.provider.MediaStore;
import android.util.Log;
import android.webkit.MimeTypeMap;
import com.google.appinventor.components.annotations.DesignerComponent;
import com.google.appinventor.components.annotations.DesignerProperty;
import com.google.appinventor.components.annotations.PropertyCategory;
import com.google.appinventor.components.annotations.SimpleEvent;
import com.google.appinventor.components.annotations.SimpleFunction;
import com.google.appinventor.components.annotations.SimpleObject;
import com.google.appinventor.components.annotations.SimpleProperty;
import com.google.appinventor.components.annotations.UsesPermissions;
import com.google.appinventor.components.common.ComponentCategory;
import com.google.appinventor.components.common.PropertyTypeConstants;
import com.google.appinventor.components.runtime.util.MediaUtil;
import com.google.appinventor.components.runtime.*;
// Otras importaciones.
import com.google.appinventor.components.runtime.util.ErrorMessages;
import com.google.appinventor.components.runtime.util.MediaUtil;
import com.google.appinventor.components.runtime.util.AnimationUtil;
import com.google.appinventor.components.runtime.AndroidNonvisibleComponent;
import com.google.appinventor.components.runtime.ActivityResultListener;
import com.google.appinventor.components.runtime.ComponentContainer;
import com.google.appinventor.components.runtime.EventDispatcher;
import java.io.BufferedReader;
import java.io.InputStreamReader;
import android.content.Context;
/**
* Component enabling a user to select an image from the phone's gallery.
*
* @author halabelson@google.com (Hal Abelson)
*/
@DesignerComponent(version = 1,
description = "When the user open ImagePickerY, the " +
"device's image gallery appears, and the user can choose an image. After an image is " +
"picked, it is saved, and the <code>Selected</code> " +
"property will be the name of the file where the image is stored. You can store up to 1000 images." +
"Default folder to store is /storage/emulated/0/Pictures/_app_inventor_image_picker, but you can change that folder.",
category = ComponentCategory.EXTENSION,
nonVisible = true,
iconName = "data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABIAAAASCAIAAADZrBkAAAAAAXNSR0IArs4c6QAAAARnQU1BAACxjwv8YQUAAAAJcEhZcwAAEnQAABJ0Ad5mH3gAAAJNSURBVDhPlVPPaxNBFJ7Z3fxqkkLIJZBTkEQ9WfwbehFBIx48VESh6EEoemov4tFfUDwVoTcv6k1R8CIeNKcICrG2TZoUo1FMCZro/prd2Rm/2Q2lQnLwOzxe3rxvv/e9mVDXdRO3jpH/gbP8gcqbh3cWnicSCUopSkIIKSUiEDVFSVRBD2Pe3NNzGkrgWJbVarUajUa32+Wcp1KpmRDJZBI5gCRMZwzDAEXRHMdB92Xr/lLw4NLwXrvdBjPSDIIASRAIInUqdY9bp3dXxjTf923bRtJ5v4M4Go1AQAJgKkoMKbnFu/Ofrp7YXI7qigZdzIPk0PEyYiaTQYQIWIL4pt+Z/3jt1NYdFPehBo3H44VCYbW7CM1sNlssFlE0TVMKyYVT3b0bdv4Dtcn2+Rcwi5vAtFCOxWJwe+RxddwyCUoNTjASNOOxONyY5p+jT870Fl/hKNwN0SjVwwVCn1DML5Q35UMSgRq8S8fjo+hbWG+9Xq/V3m43txlzNY1qOkXEqfqGHXwPPJ0FIyYGpt90bYFXMxyOQLuReUSShDDy8MdKqVQaS0ipaGfbq4gHcTIcjzFGMupWsGHkEQHARsIhJwGPYnZ2Fsn+rYwpUplTahORTqfL5fL64LrneeDncrnIv8v7A+fdVDVN0/L5PPxUKhXcZCqVhpDt9/ac2oXOy6k07FUEQteNmIE/h8b4r6G78c16drHzGqdTaXgfXDJfWCz4aflf+tab6tbalc+b0SnFg0jenot+HMTGwhokXbHn8N5vr7n0tT8+AAj5C+YNX6G0QHC+AAAAAElFTkSuQmCC")
// LAS TRES LINEAS DE ARRIBA FUERON MODIFICADAS.
@UsesPermissions(permissionNames = "android.permission.WRITE_EXTERNAL_STORAGE")
@SimpleObject(external = true) // ESTO HA SIDO MODIFICADO.
public class KIO4_ImagePickerY extends AndroidNonvisibleComponent implements Component, ActivityResultListener {
private ComponentContainer container;
private Context context;
protected int requestCode;
private static final String LOG_TAG = "KIO4_ImagePickerY";
private static final String DEFAULT_imagePickerDirectoryName = "/storage/emulated/0/Pictures/_app_inventor_image_picker";
// directory on external storage for storing the files for the saved images
private String imagePickerDirectoryName = "/storage/emulated/0/Pictures/_app_inventor_image_picker";
// prefix for image file names
private static final String FILE_PREFIX = "img_";
// max number of files to save in image directory
private static int maxSavedFiles = 1000; // ESTA LINEA HA SIDO MODIFICADA.
// The media path (URI) for the selected image file created by MediaUtil
private String selectionURI;
// The path to the saved image
private String selectionSavedImage = "";
/**
* Create a new ImagePicker component.
*
* @param container the parent container.
*/
public KIO4_ImagePickerY(ComponentContainer container) {
super(container.$form());
this.container = container;
context = (Context) container.$context();
ImagePickerDirectoryName(DEFAULT_imagePickerDirectoryName);
}
/**
* Path to the file containing the image that was selected.
*/
@SimpleProperty(description = "Path to the file containing the image that was selected.",
category = PropertyCategory.BEHAVIOR)
public String Selection() {
return selectionSavedImage;
}
@SimpleProperty(category = PropertyCategory.BEHAVIOR, description = "Directory destiny. Set absolute address: /storage/emulated/..." )
public String ImagePickerDirectoryName() {
return imagePickerDirectoryName;
}
@DesignerProperty(editorType = PropertyTypeConstants.PROPERTY_TYPE_STRING, defaultValue = KIO4_ImagePickerY.DEFAULT_imagePickerDirectoryName + "")
@SimpleProperty(description = "imagePickerDirectoryName. ")
public void ImagePickerDirectoryName(String nuevoImagePickerDirectoryName) {
this.imagePickerDirectoryName = nuevoImagePickerDirectoryName;
}
protected Intent getIntent() {
return new Intent(Intent.ACTION_PICK, MediaStore.Images.Media.INTERNAL_CONTENT_URI);
}
/**
* Callback method to get the result returned by the image picker activity
*
* @param requestCode a code identifying the request.
* @param resultCode a code specifying success or failure of the activity
* @param data the returned data, in this case an Intent whose data field
* contains the image's content URI.
*/
public void resultReturned(int requestCode, int resultCode, Intent data) {
if (requestCode == this.requestCode && resultCode == Activity.RESULT_OK) {
Uri selectedImage = data.getData();
selectionURI = selectedImage.toString();
Log.i(LOG_TAG, "selectionURI = " + selectionURI);
// get the file type extension from the intent data Uri
ContentResolver cR = container.$context().getContentResolver();
MimeTypeMap mime = MimeTypeMap.getSingleton();
String extension = "." + mime.getExtensionFromMimeType(cR.getType(selectedImage));
Log.i(LOG_TAG, "extension = " + extension);
// save the image to a temp file in external storage, using a name
// that includes the extension
saveSelectedImageToExternalStorage(extension);
AfterPicking();
}
}
private void saveSelectedImageToExternalStorage(String extension) {
// clear imageFile for new save attempt
// This will be the stored picture
selectionSavedImage = "";
// create a temp file for holding the image that was picked
// This is not the external stored file: This is in the internal directory used by MediaUtil
File tempFile = null;
// copy the picture at the image URI to the temp file
try {
tempFile = MediaUtil.copyMediaToTempFile(container.$form(), selectionURI);
} catch (IOException e) {
Log.i(LOG_TAG, "copyMediaToTempFile failed: " + e.getMessage());
container.$form().dispatchErrorOccurredEvent(this, "ImagePicker",
ErrorMessages.ERROR_CANNOT_COPY_MEDIA, e.getMessage());
return;
}
// copy the temp file to external storage
Log.i(LOG_TAG, "temp file path is: " + tempFile.getPath());
// Copy file will signal a screen error if the copy fails.
copyToExternalStorageAndDeleteSource(tempFile, extension);
}
private void copyToExternalStorageAndDeleteSource(File source, String extension) {
File dest = null;
InputStream inStream = null;
OutputStream outStream = null;
// String fullDirname = Environment.getExternalStorageDirectory() + imagePickerDirectoryName;
String fullDirname = imagePickerDirectoryName;
File destDirectory = new File(fullDirname);
try {
destDirectory.mkdirs();
dest = File.createTempFile (FILE_PREFIX, extension, destDirectory);
selectionSavedImage = dest.getPath();
// Uncomment this to delete imageFile when the application stops
// dest.deleteOnExit();
Log.i(LOG_TAG, "saved file path is: " + selectionSavedImage);
inStream = new FileInputStream(source);
outStream = new FileOutputStream(dest);
byte[] buffer = new byte[1024];
int length;
// copy the file content in bytes
while ((length = inStream.read(buffer)) > 0){
outStream.write(buffer, 0, length);
}
inStream.close();
outStream.close();
Log.i(LOG_TAG, "Image was copied to " + selectionSavedImage);
// this can be uncommented to show the alert, but the alert
// is pretty annoying
// new (container.$form()).ShowAlert("Image was copied to " + selectedImage);
} catch(IOException e) {
String err = "destination is " + selectionSavedImage + ": " + "error is " + e.getMessage();
Log.i(LOG_TAG, "copyFile failed. " + err);
container.$form().dispatchErrorOccurredEvent(this, "SaveImage",
ErrorMessages.ERROR_CANNOT_SAVE_IMAGE, err);
selectionSavedImage = "";
dest.delete();
}
// clean up the temp file. This isn't critical because MudiaUtil.copyMediaToTempFile
// marks this with deleteOnExit, but it's nice to clean up here.
source.delete();
trimDirectory(maxSavedFiles, destDirectory);
}
// keep only the last N files, where N = maxSavedFiles
private void trimDirectory(int maxSavedFiles, File directory) {
File[] files = directory.listFiles();
Arrays.sort(files, new Comparator<File>(){
public int compare(File f1, File f2)
{
return Long.valueOf(f1.lastModified()).compareTo(f2.lastModified());
} });
int excess = files.length - maxSavedFiles;
for (int i = 0; i < excess; i++) {
files[i].delete();
}
}
////////////////////////// Obtenido de PICKER.java //////////////////////////////////////////////////
public void click() {
BeforePicking();
if (requestCode == 0) { // only need to register once
requestCode = container.$form().registerForActivityResult(this);
}
container.$context().startActivityForResult(getIntent(), requestCode);
// String openAnim = container.$form().getOpenAnimType();
// AnimationUtil.ApplyOpenScreenAnimation(container.$context(), openAnim);
}
// Functions
/**
* Opens the picker, as though the user clicked on it.
*/
@SimpleFunction(description = "Opens the picker, as though the user clicked on it.")
public void Open() {
click();
}
// Events
/**
* Event to raise when the button of the component is clicked or the list is shown
* using the Open block. This event occurs before the list of items is displayed, and
* can be used to prepare the list before it is shown.
*/
@SimpleEvent
public void BeforePicking() {
EventDispatcher.dispatchEvent(this, "BeforePicking");
}
/**
* Event to be raised after the picker activity returns its
* result and the properties have been filled in.
*/
@SimpleEvent
public void AfterPicking() {
EventDispatcher.dispatchEvent(this, "AfterPicking");
}
/////////////////// Obtener el directorio ASD /storage/emulated/0/Android/data/namepackage/files /////////////
@SimpleFunction(description = "Get App Specific Directory path (ASD). Example: /storage/emulated/0/Android/data/<namepackage>/files")
public String GetAsdPath() {
return context.getExternalFilesDir(null).toString();
}
}