runs in android now but doesn't work yet

This commit is contained in:
Andrew Stephan 2014-02-27 12:00:53 -05:00
parent e6d0426a51
commit 37c91fcaed
34 changed files with 1061 additions and 1 deletions

2
.gitignore vendored
View File

@ -11,4 +11,4 @@
# Xcode 4
xcuserdata/
project.xcworkspace/
tags

View File

@ -56,5 +56,52 @@
<!-- android -->
<platform name="android">
<config-file target="config.xml" parent="/*">
<feature name="ImagePicker">
<param name="android-package" value="com.synconset.ImagePicker"/>
</feature>
</config-file>
<config-file target="AndroidManifest.xml" parent="/manifest/application">
<activity android:label="@string/multi_app_name" android:name="com.synconset.MultiImageChooserActivity">
<intent-filter>
<action android:name="com.synconset.imagepicker.PICK" />
<category android:name="android.intent.category.DEFAULT" />
</intent-filter>
</activity>
</config-file>
<source-file src="src/Android/com/synconset/ImagePicker/ImagePicker.java" target-dir="src/com/synconset" />
<source-file src="src/android/Library/src/ImageFetcher.java" target-dir="src/com/synconset"/>
<source-file src="src/android/Library/src/MultiImageChooserActivity.java" target-dir="src/com/synconset"/>
<source-file src="src/android/Library/res/anim/image_pop_in.xml" target-dir="res/anim"/>
<source-file src="src/android/Library/res/drawable/grid_background.xml" target-dir="res/drawable"/>
<source-file src="src/android/Library/res/drawable-hdpi/image_bg.9.png" target-dir="res/drawable-hdpi"/>
<source-file src="src/android/Library/res/drawable-hdpi/loading_icon.png" target-dir="res/drawable-hdpi"/>
<source-file src="src/android/Library/res/drawable-mdpi/ic_action_discard_dark.png" target-dir="res/drawable-mdpi"/>
<source-file src="src/android/Library/res/drawable-mdpi/ic_action_discard_light.png" target-dir="res/drawable-mdpi"/>
<source-file src="src/android/Library/res/drawable-mdpi/ic_action_done_dark.png" target-dir="res/drawable-mdpi"/>
<source-file src="src/android/Library/res/drawable-mdpi/ic_action_done_light.png" target-dir="res/drawable-mdpi"/>
<source-file src="src/android/Library/res/drawable-mdpi/ic_launcher.png" target-dir="res/drawable-mdpi"/>
<source-file src="src/android/Library/res/drawable-xhdpi/ic_action_discard_dark.png" target-dir="res/drawable-xhdpi"/>
<source-file src="src/android/Library/res/drawable-xhdpi/ic_action_discard_light.png" target-dir="res/drawable-xhdpi"/>
<source-file src="src/android/Library/res/drawable-xhdpi/ic_action_done_dark.png" target-dir="res/drawable-xhdpi"/>
<source-file src="src/android/Library/res/drawable-xhdpi/ic_action_done_light.png" target-dir="res/drawable-xhdpi"/>
<source-file src="src/android/Library/res/drawable-xhdpi/ic_launcher.png" target-dir="res/drawable-xhdpi"/>
<source-file src="src/android/Library/res/layout/actionbar_custom_view_done_discard.xml" target-dir="res/layout"/>
<source-file src="src/android/Library/res/layout/actionbar_discard_button.xml" target-dir="res/layout"/>
<source-file src="src/android/Library/res/layout/actionbar_done_button.xml" target-dir="res/layout"/>
<source-file src="src/android/Library/res/layout/multiselectorgrid.xml" target-dir="res/layout"/>
<source-file src="src/android/Library/res/values/multiimagechooser_strings_en.xml" target-dir="res/values"/>
<source-file src="src/android/Library/res/values/themes.xml" target-dir="res/values"/>
<source-file src="src/android/Library/res/values-de/multiimagechooser_strings_de.xml" target-dir="res/values-de"/>
<source-file src="src/android/Library/res/values-es/multiimagechooser_strings_es.xml" target-dir="res/values-es"/>
<source-file src="src/android/Library/res/values-fr/multiimagechooser_strings_fr.xml" target-dir="res/values-fr"/>
<source-file src="src/android/Library/res/values-hu/multiimagechooser_strings_hu.xml" target-dir="res/values-hu"/>
<source-file src="src/android/Library/res/values-ja/multiimagechooser_strings_ja.xml" target-dir="res/values-ja"/>
<source-file src="src/android/Library/res/values-ko/multiimagechooser_strings_ko.xml" target-dir="res/values-ko"/>
</platform>
</plugin>

View File

@ -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" />

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.0 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.5 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.0 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.7 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 415 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 386 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 454 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 400 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.6 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.5 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 724 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 684 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 825 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 759 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 5.1 KiB

