mirror of
https://gitee.com/shuto/cordova-imagePicker.git
synced 2026-05-23 00:05:03 +08:00
handled some memory problems and handled image orientation
This commit is contained in:
@@ -3,18 +3,6 @@ cordova-imagePicker
|
||||
|
||||
Cordova Plugin For Multiple Image Selection - implemented for iOS and Android 4.0 and above.
|
||||
|
||||
For iOS this plugin uses the ELCImagePickerController, with slight modifications for the iOS image picker. ELCImagePicker uses the MIT License which can be found in the file LICENSE.
|
||||
|
||||
https://github.com/B-Sides/ELCImagePickerController
|
||||
|
||||
For Android this plugin uses MultiImageChooser, with modifications. MultiImageChooser uses the BSD 2-Clause License which can be found in the file BSD_LICENSE. Some code inside MultImageChooser is licensed under the Apache license which can be found in the file APACHE_LICENSE.
|
||||
|
||||
https://github.com/derosa/MultiImageChooser
|
||||
|
||||
Code(FakeR) was also taken from the phonegap BarCodeScanner plugin. This code uses the MIT license.
|
||||
|
||||
https://github.com/wildabeast/BarcodeScanner
|
||||
|
||||
## Installing the plugin
|
||||
|
||||
The plugin conforms to the Cordova plugin specification, it can be installed
|
||||
@@ -81,6 +69,26 @@ window.imagePicker.getPictures(
|
||||
|
||||
The plugin returns images that are stored in a temporary directory. These images will often not be deleted automatically though. The files should be moved or deleted after you get their filepaths in javascript.
|
||||
|
||||
## Libraries used
|
||||
|
||||
#### ELCImagePicker
|
||||
|
||||
For iOS this plugin uses the ELCImagePickerController, with slight modifications for the iOS image picker. ELCImagePicker uses the MIT License which can be found in the file LICENSE.
|
||||
|
||||
https://github.com/B-Sides/ELCImagePickerController
|
||||
|
||||
#### MultiImageChooser
|
||||
|
||||
For Android this plugin uses MultiImageChooser, with modifications. MultiImageChooser uses the BSD 2-Clause License which can be found in the file BSD_LICENSE. Some code inside MultImageChooser is licensed under the Apache license which can be found in the file APACHE_LICENSE.
|
||||
|
||||
https://github.com/derosa/MultiImageChooser
|
||||
|
||||
#### FakeR
|
||||
|
||||
Code(FakeR) was also taken from the phonegap BarCodeScanner plugin. This code uses the MIT license.
|
||||
|
||||
https://github.com/wildabeast/BarcodeScanner
|
||||
|
||||
## License
|
||||
|
||||
The MIT License
|
||||
|
||||
@@ -30,6 +30,7 @@ import android.graphics.Bitmap;
|
||||
import android.graphics.Color;
|
||||
import android.graphics.drawable.ColorDrawable;
|
||||
import android.graphics.drawable.Drawable;
|
||||
import android.graphics.Matrix;
|
||||
import android.os.AsyncTask;
|
||||
import android.os.Build;
|
||||
import android.os.Handler;
|
||||
@@ -62,14 +63,14 @@ public class ImageFetcher {
|
||||
executor = Executors.newCachedThreadPool();
|
||||
}
|
||||
|
||||
public void fetch(Integer id, ImageView imageView, int colWidth) {
|
||||
public void fetch(Integer id, ImageView imageView, int colWidth, int rotate) {
|
||||
resetPurgeTimer();
|
||||
this.colWidth = colWidth;
|
||||
this.origId = id;
|
||||
Bitmap bitmap = getBitmapFromCache(id);
|
||||
|
||||
if (bitmap == null) {
|
||||
forceDownload(id, imageView);
|
||||
forceDownload(id, imageView, rotate);
|
||||
} else {
|
||||
cancelPotentialDownload(id, imageView);
|
||||
imageView.setImageBitmap(bitmap);
|
||||
@@ -80,14 +81,14 @@ public class ImageFetcher {
|
||||
* Same as download but the image is always downloaded and the cache is not
|
||||
* used. Kept private at the moment as its interest is not clear.
|
||||
*/
|
||||
private void forceDownload(Integer position, ImageView imageView) {
|
||||
private void forceDownload(Integer position, ImageView imageView, int rotate) {
|
||||
if (position == null) {
|
||||
imageView.setImageDrawable(null);
|
||||
return;
|
||||
}
|
||||
|
||||
if (cancelPotentialDownload(position, imageView)) {
|
||||
BitmapFetcherTask task = new BitmapFetcherTask(imageView.getContext(), imageView);
|
||||
BitmapFetcherTask task = new BitmapFetcherTask(imageView.getContext(), imageView, rotate);
|
||||
DownloadedDrawable downloadedDrawable = new DownloadedDrawable(imageView.getContext(), task, origId);
|
||||
imageView.setImageDrawable(downloadedDrawable);
|
||||
imageView.setMinimumHeight(colWidth);
|
||||
@@ -164,10 +165,12 @@ public class ImageFetcher {
|
||||
private Integer position;
|
||||
private final WeakReference<ImageView> imageViewReference;
|
||||
private final Context mContext;
|
||||
private final int rotate;
|
||||
|
||||
public BitmapFetcherTask(Context context, ImageView imageView) {
|
||||
public BitmapFetcherTask(Context context, ImageView imageView, int rotate) {
|
||||
imageViewReference = new WeakReference<ImageView>(imageView);
|
||||
mContext = context;
|
||||
this.rotate = rotate;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -190,6 +193,11 @@ public class ImageFetcher {
|
||||
if (isCancelled()) {
|
||||
return null;
|
||||
} else {
|
||||
if (rotate != 0) {
|
||||
Matrix matrix = new Matrix();
|
||||
matrix.setRotate(rotate);
|
||||
thumb = Bitmap.createBitmap(thumb, 0, 0, thumb.getWidth(), thumb.getHeight(), matrix, true);
|
||||
}
|
||||
return thumb;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -36,8 +36,10 @@ import java.io.FileOutputStream;
|
||||
import java.io.IOException;
|
||||
import java.io.OutputStream;
|
||||
import java.util.ArrayList;
|
||||
import java.util.HashSet;
|
||||
import java.util.HashMap;
|
||||
import java.util.Iterator;
|
||||
import java.util.Map;
|
||||
import java.util.Map.Entry;
|
||||
import java.util.Set;
|
||||
|
||||
import com.synconset.FakeR;
|
||||
@@ -77,13 +79,7 @@ import android.widget.TextView;
|
||||
|
||||
public class MultiImageChooserActivity extends Activity implements OnItemClickListener,
|
||||
LoaderManager.LoaderCallbacks<Cursor> {
|
||||
private static final String TAG = "Collage";
|
||||
public static final String COL_WIDTH_KEY = "COL_WIDTH";
|
||||
public static final String FLURRY_EVENT_ADD_MULTIPLE_IMAGES = "Add multiple images";
|
||||
|
||||
// El tamaño por defecto es 100 porque los thumbnails MICRO_KIND son de
|
||||
// 96x96
|
||||
private static final int DEFAULT_COLUMN_WIDTH = 120;
|
||||
private static final String TAG = "ImagePicker";
|
||||
|
||||
public static final int NOLIMIT = -1;
|
||||
public static final String MAX_IMAGES_KEY = "MAX_IMAGES";
|
||||
@@ -94,13 +90,13 @@ public class MultiImageChooserActivity extends Activity implements OnItemClickLi
|
||||
private ImageAdapter ia;
|
||||
|
||||
private Cursor imagecursor, actualimagecursor;
|
||||
private int image_column_index, actual_image_column_index;
|
||||
private int image_column_index, image_column_orientation, actual_image_column_index, orientation_column_index;
|
||||
private int colWidth;
|
||||
|
||||
private static final int CURSORLOADER_THUMBS = 0;
|
||||
private static final int CURSORLOADER_REAL = 1;
|
||||
|
||||
private Set<String> fileNames = new HashSet<String>();
|
||||
private Map<String, Integer> fileNames = new HashMap<String, Integer>();
|
||||
|
||||
private SparseBooleanArray checkStatus = new SparseBooleanArray();
|
||||
|
||||
@@ -115,7 +111,7 @@ public class MultiImageChooserActivity extends Activity implements OnItemClickLi
|
||||
|
||||
private final ImageFetcher fetcher = new ImageFetcher();
|
||||
|
||||
private int selectedColor = Color.GREEN;
|
||||
private int selectedColor = 0xff32b2e1;
|
||||
private boolean shouldRequestThumb = true;
|
||||
|
||||
private FakeR fakeR;
|
||||
@@ -135,8 +131,6 @@ public class MultiImageChooserActivity extends Activity implements OnItemClickLi
|
||||
quality = getIntent().getIntExtra(QUALITY_KEY, 0);
|
||||
maxImageCount = maxImages;
|
||||
|
||||
colWidth = getIntent().getIntExtra(COL_WIDTH_KEY, DEFAULT_COLUMN_WIDTH);
|
||||
|
||||
Display display = getWindowManager().getDefaultDisplay();
|
||||
int width = display.getWidth();
|
||||
|
||||
@@ -169,7 +163,6 @@ public class MultiImageChooserActivity extends Activity implements OnItemClickLi
|
||||
}
|
||||
}
|
||||
});
|
||||
selectedColor = 0xff32b2e1;
|
||||
|
||||
ia = new ImageAdapter(this);
|
||||
gridView.setAdapter(ia);
|
||||
@@ -187,6 +180,7 @@ public class MultiImageChooserActivity extends Activity implements OnItemClickLi
|
||||
@Override
|
||||
public void onItemClick(AdapterView<?> arg0, View view, int position, long id) {
|
||||
String name = getImageName(position);
|
||||
int rotation = getImageRotation(position);
|
||||
|
||||
if (name == null) {
|
||||
return;
|
||||
@@ -207,33 +201,29 @@ public class MultiImageChooserActivity extends Activity implements OnItemClickLi
|
||||
}
|
||||
|
||||
if (isChecked) {
|
||||
if (fileNames.add(name)) {
|
||||
if (maxImageCount == 1) {
|
||||
this.selectClicked(null);
|
||||
} else {
|
||||
maxImages--;
|
||||
ImageView imageView = (ImageView)view;
|
||||
if (android.os.Build.VERSION.SDK_INT>=16) {
|
||||
imageView.setImageAlpha(128);
|
||||
} else {
|
||||
imageView.setAlpha(128);
|
||||
}
|
||||
view.setBackgroundColor(selectedColor);
|
||||
}
|
||||
}
|
||||
} else {
|
||||
if (fileNames.remove(name)) {
|
||||
// Solo incrementa los slots libres si hemos
|
||||
// "liberado" uno...
|
||||
maxImages++;
|
||||
fileNames.put(name, new Integer(rotation));
|
||||
if (maxImageCount == 1) {
|
||||
this.selectClicked(null);
|
||||
} else {
|
||||
maxImages--;
|
||||
ImageView imageView = (ImageView)view;
|
||||
if (android.os.Build.VERSION.SDK_INT>=16) {
|
||||
imageView.setImageAlpha(255);
|
||||
imageView.setImageAlpha(128);
|
||||
} else {
|
||||
imageView.setAlpha(255);
|
||||
imageView.setAlpha(128);
|
||||
}
|
||||
view.setBackgroundColor(Color.TRANSPARENT);
|
||||
view.setBackgroundColor(selectedColor);
|
||||
}
|
||||
} else {
|
||||
fileNames.remove(name);
|
||||
maxImages++;
|
||||
ImageView imageView = (ImageView)view;
|
||||
if (android.os.Build.VERSION.SDK_INT>=16) {
|
||||
imageView.setImageAlpha(255);
|
||||
} else {
|
||||
imageView.setAlpha(255);
|
||||
}
|
||||
view.setBackgroundColor(Color.TRANSPARENT);
|
||||
}
|
||||
|
||||
checkStatus.put(position, isChecked);
|
||||
@@ -249,9 +239,11 @@ public class MultiImageChooserActivity extends Activity implements OnItemClickLi
|
||||
|
||||
case CURSORLOADER_THUMBS:
|
||||
img.add(MediaStore.Images.Media._ID);
|
||||
img.add(MediaStore.Images.Media.ORIENTATION);
|
||||
break;
|
||||
case CURSORLOADER_REAL:
|
||||
img.add(MediaStore.Images.Thumbnails.DATA);
|
||||
img.add(MediaStore.Images.Media.ORIENTATION);
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
@@ -270,17 +262,20 @@ public class MultiImageChooserActivity extends Activity implements OnItemClickLi
|
||||
}
|
||||
|
||||
switch (loader.getId()) {
|
||||
case CURSORLOADER_THUMBS:
|
||||
imagecursor = cursor;
|
||||
image_column_index = imagecursor.getColumnIndex(MediaStore.Images.Media._ID);
|
||||
ia.notifyDataSetChanged();
|
||||
break;
|
||||
case CURSORLOADER_REAL:
|
||||
actualimagecursor = cursor;
|
||||
actual_image_column_index = actualimagecursor.getColumnIndexOrThrow(MediaStore.Images.Media.DATA);
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
case CURSORLOADER_THUMBS:
|
||||
imagecursor = cursor;
|
||||
image_column_index = imagecursor.getColumnIndex(MediaStore.Images.Media._ID);
|
||||
image_column_orientation = imagecursor.getColumnIndex(MediaStore.Images.Media.ORIENTATION);
|
||||
ia.notifyDataSetChanged();
|
||||
break;
|
||||
case CURSORLOADER_REAL:
|
||||
actualimagecursor = cursor;
|
||||
String[] columns = actualimagecursor.getColumnNames();
|
||||
actual_image_column_index = actualimagecursor.getColumnIndexOrThrow(MediaStore.Images.Media.DATA);
|
||||
orientation_column_index = actualimagecursor.getColumnIndexOrThrow(MediaStore.Images.Media.ORIENTATION);
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -293,7 +288,6 @@ public class MultiImageChooserActivity extends Activity implements OnItemClickLi
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
public void cancelClicked(View ignored) {
|
||||
setResult(RESULT_CANCELED);
|
||||
finish();
|
||||
@@ -309,7 +303,7 @@ public class MultiImageChooserActivity extends Activity implements OnItemClickLi
|
||||
progress.dismiss();
|
||||
finish();
|
||||
} else {
|
||||
new ResizeImagesTask().execute(fileNames);
|
||||
new ResizeImagesTask().execute(fileNames.entrySet());
|
||||
}
|
||||
}
|
||||
|
||||
@@ -321,7 +315,6 @@ public class MultiImageChooserActivity extends Activity implements OnItemClickLi
|
||||
((TextView) getActionBar().getCustomView().findViewById(fakeR.getId("id", "actionbar_done_textview")))
|
||||
.setEnabled(fileNames.size() != 0);
|
||||
getActionBar().getCustomView().findViewById(fakeR.getId("id", "actionbar_done")).setEnabled(fileNames.size() != 0);
|
||||
|
||||
}
|
||||
|
||||
private void setupHeader() {
|
||||
@@ -360,8 +353,7 @@ public class MultiImageChooserActivity extends Activity implements OnItemClickLi
|
||||
}
|
||||
});
|
||||
|
||||
// Show the custom action bar view and hide the normal Home icon and
|
||||
// title.
|
||||
// Show the custom action bar view and hide the normal Home icon and title.
|
||||
final ActionBar actionBar = getActionBar();
|
||||
actionBar.setDisplayOptions(ActionBar.DISPLAY_SHOW_CUSTOM, ActionBar.DISPLAY_SHOW_CUSTOM
|
||||
| ActionBar.DISPLAY_SHOW_HOME | ActionBar.DISPLAY_SHOW_TITLE);
|
||||
@@ -380,7 +372,19 @@ public class MultiImageChooserActivity extends Activity implements OnItemClickLi
|
||||
}
|
||||
return name;
|
||||
}
|
||||
|
||||
private int getImageRotation(int position) {
|
||||
actualimagecursor.moveToPosition(position);
|
||||
int rotation = 0;
|
||||
|
||||
try {
|
||||
rotation = actualimagecursor.getInt(orientation_column_index);
|
||||
} catch (Exception e) {
|
||||
return rotation;
|
||||
}
|
||||
return rotation;
|
||||
}
|
||||
|
||||
public boolean isChecked(int position) {
|
||||
boolean ret = checkStatus.get(position);
|
||||
return ret;
|
||||
@@ -453,6 +457,7 @@ public class MultiImageChooserActivity extends Activity implements OnItemClickLi
|
||||
}
|
||||
|
||||
final int id = imagecursor.getInt(image_column_index);
|
||||
final int rotate = imagecursor.getInt(image_column_orientation);
|
||||
if (isChecked(pos)) {
|
||||
if (android.os.Build.VERSION.SDK_INT>=16) {
|
||||
imageView.setImageAlpha(128);
|
||||
@@ -469,7 +474,7 @@ public class MultiImageChooserActivity extends Activity implements OnItemClickLi
|
||||
imageView.setBackgroundColor(Color.TRANSPARENT);
|
||||
}
|
||||
if (shouldRequestThumb) {
|
||||
fetcher.fetch(Integer.valueOf(id), imageView, colWidth);
|
||||
fetcher.fetch(Integer.valueOf(id), imageView, colWidth, rotate);
|
||||
}
|
||||
|
||||
return imageView;
|
||||
@@ -477,43 +482,59 @@ public class MultiImageChooserActivity extends Activity implements OnItemClickLi
|
||||
}
|
||||
|
||||
|
||||
private class ResizeImagesTask extends AsyncTask<Set<String>, Void, ArrayList<String>> {
|
||||
private class ResizeImagesTask extends AsyncTask<Set<Entry<String, Integer>>, Void, ArrayList<String>> {
|
||||
@Override
|
||||
protected ArrayList<String> doInBackground(Set<String>... fileSets) {
|
||||
Set<String> fileNames = fileSets[0];
|
||||
protected ArrayList<String> doInBackground(Set<Entry<String, Integer>>... fileSets) {
|
||||
Set<Entry<String, Integer>> fileNames = fileSets[0];
|
||||
ArrayList<String> al = new ArrayList<String>();
|
||||
try {
|
||||
Iterator<String> i = fileNames.iterator();
|
||||
Iterator<Entry<String, Integer>> i = fileNames.iterator();
|
||||
Bitmap bmp;
|
||||
while(i.hasNext()) {
|
||||
File file = new File(i.next());
|
||||
Bitmap bmp = this.getBitmap(file);
|
||||
int width = bmp.getWidth();
|
||||
int height = bmp.getHeight();
|
||||
float widthScale = 1.0f;
|
||||
float heightScale = 1.0f;
|
||||
float scale = 1.0f;
|
||||
if (desiredWidth > 0 || desiredHeight > 0) {
|
||||
if (desiredHeight == 0 && desiredWidth < width) {
|
||||
scale = (float)desiredWidth/width;
|
||||
} else if (desiredWidth == 0 && desiredHeight < height) {
|
||||
scale = (float)desiredHeight/height;
|
||||
} else {
|
||||
if (desiredWidth > 0 && desiredWidth < width) {
|
||||
widthScale = (float)desiredWidth/width;
|
||||
}
|
||||
if (desiredHeight > 0 && desiredHeight < height) {
|
||||
heightScale = (float)desiredHeight/height;
|
||||
}
|
||||
if (widthScale < heightScale) {
|
||||
scale = widthScale;
|
||||
} else {
|
||||
scale = heightScale;
|
||||
Entry<String, Integer> imageInfo = i.next();
|
||||
File file = new File(imageInfo.getKey());
|
||||
BitmapFactory.Options options = new BitmapFactory.Options();
|
||||
options.inSampleSize = 1;
|
||||
options.inJustDecodeBounds = true;
|
||||
BitmapFactory.decodeFile(file.getAbsolutePath(), options);
|
||||
int width = options.outWidth;
|
||||
int height = options.outHeight;
|
||||
float scale = calculateScale(width, height);
|
||||
if (scale < 1) {
|
||||
int finalWidth = (int)(width * scale);
|
||||
int finalHeight = (int)(height * scale);
|
||||
int inSampleSize = calculateInSampleSize(options, finalWidth, finalHeight);
|
||||
options = new BitmapFactory.Options();
|
||||
options.inSampleSize = inSampleSize;
|
||||
bmp = BitmapFactory.decodeFile(file.getAbsolutePath(), options);
|
||||
if (bmp == null) {
|
||||
throw new IOException("The image file could not be opened.");
|
||||
}
|
||||
scale = calculateScale(options.outWidth, options.outHeight);
|
||||
bmp = this.getResizedBitmap(bmp, scale);
|
||||
} else {
|
||||
try {
|
||||
bmp = BitmapFactory.decodeFile(file.getAbsolutePath());
|
||||
} catch(OutOfMemoryError e) {
|
||||
options = new BitmapFactory.Options();
|
||||
options.inSampleSize = 2;
|
||||
try {
|
||||
bmp = BitmapFactory.decodeFile(file.getAbsolutePath(), options);
|
||||
} catch(OutOfMemoryError e2) {
|
||||
options = new BitmapFactory.Options();
|
||||
options.inSampleSize = 4;
|
||||
bmp = BitmapFactory.decodeFile(file.getAbsolutePath(), options);
|
||||
}
|
||||
}
|
||||
}
|
||||
if (scale < 1) {
|
||||
bmp = this.getResizedBitmap(bmp, scale);
|
||||
|
||||
int rotate = imageInfo.getValue().intValue();
|
||||
if (rotate != 0) {
|
||||
Matrix matrix = new Matrix();
|
||||
matrix.setRotate(rotate);
|
||||
bmp = Bitmap.createBitmap(bmp, 0, 0, bmp.getWidth(), bmp.getHeight(), matrix, true);
|
||||
}
|
||||
|
||||
file = this.storeImage(bmp, file.getName());
|
||||
al.add(Uri.fromFile(file).toString());
|
||||
}
|
||||
@@ -589,14 +610,52 @@ public class MultiImageChooserActivity extends Activity implements OnItemClickLi
|
||||
Bitmap resizedBitmap = Bitmap.createBitmap(bm, 0, 0, width, height, matrix, false);
|
||||
return resizedBitmap;
|
||||
}
|
||||
}
|
||||
|
||||
private Bitmap getBitmap(File file) throws IOException {
|
||||
Bitmap bmp;
|
||||
bmp = BitmapFactory.decodeFile(file.getAbsolutePath());
|
||||
if (bmp == null) {
|
||||
throw new IOException("The image file could not be opened.");
|
||||
private int calculateInSampleSize(BitmapFactory.Options options, int reqWidth, int reqHeight) {
|
||||
// Raw height and width of image
|
||||
final int height = options.outHeight;
|
||||
final int width = options.outWidth;
|
||||
int inSampleSize = 1;
|
||||
|
||||
if (height > reqHeight || width > reqWidth) {
|
||||
final int halfHeight = height / 2;
|
||||
final int halfWidth = width / 2;
|
||||
|
||||
// Calculate the largest inSampleSize value that is a power of 2 and keeps both
|
||||
// height and width larger than the requested height and width.
|
||||
while ((halfHeight / inSampleSize) > reqHeight && (halfWidth / inSampleSize) > reqWidth) {
|
||||
inSampleSize *= 2;
|
||||
}
|
||||
return bmp;
|
||||
}
|
||||
|
||||
return inSampleSize;
|
||||
}
|
||||
|
||||
private float calculateScale(int width, int height) {
|
||||
float widthScale = 1.0f;
|
||||
float heightScale = 1.0f;
|
||||
float scale = 1.0f;
|
||||
if (desiredWidth > 0 || desiredHeight > 0) {
|
||||
if (desiredHeight == 0 && desiredWidth < width) {
|
||||
scale = (float)desiredWidth/width;
|
||||
} else if (desiredWidth == 0 && desiredHeight < height) {
|
||||
scale = (float)desiredHeight/height;
|
||||
} else {
|
||||
if (desiredWidth > 0 && desiredWidth < width) {
|
||||
widthScale = (float)desiredWidth/width;
|
||||
}
|
||||
if (desiredHeight > 0 && desiredHeight < height) {
|
||||
heightScale = (float)desiredHeight/height;
|
||||
}
|
||||
if (widthScale < heightScale) {
|
||||
scale = widthScale;
|
||||
} else {
|
||||
scale = heightScale;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return scale;
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user