sorted images correctly, made the theme light, and added an alert saying when no more pictures can be added
@@ -0,0 +1,11 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<scale xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:duration="100"
|
||||
android:fillAfter="false"
|
||||
android:fromXScale="0.5"
|
||||
android:fromYScale="0.5"
|
||||
android:interpolator="@android:anim/overshoot_interpolator"
|
||||
android:pivotX="50%"
|
||||
android:pivotY="50%"
|
||||
android:toXScale="1"
|
||||
android:toYScale="1" />
|
||||
|
After Width: | Height: | Size: 4.0 KiB |
|
After Width: | Height: | Size: 1.5 KiB |
|
After Width: | Height: | Size: 3.0 KiB |
|
After Width: | Height: | Size: 1.7 KiB |
|
After Width: | Height: | Size: 415 B |
|
After Width: | Height: | Size: 386 B |
|
After Width: | Height: | Size: 454 B |
|
After Width: | Height: | Size: 400 B |
|
After Width: | Height: | Size: 2.6 KiB |
|
After Width: | Height: | Size: 2.5 KiB |
|
After Width: | Height: | Size: 724 B |
|
After Width: | Height: | Size: 684 B |
|
After Width: | Height: | Size: 825 B |
|
After Width: | Height: | Size: 759 B |
|
After Width: | Height: | Size: 5.1 KiB |
@@ -0,0 +1,12 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<shape xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:shape="rectangle" >
|
||||
<gradient
|
||||
xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:angle="90"
|
||||
android:centerX="0"
|
||||
android:centerY="0"
|
||||
android:startColor="#FF000000"
|
||||
android:endColor="#FF8E8E8E"
|
||||
android:type="linear" />
|
||||
</shape>
|
||||
@@ -0,0 +1,27 @@
|
||||
<!--
|
||||
Copyright 2012 Roman Nurik
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
-->
|
||||
|
||||
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent"
|
||||
android:orientation="horizontal"
|
||||
android:divider="?android:attr/dividerVertical"
|
||||
android:showDividers="middle"
|
||||
android:dividerPadding="12dp">
|
||||
|
||||
<include layout="@layout/actionbar_discard_button" />
|
||||
<include layout="@layout/actionbar_done_button" />
|
||||
</LinearLayout>
|
||||
@@ -0,0 +1,33 @@
|
||||
<!--
|
||||
Copyright 2012 Roman Nurik
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
-->
|
||||
<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:id="@+id/actionbar_discard"
|
||||
android:layout_height="match_parent"
|
||||
android:layout_weight="1"
|
||||
android:layout_width="0dp"
|
||||
style="?android:actionButtonStyle" >
|
||||
<TextView
|
||||
android:drawableLeft="@drawable/ic_action_discard_light"
|
||||
android:drawablePadding="8dp"
|
||||
android:gravity="center_vertical"
|
||||
android:id="@+id/actionbar_discard_textview"
|
||||
android:layout_gravity="center"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_width="wrap_content"
|
||||
android:paddingRight="20dp"
|
||||
android:text="@string/discard"
|
||||
style="?android:actionBarTabTextStyle" />
|
||||
</FrameLayout>
|
||||
@@ -0,0 +1,34 @@
|
||||
<!--
|
||||
Copyright 2012 Roman Nurik
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
-->
|
||||
|
||||
<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
style="?android:actionButtonStyle"
|
||||
android:id="@+id/actionbar_done"
|
||||
android:layout_width="0dp"
|
||||
android:layout_height="match_parent"
|
||||
android:layout_weight="1">
|
||||
|
||||
<TextView style="?android:actionBarTabTextStyle"
|
||||
android:id="@+id/actionbar_done_textview"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_gravity="center"
|
||||
android:paddingRight="20dp"
|
||||
android:drawableLeft="@drawable/ic_action_done_light"
|
||||
android:drawablePadding="8dp"
|
||||
android:gravity="center_vertical"
|
||||
android:text="@string/done" />
|
||||
</FrameLayout>
|
||||
@@ -0,0 +1,23 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:layout_height="fill_parent"
|
||||
android:layout_width="fill_parent"
|
||||
android:orientation="vertical" > <!-- android:background="@drawable/image_bg" -->
|
||||
|
||||
<GridView
|
||||
android:id="@+id/gridview"
|
||||
android:layout_width="fill_parent"
|
||||
android:layout_height="0dp"
|
||||
android:layout_weight="1"
|
||||
android:columnWidth="0dip"
|
||||
android:fadingEdgeLength="10dip"
|
||||
android:fastScrollEnabled="true"
|
||||
android:gravity="center"
|
||||
android:horizontalSpacing="8dip"
|
||||
android:numColumns="3"
|
||||
android:requiresFadingEdge="vertical"
|
||||
android:scrollingCache="true"
|
||||
android:stretchMode="columnWidth"
|
||||
android:verticalSpacing="8dip" />
|
||||
|
||||
</LinearLayout>
|
||||
@@ -0,0 +1,7 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<resources>
|
||||
<string name="multi_app_name">MultiImageChooser</string>
|
||||
<string name="free_version_label">Free version - Images left: %d</string>
|
||||
<string name="error_database">There was an error opening the images database. Please report the problem.</string>
|
||||
<string name="requesting_thumbnails">Requesting thumbnails, please be patient</string>
|
||||
</resources>
|
||||
@@ -0,0 +1,7 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<resources>
|
||||
<string name="multi_app_name">MultiImageChooser</string>
|
||||
<string name="requesting_thumbnails">Solicitando miniaturas. Por favor, espere…</string>
|
||||
<string name="free_version_label">Versión gratuita - Imágenes restantes: %d</string>
|
||||
<string name="error_database">Error al abrir la base de datos de imágenes.</string>
|
||||
</resources>
|
||||
@@ -0,0 +1,6 @@
|
||||
<resources>
|
||||
<string name="multi_app_name">"MultiImageChooser"</string>
|
||||
<string name="free_version_label">"La version gratuite - gauche Images:%d"</string>
|
||||
<string name="error_database">"Il y avait une erreur d'ouvrir la base de données images. S'il vous plaît signaler le problème."</string>
|
||||
<string name="requesting_thumbnails">"Demande de vignettes, s'il vous plaît soyez patient"</string>
|
||||
</resources>
|
||||
@@ -0,0 +1,8 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<resources>
|
||||
<string name="multi_app_name">MultiImageChooser</string>
|
||||
|
||||
<string name="free_version_label">Ingyenes verzió - hátralévő képek: %d</string>
|
||||
<string name="error_database">Képadatbázis megnyitási hiba történt. Kérjük, jelentse a problémát.</string>
|
||||
<string name="requesting_thumbnails">Miniatűrök lekérése, kérjük legyen türelemmel</string>
|
||||
</resources>
|
||||
@@ -0,0 +1,7 @@
|
||||
<?xml version='1.0' encoding='UTF-8'?>
|
||||
<resources>
|
||||
<string name="multi_app_name">MultiImageChooser</string>
|
||||
<string name="free_version_label">無料版 - 残りの画像: %d</string>
|
||||
<string name="error_database">画像データベースを開く際にエラーがありました。問題を報告してください。</string>
|
||||
<string name="requesting_thumbnails">サムネイルをリクエスト中です。お待ちください。</string>
|
||||
</resources>
|
||||
@@ -0,0 +1,7 @@
|
||||
<?xml version='1.0' encoding='UTF-8'?>
|
||||
<resources>
|
||||
<string name="multi_app_name">MultiImageChooser</string>
|
||||
<string name="free_version_label">무료 버전 - 남은 이미지: %d</string>
|
||||
<string name="error_database">이미지 데이터베이스를 여는 데 오류가 발생했습니다. 문제를 보고하세요.</string>
|
||||
<string name="requesting_thumbnails">썸네일 요청 중. 기다려주세요</string>
|
||||
</resources>
|
||||
@@ -0,0 +1,9 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<resources>
|
||||
<string name="multi_app_name">MultiImageChooser</string>
|
||||
<string name="free_version_label">Free version - Images left: %d</string>
|
||||
<string name="error_database">There was an error opening the images database. Please report the problem.</string>
|
||||
<string name="requesting_thumbnails">Requesting thumbnails, please be patient</string>
|
||||
<string name="discard">Cancel</string>
|
||||
<string name="done">OK</string>
|
||||
</resources>
|
||||
@@ -0,0 +1,5 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<resources>
|
||||
<style name="MyTheme" parent="android:Theme.Holo.Light">
|
||||
</style>
|
||||
</resources>
|
||||
@@ -0,0 +1,368 @@
|
||||
/*
|
||||
* Copyright (C) 2010 The Android Open Source Project
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package com.synconset;
|
||||
|
||||
import java.lang.ref.SoftReference;
|
||||
import java.lang.ref.WeakReference;
|
||||
import java.util.HashMap;
|
||||
import java.util.LinkedHashMap;
|
||||
import java.util.concurrent.ConcurrentHashMap;
|
||||
import java.util.concurrent.ExecutorService;
|
||||
import java.util.concurrent.Executors;
|
||||
import java.util.concurrent.RejectedExecutionException;
|
||||
|
||||
import android.content.Context;
|
||||
import android.graphics.Bitmap;
|
||||
import android.graphics.Color;
|
||||
import android.graphics.drawable.ColorDrawable;
|
||||
import android.graphics.drawable.Drawable;
|
||||
import android.os.AsyncTask;
|
||||
import android.os.Build;
|
||||
import android.os.Handler;
|
||||
import android.provider.MediaStore;
|
||||
import android.util.Log;
|
||||
import android.view.View;
|
||||
import android.view.animation.Animation;
|
||||
import android.view.animation.AnimationUtils;
|
||||
import android.widget.ImageView;
|
||||
|
||||
/**
|
||||
* This helper class download images from the Internet and binds those with the
|
||||
* provided ImageView.
|
||||
*
|
||||
* <p>
|
||||
* It requires the INTERNET permission, which should be added to your
|
||||
* application's manifest file.
|
||||
* </p>
|
||||
*
|
||||
* A local cache of downloaded images is maintained internally to improve
|
||||
* performance.
|
||||
*/
|
||||
public class ImageFetcher {
|
||||
|
||||
private int colWidth;
|
||||
private long origId;
|
||||
private ExecutorService executor;
|
||||
|
||||
public ImageFetcher() {
|
||||
executor = Executors.newCachedThreadPool();
|
||||
}
|
||||
|
||||
public void fetch(Integer id, ImageView imageView, int colWidth) {
|
||||
resetPurgeTimer();
|
||||
this.colWidth = colWidth;
|
||||
this.origId = id;
|
||||
Bitmap bitmap = getBitmapFromCache(id);
|
||||
|
||||
if (bitmap == null) {
|
||||
forceDownload(id, imageView);
|
||||
} else {
|
||||
cancelPotentialDownload(id, imageView);
|
||||
imageView.setImageBitmap(bitmap);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 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) {
|
||||
if (position == null) {
|
||||
imageView.setImageDrawable(null);
|
||||
return;
|
||||
}
|
||||
|
||||
if (cancelPotentialDownload(position, imageView)) {
|
||||
BitmapFetcherTask task = new BitmapFetcherTask(imageView.getContext(), imageView);
|
||||
DownloadedDrawable downloadedDrawable = new DownloadedDrawable(imageView.getContext(), task, origId);
|
||||
imageView.setImageDrawable(downloadedDrawable);
|
||||
imageView.setMinimumHeight(colWidth);
|
||||
|
||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.HONEYCOMB) {
|
||||
task.executeOnExecutor(executor, position);
|
||||
} else {
|
||||
try {
|
||||
task.execute(position);
|
||||
} catch (RejectedExecutionException e) {
|
||||
// Oh :(
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns true if the current download has been canceled or if there was no
|
||||
* download in progress on this image view. Returns false if the download in
|
||||
* progress deals with the same url. The download is not stopped in that
|
||||
* case.
|
||||
*/
|
||||
private static boolean cancelPotentialDownload(Integer position, ImageView imageView) {
|
||||
BitmapFetcherTask bitmapDownloaderTask = getBitmapDownloaderTask(imageView);
|
||||
long origId = getOrigId(imageView);
|
||||
|
||||
if (bitmapDownloaderTask != null) {
|
||||
Integer bitmapPosition = bitmapDownloaderTask.position;
|
||||
if ((bitmapPosition == null) || (!bitmapPosition.equals(position))) {
|
||||
// Log.d("DAVID", "Canceling...");
|
||||
MediaStore.Images.Thumbnails.cancelThumbnailRequest(imageView.getContext().getContentResolver(),
|
||||
origId, 12345);
|
||||
bitmapDownloaderTask.cancel(true);
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param imageView
|
||||
* Any imageView
|
||||
* @return Retrieve the currently active download task (if any) associated
|
||||
* with this imageView. null if there is no such task.
|
||||
*/
|
||||
private static BitmapFetcherTask getBitmapDownloaderTask(ImageView imageView) {
|
||||
if (imageView != null) {
|
||||
Drawable drawable = imageView.getDrawable();
|
||||
if (drawable instanceof DownloadedDrawable) {
|
||||
DownloadedDrawable downloadedDrawable = (DownloadedDrawable) drawable;
|
||||
return downloadedDrawable.getBitmapDownloaderTask();
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
private static long getOrigId(ImageView imageView) {
|
||||
if (imageView != null) {
|
||||
Drawable drawable = imageView.getDrawable();
|
||||
if (drawable instanceof DownloadedDrawable) {
|
||||
DownloadedDrawable downloadedDrawable = (DownloadedDrawable) drawable;
|
||||
return downloadedDrawable.getOrigId();
|
||||
}
|
||||
}
|
||||
return -1;
|
||||
}
|
||||
|
||||
/**
|
||||
* The actual AsyncTask that will asynchronously download the image.
|
||||
*/
|
||||
class BitmapFetcherTask extends AsyncTask<Integer, Void, Bitmap> {
|
||||
private Integer position;
|
||||
private final WeakReference<ImageView> imageViewReference;
|
||||
private final Context mContext;
|
||||
|
||||
public BitmapFetcherTask(Context context, ImageView imageView) {
|
||||
imageViewReference = new WeakReference<ImageView>(imageView);
|
||||
mContext = context;
|
||||
}
|
||||
|
||||
/**
|
||||
* Actual download method.
|
||||
*/
|
||||
@Override
|
||||
protected Bitmap doInBackground(Integer... params) {
|
||||
position = params[0];
|
||||
if (isCancelled()) {
|
||||
return null;
|
||||
}
|
||||
Bitmap thumb = MediaStore.Images.Thumbnails.getThumbnail(mContext.getContentResolver(), position, 12345,
|
||||
MediaStore.Images.Thumbnails.MINI_KIND, null);
|
||||
if (isCancelled()) {
|
||||
return null;
|
||||
}
|
||||
if (thumb == null) {
|
||||
return null;
|
||||
} else {
|
||||
if (isCancelled()) {
|
||||
return null;
|
||||
} else {
|
||||
return thumb;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
private void setInvisible() {
|
||||
// Log.d("COLLAGE", "Setting something invisible...");
|
||||
if (imageViewReference != null) {
|
||||
final ImageView imageView = imageViewReference.get();
|
||||
BitmapFetcherTask bitmapDownloaderTask = getBitmapDownloaderTask(imageView);
|
||||
if (this == bitmapDownloaderTask) {
|
||||
imageView.setVisibility(View.GONE);
|
||||
imageView.setClickable(false);
|
||||
imageView.setEnabled(false);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Once the image is downloaded, associates it to the imageView
|
||||
*/
|
||||
@Override
|
||||
protected void onPostExecute(Bitmap bitmap) {
|
||||
if (isCancelled()) {
|
||||
bitmap = null;
|
||||
}
|
||||
addBitmapToCache(position, bitmap);
|
||||
if (imageViewReference != null) {
|
||||
ImageView imageView = imageViewReference.get();
|
||||
BitmapFetcherTask bitmapDownloaderTask = getBitmapDownloaderTask(imageView);
|
||||
if (this == bitmapDownloaderTask) {
|
||||
imageView.setImageBitmap(bitmap);
|
||||
Animation anim = AnimationUtils.loadAnimation(imageView.getContext(), android.R.anim.fade_in);
|
||||
imageView.setAnimation(anim);
|
||||
anim.start();
|
||||
}
|
||||
} else {
|
||||
setInvisible();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* A fake Drawable that will be attached to the imageView while the download
|
||||
* is in progress.
|
||||
*
|
||||
* <p>
|
||||
* Contains a reference to the actual download task, so that a download task
|
||||
* can be stopped if a new binding is required, and makes sure that only the
|
||||
* last started download process can bind its result, independently of the
|
||||
* download finish order.
|
||||
* </p>
|
||||
*/
|
||||
static class DownloadedDrawable extends ColorDrawable {
|
||||
private final WeakReference<BitmapFetcherTask> bitmapDownloaderTaskReference;
|
||||
private long origId;
|
||||
|
||||
public DownloadedDrawable(Context mContext, BitmapFetcherTask bitmapDownloaderTask, long origId) {
|
||||
super(Color.TRANSPARENT);
|
||||
bitmapDownloaderTaskReference = new WeakReference<BitmapFetcherTask>(bitmapDownloaderTask);
|
||||
this.origId = origId;
|
||||
}
|
||||
|
||||
public long getOrigId() {
|
||||
return origId;
|
||||
}
|
||||
|
||||
public BitmapFetcherTask getBitmapDownloaderTask() {
|
||||
return bitmapDownloaderTaskReference.get();
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* Cache-related fields and methods.
|
||||
*
|
||||
* We use a hard and a soft cache. A soft reference cache is too aggressively cleared by the
|
||||
* Garbage Collector.
|
||||
*/
|
||||
|
||||
private static final int HARD_CACHE_CAPACITY = 100;
|
||||
private static final int DELAY_BEFORE_PURGE = 10 * 1000; // in milliseconds
|
||||
|
||||
// Hard cache, with a fixed maximum capacity and a life duration
|
||||
private final HashMap<Integer, Bitmap> sHardBitmapCache = new LinkedHashMap<Integer, Bitmap>(
|
||||
HARD_CACHE_CAPACITY / 2, 0.75f, true) {
|
||||
@Override
|
||||
protected boolean removeEldestEntry(LinkedHashMap.Entry<Integer, Bitmap> eldest) {
|
||||
if (size() > HARD_CACHE_CAPACITY) {
|
||||
// Entries push-out of hard reference cache are transferred to
|
||||
// soft reference cache
|
||||
sSoftBitmapCache.put(eldest.getKey(), new SoftReference<Bitmap>(eldest.getValue()));
|
||||
return true;
|
||||
} else
|
||||
return false;
|
||||
}
|
||||
};
|
||||
|
||||
// Soft cache for bitmaps kicked out of hard cache
|
||||
private final static ConcurrentHashMap<Integer, SoftReference<Bitmap>> sSoftBitmapCache = new ConcurrentHashMap<Integer, SoftReference<Bitmap>>(
|
||||
HARD_CACHE_CAPACITY / 2);
|
||||
|
||||
private final Handler purgeHandler = new Handler();
|
||||
|
||||
private final Runnable purger = new Runnable() {
|
||||
public void run() {
|
||||
clearCache();
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* Adds this bitmap to the cache.
|
||||
*
|
||||
* @param bitmap
|
||||
* The newly downloaded bitmap.
|
||||
*/
|
||||
private void addBitmapToCache(Integer position, Bitmap bitmap) {
|
||||
if (bitmap != null) {
|
||||
synchronized (sHardBitmapCache) {
|
||||
sHardBitmapCache.put(position, bitmap);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @param position
|
||||
* The URL of the image that will be retrieved from the cache.
|
||||
* @return The cached bitmap or null if it was not found.
|
||||
*/
|
||||
private Bitmap getBitmapFromCache(Integer position) {
|
||||
// First try the hard reference cache
|
||||
synchronized (sHardBitmapCache) {
|
||||
final Bitmap bitmap = sHardBitmapCache.get(position);
|
||||
if (bitmap != null) {
|
||||
// Log.d("CACHE ****** ", "Hard hit!");
|
||||
// Bitmap found in hard cache
|
||||
// Move element to first position, so that it is removed last
|
||||
return bitmap;
|
||||
}
|
||||
}
|
||||
|
||||
// Then try the soft reference cache
|
||||
SoftReference<Bitmap> bitmapReference = sSoftBitmapCache.get(position);
|
||||
if (bitmapReference != null) {
|
||||
final Bitmap bitmap = bitmapReference.get();
|
||||
if (bitmap != null) {
|
||||
// Bitmap found in soft cache
|
||||
// Log.d("CACHE ****** ", "Soft hit!");
|
||||
return bitmap;
|
||||
} else {
|
||||
// Soft reference has been Garbage Collected
|
||||
sSoftBitmapCache.remove(position);
|
||||
}
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Clears the image cache used internally to improve performance. Note that
|
||||
* for memory efficiency reasons, the cache will automatically be cleared
|
||||
* after a certain inactivity delay.
|
||||
*/
|
||||
public void clearCache() {
|
||||
// sHardBitmapCache.clear();
|
||||
// sSoftBitmapCache.clear();
|
||||
}
|
||||
|
||||
/**
|
||||
* Allow a new delay before the automatic cache clear is done.
|
||||
*/
|
||||
private void resetPurgeTimer() {
|
||||
// purgeHandler.removeCallbacks(purger);
|
||||
// purgeHandler.postDelayed(purger, DELAY_BEFORE_PURGE);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,444 @@
|
||||
/*
|
||||
* Copyright (c) 2012, David Erosa
|
||||
*
|
||||
* All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions are met:
|
||||
*
|
||||
* Redistributions of source code must retain the above copyright notice,
|
||||
* this list of conditions and the following disclaimer.
|
||||
* Redistributions in binary form must reproduce the above copyright notice,
|
||||
* this list of conditions and the following disclaimer in the
|
||||
* documentation and/or other materials provided with the distribution.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
|
||||
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
||||
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
|
||||
* ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
|
||||
* LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
|
||||
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
|
||||
* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
|
||||
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
|
||||
* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDIN G NEGLIGENCE OR OTHERWISE)
|
||||
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
|
||||
* POSSIBILITY OF SUCH DAMAGE
|
||||
*
|
||||
* Code modified by Andrew Stephan for Sync OnSet
|
||||
*
|
||||
*/
|
||||
|
||||
package com.synconset;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.HashSet;
|
||||
import java.util.Set;
|
||||
|
||||
import com.wymsee.apps.synconset.R;
|
||||
import android.app.Activity;
|
||||
import android.app.ActionBar;
|
||||
import android.app.AlertDialog;
|
||||
import android.app.LoaderManager;
|
||||
import android.content.Context;
|
||||
import android.content.CursorLoader;
|
||||
import android.content.DialogInterface;
|
||||
import android.content.Intent;
|
||||
import android.content.Loader;
|
||||
import android.database.Cursor;
|
||||
import android.graphics.Bitmap;
|
||||
import android.graphics.BitmapFactory;
|
||||
import android.graphics.Color;
|
||||
import android.os.Bundle;
|
||||
import android.provider.MediaStore;
|
||||
import android.util.Log;
|
||||
import android.util.SparseBooleanArray;
|
||||
import android.view.Display;
|
||||
import android.view.LayoutInflater;
|
||||
import android.view.View;
|
||||
import android.view.ViewGroup;
|
||||
import android.widget.AbsListView;
|
||||
import android.widget.AbsListView.OnScrollListener;
|
||||
import android.widget.AdapterView;
|
||||
import android.widget.AdapterView.OnItemClickListener;
|
||||
import android.widget.BaseAdapter;
|
||||
import android.widget.GridView;
|
||||
import android.widget.ImageView;
|
||||
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;
|
||||
|
||||
public static final int NOLIMIT = -1;
|
||||
public static final String MAX_IMAGES_KEY = "MAX_IMAGES";
|
||||
|
||||
private ImageAdapter ia;
|
||||
|
||||
private Cursor imagecursor, actualimagecursor;
|
||||
private int image_column_index, actual_image_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 SparseBooleanArray checkStatus = new SparseBooleanArray();
|
||||
|
||||
private int maxImages;
|
||||
private int maxImageCount;
|
||||
|
||||
private GridView gridView;
|
||||
|
||||
private final ImageFetcher fetcher = new ImageFetcher();
|
||||
|
||||
private int selectedColor = Color.GREEN;
|
||||
private boolean shouldRequestThumb = true;
|
||||
|
||||
@Override
|
||||
public void onCreate(Bundle savedInstanceState) {
|
||||
super.onCreate(savedInstanceState);
|
||||
setContentView(R.layout.multiselectorgrid);
|
||||
fileNames.clear();
|
||||
|
||||
maxImages = getIntent().getIntExtra(MAX_IMAGES_KEY, NOLIMIT);
|
||||
maxImageCount = maxImages;
|
||||
|
||||
colWidth = getIntent().getIntExtra(COL_WIDTH_KEY, DEFAULT_COLUMN_WIDTH);
|
||||
|
||||
Display display = getWindowManager().getDefaultDisplay();
|
||||
int width = display.getWidth();
|
||||
|
||||
colWidth = width / 4;
|
||||
|
||||
// int bgColor = getIntent().getIntExtra("BG_COLOR", Color.BLACK);
|
||||
|
||||
gridView = (GridView) findViewById(R.id.gridview);
|
||||
//gridView.setColumnWidth(colWidth);
|
||||
gridView.setOnItemClickListener(this);
|
||||
gridView.setOnScrollListener(new OnScrollListener() {
|
||||
|
||||
private int lastFirstItem = 0;
|
||||
private long timestamp = System.currentTimeMillis();
|
||||
|
||||
@Override
|
||||
public void onScrollStateChanged(AbsListView view, int scrollState) {
|
||||
if (scrollState == SCROLL_STATE_IDLE) {
|
||||
// Log.d(TAG, "IDLE - Reload!");
|
||||
shouldRequestThumb = true;
|
||||
ia.notifyDataSetChanged();
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onScroll(AbsListView view, int firstVisibleItem, int visibleItemCount, int totalItemCount) {
|
||||
float dt = System.currentTimeMillis() - timestamp;
|
||||
if (firstVisibleItem != lastFirstItem) {
|
||||
double speed = 1 / dt * 1000;
|
||||
lastFirstItem = firstVisibleItem;
|
||||
timestamp = System.currentTimeMillis();
|
||||
// Log.d(TAG, "Speed: " + speed + " elements/second");
|
||||
|
||||
// Limitarlo si vamos a más de una página por segundo...
|
||||
shouldRequestThumb = speed < visibleItemCount;
|
||||
}
|
||||
}
|
||||
});
|
||||
selectedColor = 0xff32b2e1;
|
||||
// selectedColor = Color.RED;
|
||||
|
||||
// gridView.setBackgroundColor(bgColor);
|
||||
// gridView.setBackgroundResource(R.drawable.grid_background);
|
||||
|
||||
ia = new ImageAdapter(this);
|
||||
gridView.setAdapter(ia);
|
||||
|
||||
LoaderManager.enableDebugLogging(false);
|
||||
getLoaderManager().initLoader(CURSORLOADER_THUMBS, null, this);
|
||||
getLoaderManager().initLoader(CURSORLOADER_REAL, null, this);
|
||||
setupHeader();
|
||||
updateAcceptButton();
|
||||
}
|
||||
|
||||
private void setupHeader() {
|
||||
// From Roman Nkk's code
|
||||
// https://plus.google.com/113735310430199015092/posts/R49wVvcDoEW
|
||||
// Inflate a "Done/Discard" custom action bar view
|
||||
/*
|
||||
* Copyright 2013 The Android Open Source Project
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
LayoutInflater inflater = (LayoutInflater) getActionBar().getThemedContext().getSystemService(
|
||||
LAYOUT_INFLATER_SERVICE);
|
||||
final View customActionBarView = inflater.inflate(R.layout.actionbar_custom_view_done_discard, null);
|
||||
customActionBarView.findViewById(R.id.actionbar_done).setOnClickListener(new View.OnClickListener() {
|
||||
@Override
|
||||
public void onClick(View v) {
|
||||
// "Done"
|
||||
selectClicked(null);
|
||||
}
|
||||
});
|
||||
customActionBarView.findViewById(R.id.actionbar_discard).setOnClickListener(new View.OnClickListener() {
|
||||
@Override
|
||||
public void onClick(View v) {
|
||||
finish();
|
||||
}
|
||||
});
|
||||
|
||||
// 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);
|
||||
actionBar.setCustomView(customActionBarView, new ActionBar.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT,
|
||||
ViewGroup.LayoutParams.MATCH_PARENT));
|
||||
}
|
||||
|
||||
public class SquareImageView extends ImageView {
|
||||
public SquareImageView(Context context) {
|
||||
super(context);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
|
||||
super.onMeasure(widthMeasureSpec, widthMeasureSpec);
|
||||
}
|
||||
}
|
||||
|
||||
public class ImageAdapter extends BaseAdapter {
|
||||
private final Bitmap mPlaceHolderBitmap;
|
||||
|
||||
public ImageAdapter(Context c) {
|
||||
Bitmap tmpHolderBitmap = BitmapFactory.decodeResource(getResources(), R.drawable.loading_icon);
|
||||
mPlaceHolderBitmap = Bitmap.createScaledBitmap(tmpHolderBitmap, colWidth, colWidth, false);
|
||||
if (tmpHolderBitmap != mPlaceHolderBitmap) {
|
||||
tmpHolderBitmap.recycle();
|
||||
tmpHolderBitmap = null;
|
||||
}
|
||||
}
|
||||
|
||||
public int getCount() {
|
||||
if (imagecursor != null) {
|
||||
return imagecursor.getCount();
|
||||
} else {
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
public Object getItem(int position) {
|
||||
return position;
|
||||
}
|
||||
|
||||
public long getItemId(int position) {
|
||||
return position;
|
||||
}
|
||||
|
||||
// create a new ImageView for each item referenced by the Adapter
|
||||
public View getView(int pos, View convertView, ViewGroup parent) {
|
||||
|
||||
if (convertView == null) {
|
||||
ImageView temp = new SquareImageView(MultiImageChooserActivity.this);
|
||||
temp.setScaleType(ImageView.ScaleType.CENTER_CROP);
|
||||
convertView = (View)temp;
|
||||
}
|
||||
|
||||
ImageView imageView = (ImageView)convertView;
|
||||
imageView.setImageBitmap(null);
|
||||
|
||||
final int position = pos;
|
||||
|
||||
if (!imagecursor.moveToPosition(position)) {
|
||||
return imageView;
|
||||
}
|
||||
|
||||
if (image_column_index == -1) {
|
||||
return imageView;
|
||||
}
|
||||
|
||||
final int id = imagecursor.getInt(image_column_index);
|
||||
if (isChecked(pos)) {
|
||||
imageView.setImageAlpha(128);
|
||||
imageView.setBackgroundColor(selectedColor);
|
||||
} else {
|
||||
imageView.setImageAlpha(255);
|
||||
imageView.setBackgroundColor(Color.TRANSPARENT);
|
||||
}
|
||||
if (shouldRequestThumb) {
|
||||
fetcher.fetch(Integer.valueOf(id), imageView, colWidth);
|
||||
}
|
||||
|
||||
return imageView;
|
||||
}
|
||||
}
|
||||
|
||||
private String getImageName(int position) {
|
||||
actualimagecursor.moveToPosition(position);
|
||||
String name = null;
|
||||
|
||||
try {
|
||||
name = actualimagecursor.getString(actual_image_column_index);
|
||||
} catch (Exception e) {
|
||||
return null;
|
||||
}
|
||||
return name;
|
||||
}
|
||||
|
||||
private void setChecked(int position, boolean b) {
|
||||
checkStatus.put(position, b);
|
||||
}
|
||||
|
||||
public boolean isChecked(int position) {
|
||||
boolean ret = checkStatus.get(position);
|
||||
return ret;
|
||||
}
|
||||
|
||||
public void cancelClicked(View ignored) {
|
||||
setResult(RESULT_CANCELED);
|
||||
finish();
|
||||
}
|
||||
|
||||
public void selectClicked(View ignored) {
|
||||
Intent data = new Intent();
|
||||
if (fileNames.isEmpty()) {
|
||||
this.setResult(RESULT_CANCELED);
|
||||
} else {
|
||||
ArrayList<String> al = new ArrayList<String>();
|
||||
al.addAll(fileNames);
|
||||
Bundle res = new Bundle();
|
||||
res.putStringArrayList("MULTIPLEFILENAMES", al);
|
||||
if (imagecursor != null) {
|
||||
res.putInt("TOTALFILES", imagecursor.getCount());
|
||||
}
|
||||
|
||||
data.putExtras(res);
|
||||
this.setResult(RESULT_OK, data);
|
||||
}
|
||||
// Log.d(TAG, "Returning " + fileNames.size() + " items");
|
||||
finish();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onItemClick(AdapterView<?> arg0, View view, int position, long id) {
|
||||
String name = getImageName(position);
|
||||
|
||||
if (name == null) {
|
||||
return;
|
||||
}
|
||||
boolean isChecked = !isChecked(position);
|
||||
if (maxImages == 0 && isChecked) {
|
||||
isChecked = false;
|
||||
AlertDialog.Builder builder = new AlertDialog.Builder(this);
|
||||
builder.setTitle("Maximum " + maxImageCount + " Photos");
|
||||
builder.setMessage("You can only select " + maxImageCount + " photos at a time.");
|
||||
builder.setPositiveButton("OK", new DialogInterface.OnClickListener() {
|
||||
public void onClick(DialogInterface dialog, int which) {
|
||||
dialog.cancel();
|
||||
}
|
||||
});
|
||||
AlertDialog alert = builder.create();
|
||||
alert.show();
|
||||
}
|
||||
|
||||
if (isChecked) {
|
||||
if (fileNames.add(name)) {
|
||||
if (maxImageCount == 1) {
|
||||
this.selectClicked(null);
|
||||
} else {
|
||||
maxImages--;
|
||||
ImageView imageView = (ImageView)view;
|
||||
imageView.setImageAlpha(128);
|
||||
view.setBackgroundColor(selectedColor);
|
||||
}
|
||||
}
|
||||
} else {
|
||||
if (fileNames.remove(name)) {
|
||||
// Solo incrementa los slots libres si hemos
|
||||
// "liberado" uno...
|
||||
maxImages++;
|
||||
ImageView imageView = (ImageView)view;
|
||||
imageView.setImageAlpha(255);
|
||||
view.setBackgroundColor(Color.TRANSPARENT);
|
||||
}
|
||||
}
|
||||
|
||||
setChecked(position, isChecked);
|
||||
updateAcceptButton();
|
||||
}
|
||||
|
||||
private void updateAcceptButton() {
|
||||
((TextView) getActionBar().getCustomView().findViewById(R.id.actionbar_done_textview))
|
||||
.setEnabled(fileNames.size() != 0);
|
||||
getActionBar().getCustomView().findViewById(R.id.actionbar_done).setEnabled(fileNames.size() != 0);
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public Loader<Cursor> onCreateLoader(int cursorID, Bundle arg1) {
|
||||
CursorLoader cl = null;
|
||||
|
||||
ArrayList<String> img = new ArrayList<String>();
|
||||
switch (cursorID) {
|
||||
|
||||
case CURSORLOADER_THUMBS:
|
||||
img.add(MediaStore.Images.Media._ID);
|
||||
break;
|
||||
case CURSORLOADER_REAL:
|
||||
img.add(MediaStore.Images.Thumbnails.DATA);
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
cl = new CursorLoader(MultiImageChooserActivity.this, MediaStore.Images.Media.EXTERNAL_CONTENT_URI,
|
||||
img.toArray(new String[img.size()]), null, null, "DATE_MODIFIED DESC");
|
||||
return cl;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onLoadFinished(Loader<Cursor> loader, Cursor cursor) {
|
||||
if (cursor == null) {
|
||||
// NULL cursor. This usually means there's no image database yet....
|
||||
return;
|
||||
}
|
||||
|
||||
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;
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onLoaderReset(Loader<Cursor> loader) {
|
||||
if (loader.getId() == CURSORLOADER_THUMBS) {
|
||||
imagecursor = null;
|
||||
} else if (loader.getId() == CURSORLOADER_REAL) {
|
||||
actualimagecursor = null;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,160 @@
|
||||
/**
|
||||
* An Image Picker Plugin for Cordova/PhoneGap.
|
||||
*/
|
||||
package com.synconset;
|
||||
|
||||
import org.apache.cordova.CallbackContext;
|
||||
import org.apache.cordova.CordovaPlugin;
|
||||
|
||||
import org.json.JSONArray;
|
||||
import org.json.JSONException;
|
||||
import org.json.JSONObject;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.io.File;
|
||||
import java.io.FileOutputStream;
|
||||
import java.io.IOException;
|
||||
import java.io.OutputStream;
|
||||
|
||||
import android.app.Activity;
|
||||
import android.content.Intent;
|
||||
import android.graphics.Bitmap;
|
||||
import android.graphics.BitmapFactory;
|
||||
import android.graphics.Matrix;
|
||||
import android.net.Uri;
|
||||
import android.util.Log;
|
||||
|
||||
public class ImagePicker extends CordovaPlugin {
|
||||
public static String TAG = "ImagePicker";
|
||||
|
||||
private CallbackContext callbackContext;
|
||||
private JSONObject params;
|
||||
|
||||
public boolean execute(String action, final JSONArray args, final CallbackContext callbackContext) throws JSONException {
|
||||
this.callbackContext = callbackContext;
|
||||
this.params = args.getJSONObject(0);
|
||||
if (action.equals("getPictures")) {
|
||||
Intent intent = new Intent(cordova.getActivity(), MultiImageChooserActivity.class);
|
||||
int max = 20;
|
||||
if (this.params.has("maximumImagesCount")) {
|
||||
max = this.params.getInt("maximumImagesCount");
|
||||
}
|
||||
intent.putExtra("MAX_IMAGES", max);
|
||||
if (this.cordova != null) {
|
||||
this.cordova.startActivityForResult((CordovaPlugin) this, intent, 0);
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
public void onActivityResult(int requestCode, int resultCode, Intent data) {
|
||||
if (resultCode == Activity.RESULT_OK && data != null) {
|
||||
ArrayList<String> fileNames = data.getStringArrayListExtra("MULTIPLEFILENAMES");
|
||||
JSONArray res = new JSONArray();
|
||||
try {
|
||||
for (int i = 0; i < fileNames.size(); i++) {
|
||||
File file = new File(fileNames.get(i));
|
||||
Bitmap bmp = this.getBitmap(file);
|
||||
int width = bmp.getWidth();
|
||||
int height = bmp.getHeight();
|
||||
int desiredWidth = 0;
|
||||
int desiredHeight = 0;
|
||||
if (this.params.has("width")) {
|
||||
desiredWidth = this.params.getInt("width");
|
||||
}
|
||||
if (this.params.has("height")) {
|
||||
desiredHeight = this.params.getInt("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;
|
||||
}
|
||||
}
|
||||
}
|
||||
if (scale < 1) {
|
||||
bmp = this.getResizedBitmap(bmp, scale);
|
||||
}
|
||||
file = this.storeImage(bmp, file.getName());
|
||||
res.put(Uri.fromFile(file).toString());
|
||||
}
|
||||
this.callbackContext.success(res);
|
||||
} catch(IOException e) {
|
||||
this.callbackContext.error("There was an error importing pictures");
|
||||
} catch (JSONException e) {
|
||||
this.callbackContext.error("There was an error importing pictures");
|
||||
}
|
||||
} else if (resultCode == Activity.RESULT_CANCELED) {
|
||||
JSONArray res = new JSONArray();
|
||||
this.callbackContext.success(res);
|
||||
} else {
|
||||
this.callbackContext.error("No images selected");
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* The following functions are originally from
|
||||
* https://github.com/raananw/PhoneGap-Image-Resizer
|
||||
*
|
||||
* They have been modified by Andrew Stephan for Sync OnSet
|
||||
*
|
||||
* The software is open source, MIT Licensed.
|
||||
* Copyright (C) 2012, webXells GmbH All Rights Reserved.
|
||||
*/
|
||||
private File storeImage(Bitmap bmp, String fileName) throws JSONException, IOException {
|
||||
int quality = 100;
|
||||
if (this.params.has("quality")) {
|
||||
quality = this.params.getInt("quality");
|
||||
}
|
||||
int index = fileName.lastIndexOf('.');
|
||||
String name = fileName.substring(0, index);
|
||||
String ext = fileName.substring(index);
|
||||
File file = File.createTempFile(name, ext);
|
||||
OutputStream outStream = new FileOutputStream(file);
|
||||
if (ext.compareToIgnoreCase(".png") == 0) {
|
||||
bmp.compress(Bitmap.CompressFormat.PNG, quality, outStream);
|
||||
} else {
|
||||
bmp.compress(Bitmap.CompressFormat.JPEG, quality, outStream);
|
||||
}
|
||||
outStream.flush();
|
||||
outStream.close();
|
||||
return file;
|
||||
}
|
||||
|
||||
private Bitmap getResizedBitmap(Bitmap bm, float factor) {
|
||||
int width = bm.getWidth();
|
||||
int height = bm.getHeight();
|
||||
// create a matrix for the manipulation
|
||||
Matrix matrix = new Matrix();
|
||||
// resize the bit map
|
||||
matrix.postScale(factor, factor);
|
||||
// recreate the new Bitmap
|
||||
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.");
|
||||
}
|
||||
return bmp;
|
||||
}
|
||||
}
|
||||