View File

@ -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>

View File

@ -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>

View File

@ -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_dark"
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>

View File

@ -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_dark"
android:drawablePadding="8dp"
android:gravity="center_vertical"
android:text="@string/done" />
</FrameLayout>

View File

@ -0,0 +1,33 @@
<?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" -->
<TextView
android:gravity="center"
android:id="@+id/label_images_left"
android:layout_height="wrap_content"
android:layout_weight="0"
android:layout_width="fill_parent"
android:paddingBottom="2dip"
android:paddingTop="2dip"
android:text=""
android:textSize="18sp"
android:textStyle="bold"
android:visibility="gone" />
<GridView
android:fadingEdgeLength="10dip"
android:requiresFadingEdge="vertical"
android:columnWidth="0dip"
android:fastScrollEnabled="true"
android:gravity="center"
android:horizontalSpacing="8dip"
android:id="@+id/gridview"
android:layout_height="fill_parent"
android:layout_weight="1"
android:layout_width="fill_parent"
android:numColumns="auto_fit"
android:scrollingCache="true"
android:stretchMode="columnWidth"
android:verticalSpacing="8dip" />
</LinearLayout>

View File

@ -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>

View File

@ -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>

View File

@ -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>

View File

@ -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>

View File

@ -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>

View File

@ -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>

View File

@ -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>

View File

@ -0,0 +1,5 @@
<?xml version="1.0" encoding="utf-8"?>
<resources>
<style name="MyTheme" parent="android:Theme.Holo.Light">
</style>
</resources>

View File

@ -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.MICRO_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);
}
}

View File

@ -0,0 +1,395 @@
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.LoaderManager;
import android.content.Context;
import android.content.CursorLoader;
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 TextView freeLabel = null;
private int maxImages;
private boolean unlimitedImages = false;
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);
unlimitedImages = maxImages == NOLIMIT;
if (!unlimitedImages) {
freeLabel = (TextView) findViewById(R.id.label_images_left);
freeLabel.setVisibility(View.VISIBLE);
updateLabel();
}
colWidth = getIntent().getIntExtra(COL_WIDTH_KEY, DEFAULT_COLUMN_WIDTH);
Display display = getWindowManager().getDefaultDisplay();
@SuppressWarnings("deprecation")
int width = display.getWidth();
int testColWidth = width / 3;
if (testColWidth > colWidth) {
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 Nurik's code
// https://plus.google.com/113735310430199015092/posts/R49wVvcDoEW
// Inflate a "Done/Discard" custom action bar view.
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));
}
private void updateLabel() {
if (freeLabel != null) {
String text = String.format(getString(R.string.free_version_label), maxImages);
freeLabel.setText(text);
if (maxImages == 0) {
freeLabel.setTextColor(Color.RED);
} else {
freeLabel.setTextColor(Color.WHITE);
}
}
}
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) {
convertView = new ImageView(MultiImageChooserActivity.this);
}
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.setBackgroundColor(selectedColor);
} else {
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);
// PhotoMix.Log("DAVID", "Posicion " + position + " isChecked: " +
// isChecked);
if (!unlimitedImages && maxImages == 0 && isChecked) {
// PhotoMix.Log("DAVID", "Aquí no debería entrar...");
isChecked = false;
}
if (isChecked) {
// Solo se resta un slot si hemos introducido un
// filename de verdad...
if (fileNames.add(name)) {
maxImages--;
view.setBackgroundColor(selectedColor);
}
} else {
if (fileNames.remove(name)) {
// Solo incrementa los slots libres si hemos
// "liberado" uno...
maxImages++;
view.setBackgroundColor(Color.TRANSPARENT);
}
}
setChecked(position, isChecked);
updateAcceptButton();
updateLabel();
}
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, null);
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;
}
}
}

View File

@ -0,0 +1,44 @@
/**
* An Internal Storage 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.IOException;
import android.app.Activity;
import android.content.Intent;
import android.util.Log;
public class ImagePicker extends CordovaPlugin {
public boolean execute(String action, final JSONArray args, final CallbackContext callbackContext) throws JSONException {
if (action.equals("getPictures")) {
Intent intent = new Intent(cordova.getActivity(), MultiImageChooserActivity.class);
intent.putExtra("MAX_IMAGES", 20);
if (this.cordova != null) {
this.cordova.startActivityForResult((CordovaPlugin) this, intent, 0);
}
}
return true;
}
public void onActivityResult(int requestCode, int resultCode, Intent data) {
Log.d("PLUGIN", "result code = " + resultCode);
if (resultCode == 1 && data != null) {
ArrayList<String> fileNames = data.getStringArrayListExtra("MULTIPLEFILNAMES");
for (int i = 0; i < fileNames.size(); i++) {
Log.d("PLUGIN", fileNames.get(i));
}
}
}
}