AI2 Component  (Version nb184)
ImagePicker.java
Go to the documentation of this file.
1 // -*- Mode: java; c-basic-offset: 2; -*-
2 // Copyright 2009-2011 Google, All Rights reserved
3 // Copyright 2011-2020 MIT, All rights reserved
4 // Released under the Apache License, Version 2.0
5 // http://www.apache.org/licenses/LICENSE-2.0
6 
7 package com.google.appinventor.components.runtime;
8 
9 import android.Manifest;
10 import android.app.Activity;
11 import android.content.ContentResolver;
12 import android.content.Intent;
13 import android.net.Uri;
14 import android.provider.MediaStore;
15 import android.util.Log;
16 import android.webkit.MimeTypeMap;
17 
29 
30 import java.io.File;
31 import java.io.IOException;
32 import java.util.Arrays;
33 import java.util.Comparator;
34 
44 @DesignerComponent(version = YaVersion.IMAGEPICKER_COMPONENT_VERSION,
45  description = "A special-purpose button. When the user taps an image picker, the " +
46  "device's image gallery appears, and the user can choose an image. After an image is " +
47  "picked, it is saved, and the <code>Selected</code> " +
48  "property will be the name of the file where the image is stored. In order to not " +
49  "fill up storage, a maximum of 10 images will be stored. Picking more images " +
50  "will delete previous images, in order from oldest to newest.",
51  category = ComponentCategory.MEDIA)
52 
53 @UsesPermissions(permissionNames = "android.permission.WRITE_EXTERNAL_STORAGE")
54 @SimpleObject
55 public class ImagePicker extends Picker implements ActivityResultListener {
56 
57  private static final String LOG_TAG = "ImagePicker";
58 
59  // directory on external storage for storing the files for the saved images
60  private static final String imagePickerDirectoryName = "/Pictures/_app_inventor_image_picker";
61 
62  // prefix for image file names
63  private static final String FILE_PREFIX = "picked_image";
64 
65  // max number of files to save in image directory
66  private static int maxSavedFiles = 10;
67 
68  // The media path (URI) for the selected image file created by MediaUtil
69  private String selectionURI;
70 
71  // The path to the saved image
72  private String selectionSavedImage = "";
73 
74  // Flag to indicate whether we have permission to write imgaes to external storage
75  private boolean havePermission = false;
76 
83  super(container);
84  }
85 
89  @SimpleProperty(description = "Path to the file containing the image that was selected.",
90  category = PropertyCategory.BEHAVIOR)
91  public String Selection() {
92  return selectionSavedImage;
93  }
94 
95  @Override
96  protected Intent getIntent() {
97  return new Intent(Intent.ACTION_PICK, MediaStore.Images.Media.INTERNAL_CONTENT_URI);
98  }
99 
100  @Override
101  public void click() {
102  if (!havePermission) {
103  container.$form().askPermission(Manifest.permission.WRITE_EXTERNAL_STORAGE,
105  @Override
106  public void HandlePermissionResponse(String permission, boolean granted) {
107  if (granted) {
108  havePermission = true;
109  click();
110  } else {
112  permission);
113  }
114  }
115  });
116  return;
117  }
118  super.click();
119  }
120 
129  public void resultReturned(int requestCode, int resultCode, Intent data) {
130  if (requestCode == this.requestCode && resultCode == Activity.RESULT_OK) {
131  Uri selectedImage = data.getData();
132  selectionURI = selectedImage.toString();
133  Log.i(LOG_TAG, "selectionURI = " + selectionURI);
134 
135  // get the file type extension from the intent data Uri
136  ContentResolver cR = container.$context().getContentResolver();
137  MimeTypeMap mime = MimeTypeMap.getSingleton();
138  String extension = "." + mime.getExtensionFromMimeType(cR.getType(selectedImage));
139  Log.i(LOG_TAG, "extension = " + extension);
140 
141  // save the image to a temp file in external storage, using a name
142  // that includes the extension
143  saveSelectedImageToExternalStorage(extension);
144  AfterPicking();
145  }
146  }
147 
148  private void saveSelectedImageToExternalStorage(String extension) {
149  if (container.$form().isDeniedPermission(Manifest.permission.WRITE_EXTERNAL_STORAGE)) {
150  container.$form().dispatchPermissionDeniedEvent(this, "ImagePicker",
151  Manifest.permission.WRITE_EXTERNAL_STORAGE);
152  return;
153 
154  }
155  // clear imageFile for new save attempt
156  // This will be the stored picture
157  selectionSavedImage = "";
158  // create a temp file for holding the image that was picked
159  // This is not the external stored file: This is in the internal directory used by MediaUtil
160  File tempFile = null;
161 
162  // copy the picture at the image URI to the temp file
163  try {
164  tempFile = MediaUtil.copyMediaToTempFile(container.$form(), selectionURI);
165  } catch (IOException e) {
166  Log.i(LOG_TAG, "copyMediaToTempFile failed: " + e.getMessage());
167  container.$form().dispatchErrorOccurredEvent(this, "ImagePicker",
168  ErrorMessages.ERROR_CANNOT_COPY_MEDIA, e.getMessage());
169  return;
170  }
171 
172  // copy the temp file to external storage
173  Log.i(LOG_TAG, "temp file path is: " + tempFile.getPath());
174  // Copy file will signal a screen error if the copy fails.
175  copyToExternalStorageAndDeleteSource(tempFile, extension);
176  }
177 
178  private void copyToExternalStorageAndDeleteSource(File source, String extension) {
179 
180  File dest = null;
181 
182  String fullDirname = QUtil.getExternalStoragePath(container.$form()) + imagePickerDirectoryName;
183  File destDirectory = new File(fullDirname);
184 
185  try {
186  destDirectory.mkdirs();
187  dest = File.createTempFile (FILE_PREFIX, extension, destDirectory);
188 
189  selectionSavedImage = dest.getPath();
190  // Uncomment this to delete imageFile when the application stops
191  // dest.deleteOnExit();
192  Log.i(LOG_TAG, "saved file path is: " + selectionSavedImage);
193 
194  FileUtil.copyFile(source.getAbsolutePath(), dest.getAbsolutePath());
195 
196  Log.i(LOG_TAG, "Image was copied to " + selectionSavedImage);
197  // this can be uncommented to show the alert, but the alert
198  // is pretty annoying
199  // new (container.$form()).ShowAlert("Image was copied to " + selectedImage);
200 
201  } catch(IOException e) {
202  String err = "destination is " + selectionSavedImage + ": " + "error is " + e.getMessage();
203  Log.i(LOG_TAG, "copyFile failed. " + err);
204  container.$form().dispatchErrorOccurredEvent(this, "SaveImage",
205  ErrorMessages.ERROR_CANNOT_SAVE_IMAGE, err);
206  selectionSavedImage = "";
207  dest.delete();
208  }
209 
210  // clean up the temp file. This isn't critical because MudiaUtil.copyMediaToTempFile
211  // marks this with deleteOnExit, but it's nice to clean up here.
212  source.delete();
213  trimDirectory(maxSavedFiles, destDirectory);
214  }
215 
216  // keep only the last N files, where N = maxSavedFiles
217  private void trimDirectory(int maxSavedFiles, File directory) {
218 
219  File[] files = directory.listFiles();
220 
221  Arrays.sort(files, new Comparator<File>(){
222  public int compare(File f1, File f2)
223  {
224  return Long.valueOf(f1.lastModified()).compareTo(f2.lastModified());
225  } });
226 
227  int excess = files.length - maxSavedFiles;
228  for (int i = 0; i < excess; i++) {
229  files[i].delete();
230  }
231 
232  }
233 
234 }
com.google.appinventor.components.runtime.ImagePicker.click
void click()
Definition: ImagePicker.java:101
com.google.appinventor.components.runtime.Form.askPermission
void askPermission(final String permission, final PermissionResultHandler responseRequestor)
Definition: Form.java:2633
com.google.appinventor.components.runtime.util.ErrorMessages
Definition: ErrorMessages.java:17
com.google.appinventor.components.runtime.util
-*- mode: java; c-basic-offset: 2; -*-
Definition: AccountChooser.java:7
com.google.appinventor.components.runtime.util.FileUtil
Definition: FileUtil.java:37
com.google.appinventor.components.common.YaVersion
Definition: YaVersion.java:14
com.google.appinventor.components
com.google.appinventor.components.runtime.util.MediaUtil
Definition: MediaUtil.java:53
com.google.appinventor.components.annotations.DesignerComponent
Definition: DesignerComponent.java:22
com.google.appinventor.components.annotations.PropertyCategory.BEHAVIOR
BEHAVIOR
Definition: PropertyCategory.java:15
com.google.appinventor.components.runtime.Form.dispatchPermissionDeniedEvent
void dispatchPermissionDeniedEvent(final Component component, final String functionName, final PermissionException exception)
Definition: Form.java:987
com.google.appinventor.components.runtime.util.QUtil
Definition: QUtil.java:18
com.google.appinventor.components.annotations.UsesPermissions
Definition: UsesPermissions.java:21
com.google.appinventor.components.runtime.ImagePicker.getIntent
Intent getIntent()
Definition: ImagePicker.java:96
com.google.appinventor.components.runtime.MediaStore
Definition: MediaStore.java:72
com.google.appinventor.components.runtime.PermissionResultHandler
Definition: PermissionResultHandler.java:15
com.google.appinventor.components.runtime.Picker.requestCode
int requestCode
Definition: Picker.java:25
com.google.appinventor.components.runtime.ImagePicker.resultReturned
void resultReturned(int requestCode, int resultCode, Intent data)
Definition: ImagePicker.java:129
com.google.appinventor.components.annotations.SimpleProperty
Definition: SimpleProperty.java:23
com.google.appinventor.components.runtime.Picker.AfterPicking
void AfterPicking()
Definition: Picker.java:75
com.google.appinventor.components.runtime.Picker.container
final ComponentContainer container
Definition: Picker.java:21
com.google.appinventor.components.annotations.PropertyCategory
Definition: PropertyCategory.java:13
com.google.appinventor.components.runtime.ImagePicker.Selection
String Selection()
Definition: ImagePicker.java:91
com.google.appinventor.components.runtime.ComponentContainer
Definition: ComponentContainer.java:16
com.google.appinventor.components.runtime
Copyright 2009-2011 Google, All Rights reserved.
Definition: AccelerometerSensor.java:8
com.google.appinventor.components.runtime.ImagePicker.ImagePicker
ImagePicker(ComponentContainer container)
Definition: ImagePicker.java:82
com.google.appinventor.components.common
Definition: ComponentCategory.java:7
com.google.appinventor.components.common.ComponentCategory
Definition: ComponentCategory.java:48
com.google.appinventor.components.runtime.Form.dispatchErrorOccurredEvent
void dispatchErrorOccurredEvent(final Component component, final String functionName, final int errorNumber, final Object... messageArgs)
Definition: Form.java:1011
com.google.appinventor.components.runtime.ActivityResultListener
Definition: ActivityResultListener.java:16
com.google.appinventor.components.runtime.Picker
Definition: Picker.java:20
com.google.appinventor.components.annotations.SimpleObject
Definition: SimpleObject.java:23
com.google
com
com.google.appinventor.components.runtime.ComponentContainer.$form
Form $form()
com.google.appinventor.components.runtime.ComponentContainer.$context
Activity $context()
com.google.appinventor.components.runtime.ImagePicker
Definition: ImagePicker.java:55
com.google.appinventor.components.annotations
com.google.appinventor.components.runtime.Form.isDeniedPermission
boolean isDeniedPermission(String permission)
Definition: Form.java:2596
com.google.appinventor