mirror of
https://gitlab.winehq.org/wine/wine-gecko.git
synced 2024-09-13 09:24:08 -07:00
Bug 963046 - Add Picasso image loading library to the tree (r=nalexander)
This commit is contained in:
parent
9b819f68a5
commit
a53b198de4
@ -64,6 +64,7 @@ ALL_JARS = \
|
||||
gecko-browser.jar \
|
||||
gecko-mozglue.jar \
|
||||
gecko-util.jar \
|
||||
squareup-picasso.jar \
|
||||
sync-thirdparty.jar \
|
||||
websockets.jar \
|
||||
$(NULL)
|
||||
|
@ -408,11 +408,48 @@ gbjar.generated_sources += sync_generated_java_files
|
||||
gbjar.extra_jars = [
|
||||
'gecko-mozglue.jar',
|
||||
'gecko-util.jar',
|
||||
'squareup-picasso.jar',
|
||||
'sync-thirdparty.jar',
|
||||
'websockets.jar',
|
||||
]
|
||||
gbjar.javac_flags += ['-Xlint:all,-deprecation,-fallthrough']
|
||||
|
||||
spjar = add_java_jar('squareup-picasso')
|
||||
spjar.sources += [ thirdparty_source_dir + f for f in [
|
||||
'com/squareup/picasso/Action.java',
|
||||
'com/squareup/picasso/AssetBitmapHunter.java',
|
||||
'com/squareup/picasso/BitmapHunter.java',
|
||||
'com/squareup/picasso/Cache.java',
|
||||
'com/squareup/picasso/Callback.java',
|
||||
'com/squareup/picasso/ContactsPhotoBitmapHunter.java',
|
||||
'com/squareup/picasso/ContentStreamBitmapHunter.java',
|
||||
'com/squareup/picasso/DeferredRequestCreator.java',
|
||||
'com/squareup/picasso/Dispatcher.java',
|
||||
'com/squareup/picasso/Downloader.java',
|
||||
'com/squareup/picasso/FetchAction.java',
|
||||
'com/squareup/picasso/FileBitmapHunter.java',
|
||||
'com/squareup/picasso/GetAction.java',
|
||||
'com/squareup/picasso/ImageViewAction.java',
|
||||
'com/squareup/picasso/LruCache.java',
|
||||
'com/squareup/picasso/MarkableInputStream.java',
|
||||
'com/squareup/picasso/MediaStoreBitmapHunter.java',
|
||||
'com/squareup/picasso/NetworkBitmapHunter.java',
|
||||
'com/squareup/picasso/Picasso.java',
|
||||
'com/squareup/picasso/PicassoDrawable.java',
|
||||
'com/squareup/picasso/PicassoExecutorService.java',
|
||||
'com/squareup/picasso/Request.java',
|
||||
'com/squareup/picasso/RequestCreator.java',
|
||||
'com/squareup/picasso/ResourceBitmapHunter.java',
|
||||
'com/squareup/picasso/Stats.java',
|
||||
'com/squareup/picasso/StatsSnapshot.java',
|
||||
'com/squareup/picasso/Target.java',
|
||||
'com/squareup/picasso/TargetAction.java',
|
||||
'com/squareup/picasso/Transformation.java',
|
||||
'com/squareup/picasso/UrlConnectionDownloader.java',
|
||||
'com/squareup/picasso/Utils.java',
|
||||
] ]
|
||||
#spjar.javac_flags += ['-Xlint:all']
|
||||
|
||||
ANDROID_RES_DIRS += [
|
||||
SRCDIR + '/resources',
|
||||
TOPSRCDIR + '/' + CONFIG['MOZ_BRANDING_DIRECTORY'] + '/res',
|
||||
|
83
mobile/android/thirdparty/com/squareup/picasso/Action.java
vendored
Normal file
83
mobile/android/thirdparty/com/squareup/picasso/Action.java
vendored
Normal file
@ -0,0 +1,83 @@
|
||||
/*
|
||||
* Copyright (C) 2013 Square, Inc.
|
||||
*
|
||||
* 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.squareup.picasso;
|
||||
|
||||
import android.graphics.Bitmap;
|
||||
import android.graphics.drawable.Drawable;
|
||||
import java.lang.ref.ReferenceQueue;
|
||||
import java.lang.ref.WeakReference;
|
||||
|
||||
abstract class Action<T> {
|
||||
static class RequestWeakReference<T> extends WeakReference<T> {
|
||||
final Action action;
|
||||
|
||||
public RequestWeakReference(Action action, T referent, ReferenceQueue<? super T> q) {
|
||||
super(referent, q);
|
||||
this.action = action;
|
||||
}
|
||||
}
|
||||
|
||||
final Picasso picasso;
|
||||
final Request data;
|
||||
final WeakReference<T> target;
|
||||
final boolean skipCache;
|
||||
final boolean noFade;
|
||||
final int errorResId;
|
||||
final Drawable errorDrawable;
|
||||
final String key;
|
||||
|
||||
boolean cancelled;
|
||||
|
||||
Action(Picasso picasso, T target, Request data, boolean skipCache, boolean noFade,
|
||||
int errorResId, Drawable errorDrawable, String key) {
|
||||
this.picasso = picasso;
|
||||
this.data = data;
|
||||
this.target = new RequestWeakReference<T>(this, target, picasso.referenceQueue);
|
||||
this.skipCache = skipCache;
|
||||
this.noFade = noFade;
|
||||
this.errorResId = errorResId;
|
||||
this.errorDrawable = errorDrawable;
|
||||
this.key = key;
|
||||
}
|
||||
|
||||
abstract void complete(Bitmap result, Picasso.LoadedFrom from);
|
||||
|
||||
abstract void error();
|
||||
|
||||
void cancel() {
|
||||
cancelled = true;
|
||||
}
|
||||
|
||||
Request getData() {
|
||||
return data;
|
||||
}
|
||||
|
||||
T getTarget() {
|
||||
return target.get();
|
||||
}
|
||||
|
||||
String getKey() {
|
||||
return key;
|
||||
}
|
||||
|
||||
boolean isCancelled() {
|
||||
return cancelled;
|
||||
}
|
||||
|
||||
Picasso getPicasso() {
|
||||
return picasso;
|
||||
}
|
||||
}
|
51
mobile/android/thirdparty/com/squareup/picasso/AssetBitmapHunter.java
vendored
Normal file
51
mobile/android/thirdparty/com/squareup/picasso/AssetBitmapHunter.java
vendored
Normal file
@ -0,0 +1,51 @@
|
||||
package com.squareup.picasso;
|
||||
|
||||
import android.content.Context;
|
||||
import android.content.res.AssetManager;
|
||||
import android.graphics.Bitmap;
|
||||
import android.graphics.BitmapFactory;
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
|
||||
import static com.squareup.picasso.Picasso.LoadedFrom.DISK;
|
||||
|
||||
class AssetBitmapHunter extends BitmapHunter {
|
||||
private AssetManager assetManager;
|
||||
|
||||
public AssetBitmapHunter(Context context, Picasso picasso, Dispatcher dispatcher, Cache cache,
|
||||
Stats stats, Action action) {
|
||||
super(picasso, dispatcher, cache, stats, action);
|
||||
assetManager = context.getAssets();
|
||||
}
|
||||
|
||||
@Override Bitmap decode(Request data) throws IOException {
|
||||
String filePath = data.uri.toString().substring(ASSET_PREFIX_LENGTH);
|
||||
return decodeAsset(filePath);
|
||||
}
|
||||
|
||||
@Override Picasso.LoadedFrom getLoadedFrom() {
|
||||
return DISK;
|
||||
}
|
||||
|
||||
Bitmap decodeAsset(String filePath) throws IOException {
|
||||
BitmapFactory.Options options = null;
|
||||
if (data.hasSize()) {
|
||||
options = new BitmapFactory.Options();
|
||||
options.inJustDecodeBounds = true;
|
||||
InputStream is = null;
|
||||
try {
|
||||
is = assetManager.open(filePath);
|
||||
BitmapFactory.decodeStream(is, null, options);
|
||||
} finally {
|
||||
Utils.closeQuietly(is);
|
||||
}
|
||||
calculateInSampleSize(data.targetWidth, data.targetHeight, options);
|
||||
}
|
||||
InputStream is = assetManager.open(filePath);
|
||||
try {
|
||||
return BitmapFactory.decodeStream(is, null, options);
|
||||
} finally {
|
||||
Utils.closeQuietly(is);
|
||||
}
|
||||
}
|
||||
}
|
357
mobile/android/thirdparty/com/squareup/picasso/BitmapHunter.java
vendored
Normal file
357
mobile/android/thirdparty/com/squareup/picasso/BitmapHunter.java
vendored
Normal file
@ -0,0 +1,357 @@
|
||||
/*
|
||||
* Copyright (C) 2013 Square, Inc.
|
||||
*
|
||||
* 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.squareup.picasso;
|
||||
|
||||
import android.content.Context;
|
||||
import android.graphics.Bitmap;
|
||||
import android.graphics.BitmapFactory;
|
||||
import android.graphics.Matrix;
|
||||
import android.net.NetworkInfo;
|
||||
import android.net.Uri;
|
||||
import android.provider.MediaStore;
|
||||
import java.io.IOException;
|
||||
import java.io.PrintWriter;
|
||||
import java.io.StringWriter;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.concurrent.Future;
|
||||
|
||||
import static android.content.ContentResolver.SCHEME_ANDROID_RESOURCE;
|
||||
import static android.content.ContentResolver.SCHEME_CONTENT;
|
||||
import static android.content.ContentResolver.SCHEME_FILE;
|
||||
import static android.provider.ContactsContract.Contacts;
|
||||
import static com.squareup.picasso.Picasso.LoadedFrom.MEMORY;
|
||||
|
||||
abstract class BitmapHunter implements Runnable {
|
||||
|
||||
/**
|
||||
* Global lock for bitmap decoding to ensure that we are only are decoding one at a time. Since
|
||||
* this will only ever happen in background threads we help avoid excessive memory thrashing as
|
||||
* well as potential OOMs. Shamelessly stolen from Volley.
|
||||
*/
|
||||
private static final Object DECODE_LOCK = new Object();
|
||||
private static final String ANDROID_ASSET = "android_asset";
|
||||
protected static final int ASSET_PREFIX_LENGTH =
|
||||
(SCHEME_FILE + ":///" + ANDROID_ASSET + "/").length();
|
||||
|
||||
final Picasso picasso;
|
||||
final Dispatcher dispatcher;
|
||||
final Cache cache;
|
||||
final Stats stats;
|
||||
final String key;
|
||||
final Request data;
|
||||
final List<Action> actions;
|
||||
final boolean skipMemoryCache;
|
||||
|
||||
Bitmap result;
|
||||
Future<?> future;
|
||||
Picasso.LoadedFrom loadedFrom;
|
||||
Exception exception;
|
||||
int exifRotation; // Determined during decoding of original resource.
|
||||
|
||||
BitmapHunter(Picasso picasso, Dispatcher dispatcher, Cache cache, Stats stats, Action action) {
|
||||
this.picasso = picasso;
|
||||
this.dispatcher = dispatcher;
|
||||
this.cache = cache;
|
||||
this.stats = stats;
|
||||
this.key = action.getKey();
|
||||
this.data = action.getData();
|
||||
this.skipMemoryCache = action.skipCache;
|
||||
this.actions = new ArrayList<Action>(4);
|
||||
attach(action);
|
||||
}
|
||||
|
||||
protected void setExifRotation(int exifRotation) {
|
||||
this.exifRotation = exifRotation;
|
||||
}
|
||||
|
||||
@Override public void run() {
|
||||
try {
|
||||
Thread.currentThread().setName(Utils.THREAD_PREFIX + data.getName());
|
||||
|
||||
result = hunt();
|
||||
|
||||
if (result == null) {
|
||||
dispatcher.dispatchFailed(this);
|
||||
} else {
|
||||
dispatcher.dispatchComplete(this);
|
||||
}
|
||||
} catch (Downloader.ResponseException e) {
|
||||
exception = e;
|
||||
dispatcher.dispatchFailed(this);
|
||||
} catch (IOException e) {
|
||||
exception = e;
|
||||
dispatcher.dispatchRetry(this);
|
||||
} catch (OutOfMemoryError e) {
|
||||
StringWriter writer = new StringWriter();
|
||||
stats.createSnapshot().dump(new PrintWriter(writer));
|
||||
exception = new RuntimeException(writer.toString(), e);
|
||||
dispatcher.dispatchFailed(this);
|
||||
} catch (Exception e) {
|
||||
exception = e;
|
||||
dispatcher.dispatchFailed(this);
|
||||
} finally {
|
||||
Thread.currentThread().setName(Utils.THREAD_IDLE_NAME);
|
||||
}
|
||||
}
|
||||
|
||||
abstract Bitmap decode(Request data) throws IOException;
|
||||
|
||||
Bitmap hunt() throws IOException {
|
||||
Bitmap bitmap;
|
||||
|
||||
if (!skipMemoryCache) {
|
||||
bitmap = cache.get(key);
|
||||
if (bitmap != null) {
|
||||
stats.dispatchCacheHit();
|
||||
loadedFrom = MEMORY;
|
||||
return bitmap;
|
||||
}
|
||||
}
|
||||
|
||||
bitmap = decode(data);
|
||||
|
||||
if (bitmap != null) {
|
||||
stats.dispatchBitmapDecoded(bitmap);
|
||||
if (data.needsTransformation() || exifRotation != 0) {
|
||||
synchronized (DECODE_LOCK) {
|
||||
if (data.needsMatrixTransform() || exifRotation != 0) {
|
||||
bitmap = transformResult(data, bitmap, exifRotation);
|
||||
}
|
||||
if (data.hasCustomTransformations()) {
|
||||
bitmap = applyCustomTransformations(data.transformations, bitmap);
|
||||
}
|
||||
}
|
||||
stats.dispatchBitmapTransformed(bitmap);
|
||||
}
|
||||
}
|
||||
|
||||
return bitmap;
|
||||
}
|
||||
|
||||
void attach(Action action) {
|
||||
actions.add(action);
|
||||
}
|
||||
|
||||
void detach(Action action) {
|
||||
actions.remove(action);
|
||||
}
|
||||
|
||||
boolean cancel() {
|
||||
return actions.isEmpty() && future != null && future.cancel(false);
|
||||
}
|
||||
|
||||
boolean isCancelled() {
|
||||
return future != null && future.isCancelled();
|
||||
}
|
||||
|
||||
boolean shouldSkipMemoryCache() {
|
||||
return skipMemoryCache;
|
||||
}
|
||||
|
||||
boolean shouldRetry(boolean airplaneMode, NetworkInfo info) {
|
||||
return false;
|
||||
}
|
||||
|
||||
Bitmap getResult() {
|
||||
return result;
|
||||
}
|
||||
|
||||
String getKey() {
|
||||
return key;
|
||||
}
|
||||
|
||||
Request getData() {
|
||||
return data;
|
||||
}
|
||||
|
||||
List<Action> getActions() {
|
||||
return actions;
|
||||
}
|
||||
|
||||
Exception getException() {
|
||||
return exception;
|
||||
}
|
||||
|
||||
Picasso.LoadedFrom getLoadedFrom() {
|
||||
return loadedFrom;
|
||||
}
|
||||
|
||||
static BitmapHunter forRequest(Context context, Picasso picasso, Dispatcher dispatcher,
|
||||
Cache cache, Stats stats, Action action, Downloader downloader) {
|
||||
if (action.getData().resourceId != 0) {
|
||||
return new ResourceBitmapHunter(context, picasso, dispatcher, cache, stats, action);
|
||||
}
|
||||
Uri uri = action.getData().uri;
|
||||
String scheme = uri.getScheme();
|
||||
if (SCHEME_CONTENT.equals(scheme)) {
|
||||
if (Contacts.CONTENT_URI.getHost().equals(uri.getHost()) //
|
||||
&& !uri.getPathSegments().contains(Contacts.Photo.CONTENT_DIRECTORY)) {
|
||||
return new ContactsPhotoBitmapHunter(context, picasso, dispatcher, cache, stats, action);
|
||||
} else if (MediaStore.AUTHORITY.equals(uri.getAuthority())) {
|
||||
return new MediaStoreBitmapHunter(context, picasso, dispatcher, cache, stats, action);
|
||||
} else {
|
||||
return new ContentStreamBitmapHunter(context, picasso, dispatcher, cache, stats, action);
|
||||
}
|
||||
} else if (SCHEME_FILE.equals(scheme)) {
|
||||
if (!uri.getPathSegments().isEmpty() && ANDROID_ASSET.equals(uri.getPathSegments().get(0))) {
|
||||
return new AssetBitmapHunter(context, picasso, dispatcher, cache, stats, action);
|
||||
}
|
||||
return new FileBitmapHunter(context, picasso, dispatcher, cache, stats, action);
|
||||
} else if (SCHEME_ANDROID_RESOURCE.equals(scheme)) {
|
||||
return new ResourceBitmapHunter(context, picasso, dispatcher, cache, stats, action);
|
||||
} else {
|
||||
return new NetworkBitmapHunter(picasso, dispatcher, cache, stats, action, downloader);
|
||||
}
|
||||
}
|
||||
|
||||
static void calculateInSampleSize(int reqWidth, int reqHeight, BitmapFactory.Options options) {
|
||||
calculateInSampleSize(reqWidth, reqHeight, options.outWidth, options.outHeight, options);
|
||||
}
|
||||
|
||||
static void calculateInSampleSize(int reqWidth, int reqHeight, int width, int height,
|
||||
BitmapFactory.Options options) {
|
||||
int sampleSize = 1;
|
||||
if (height > reqHeight || width > reqWidth) {
|
||||
final int heightRatio = Math.round((float) height / (float) reqHeight);
|
||||
final int widthRatio = Math.round((float) width / (float) reqWidth);
|
||||
sampleSize = heightRatio < widthRatio ? heightRatio : widthRatio;
|
||||
}
|
||||
|
||||
options.inSampleSize = sampleSize;
|
||||
options.inJustDecodeBounds = false;
|
||||
}
|
||||
|
||||
static Bitmap applyCustomTransformations(List<Transformation> transformations, Bitmap result) {
|
||||
for (int i = 0, count = transformations.size(); i < count; i++) {
|
||||
final Transformation transformation = transformations.get(i);
|
||||
Bitmap newResult = transformation.transform(result);
|
||||
|
||||
if (newResult == null) {
|
||||
final StringBuilder builder = new StringBuilder() //
|
||||
.append("Transformation ")
|
||||
.append(transformation.key())
|
||||
.append(" returned null after ")
|
||||
.append(i)
|
||||
.append(" previous transformation(s).\n\nTransformation list:\n");
|
||||
for (Transformation t : transformations) {
|
||||
builder.append(t.key()).append('\n');
|
||||
}
|
||||
Picasso.HANDLER.post(new Runnable() {
|
||||
@Override public void run() {
|
||||
throw new NullPointerException(builder.toString());
|
||||
}
|
||||
});
|
||||
return null;
|
||||
}
|
||||
|
||||
if (newResult == result && result.isRecycled()) {
|
||||
Picasso.HANDLER.post(new Runnable() {
|
||||
@Override public void run() {
|
||||
throw new IllegalStateException("Transformation "
|
||||
+ transformation.key()
|
||||
+ " returned input Bitmap but recycled it.");
|
||||
}
|
||||
});
|
||||
return null;
|
||||
}
|
||||
|
||||
// If the transformation returned a new bitmap ensure they recycled the original.
|
||||
if (newResult != result && !result.isRecycled()) {
|
||||
Picasso.HANDLER.post(new Runnable() {
|
||||
@Override public void run() {
|
||||
throw new IllegalStateException("Transformation "
|
||||
+ transformation.key()
|
||||
+ " mutated input Bitmap but failed to recycle the original.");
|
||||
}
|
||||
});
|
||||
return null;
|
||||
}
|
||||
|
||||
result = newResult;
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
static Bitmap transformResult(Request data, Bitmap result, int exifRotation) {
|
||||
int inWidth = result.getWidth();
|
||||
int inHeight = result.getHeight();
|
||||
|
||||
int drawX = 0;
|
||||
int drawY = 0;
|
||||
int drawWidth = inWidth;
|
||||
int drawHeight = inHeight;
|
||||
|
||||
Matrix matrix = new Matrix();
|
||||
|
||||
if (data.needsMatrixTransform()) {
|
||||
int targetWidth = data.targetWidth;
|
||||
int targetHeight = data.targetHeight;
|
||||
|
||||
float targetRotation = data.rotationDegrees;
|
||||
if (targetRotation != 0) {
|
||||
if (data.hasRotationPivot) {
|
||||
matrix.setRotate(targetRotation, data.rotationPivotX, data.rotationPivotY);
|
||||
} else {
|
||||
matrix.setRotate(targetRotation);
|
||||
}
|
||||
}
|
||||
|
||||
if (data.centerCrop) {
|
||||
float widthRatio = targetWidth / (float) inWidth;
|
||||
float heightRatio = targetHeight / (float) inHeight;
|
||||
float scale;
|
||||
if (widthRatio > heightRatio) {
|
||||
scale = widthRatio;
|
||||
int newSize = (int) Math.ceil(inHeight * (heightRatio / widthRatio));
|
||||
drawY = (inHeight - newSize) / 2;
|
||||
drawHeight = newSize;
|
||||
} else {
|
||||
scale = heightRatio;
|
||||
int newSize = (int) Math.ceil(inWidth * (widthRatio / heightRatio));
|
||||
drawX = (inWidth - newSize) / 2;
|
||||
drawWidth = newSize;
|
||||
}
|
||||
matrix.preScale(scale, scale);
|
||||
} else if (data.centerInside) {
|
||||
float widthRatio = targetWidth / (float) inWidth;
|
||||
float heightRatio = targetHeight / (float) inHeight;
|
||||
float scale = widthRatio < heightRatio ? widthRatio : heightRatio;
|
||||
matrix.preScale(scale, scale);
|
||||
} else if (targetWidth != 0 && targetHeight != 0 //
|
||||
&& (targetWidth != inWidth || targetHeight != inHeight)) {
|
||||
// If an explicit target size has been specified and they do not match the results bounds,
|
||||
// pre-scale the existing matrix appropriately.
|
||||
float sx = targetWidth / (float) inWidth;
|
||||
float sy = targetHeight / (float) inHeight;
|
||||
matrix.preScale(sx, sy);
|
||||
}
|
||||
}
|
||||
|
||||
if (exifRotation != 0) {
|
||||
matrix.preRotate(exifRotation);
|
||||
}
|
||||
|
||||
Bitmap newResult =
|
||||
Bitmap.createBitmap(result, drawX, drawY, drawWidth, drawHeight, matrix, true);
|
||||
if (newResult != result) {
|
||||
result.recycle();
|
||||
result = newResult;
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
}
|
64
mobile/android/thirdparty/com/squareup/picasso/Cache.java
vendored
Normal file
64
mobile/android/thirdparty/com/squareup/picasso/Cache.java
vendored
Normal file
@ -0,0 +1,64 @@
|
||||
/*
|
||||
* Copyright (C) 2013 Square, Inc.
|
||||
*
|
||||
* 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.squareup.picasso;
|
||||
|
||||
import android.graphics.Bitmap;
|
||||
|
||||
/**
|
||||
* A memory cache for storing the most recently used images.
|
||||
* <p/>
|
||||
* <em>Note:</em> The {@link Cache} is accessed by multiple threads. You must ensure
|
||||
* your {@link Cache} implementation is thread safe when {@link Cache#get(String)} or {@link
|
||||
* Cache#set(String, android.graphics.Bitmap)} is called.
|
||||
*/
|
||||
public interface Cache {
|
||||
/** Retrieve an image for the specified {@code key} or {@code null}. */
|
||||
Bitmap get(String key);
|
||||
|
||||
/** Store an image in the cache for the specified {@code key}. */
|
||||
void set(String key, Bitmap bitmap);
|
||||
|
||||
/** Returns the current size of the cache in bytes. */
|
||||
int size();
|
||||
|
||||
/** Returns the maximum size in bytes that the cache can hold. */
|
||||
int maxSize();
|
||||
|
||||
/** Clears the cache. */
|
||||
void clear();
|
||||
|
||||
/** A cache which does not store any values. */
|
||||
Cache NONE = new Cache() {
|
||||
@Override public Bitmap get(String key) {
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override public void set(String key, Bitmap bitmap) {
|
||||
// Ignore.
|
||||
}
|
||||
|
||||
@Override public int size() {
|
||||
return 0;
|
||||
}
|
||||
|
||||
@Override public int maxSize() {
|
||||
return 0;
|
||||
}
|
||||
|
||||
@Override public void clear() {
|
||||
}
|
||||
};
|
||||
}
|
31
mobile/android/thirdparty/com/squareup/picasso/Callback.java
vendored
Normal file
31
mobile/android/thirdparty/com/squareup/picasso/Callback.java
vendored
Normal file
@ -0,0 +1,31 @@
|
||||
/*
|
||||
* Copyright (C) 2013 Square, Inc.
|
||||
*
|
||||
* 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.squareup.picasso;
|
||||
|
||||
public interface Callback {
|
||||
void onSuccess();
|
||||
|
||||
void onError();
|
||||
|
||||
public static class EmptyCallback implements Callback {
|
||||
|
||||
@Override public void onSuccess() {
|
||||
}
|
||||
|
||||
@Override public void onError() {
|
||||
}
|
||||
}
|
||||
}
|
130
mobile/android/thirdparty/com/squareup/picasso/ContactsPhotoBitmapHunter.java
vendored
Normal file
130
mobile/android/thirdparty/com/squareup/picasso/ContactsPhotoBitmapHunter.java
vendored
Normal file
@ -0,0 +1,130 @@
|
||||
/*
|
||||
* Copyright (C) 2013 Square, Inc.
|
||||
*
|
||||
* 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.squareup.picasso;
|
||||
|
||||
import android.annotation.TargetApi;
|
||||
import android.content.ContentResolver;
|
||||
import android.content.Context;
|
||||
import android.content.UriMatcher;
|
||||
import android.graphics.Bitmap;
|
||||
import android.graphics.BitmapFactory;
|
||||
import android.net.Uri;
|
||||
import android.provider.ContactsContract;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
|
||||
import static android.os.Build.VERSION.SDK_INT;
|
||||
import static android.os.Build.VERSION_CODES.ICE_CREAM_SANDWICH;
|
||||
import static android.provider.ContactsContract.Contacts.openContactPhotoInputStream;
|
||||
import static com.squareup.picasso.Picasso.LoadedFrom.DISK;
|
||||
|
||||
class ContactsPhotoBitmapHunter extends BitmapHunter {
|
||||
/** A lookup uri (e.g. content://com.android.contacts/contacts/lookup/3570i61d948d30808e537) */
|
||||
private static final int ID_LOOKUP = 1;
|
||||
/** A contact thumbnail uri (e.g. content://com.android.contacts/contacts/38/photo) */
|
||||
private static final int ID_THUMBNAIL = 2;
|
||||
/** A contact uri (e.g. content://com.android.contacts/contacts/38) */
|
||||
private static final int ID_CONTACT = 3;
|
||||
/**
|
||||
* A contact display photo (high resolution) uri
|
||||
* (e.g. content://com.android.contacts/display_photo/5)
|
||||
*/
|
||||
private static final int ID_DISPLAY_PHOTO = 4;
|
||||
|
||||
private static final UriMatcher matcher;
|
||||
|
||||
static {
|
||||
matcher = new UriMatcher(UriMatcher.NO_MATCH);
|
||||
matcher.addURI(ContactsContract.AUTHORITY, "contacts/lookup/*/#", ID_LOOKUP);
|
||||
matcher.addURI(ContactsContract.AUTHORITY, "contacts/lookup/*", ID_LOOKUP);
|
||||
matcher.addURI(ContactsContract.AUTHORITY, "contacts/#/photo", ID_THUMBNAIL);
|
||||
matcher.addURI(ContactsContract.AUTHORITY, "contacts/#", ID_CONTACT);
|
||||
matcher.addURI(ContactsContract.AUTHORITY, "display_photo/#", ID_DISPLAY_PHOTO);
|
||||
}
|
||||
|
||||
final Context context;
|
||||
|
||||
ContactsPhotoBitmapHunter(Context context, Picasso picasso, Dispatcher dispatcher, Cache cache,
|
||||
Stats stats, Action action) {
|
||||
super(picasso, dispatcher, cache, stats, action);
|
||||
this.context = context;
|
||||
}
|
||||
|
||||
@Override Bitmap decode(Request data) throws IOException {
|
||||
InputStream is = null;
|
||||
try {
|
||||
is = getInputStream();
|
||||
return decodeStream(is, data);
|
||||
} finally {
|
||||
Utils.closeQuietly(is);
|
||||
}
|
||||
}
|
||||
|
||||
@Override Picasso.LoadedFrom getLoadedFrom() {
|
||||
return DISK;
|
||||
}
|
||||
|
||||
private InputStream getInputStream() throws IOException {
|
||||
ContentResolver contentResolver = context.getContentResolver();
|
||||
Uri uri = getData().uri;
|
||||
switch (matcher.match(uri)) {
|
||||
case ID_LOOKUP:
|
||||
uri = ContactsContract.Contacts.lookupContact(contentResolver, uri);
|
||||
if (uri == null) {
|
||||
return null;
|
||||
}
|
||||
// Resolved the uri to a contact uri, intentionally fall through to process the resolved uri
|
||||
case ID_CONTACT:
|
||||
if (SDK_INT < ICE_CREAM_SANDWICH) {
|
||||
return openContactPhotoInputStream(contentResolver, uri);
|
||||
} else {
|
||||
return ContactPhotoStreamIcs.get(contentResolver, uri);
|
||||
}
|
||||
case ID_THUMBNAIL:
|
||||
case ID_DISPLAY_PHOTO:
|
||||
return contentResolver.openInputStream(uri);
|
||||
default:
|
||||
throw new IllegalStateException("Invalid uri: " + uri);
|
||||
}
|
||||
}
|
||||
|
||||
private Bitmap decodeStream(InputStream stream, Request data) throws IOException {
|
||||
if (stream == null) {
|
||||
return null;
|
||||
}
|
||||
BitmapFactory.Options options = null;
|
||||
if (data.hasSize()) {
|
||||
options = new BitmapFactory.Options();
|
||||
options.inJustDecodeBounds = true;
|
||||
InputStream is = getInputStream();
|
||||
try {
|
||||
BitmapFactory.decodeStream(is, null, options);
|
||||
} finally {
|
||||
Utils.closeQuietly(is);
|
||||
}
|
||||
calculateInSampleSize(data.targetWidth, data.targetHeight, options);
|
||||
}
|
||||
return BitmapFactory.decodeStream(stream, null, options);
|
||||
}
|
||||
|
||||
@TargetApi(ICE_CREAM_SANDWICH)
|
||||
private static class ContactPhotoStreamIcs {
|
||||
static InputStream get(ContentResolver contentResolver, Uri uri) {
|
||||
return openContactPhotoInputStream(contentResolver, uri, true);
|
||||
}
|
||||
}
|
||||
}
|
67
mobile/android/thirdparty/com/squareup/picasso/ContentStreamBitmapHunter.java
vendored
Normal file
67
mobile/android/thirdparty/com/squareup/picasso/ContentStreamBitmapHunter.java
vendored
Normal file
@ -0,0 +1,67 @@
|
||||
/*
|
||||
* Copyright (C) 2013 Square, Inc.
|
||||
*
|
||||
* 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.squareup.picasso;
|
||||
|
||||
import android.content.ContentResolver;
|
||||
import android.content.Context;
|
||||
import android.graphics.Bitmap;
|
||||
import android.graphics.BitmapFactory;
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
|
||||
import static com.squareup.picasso.Picasso.LoadedFrom.DISK;
|
||||
|
||||
class ContentStreamBitmapHunter extends BitmapHunter {
|
||||
final Context context;
|
||||
|
||||
ContentStreamBitmapHunter(Context context, Picasso picasso, Dispatcher dispatcher, Cache cache,
|
||||
Stats stats, Action action) {
|
||||
super(picasso, dispatcher, cache, stats, action);
|
||||
this.context = context;
|
||||
}
|
||||
|
||||
@Override Bitmap decode(Request data)
|
||||
throws IOException {
|
||||
return decodeContentStream(data);
|
||||
}
|
||||
|
||||
@Override Picasso.LoadedFrom getLoadedFrom() {
|
||||
return DISK;
|
||||
}
|
||||
|
||||
protected Bitmap decodeContentStream(Request data) throws IOException {
|
||||
ContentResolver contentResolver = context.getContentResolver();
|
||||
BitmapFactory.Options options = null;
|
||||
if (data.hasSize()) {
|
||||
options = new BitmapFactory.Options();
|
||||
options.inJustDecodeBounds = true;
|
||||
InputStream is = null;
|
||||
try {
|
||||
is = contentResolver.openInputStream(data.uri);
|
||||
BitmapFactory.decodeStream(is, null, options);
|
||||
} finally {
|
||||
Utils.closeQuietly(is);
|
||||
}
|
||||
calculateInSampleSize(data.targetWidth, data.targetHeight, options);
|
||||
}
|
||||
InputStream is = contentResolver.openInputStream(data.uri);
|
||||
try {
|
||||
return BitmapFactory.decodeStream(is, null, options);
|
||||
} finally {
|
||||
Utils.closeQuietly(is);
|
||||
}
|
||||
}
|
||||
}
|
70
mobile/android/thirdparty/com/squareup/picasso/DeferredRequestCreator.java
vendored
Normal file
70
mobile/android/thirdparty/com/squareup/picasso/DeferredRequestCreator.java
vendored
Normal file
@ -0,0 +1,70 @@
|
||||
/*
|
||||
* Copyright (C) 2013 Square, Inc.
|
||||
*
|
||||
* 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.squareup.picasso;
|
||||
|
||||
import android.view.ViewTreeObserver;
|
||||
import android.widget.ImageView;
|
||||
import java.lang.ref.WeakReference;
|
||||
|
||||
class DeferredRequestCreator implements ViewTreeObserver.OnPreDrawListener {
|
||||
|
||||
final RequestCreator creator;
|
||||
final WeakReference<ImageView> target;
|
||||
Callback callback;
|
||||
|
||||
DeferredRequestCreator(RequestCreator creator, ImageView target, Callback callback) {
|
||||
this.creator = creator;
|
||||
this.target = new WeakReference<ImageView>(target);
|
||||
this.callback = callback;
|
||||
target.getViewTreeObserver().addOnPreDrawListener(this);
|
||||
}
|
||||
|
||||
@Override public boolean onPreDraw() {
|
||||
ImageView target = this.target.get();
|
||||
if (target == null) {
|
||||
return true;
|
||||
}
|
||||
ViewTreeObserver vto = target.getViewTreeObserver();
|
||||
if (!vto.isAlive()) {
|
||||
return true;
|
||||
}
|
||||
|
||||
int width = target.getMeasuredWidth();
|
||||
int height = target.getMeasuredHeight();
|
||||
|
||||
if (width <= 0 || height <= 0) {
|
||||
return true;
|
||||
}
|
||||
|
||||
vto.removeOnPreDrawListener(this);
|
||||
|
||||
this.creator.unfit().resize(width, height).into(target, callback);
|
||||
return true;
|
||||
}
|
||||
|
||||
void cancel() {
|
||||
callback = null;
|
||||
ImageView target = this.target.get();
|
||||
if (target == null) {
|
||||
return;
|
||||
}
|
||||
ViewTreeObserver vto = target.getViewTreeObserver();
|
||||
if (!vto.isAlive()) {
|
||||
return;
|
||||
}
|
||||
vto.removeOnPreDrawListener(this);
|
||||
}
|
||||
}
|
315
mobile/android/thirdparty/com/squareup/picasso/Dispatcher.java
vendored
Normal file
315
mobile/android/thirdparty/com/squareup/picasso/Dispatcher.java
vendored
Normal file
@ -0,0 +1,315 @@
|
||||
/*
|
||||
* Copyright (C) 2013 Square, Inc.
|
||||
*
|
||||
* 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.squareup.picasso;
|
||||
|
||||
import android.Manifest;
|
||||
import android.content.BroadcastReceiver;
|
||||
import android.content.Context;
|
||||
import android.content.Intent;
|
||||
import android.content.IntentFilter;
|
||||
import android.net.ConnectivityManager;
|
||||
import android.net.NetworkInfo;
|
||||
import android.os.Bundle;
|
||||
import android.os.Handler;
|
||||
import android.os.HandlerThread;
|
||||
import android.os.Looper;
|
||||
import android.os.Message;
|
||||
import java.util.ArrayList;
|
||||
import java.util.LinkedHashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.concurrent.ExecutorService;
|
||||
|
||||
import static android.content.Context.CONNECTIVITY_SERVICE;
|
||||
import static android.content.Intent.ACTION_AIRPLANE_MODE_CHANGED;
|
||||
import static android.net.ConnectivityManager.CONNECTIVITY_ACTION;
|
||||
import static android.os.Process.THREAD_PRIORITY_BACKGROUND;
|
||||
import static com.squareup.picasso.BitmapHunter.forRequest;
|
||||
|
||||
class Dispatcher {
|
||||
private static final int RETRY_DELAY = 500;
|
||||
private static final int AIRPLANE_MODE_ON = 1;
|
||||
private static final int AIRPLANE_MODE_OFF = 0;
|
||||
|
||||
static final int REQUEST_SUBMIT = 1;
|
||||
static final int REQUEST_CANCEL = 2;
|
||||
static final int REQUEST_GCED = 3;
|
||||
static final int HUNTER_COMPLETE = 4;
|
||||
static final int HUNTER_RETRY = 5;
|
||||
static final int HUNTER_DECODE_FAILED = 6;
|
||||
static final int HUNTER_DELAY_NEXT_BATCH = 7;
|
||||
static final int HUNTER_BATCH_COMPLETE = 8;
|
||||
static final int NETWORK_STATE_CHANGE = 9;
|
||||
static final int AIRPLANE_MODE_CHANGE = 10;
|
||||
|
||||
private static final String DISPATCHER_THREAD_NAME = "Dispatcher";
|
||||
private static final int BATCH_DELAY = 200; // ms
|
||||
|
||||
final DispatcherThread dispatcherThread;
|
||||
final Context context;
|
||||
final ExecutorService service;
|
||||
final Downloader downloader;
|
||||
final Map<String, BitmapHunter> hunterMap;
|
||||
final Handler handler;
|
||||
final Handler mainThreadHandler;
|
||||
final Cache cache;
|
||||
final Stats stats;
|
||||
final List<BitmapHunter> batch;
|
||||
final NetworkBroadcastReceiver receiver;
|
||||
|
||||
NetworkInfo networkInfo;
|
||||
boolean airplaneMode;
|
||||
|
||||
Dispatcher(Context context, ExecutorService service, Handler mainThreadHandler,
|
||||
Downloader downloader, Cache cache, Stats stats) {
|
||||
this.dispatcherThread = new DispatcherThread();
|
||||
this.dispatcherThread.start();
|
||||
this.context = context;
|
||||
this.service = service;
|
||||
this.hunterMap = new LinkedHashMap<String, BitmapHunter>();
|
||||
this.handler = new DispatcherHandler(dispatcherThread.getLooper(), this);
|
||||
this.downloader = downloader;
|
||||
this.mainThreadHandler = mainThreadHandler;
|
||||
this.cache = cache;
|
||||
this.stats = stats;
|
||||
this.batch = new ArrayList<BitmapHunter>(4);
|
||||
this.airplaneMode = Utils.isAirplaneModeOn(this.context);
|
||||
this.receiver = new NetworkBroadcastReceiver(this.context);
|
||||
receiver.register();
|
||||
}
|
||||
|
||||
void shutdown() {
|
||||
service.shutdown();
|
||||
dispatcherThread.quit();
|
||||
receiver.unregister();
|
||||
}
|
||||
|
||||
void dispatchSubmit(Action action) {
|
||||
handler.sendMessage(handler.obtainMessage(REQUEST_SUBMIT, action));
|
||||
}
|
||||
|
||||
void dispatchCancel(Action action) {
|
||||
handler.sendMessage(handler.obtainMessage(REQUEST_CANCEL, action));
|
||||
}
|
||||
|
||||
void dispatchComplete(BitmapHunter hunter) {
|
||||
handler.sendMessage(handler.obtainMessage(HUNTER_COMPLETE, hunter));
|
||||
}
|
||||
|
||||
void dispatchRetry(BitmapHunter hunter) {
|
||||
handler.sendMessageDelayed(handler.obtainMessage(HUNTER_RETRY, hunter), RETRY_DELAY);
|
||||
}
|
||||
|
||||
void dispatchFailed(BitmapHunter hunter) {
|
||||
handler.sendMessage(handler.obtainMessage(HUNTER_DECODE_FAILED, hunter));
|
||||
}
|
||||
|
||||
void dispatchNetworkStateChange(NetworkInfo info) {
|
||||
handler.sendMessage(handler.obtainMessage(NETWORK_STATE_CHANGE, info));
|
||||
}
|
||||
|
||||
void dispatchAirplaneModeChange(boolean airplaneMode) {
|
||||
handler.sendMessage(handler.obtainMessage(AIRPLANE_MODE_CHANGE,
|
||||
airplaneMode ? AIRPLANE_MODE_ON : AIRPLANE_MODE_OFF, 0));
|
||||
}
|
||||
|
||||
void performSubmit(Action action) {
|
||||
BitmapHunter hunter = hunterMap.get(action.getKey());
|
||||
if (hunter != null) {
|
||||
hunter.attach(action);
|
||||
return;
|
||||
}
|
||||
|
||||
if (service.isShutdown()) {
|
||||
return;
|
||||
}
|
||||
|
||||
hunter = forRequest(context, action.getPicasso(), this, cache, stats, action, downloader);
|
||||
hunter.future = service.submit(hunter);
|
||||
hunterMap.put(action.getKey(), hunter);
|
||||
}
|
||||
|
||||
void performCancel(Action action) {
|
||||
String key = action.getKey();
|
||||
BitmapHunter hunter = hunterMap.get(key);
|
||||
if (hunter != null) {
|
||||
hunter.detach(action);
|
||||
if (hunter.cancel()) {
|
||||
hunterMap.remove(key);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void performRetry(BitmapHunter hunter) {
|
||||
if (hunter.isCancelled()) return;
|
||||
|
||||
if (service.isShutdown()) {
|
||||
performError(hunter);
|
||||
return;
|
||||
}
|
||||
|
||||
if (hunter.shouldRetry(airplaneMode, networkInfo)) {
|
||||
hunter.future = service.submit(hunter);
|
||||
} else {
|
||||
performError(hunter);
|
||||
}
|
||||
}
|
||||
|
||||
void performComplete(BitmapHunter hunter) {
|
||||
if (!hunter.shouldSkipMemoryCache()) {
|
||||
cache.set(hunter.getKey(), hunter.getResult());
|
||||
}
|
||||
hunterMap.remove(hunter.getKey());
|
||||
batch(hunter);
|
||||
}
|
||||
|
||||
void performBatchComplete() {
|
||||
List<BitmapHunter> copy = new ArrayList<BitmapHunter>(batch);
|
||||
batch.clear();
|
||||
mainThreadHandler.sendMessage(mainThreadHandler.obtainMessage(HUNTER_BATCH_COMPLETE, copy));
|
||||
}
|
||||
|
||||
void performError(BitmapHunter hunter) {
|
||||
hunterMap.remove(hunter.getKey());
|
||||
batch(hunter);
|
||||
}
|
||||
|
||||
void performAirplaneModeChange(boolean airplaneMode) {
|
||||
this.airplaneMode = airplaneMode;
|
||||
}
|
||||
|
||||
void performNetworkStateChange(NetworkInfo info) {
|
||||
networkInfo = info;
|
||||
if (service instanceof PicassoExecutorService) {
|
||||
((PicassoExecutorService) service).adjustThreadCount(info);
|
||||
}
|
||||
}
|
||||
|
||||
private void batch(BitmapHunter hunter) {
|
||||
if (hunter.isCancelled()) {
|
||||
return;
|
||||
}
|
||||
batch.add(hunter);
|
||||
if (!handler.hasMessages(HUNTER_DELAY_NEXT_BATCH)) {
|
||||
handler.sendEmptyMessageDelayed(HUNTER_DELAY_NEXT_BATCH, BATCH_DELAY);
|
||||
}
|
||||
}
|
||||
|
||||
private static class DispatcherHandler extends Handler {
|
||||
private final Dispatcher dispatcher;
|
||||
|
||||
public DispatcherHandler(Looper looper, Dispatcher dispatcher) {
|
||||
super(looper);
|
||||
this.dispatcher = dispatcher;
|
||||
}
|
||||
|
||||
@Override public void handleMessage(final Message msg) {
|
||||
switch (msg.what) {
|
||||
case REQUEST_SUBMIT: {
|
||||
Action action = (Action) msg.obj;
|
||||
dispatcher.performSubmit(action);
|
||||
break;
|
||||
}
|
||||
case REQUEST_CANCEL: {
|
||||
Action action = (Action) msg.obj;
|
||||
dispatcher.performCancel(action);
|
||||
break;
|
||||
}
|
||||
case HUNTER_COMPLETE: {
|
||||
BitmapHunter hunter = (BitmapHunter) msg.obj;
|
||||
dispatcher.performComplete(hunter);
|
||||
break;
|
||||
}
|
||||
case HUNTER_RETRY: {
|
||||
BitmapHunter hunter = (BitmapHunter) msg.obj;
|
||||
dispatcher.performRetry(hunter);
|
||||
break;
|
||||
}
|
||||
case HUNTER_DECODE_FAILED: {
|
||||
BitmapHunter hunter = (BitmapHunter) msg.obj;
|
||||
dispatcher.performError(hunter);
|
||||
break;
|
||||
}
|
||||
case HUNTER_DELAY_NEXT_BATCH: {
|
||||
dispatcher.performBatchComplete();
|
||||
break;
|
||||
}
|
||||
case NETWORK_STATE_CHANGE: {
|
||||
NetworkInfo info = (NetworkInfo) msg.obj;
|
||||
dispatcher.performNetworkStateChange(info);
|
||||
break;
|
||||
}
|
||||
case AIRPLANE_MODE_CHANGE: {
|
||||
dispatcher.performAirplaneModeChange(msg.arg1 == AIRPLANE_MODE_ON);
|
||||
break;
|
||||
}
|
||||
default:
|
||||
Picasso.HANDLER.post(new Runnable() {
|
||||
@Override public void run() {
|
||||
throw new AssertionError("Unknown handler message received: " + msg.what);
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static class DispatcherThread extends HandlerThread {
|
||||
DispatcherThread() {
|
||||
super(Utils.THREAD_PREFIX + DISPATCHER_THREAD_NAME, THREAD_PRIORITY_BACKGROUND);
|
||||
}
|
||||
}
|
||||
|
||||
private class NetworkBroadcastReceiver extends BroadcastReceiver {
|
||||
private static final String EXTRA_AIRPLANE_STATE = "state";
|
||||
|
||||
private final ConnectivityManager connectivityManager;
|
||||
|
||||
NetworkBroadcastReceiver(Context context) {
|
||||
connectivityManager = (ConnectivityManager) context.getSystemService(CONNECTIVITY_SERVICE);
|
||||
}
|
||||
|
||||
void register() {
|
||||
boolean shouldScanState = service instanceof PicassoExecutorService && //
|
||||
Utils.hasPermission(context, Manifest.permission.ACCESS_NETWORK_STATE);
|
||||
IntentFilter filter = new IntentFilter();
|
||||
filter.addAction(ACTION_AIRPLANE_MODE_CHANGED);
|
||||
if (shouldScanState) {
|
||||
filter.addAction(CONNECTIVITY_ACTION);
|
||||
}
|
||||
context.registerReceiver(this, filter);
|
||||
}
|
||||
|
||||
void unregister() {
|
||||
context.unregisterReceiver(this);
|
||||
}
|
||||
|
||||
@Override public void onReceive(Context context, Intent intent) {
|
||||
// On some versions of Android this may be called with a null Intent
|
||||
if (null == intent) {
|
||||
return;
|
||||
}
|
||||
|
||||
String action = intent.getAction();
|
||||
Bundle extras = intent.getExtras();
|
||||
|
||||
if (ACTION_AIRPLANE_MODE_CHANGED.equals(action)) {
|
||||
dispatchAirplaneModeChange(extras.getBoolean(EXTRA_AIRPLANE_STATE, false));
|
||||
} else if (CONNECTIVITY_ACTION.equals(action)) {
|
||||
dispatchNetworkStateChange(connectivityManager.getActiveNetworkInfo());
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
99
mobile/android/thirdparty/com/squareup/picasso/Downloader.java
vendored
Normal file
99
mobile/android/thirdparty/com/squareup/picasso/Downloader.java
vendored
Normal file
@ -0,0 +1,99 @@
|
||||
/*
|
||||
* Copyright (C) 2013 Square, Inc.
|
||||
*
|
||||
* 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.squareup.picasso;
|
||||
|
||||
import android.graphics.Bitmap;
|
||||
import android.net.Uri;
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
|
||||
/** A mechanism to load images from external resources such as a disk cache and/or the internet. */
|
||||
public interface Downloader {
|
||||
/**
|
||||
* Download the specified image {@code url} from the internet.
|
||||
*
|
||||
* @param uri Remote image URL.
|
||||
* @param localCacheOnly If {@code true} the URL should only be loaded if available in a local
|
||||
* disk cache.
|
||||
* @return {@link Response} containing either a {@link Bitmap} representation of the request or an
|
||||
* {@link InputStream} for the image data. {@code null} can be returned to indicate a problem
|
||||
* loading the bitmap.
|
||||
* @throws IOException if the requested URL cannot successfully be loaded.
|
||||
*/
|
||||
Response load(Uri uri, boolean localCacheOnly) throws IOException;
|
||||
|
||||
/** Thrown for non-2XX responses. */
|
||||
class ResponseException extends IOException {
|
||||
public ResponseException(String message) {
|
||||
super(message);
|
||||
}
|
||||
}
|
||||
|
||||
/** Response stream or bitmap and info. */
|
||||
class Response {
|
||||
final InputStream stream;
|
||||
final Bitmap bitmap;
|
||||
final boolean cached;
|
||||
|
||||
/**
|
||||
* Response image and info.
|
||||
*
|
||||
* @param bitmap Image.
|
||||
* @param loadedFromCache {@code true} if the source of the image is from a local disk cache.
|
||||
*/
|
||||
public Response(Bitmap bitmap, boolean loadedFromCache) {
|
||||
if (bitmap == null) {
|
||||
throw new IllegalArgumentException("Bitmap may not be null.");
|
||||
}
|
||||
this.stream = null;
|
||||
this.bitmap = bitmap;
|
||||
this.cached = loadedFromCache;
|
||||
}
|
||||
|
||||
/**
|
||||
* Response stream and info.
|
||||
*
|
||||
* @param stream Image data stream.
|
||||
* @param loadedFromCache {@code true} if the source of the stream is from a local disk cache.
|
||||
*/
|
||||
public Response(InputStream stream, boolean loadedFromCache) {
|
||||
if (stream == null) {
|
||||
throw new IllegalArgumentException("Stream may not be null.");
|
||||
}
|
||||
this.stream = stream;
|
||||
this.bitmap = null;
|
||||
this.cached = loadedFromCache;
|
||||
}
|
||||
|
||||
/**
|
||||
* Input stream containing image data.
|
||||
* <p>
|
||||
* If this returns {@code null}, image data will be available via {@link #getBitmap()}.
|
||||
*/
|
||||
public InputStream getInputStream() {
|
||||
return stream;
|
||||
}
|
||||
|
||||
/**
|
||||
* Bitmap representing the image.
|
||||
* <p>
|
||||
* If this returns {@code null}, image data will be available via {@link #getInputStream()}.
|
||||
*/
|
||||
public Bitmap getBitmap() {
|
||||
return bitmap;
|
||||
}
|
||||
}
|
||||
}
|
30
mobile/android/thirdparty/com/squareup/picasso/FetchAction.java
vendored
Normal file
30
mobile/android/thirdparty/com/squareup/picasso/FetchAction.java
vendored
Normal file
@ -0,0 +1,30 @@
|
||||
/*
|
||||
* Copyright (C) 2013 Square, Inc.
|
||||
*
|
||||
* 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.squareup.picasso;
|
||||
|
||||
import android.graphics.Bitmap;
|
||||
|
||||
class FetchAction extends Action<Void> {
|
||||
FetchAction(Picasso picasso, Request data, boolean skipCache, String key) {
|
||||
super(picasso, null, data, skipCache, false, 0, null, key);
|
||||
}
|
||||
|
||||
@Override void complete(Bitmap result, Picasso.LoadedFrom from) {
|
||||
}
|
||||
|
||||
@Override public void error() {
|
||||
}
|
||||
}
|
57
mobile/android/thirdparty/com/squareup/picasso/FileBitmapHunter.java
vendored
Normal file
57
mobile/android/thirdparty/com/squareup/picasso/FileBitmapHunter.java
vendored
Normal file
@ -0,0 +1,57 @@
|
||||
/*
|
||||
* Copyright (C) 2013 Square, Inc.
|
||||
*
|
||||
* 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.squareup.picasso;
|
||||
|
||||
import android.content.Context;
|
||||
import android.graphics.Bitmap;
|
||||
import android.media.ExifInterface;
|
||||
import android.net.Uri;
|
||||
import java.io.IOException;
|
||||
|
||||
import static android.media.ExifInterface.ORIENTATION_NORMAL;
|
||||
import static android.media.ExifInterface.ORIENTATION_ROTATE_180;
|
||||
import static android.media.ExifInterface.ORIENTATION_ROTATE_270;
|
||||
import static android.media.ExifInterface.ORIENTATION_ROTATE_90;
|
||||
import static android.media.ExifInterface.TAG_ORIENTATION;
|
||||
|
||||
class FileBitmapHunter extends ContentStreamBitmapHunter {
|
||||
|
||||
FileBitmapHunter(Context context, Picasso picasso, Dispatcher dispatcher, Cache cache,
|
||||
Stats stats, Action action) {
|
||||
super(context, picasso, dispatcher, cache, stats, action);
|
||||
}
|
||||
|
||||
@Override Bitmap decode(Request data)
|
||||
throws IOException {
|
||||
setExifRotation(getFileExifRotation(data.uri));
|
||||
return super.decode(data);
|
||||
}
|
||||
|
||||
static int getFileExifRotation(Uri uri) throws IOException {
|
||||
ExifInterface exifInterface = new ExifInterface(uri.getPath());
|
||||
int orientation = exifInterface.getAttributeInt(TAG_ORIENTATION, ORIENTATION_NORMAL);
|
||||
switch (orientation) {
|
||||
case ORIENTATION_ROTATE_90:
|
||||
return 90;
|
||||
case ORIENTATION_ROTATE_180:
|
||||
return 180;
|
||||
case ORIENTATION_ROTATE_270:
|
||||
return 270;
|
||||
default:
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
}
|
30
mobile/android/thirdparty/com/squareup/picasso/GetAction.java
vendored
Normal file
30
mobile/android/thirdparty/com/squareup/picasso/GetAction.java
vendored
Normal file
@ -0,0 +1,30 @@
|
||||
/*
|
||||
* Copyright (C) 2013 Square, Inc.
|
||||
*
|
||||
* 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.squareup.picasso;
|
||||
|
||||
import android.graphics.Bitmap;
|
||||
|
||||
class GetAction extends Action<Void> {
|
||||
GetAction(Picasso picasso, Request data, boolean skipCache, String key) {
|
||||
super(picasso, null, data, skipCache, false, 0, null, key);
|
||||
}
|
||||
|
||||
@Override void complete(Bitmap result, Picasso.LoadedFrom from) {
|
||||
}
|
||||
|
||||
@Override public void error() {
|
||||
}
|
||||
}
|
75
mobile/android/thirdparty/com/squareup/picasso/ImageViewAction.java
vendored
Normal file
75
mobile/android/thirdparty/com/squareup/picasso/ImageViewAction.java
vendored
Normal file
@ -0,0 +1,75 @@
|
||||
/*
|
||||
* Copyright (C) 2013 Square, Inc.
|
||||
*
|
||||
* 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.squareup.picasso;
|
||||
|
||||
import android.content.Context;
|
||||
import android.graphics.Bitmap;
|
||||
import android.graphics.drawable.Drawable;
|
||||
import android.widget.ImageView;
|
||||
|
||||
class ImageViewAction extends Action<ImageView> {
|
||||
|
||||
Callback callback;
|
||||
|
||||
ImageViewAction(Picasso picasso, ImageView imageView, Request data, boolean skipCache,
|
||||
boolean noFade, int errorResId, Drawable errorDrawable, String key, Callback callback) {
|
||||
super(picasso, imageView, data, skipCache, noFade, errorResId, errorDrawable, key);
|
||||
this.callback = callback;
|
||||
}
|
||||
|
||||
@Override public void complete(Bitmap result, Picasso.LoadedFrom from) {
|
||||
if (result == null) {
|
||||
throw new AssertionError(
|
||||
String.format("Attempted to complete action with no result!\n%s", this));
|
||||
}
|
||||
|
||||
ImageView target = this.target.get();
|
||||
if (target == null) {
|
||||
return;
|
||||
}
|
||||
|
||||
Context context = picasso.context;
|
||||
boolean debugging = picasso.debugging;
|
||||
PicassoDrawable.setBitmap(target, context, result, from, noFade, debugging);
|
||||
|
||||
if (callback != null) {
|
||||
callback.onSuccess();
|
||||
}
|
||||
}
|
||||
|
||||
@Override public void error() {
|
||||
ImageView target = this.target.get();
|
||||
if (target == null) {
|
||||
return;
|
||||
}
|
||||
if (errorResId != 0) {
|
||||
target.setImageResource(errorResId);
|
||||
} else if (errorDrawable != null) {
|
||||
target.setImageDrawable(errorDrawable);
|
||||
}
|
||||
|
||||
if (callback != null) {
|
||||
callback.onError();
|
||||
}
|
||||
}
|
||||
|
||||
@Override void cancel() {
|
||||
super.cancel();
|
||||
if (callback != null) {
|
||||
callback = null;
|
||||
}
|
||||
}
|
||||
}
|
146
mobile/android/thirdparty/com/squareup/picasso/LruCache.java
vendored
Normal file
146
mobile/android/thirdparty/com/squareup/picasso/LruCache.java
vendored
Normal file
@ -0,0 +1,146 @@
|
||||
/*
|
||||
* Copyright (C) 2011 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.squareup.picasso;
|
||||
|
||||
import android.content.Context;
|
||||
import android.graphics.Bitmap;
|
||||
import java.util.LinkedHashMap;
|
||||
import java.util.Map;
|
||||
|
||||
/** A memory cache which uses a least-recently used eviction policy. */
|
||||
public class LruCache implements Cache {
|
||||
final LinkedHashMap<String, Bitmap> map;
|
||||
private final int maxSize;
|
||||
|
||||
private int size;
|
||||
private int putCount;
|
||||
private int evictionCount;
|
||||
private int hitCount;
|
||||
private int missCount;
|
||||
|
||||
/** Create a cache using an appropriate portion of the available RAM as the maximum size. */
|
||||
public LruCache(Context context) {
|
||||
this(Utils.calculateMemoryCacheSize(context));
|
||||
}
|
||||
|
||||
/** Create a cache with a given maximum size in bytes. */
|
||||
public LruCache(int maxSize) {
|
||||
if (maxSize <= 0) {
|
||||
throw new IllegalArgumentException("Max size must be positive.");
|
||||
}
|
||||
this.maxSize = maxSize;
|
||||
this.map = new LinkedHashMap<String, Bitmap>(0, 0.75f, true);
|
||||
}
|
||||
|
||||
@Override public Bitmap get(String key) {
|
||||
if (key == null) {
|
||||
throw new NullPointerException("key == null");
|
||||
}
|
||||
|
||||
Bitmap mapValue;
|
||||
synchronized (this) {
|
||||
mapValue = map.get(key);
|
||||
if (mapValue != null) {
|
||||
hitCount++;
|
||||
return mapValue;
|
||||
}
|
||||
missCount++;
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override public void set(String key, Bitmap bitmap) {
|
||||
if (key == null || bitmap == null) {
|
||||
throw new NullPointerException("key == null || bitmap == null");
|
||||
}
|
||||
|
||||
Bitmap previous;
|
||||
synchronized (this) {
|
||||
putCount++;
|
||||
size += Utils.getBitmapBytes(bitmap);
|
||||
previous = map.put(key, bitmap);
|
||||
if (previous != null) {
|
||||
size -= Utils.getBitmapBytes(previous);
|
||||
}
|
||||
}
|
||||
|
||||
trimToSize(maxSize);
|
||||
}
|
||||
|
||||
private void trimToSize(int maxSize) {
|
||||
while (true) {
|
||||
String key;
|
||||
Bitmap value;
|
||||
synchronized (this) {
|
||||
if (size < 0 || (map.isEmpty() && size != 0)) {
|
||||
throw new IllegalStateException(
|
||||
getClass().getName() + ".sizeOf() is reporting inconsistent results!");
|
||||
}
|
||||
|
||||
if (size <= maxSize || map.isEmpty()) {
|
||||
break;
|
||||
}
|
||||
|
||||
Map.Entry<String, Bitmap> toEvict = map.entrySet().iterator().next();
|
||||
key = toEvict.getKey();
|
||||
value = toEvict.getValue();
|
||||
map.remove(key);
|
||||
size -= Utils.getBitmapBytes(value);
|
||||
evictionCount++;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/** Clear the cache. */
|
||||
public final void evictAll() {
|
||||
trimToSize(-1); // -1 will evict 0-sized elements
|
||||
}
|
||||
|
||||
/** Returns the sum of the sizes of the entries in this cache. */
|
||||
public final synchronized int size() {
|
||||
return size;
|
||||
}
|
||||
|
||||
/** Returns the maximum sum of the sizes of the entries in this cache. */
|
||||
public final synchronized int maxSize() {
|
||||
return maxSize;
|
||||
}
|
||||
|
||||
public final synchronized void clear() {
|
||||
evictAll();
|
||||
}
|
||||
|
||||
/** Returns the number of times {@link #get} returned a value. */
|
||||
public final synchronized int hitCount() {
|
||||
return hitCount;
|
||||
}
|
||||
|
||||
/** Returns the number of times {@link #get} returned {@code null}. */
|
||||
public final synchronized int missCount() {
|
||||
return missCount;
|
||||
}
|
||||
|
||||
/** Returns the number of times {@link #set(String, Bitmap)} was called. */
|
||||
public final synchronized int putCount() {
|
||||
return putCount;
|
||||
}
|
||||
|
||||
/** Returns the number of values that have been evicted. */
|
||||
public final synchronized int evictionCount() {
|
||||
return evictionCount;
|
||||
}
|
||||
}
|
157
mobile/android/thirdparty/com/squareup/picasso/MarkableInputStream.java
vendored
Normal file
157
mobile/android/thirdparty/com/squareup/picasso/MarkableInputStream.java
vendored
Normal file
@ -0,0 +1,157 @@
|
||||
/*
|
||||
* Copyright (C) 2013 Square, Inc.
|
||||
*
|
||||
* 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.squareup.picasso;
|
||||
|
||||
import java.io.BufferedInputStream;
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
|
||||
/**
|
||||
* An input stream wrapper that supports unlimited independent cursors for
|
||||
* marking and resetting. Each cursor is a token, and it's the caller's
|
||||
* responsibility to keep track of these.
|
||||
*/
|
||||
final class MarkableInputStream extends InputStream {
|
||||
private final InputStream in;
|
||||
|
||||
private long offset;
|
||||
private long reset;
|
||||
private long limit;
|
||||
|
||||
private long defaultMark = -1;
|
||||
|
||||
public MarkableInputStream(InputStream in) {
|
||||
if (!in.markSupported()) {
|
||||
in = new BufferedInputStream(in);
|
||||
}
|
||||
this.in = in;
|
||||
}
|
||||
|
||||
/** Marks this place in the stream so we can reset back to it later. */
|
||||
@Override public void mark(int readLimit) {
|
||||
defaultMark = savePosition(readLimit);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns an opaque token representing the current position in the stream.
|
||||
* Call {@link #reset(long)} to return to this position in the stream later.
|
||||
* It is an error to call {@link #reset(long)} after consuming more than
|
||||
* {@code readLimit} bytes from this stream.
|
||||
*/
|
||||
public long savePosition(int readLimit) {
|
||||
long offsetLimit = offset + readLimit;
|
||||
if (limit < offsetLimit) {
|
||||
setLimit(offsetLimit);
|
||||
}
|
||||
return offset;
|
||||
}
|
||||
|
||||
/**
|
||||
* Makes sure that the underlying stream can backtrack the full range from
|
||||
* {@code reset} thru {@code limit}. Since we can't call {@code mark()}
|
||||
* without also adjusting the reset-to-position on the underlying stream this
|
||||
* method resets first and then marks the union of the two byte ranges. On
|
||||
* buffered streams this additional cursor motion shouldn't result in any
|
||||
* additional I/O.
|
||||
*/
|
||||
private void setLimit(long limit) {
|
||||
try {
|
||||
if (reset < offset && offset <= this.limit) {
|
||||
in.reset();
|
||||
in.mark((int) (limit - reset));
|
||||
skip(reset, offset);
|
||||
} else {
|
||||
reset = offset;
|
||||
in.mark((int) (limit - offset));
|
||||
}
|
||||
this.limit = limit;
|
||||
} catch (IOException e) {
|
||||
throw new IllegalStateException("Unable to mark: " + e);
|
||||
}
|
||||
}
|
||||
|
||||
/** Resets the stream to the most recent {@link #mark mark}. */
|
||||
@Override public void reset() throws IOException {
|
||||
reset(defaultMark);
|
||||
}
|
||||
|
||||
/** Resets the stream to the position recorded by {@code token}. */
|
||||
public void reset(long token) throws IOException {
|
||||
if (offset > limit || token < reset) {
|
||||
throw new IOException("Cannot reset");
|
||||
}
|
||||
in.reset();
|
||||
skip(reset, token);
|
||||
offset = token;
|
||||
}
|
||||
|
||||
/** Skips {@code target - current} bytes and returns. */
|
||||
private void skip(long current, long target) throws IOException {
|
||||
while (current < target) {
|
||||
long skipped = in.skip(target - current);
|
||||
if (skipped == 0) {
|
||||
if (read() == -1) {
|
||||
break; // EOF
|
||||
} else {
|
||||
skipped = 1;
|
||||
}
|
||||
}
|
||||
current += skipped;
|
||||
}
|
||||
}
|
||||
|
||||
@Override public int read() throws IOException {
|
||||
int result = in.read();
|
||||
if (result != -1) {
|
||||
offset++;
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
@Override public int read(byte[] buffer) throws IOException {
|
||||
int count = in.read(buffer);
|
||||
if (count != -1) {
|
||||
offset += count;
|
||||
}
|
||||
return count;
|
||||
}
|
||||
|
||||
@Override public int read(byte[] buffer, int offset, int length) throws IOException {
|
||||
int count = in.read(buffer, offset, length);
|
||||
if (count != -1) {
|
||||
this.offset += count;
|
||||
}
|
||||
return count;
|
||||
}
|
||||
|
||||
@Override public long skip(long byteCount) throws IOException {
|
||||
long skipped = in.skip(byteCount);
|
||||
offset += skipped;
|
||||
return skipped;
|
||||
}
|
||||
|
||||
@Override public int available() throws IOException {
|
||||
return in.available();
|
||||
}
|
||||
|
||||
@Override public void close() throws IOException {
|
||||
in.close();
|
||||
}
|
||||
|
||||
@Override public boolean markSupported() {
|
||||
return in.markSupported();
|
||||
}
|
||||
}
|
116
mobile/android/thirdparty/com/squareup/picasso/MediaStoreBitmapHunter.java
vendored
Normal file
116
mobile/android/thirdparty/com/squareup/picasso/MediaStoreBitmapHunter.java
vendored
Normal file
@ -0,0 +1,116 @@
|
||||
/*
|
||||
* Copyright (C) 2014 Square, Inc.
|
||||
*
|
||||
* 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.squareup.picasso;
|
||||
|
||||
import android.content.ContentResolver;
|
||||
import android.content.Context;
|
||||
import android.database.Cursor;
|
||||
import android.graphics.Bitmap;
|
||||
import android.graphics.BitmapFactory;
|
||||
import android.net.Uri;
|
||||
import android.provider.MediaStore;
|
||||
import java.io.IOException;
|
||||
|
||||
import static android.content.ContentUris.parseId;
|
||||
import static android.provider.MediaStore.Images.Thumbnails.FULL_SCREEN_KIND;
|
||||
import static android.provider.MediaStore.Images.Thumbnails.MICRO_KIND;
|
||||
import static android.provider.MediaStore.Images.Thumbnails.MINI_KIND;
|
||||
import static android.provider.MediaStore.Images.Thumbnails.getThumbnail;
|
||||
import static com.squareup.picasso.MediaStoreBitmapHunter.PicassoKind.FULL;
|
||||
import static com.squareup.picasso.MediaStoreBitmapHunter.PicassoKind.MICRO;
|
||||
import static com.squareup.picasso.MediaStoreBitmapHunter.PicassoKind.MINI;
|
||||
|
||||
class MediaStoreBitmapHunter extends ContentStreamBitmapHunter {
|
||||
private static final String[] CONTENT_ORIENTATION = new String[] {
|
||||
MediaStore.Images.ImageColumns.ORIENTATION
|
||||
};
|
||||
|
||||
MediaStoreBitmapHunter(Context context, Picasso picasso, Dispatcher dispatcher, Cache cache,
|
||||
Stats stats, Action action) {
|
||||
super(context, picasso, dispatcher, cache, stats, action);
|
||||
}
|
||||
|
||||
@Override Bitmap decode(Request data) throws IOException {
|
||||
ContentResolver contentResolver = context.getContentResolver();
|
||||
setExifRotation(getExitOrientation(contentResolver, data.uri));
|
||||
|
||||
if (data.hasSize()) {
|
||||
PicassoKind picassoKind = getPicassoKind(data.targetWidth, data.targetHeight);
|
||||
if (picassoKind == FULL) {
|
||||
return super.decode(data);
|
||||
}
|
||||
|
||||
long id = parseId(data.uri);
|
||||
|
||||
BitmapFactory.Options options = new BitmapFactory.Options();
|
||||
options.inJustDecodeBounds = true;
|
||||
|
||||
calculateInSampleSize(data.targetWidth, data.targetHeight, picassoKind.width,
|
||||
picassoKind.height, options);
|
||||
|
||||
Bitmap result = getThumbnail(contentResolver, id, picassoKind.androidKind, options);
|
||||
|
||||
if (result != null) {
|
||||
return result;
|
||||
}
|
||||
}
|
||||
|
||||
return super.decode(data);
|
||||
}
|
||||
|
||||
static PicassoKind getPicassoKind(int targetWidth, int targetHeight) {
|
||||
if (targetWidth <= MICRO.width && targetHeight <= MICRO.height) {
|
||||
return MICRO;
|
||||
} else if (targetWidth <= MINI.width && targetHeight <= MINI.height) {
|
||||
return MINI;
|
||||
}
|
||||
return FULL;
|
||||
}
|
||||
|
||||
static int getExitOrientation(ContentResolver contentResolver, Uri uri) {
|
||||
Cursor cursor = null;
|
||||
try {
|
||||
cursor = contentResolver.query(uri, CONTENT_ORIENTATION, null, null, null);
|
||||
if (cursor == null || !cursor.moveToFirst()) {
|
||||
return 0;
|
||||
}
|
||||
return cursor.getInt(0);
|
||||
} catch (RuntimeException ignored) {
|
||||
// If the orientation column doesn't exist, assume no rotation.
|
||||
return 0;
|
||||
} finally {
|
||||
if (cursor != null) {
|
||||
cursor.close();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
enum PicassoKind {
|
||||
MICRO(MICRO_KIND, 96, 96),
|
||||
MINI(MINI_KIND, 512, 384),
|
||||
FULL(FULL_SCREEN_KIND, -1, -1);
|
||||
|
||||
final int androidKind;
|
||||
final int width;
|
||||
final int height;
|
||||
|
||||
PicassoKind(int androidKind, int width, int height) {
|
||||
this.androidKind = androidKind;
|
||||
this.width = width;
|
||||
this.height = height;
|
||||
}
|
||||
}
|
||||
}
|
113
mobile/android/thirdparty/com/squareup/picasso/NetworkBitmapHunter.java
vendored
Normal file
113
mobile/android/thirdparty/com/squareup/picasso/NetworkBitmapHunter.java
vendored
Normal file
@ -0,0 +1,113 @@
|
||||
/*
|
||||
* Copyright (C) 2013 Square, Inc.
|
||||
*
|
||||
* 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.squareup.picasso;
|
||||
|
||||
import android.graphics.Bitmap;
|
||||
import android.graphics.BitmapFactory;
|
||||
import android.net.NetworkInfo;
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
|
||||
import static com.squareup.picasso.Downloader.Response;
|
||||
import static com.squareup.picasso.Picasso.LoadedFrom.DISK;
|
||||
import static com.squareup.picasso.Picasso.LoadedFrom.NETWORK;
|
||||
|
||||
class NetworkBitmapHunter extends BitmapHunter {
|
||||
static final int DEFAULT_RETRY_COUNT = 2;
|
||||
private static final int MARKER = 65536;
|
||||
|
||||
private final Downloader downloader;
|
||||
|
||||
int retryCount;
|
||||
|
||||
public NetworkBitmapHunter(Picasso picasso, Dispatcher dispatcher, Cache cache, Stats stats,
|
||||
Action action, Downloader downloader) {
|
||||
super(picasso, dispatcher, cache, stats, action);
|
||||
this.downloader = downloader;
|
||||
this.retryCount = DEFAULT_RETRY_COUNT;
|
||||
}
|
||||
|
||||
@Override Bitmap decode(Request data) throws IOException {
|
||||
boolean loadFromLocalCacheOnly = retryCount == 0;
|
||||
|
||||
Response response = downloader.load(data.uri, loadFromLocalCacheOnly);
|
||||
if (response == null) {
|
||||
return null;
|
||||
}
|
||||
|
||||
loadedFrom = response.cached ? DISK : NETWORK;
|
||||
|
||||
Bitmap result = response.getBitmap();
|
||||
if (result != null) {
|
||||
return result;
|
||||
}
|
||||
|
||||
InputStream is = response.getInputStream();
|
||||
try {
|
||||
return decodeStream(is, data);
|
||||
} finally {
|
||||
Utils.closeQuietly(is);
|
||||
}
|
||||
}
|
||||
|
||||
@Override boolean shouldRetry(boolean airplaneMode, NetworkInfo info) {
|
||||
boolean hasRetries = retryCount > 0;
|
||||
if (!hasRetries) {
|
||||
return false;
|
||||
}
|
||||
retryCount--;
|
||||
return info == null || info.isConnectedOrConnecting();
|
||||
}
|
||||
|
||||
private Bitmap decodeStream(InputStream stream, Request data) throws IOException {
|
||||
if (stream == null) {
|
||||
return null;
|
||||
}
|
||||
MarkableInputStream markStream = new MarkableInputStream(stream);
|
||||
stream = markStream;
|
||||
|
||||
long mark = markStream.savePosition(MARKER);
|
||||
|
||||
boolean isWebPFile = Utils.isWebPFile(stream);
|
||||
markStream.reset(mark);
|
||||
// When decode WebP network stream, BitmapFactory throw JNI Exception and make app crash.
|
||||
// Decode byte array instead
|
||||
if (isWebPFile) {
|
||||
byte[] bytes = Utils.toByteArray(stream);
|
||||
BitmapFactory.Options options = null;
|
||||
if (data.hasSize()) {
|
||||
options = new BitmapFactory.Options();
|
||||
options.inJustDecodeBounds = true;
|
||||
|
||||
BitmapFactory.decodeByteArray(bytes, 0, bytes.length, options);
|
||||
calculateInSampleSize(data.targetWidth, data.targetHeight, options);
|
||||
}
|
||||
return BitmapFactory.decodeByteArray(bytes, 0, bytes.length, options);
|
||||
} else {
|
||||
BitmapFactory.Options options = null;
|
||||
if (data.hasSize()) {
|
||||
options = new BitmapFactory.Options();
|
||||
options.inJustDecodeBounds = true;
|
||||
|
||||
BitmapFactory.decodeStream(stream, null, options);
|
||||
calculateInSampleSize(data.targetWidth, data.targetHeight, options);
|
||||
|
||||
markStream.reset(mark);
|
||||
}
|
||||
return BitmapFactory.decodeStream(stream, null, options);
|
||||
}
|
||||
}
|
||||
}
|
522
mobile/android/thirdparty/com/squareup/picasso/Picasso.java
vendored
Normal file
522
mobile/android/thirdparty/com/squareup/picasso/Picasso.java
vendored
Normal file
@ -0,0 +1,522 @@
|
||||
/*
|
||||
* Copyright (C) 2013 Square, Inc.
|
||||
*
|
||||
* 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.squareup.picasso;
|
||||
|
||||
import android.content.Context;
|
||||
import android.graphics.Bitmap;
|
||||
import android.graphics.Color;
|
||||
import android.net.Uri;
|
||||
import android.os.Handler;
|
||||
import android.os.Looper;
|
||||
import android.os.Message;
|
||||
import android.os.Process;
|
||||
import android.widget.ImageView;
|
||||
import java.io.File;
|
||||
import java.lang.ref.ReferenceQueue;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.WeakHashMap;
|
||||
import java.util.concurrent.ExecutorService;
|
||||
|
||||
import static android.os.Process.THREAD_PRIORITY_BACKGROUND;
|
||||
import static com.squareup.picasso.Action.RequestWeakReference;
|
||||
import static com.squareup.picasso.Dispatcher.HUNTER_BATCH_COMPLETE;
|
||||
import static com.squareup.picasso.Dispatcher.REQUEST_GCED;
|
||||
import static com.squareup.picasso.Utils.THREAD_PREFIX;
|
||||
|
||||
/**
|
||||
* Image downloading, transformation, and caching manager.
|
||||
* <p/>
|
||||
* Use {@link #with(android.content.Context)} for the global singleton instance or construct your
|
||||
* own instance with {@link Builder}.
|
||||
*/
|
||||
public class Picasso {
|
||||
|
||||
/** Callbacks for Picasso events. */
|
||||
public interface Listener {
|
||||
/**
|
||||
* Invoked when an image has failed to load. This is useful for reporting image failures to a
|
||||
* remote analytics service, for example.
|
||||
*/
|
||||
void onImageLoadFailed(Picasso picasso, Uri uri, Exception exception);
|
||||
}
|
||||
|
||||
/**
|
||||
* A transformer that is called immediately before every request is submitted. This can be used to
|
||||
* modify any information about a request.
|
||||
* <p>
|
||||
* For example, if you use a CDN you can change the hostname for the image based on the current
|
||||
* location of the user in order to get faster download speeds.
|
||||
* <p>
|
||||
* <b>NOTE:</b> This is a beta feature. The API is subject to change in a backwards incompatible
|
||||
* way at any time.
|
||||
*/
|
||||
public interface RequestTransformer {
|
||||
/**
|
||||
* Transform a request before it is submitted to be processed.
|
||||
*
|
||||
* @return The original request or a new request to replace it. Must not be null.
|
||||
*/
|
||||
Request transformRequest(Request request);
|
||||
|
||||
/** A {@link RequestTransformer} which returns the original request. */
|
||||
RequestTransformer IDENTITY = new RequestTransformer() {
|
||||
@Override public Request transformRequest(Request request) {
|
||||
return request;
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
static final Handler HANDLER = new Handler(Looper.getMainLooper()) {
|
||||
@Override public void handleMessage(Message msg) {
|
||||
switch (msg.what) {
|
||||
case HUNTER_BATCH_COMPLETE: {
|
||||
@SuppressWarnings("unchecked") List<BitmapHunter> batch = (List<BitmapHunter>) msg.obj;
|
||||
for (BitmapHunter hunter : batch) {
|
||||
hunter.picasso.complete(hunter);
|
||||
}
|
||||
break;
|
||||
}
|
||||
case REQUEST_GCED: {
|
||||
Action action = (Action) msg.obj;
|
||||
action.picasso.cancelExistingRequest(action.getTarget());
|
||||
break;
|
||||
}
|
||||
default:
|
||||
throw new AssertionError("Unknown handler message received: " + msg.what);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
static Picasso singleton = null;
|
||||
|
||||
private final Listener listener;
|
||||
private final RequestTransformer requestTransformer;
|
||||
private final CleanupThread cleanupThread;
|
||||
|
||||
final Context context;
|
||||
final Dispatcher dispatcher;
|
||||
final Cache cache;
|
||||
final Stats stats;
|
||||
final Map<Object, Action> targetToAction;
|
||||
final Map<ImageView, DeferredRequestCreator> targetToDeferredRequestCreator;
|
||||
final ReferenceQueue<Object> referenceQueue;
|
||||
|
||||
boolean debugging;
|
||||
boolean shutdown;
|
||||
|
||||
Picasso(Context context, Dispatcher dispatcher, Cache cache, Listener listener,
|
||||
RequestTransformer requestTransformer, Stats stats, boolean debugging) {
|
||||
this.context = context;
|
||||
this.dispatcher = dispatcher;
|
||||
this.cache = cache;
|
||||
this.listener = listener;
|
||||
this.requestTransformer = requestTransformer;
|
||||
this.stats = stats;
|
||||
this.targetToAction = new WeakHashMap<Object, Action>();
|
||||
this.targetToDeferredRequestCreator = new WeakHashMap<ImageView, DeferredRequestCreator>();
|
||||
this.debugging = debugging;
|
||||
this.referenceQueue = new ReferenceQueue<Object>();
|
||||
this.cleanupThread = new CleanupThread(referenceQueue, HANDLER);
|
||||
this.cleanupThread.start();
|
||||
}
|
||||
|
||||
/** Cancel any existing requests for the specified target {@link ImageView}. */
|
||||
public void cancelRequest(ImageView view) {
|
||||
cancelExistingRequest(view);
|
||||
}
|
||||
|
||||
/** Cancel any existing requests for the specified {@link Target} instance. */
|
||||
public void cancelRequest(Target target) {
|
||||
cancelExistingRequest(target);
|
||||
}
|
||||
|
||||
/**
|
||||
* Start an image request using the specified URI.
|
||||
* <p>
|
||||
* Passing {@code null} as a {@code uri} will not trigger any request but will set a placeholder,
|
||||
* if one is specified.
|
||||
*
|
||||
* @see #load(File)
|
||||
* @see #load(String)
|
||||
* @see #load(int)
|
||||
*/
|
||||
public RequestCreator load(Uri uri) {
|
||||
return new RequestCreator(this, uri, 0);
|
||||
}
|
||||
|
||||
/**
|
||||
* Start an image request using the specified path. This is a convenience method for calling
|
||||
* {@link #load(Uri)}.
|
||||
* <p>
|
||||
* This path may be a remote URL, file resource (prefixed with {@code file:}), content resource
|
||||
* (prefixed with {@code content:}), or android resource (prefixed with {@code
|
||||
* android.resource:}.
|
||||
* <p>
|
||||
* Passing {@code null} as a {@code path} will not trigger any request but will set a
|
||||
* placeholder, if one is specified.
|
||||
*
|
||||
* @see #load(Uri)
|
||||
* @see #load(File)
|
||||
* @see #load(int)
|
||||
*/
|
||||
public RequestCreator load(String path) {
|
||||
if (path == null) {
|
||||
return new RequestCreator(this, null, 0);
|
||||
}
|
||||
if (path.trim().length() == 0) {
|
||||
throw new IllegalArgumentException("Path must not be empty.");
|
||||
}
|
||||
return load(Uri.parse(path));
|
||||
}
|
||||
|
||||
/**
|
||||
* Start an image request using the specified image file. This is a convenience method for
|
||||
* calling {@link #load(Uri)}.
|
||||
* <p>
|
||||
* Passing {@code null} as a {@code file} will not trigger any request but will set a
|
||||
* placeholder, if one is specified.
|
||||
*
|
||||
* @see #load(Uri)
|
||||
* @see #load(String)
|
||||
* @see #load(int)
|
||||
*/
|
||||
public RequestCreator load(File file) {
|
||||
if (file == null) {
|
||||
return new RequestCreator(this, null, 0);
|
||||
}
|
||||
return load(Uri.fromFile(file));
|
||||
}
|
||||
|
||||
/**
|
||||
* Start an image request using the specified drawable resource ID.
|
||||
*
|
||||
* @see #load(Uri)
|
||||
* @see #load(String)
|
||||
* @see #load(File)
|
||||
*/
|
||||
public RequestCreator load(int resourceId) {
|
||||
if (resourceId == 0) {
|
||||
throw new IllegalArgumentException("Resource ID must not be zero.");
|
||||
}
|
||||
return new RequestCreator(this, null, resourceId);
|
||||
}
|
||||
|
||||
/** {@code true} if debug display, logging, and statistics are enabled. */
|
||||
@SuppressWarnings("UnusedDeclaration") public boolean isDebugging() {
|
||||
return debugging;
|
||||
}
|
||||
|
||||
/** Toggle whether debug display, logging, and statistics are enabled. */
|
||||
@SuppressWarnings("UnusedDeclaration") public void setDebugging(boolean debugging) {
|
||||
this.debugging = debugging;
|
||||
}
|
||||
|
||||
/** Creates a {@link StatsSnapshot} of the current stats for this instance. */
|
||||
@SuppressWarnings("UnusedDeclaration") public StatsSnapshot getSnapshot() {
|
||||
return stats.createSnapshot();
|
||||
}
|
||||
|
||||
/** Stops this instance from accepting further requests. */
|
||||
public void shutdown() {
|
||||
if (this == singleton) {
|
||||
throw new UnsupportedOperationException("Default singleton instance cannot be shutdown.");
|
||||
}
|
||||
if (shutdown) {
|
||||
return;
|
||||
}
|
||||
cache.clear();
|
||||
cleanupThread.shutdown();
|
||||
stats.shutdown();
|
||||
dispatcher.shutdown();
|
||||
for (DeferredRequestCreator deferredRequestCreator : targetToDeferredRequestCreator.values()) {
|
||||
deferredRequestCreator.cancel();
|
||||
}
|
||||
targetToDeferredRequestCreator.clear();
|
||||
shutdown = true;
|
||||
}
|
||||
|
||||
Request transformRequest(Request request) {
|
||||
Request transformed = requestTransformer.transformRequest(request);
|
||||
if (transformed == null) {
|
||||
throw new IllegalStateException("Request transformer "
|
||||
+ requestTransformer.getClass().getCanonicalName()
|
||||
+ " returned null for "
|
||||
+ request);
|
||||
}
|
||||
return transformed;
|
||||
}
|
||||
|
||||
void defer(ImageView view, DeferredRequestCreator request) {
|
||||
targetToDeferredRequestCreator.put(view, request);
|
||||
}
|
||||
|
||||
void enqueueAndSubmit(Action action) {
|
||||
Object target = action.getTarget();
|
||||
if (target != null) {
|
||||
cancelExistingRequest(target);
|
||||
targetToAction.put(target, action);
|
||||
}
|
||||
submit(action);
|
||||
}
|
||||
|
||||
void submit(Action action) {
|
||||
dispatcher.dispatchSubmit(action);
|
||||
}
|
||||
|
||||
Bitmap quickMemoryCacheCheck(String key) {
|
||||
Bitmap cached = cache.get(key);
|
||||
if (cached != null) {
|
||||
stats.dispatchCacheHit();
|
||||
} else {
|
||||
stats.dispatchCacheMiss();
|
||||
}
|
||||
return cached;
|
||||
}
|
||||
|
||||
void complete(BitmapHunter hunter) {
|
||||
List<Action> joined = hunter.getActions();
|
||||
if (joined.isEmpty()) {
|
||||
return;
|
||||
}
|
||||
|
||||
Uri uri = hunter.getData().uri;
|
||||
Exception exception = hunter.getException();
|
||||
Bitmap result = hunter.getResult();
|
||||
LoadedFrom from = hunter.getLoadedFrom();
|
||||
|
||||
for (Action join : joined) {
|
||||
if (join.isCancelled()) {
|
||||
continue;
|
||||
}
|
||||
targetToAction.remove(join.getTarget());
|
||||
if (result != null) {
|
||||
if (from == null) {
|
||||
throw new AssertionError("LoadedFrom cannot be null.");
|
||||
}
|
||||
join.complete(result, from);
|
||||
} else {
|
||||
join.error();
|
||||
}
|
||||
}
|
||||
|
||||
if (listener != null && exception != null) {
|
||||
listener.onImageLoadFailed(this, uri, exception);
|
||||
}
|
||||
}
|
||||
|
||||
private void cancelExistingRequest(Object target) {
|
||||
Action action = targetToAction.remove(target);
|
||||
if (action != null) {
|
||||
action.cancel();
|
||||
dispatcher.dispatchCancel(action);
|
||||
}
|
||||
if (target instanceof ImageView) {
|
||||
ImageView targetImageView = (ImageView) target;
|
||||
DeferredRequestCreator deferredRequestCreator =
|
||||
targetToDeferredRequestCreator.remove(targetImageView);
|
||||
if (deferredRequestCreator != null) {
|
||||
deferredRequestCreator.cancel();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private static class CleanupThread extends Thread {
|
||||
private final ReferenceQueue<?> referenceQueue;
|
||||
private final Handler handler;
|
||||
|
||||
CleanupThread(ReferenceQueue<?> referenceQueue, Handler handler) {
|
||||
this.referenceQueue = referenceQueue;
|
||||
this.handler = handler;
|
||||
setDaemon(true);
|
||||
setName(THREAD_PREFIX + "refQueue");
|
||||
}
|
||||
|
||||
@Override public void run() {
|
||||
Process.setThreadPriority(THREAD_PRIORITY_BACKGROUND);
|
||||
while (true) {
|
||||
try {
|
||||
RequestWeakReference<?> remove = (RequestWeakReference<?>) referenceQueue.remove();
|
||||
handler.sendMessage(handler.obtainMessage(REQUEST_GCED, remove.action));
|
||||
} catch (InterruptedException e) {
|
||||
break;
|
||||
} catch (final Exception e) {
|
||||
handler.post(new Runnable() {
|
||||
@Override public void run() {
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
});
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void shutdown() {
|
||||
interrupt();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* The global default {@link Picasso} instance.
|
||||
* <p>
|
||||
* This instance is automatically initialized with defaults that are suitable to most
|
||||
* implementations.
|
||||
* <ul>
|
||||
* <li>LRU memory cache of 15% the available application RAM</li>
|
||||
* <li>Disk cache of 2% storage space up to 50MB but no less than 5MB. (Note: this is only
|
||||
* available on API 14+ <em>or</em> if you are using a standalone library that provides a disk
|
||||
* cache on all API levels like OkHttp)</li>
|
||||
* <li>Three download threads for disk and network access.</li>
|
||||
* </ul>
|
||||
* <p>
|
||||
* If these settings do not meet the requirements of your application you can construct your own
|
||||
* instance with full control over the configuration by using {@link Picasso.Builder}.
|
||||
*/
|
||||
public static Picasso with(Context context) {
|
||||
if (singleton == null) {
|
||||
singleton = new Builder(context).build();
|
||||
}
|
||||
return singleton;
|
||||
}
|
||||
|
||||
/** Fluent API for creating {@link Picasso} instances. */
|
||||
@SuppressWarnings("UnusedDeclaration") // Public API.
|
||||
public static class Builder {
|
||||
private final Context context;
|
||||
private Downloader downloader;
|
||||
private ExecutorService service;
|
||||
private Cache cache;
|
||||
private Listener listener;
|
||||
private RequestTransformer transformer;
|
||||
private boolean debugging;
|
||||
|
||||
/** Start building a new {@link Picasso} instance. */
|
||||
public Builder(Context context) {
|
||||
if (context == null) {
|
||||
throw new IllegalArgumentException("Context must not be null.");
|
||||
}
|
||||
this.context = context.getApplicationContext();
|
||||
}
|
||||
|
||||
/** Specify the {@link Downloader} that will be used for downloading images. */
|
||||
public Builder downloader(Downloader downloader) {
|
||||
if (downloader == null) {
|
||||
throw new IllegalArgumentException("Downloader must not be null.");
|
||||
}
|
||||
if (this.downloader != null) {
|
||||
throw new IllegalStateException("Downloader already set.");
|
||||
}
|
||||
this.downloader = downloader;
|
||||
return this;
|
||||
}
|
||||
|
||||
/** Specify the executor service for loading images in the background. */
|
||||
public Builder executor(ExecutorService executorService) {
|
||||
if (executorService == null) {
|
||||
throw new IllegalArgumentException("Executor service must not be null.");
|
||||
}
|
||||
if (this.service != null) {
|
||||
throw new IllegalStateException("Executor service already set.");
|
||||
}
|
||||
this.service = executorService;
|
||||
return this;
|
||||
}
|
||||
|
||||
/** Specify the memory cache used for the most recent images. */
|
||||
public Builder memoryCache(Cache memoryCache) {
|
||||
if (memoryCache == null) {
|
||||
throw new IllegalArgumentException("Memory cache must not be null.");
|
||||
}
|
||||
if (this.cache != null) {
|
||||
throw new IllegalStateException("Memory cache already set.");
|
||||
}
|
||||
this.cache = memoryCache;
|
||||
return this;
|
||||
}
|
||||
|
||||
/** Specify a listener for interesting events. */
|
||||
public Builder listener(Listener listener) {
|
||||
if (listener == null) {
|
||||
throw new IllegalArgumentException("Listener must not be null.");
|
||||
}
|
||||
if (this.listener != null) {
|
||||
throw new IllegalStateException("Listener already set.");
|
||||
}
|
||||
this.listener = listener;
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Specify a transformer for all incoming requests.
|
||||
* <p>
|
||||
* <b>NOTE:</b> This is a beta feature. The API is subject to change in a backwards incompatible
|
||||
* way at any time.
|
||||
*/
|
||||
public Builder requestTransformer(RequestTransformer transformer) {
|
||||
if (transformer == null) {
|
||||
throw new IllegalArgumentException("Transformer must not be null.");
|
||||
}
|
||||
if (this.transformer != null) {
|
||||
throw new IllegalStateException("Transformer already set.");
|
||||
}
|
||||
this.transformer = transformer;
|
||||
return this;
|
||||
}
|
||||
|
||||
/** Whether debugging is enabled or not. */
|
||||
public Builder debugging(boolean debugging) {
|
||||
this.debugging = debugging;
|
||||
return this;
|
||||
}
|
||||
|
||||
/** Create the {@link Picasso} instance. */
|
||||
public Picasso build() {
|
||||
Context context = this.context;
|
||||
|
||||
if (downloader == null) {
|
||||
downloader = Utils.createDefaultDownloader(context);
|
||||
}
|
||||
if (cache == null) {
|
||||
cache = new LruCache(context);
|
||||
}
|
||||
if (service == null) {
|
||||
service = new PicassoExecutorService();
|
||||
}
|
||||
if (transformer == null) {
|
||||
transformer = RequestTransformer.IDENTITY;
|
||||
}
|
||||
|
||||
Stats stats = new Stats(cache);
|
||||
|
||||
Dispatcher dispatcher = new Dispatcher(context, service, HANDLER, downloader, cache, stats);
|
||||
|
||||
return new Picasso(context, dispatcher, cache, listener, transformer, stats, debugging);
|
||||
}
|
||||
}
|
||||
|
||||
/** Describes where the image was loaded from. */
|
||||
public enum LoadedFrom {
|
||||
MEMORY(Color.GREEN),
|
||||
DISK(Color.YELLOW),
|
||||
NETWORK(Color.RED);
|
||||
|
||||
final int debugColor;
|
||||
|
||||
private LoadedFrom(int debugColor) {
|
||||
this.debugColor = debugColor;
|
||||
}
|
||||
}
|
||||
}
|
186
mobile/android/thirdparty/com/squareup/picasso/PicassoDrawable.java
vendored
Normal file
186
mobile/android/thirdparty/com/squareup/picasso/PicassoDrawable.java
vendored
Normal file
@ -0,0 +1,186 @@
|
||||
/*
|
||||
* Copyright (C) 2013 Square, Inc.
|
||||
*
|
||||
* 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.squareup.picasso;
|
||||
|
||||
import android.content.Context;
|
||||
import android.content.res.Resources;
|
||||
import android.graphics.Bitmap;
|
||||
import android.graphics.Canvas;
|
||||
import android.graphics.ColorFilter;
|
||||
import android.graphics.Paint;
|
||||
import android.graphics.Path;
|
||||
import android.graphics.Point;
|
||||
import android.graphics.Rect;
|
||||
import android.graphics.drawable.AnimationDrawable;
|
||||
import android.graphics.drawable.BitmapDrawable;
|
||||
import android.graphics.drawable.Drawable;
|
||||
import android.os.SystemClock;
|
||||
import android.widget.ImageView;
|
||||
|
||||
import static android.graphics.Color.WHITE;
|
||||
import static com.squareup.picasso.Picasso.LoadedFrom.MEMORY;
|
||||
|
||||
final class PicassoDrawable extends Drawable {
|
||||
// Only accessed from main thread.
|
||||
private static final Paint DEBUG_PAINT = new Paint();
|
||||
|
||||
private static final float FADE_DURATION = 200f; //ms
|
||||
|
||||
/**
|
||||
* Create or update the drawable on the target {@link ImageView} to display the supplied bitmap
|
||||
* image.
|
||||
*/
|
||||
static void setBitmap(ImageView target, Context context, Bitmap bitmap,
|
||||
Picasso.LoadedFrom loadedFrom, boolean noFade, boolean debugging) {
|
||||
Drawable placeholder = target.getDrawable();
|
||||
if (placeholder instanceof AnimationDrawable) {
|
||||
((AnimationDrawable) placeholder).stop();
|
||||
}
|
||||
PicassoDrawable drawable =
|
||||
new PicassoDrawable(context, placeholder, bitmap, loadedFrom, noFade, debugging);
|
||||
target.setImageDrawable(drawable);
|
||||
}
|
||||
|
||||
/**
|
||||
* Create or update the drawable on the target {@link ImageView} to display the supplied
|
||||
* placeholder image.
|
||||
*/
|
||||
static void setPlaceholder(ImageView target, int placeholderResId, Drawable placeholderDrawable) {
|
||||
if (placeholderResId != 0) {
|
||||
target.setImageResource(placeholderResId);
|
||||
} else {
|
||||
target.setImageDrawable(placeholderDrawable);
|
||||
}
|
||||
if (target.getDrawable() instanceof AnimationDrawable) {
|
||||
((AnimationDrawable) target.getDrawable()).start();
|
||||
}
|
||||
}
|
||||
|
||||
private final boolean debugging;
|
||||
private final float density;
|
||||
private final Picasso.LoadedFrom loadedFrom;
|
||||
final BitmapDrawable image;
|
||||
|
||||
Drawable placeholder;
|
||||
|
||||
long startTimeMillis;
|
||||
boolean animating;
|
||||
int alpha = 0xFF;
|
||||
|
||||
PicassoDrawable(Context context, Drawable placeholder, Bitmap bitmap,
|
||||
Picasso.LoadedFrom loadedFrom, boolean noFade, boolean debugging) {
|
||||
Resources res = context.getResources();
|
||||
|
||||
this.debugging = debugging;
|
||||
this.density = res.getDisplayMetrics().density;
|
||||
|
||||
this.loadedFrom = loadedFrom;
|
||||
|
||||
this.image = new BitmapDrawable(res, bitmap);
|
||||
|
||||
boolean fade = loadedFrom != MEMORY && !noFade;
|
||||
if (fade) {
|
||||
this.placeholder = placeholder;
|
||||
animating = true;
|
||||
startTimeMillis = SystemClock.uptimeMillis();
|
||||
}
|
||||
}
|
||||
|
||||
@Override public void draw(Canvas canvas) {
|
||||
if (!animating) {
|
||||
image.draw(canvas);
|
||||
} else {
|
||||
float normalized = (SystemClock.uptimeMillis() - startTimeMillis) / FADE_DURATION;
|
||||
if (normalized >= 1f) {
|
||||
animating = false;
|
||||
placeholder = null;
|
||||
image.draw(canvas);
|
||||
} else {
|
||||
if (placeholder != null) {
|
||||
placeholder.draw(canvas);
|
||||
}
|
||||
|
||||
int partialAlpha = (int) (alpha * normalized);
|
||||
image.setAlpha(partialAlpha);
|
||||
image.draw(canvas);
|
||||
image.setAlpha(alpha);
|
||||
invalidateSelf();
|
||||
}
|
||||
}
|
||||
|
||||
if (debugging) {
|
||||
drawDebugIndicator(canvas);
|
||||
}
|
||||
}
|
||||
|
||||
@Override public int getIntrinsicWidth() {
|
||||
return image.getIntrinsicWidth();
|
||||
}
|
||||
|
||||
@Override public int getIntrinsicHeight() {
|
||||
return image.getIntrinsicHeight();
|
||||
}
|
||||
|
||||
@Override public void setAlpha(int alpha) {
|
||||
this.alpha = alpha;
|
||||
if (placeholder != null) {
|
||||
placeholder.setAlpha(alpha);
|
||||
}
|
||||
image.setAlpha(alpha);
|
||||
}
|
||||
|
||||
@Override public void setColorFilter(ColorFilter cf) {
|
||||
if (placeholder != null) {
|
||||
placeholder.setColorFilter(cf);
|
||||
}
|
||||
image.setColorFilter(cf);
|
||||
}
|
||||
|
||||
@Override public int getOpacity() {
|
||||
return image.getOpacity();
|
||||
}
|
||||
|
||||
@Override protected void onBoundsChange(Rect bounds) {
|
||||
super.onBoundsChange(bounds);
|
||||
|
||||
image.setBounds(bounds);
|
||||
if (placeholder != null) {
|
||||
placeholder.setBounds(bounds);
|
||||
}
|
||||
}
|
||||
|
||||
private void drawDebugIndicator(Canvas canvas) {
|
||||
DEBUG_PAINT.setColor(WHITE);
|
||||
Path path = getTrianglePath(new Point(0, 0), (int) (16 * density));
|
||||
canvas.drawPath(path, DEBUG_PAINT);
|
||||
|
||||
DEBUG_PAINT.setColor(loadedFrom.debugColor);
|
||||
path = getTrianglePath(new Point(0, 0), (int) (15 * density));
|
||||
canvas.drawPath(path, DEBUG_PAINT);
|
||||
}
|
||||
|
||||
private static Path getTrianglePath(Point p1, int width) {
|
||||
Point p2 = new Point(p1.x + width, p1.y);
|
||||
Point p3 = new Point(p1.x, p1.y + width);
|
||||
|
||||
Path path = new Path();
|
||||
path.moveTo(p1.x, p1.y);
|
||||
path.lineTo(p2.x, p2.y);
|
||||
path.lineTo(p3.x, p3.y);
|
||||
|
||||
return path;
|
||||
}
|
||||
}
|
81
mobile/android/thirdparty/com/squareup/picasso/PicassoExecutorService.java
vendored
Normal file
81
mobile/android/thirdparty/com/squareup/picasso/PicassoExecutorService.java
vendored
Normal file
@ -0,0 +1,81 @@
|
||||
/*
|
||||
* Copyright (C) 2013 Square, Inc.
|
||||
*
|
||||
* 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.squareup.picasso;
|
||||
|
||||
import android.net.ConnectivityManager;
|
||||
import android.net.NetworkInfo;
|
||||
import android.telephony.TelephonyManager;
|
||||
import java.util.concurrent.LinkedBlockingQueue;
|
||||
import java.util.concurrent.ThreadPoolExecutor;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
|
||||
/**
|
||||
* The default {@link java.util.concurrent.ExecutorService} used for new {@link Picasso} instances.
|
||||
* <p/>
|
||||
* Exists as a custom type so that we can differentiate the use of defaults versus a user-supplied
|
||||
* instance.
|
||||
*/
|
||||
class PicassoExecutorService extends ThreadPoolExecutor {
|
||||
private static final int DEFAULT_THREAD_COUNT = 3;
|
||||
|
||||
PicassoExecutorService() {
|
||||
super(DEFAULT_THREAD_COUNT, DEFAULT_THREAD_COUNT, 0, TimeUnit.MILLISECONDS,
|
||||
new LinkedBlockingQueue<Runnable>(), new Utils.PicassoThreadFactory());
|
||||
}
|
||||
|
||||
void adjustThreadCount(NetworkInfo info) {
|
||||
if (info == null || !info.isConnectedOrConnecting()) {
|
||||
setThreadCount(DEFAULT_THREAD_COUNT);
|
||||
return;
|
||||
}
|
||||
switch (info.getType()) {
|
||||
case ConnectivityManager.TYPE_WIFI:
|
||||
case ConnectivityManager.TYPE_WIMAX:
|
||||
case ConnectivityManager.TYPE_ETHERNET:
|
||||
setThreadCount(4);
|
||||
break;
|
||||
case ConnectivityManager.TYPE_MOBILE:
|
||||
switch (info.getSubtype()) {
|
||||
case TelephonyManager.NETWORK_TYPE_LTE: // 4G
|
||||
case TelephonyManager.NETWORK_TYPE_HSPAP:
|
||||
case TelephonyManager.NETWORK_TYPE_EHRPD:
|
||||
setThreadCount(3);
|
||||
break;
|
||||
case TelephonyManager.NETWORK_TYPE_UMTS: // 3G
|
||||
case TelephonyManager.NETWORK_TYPE_CDMA:
|
||||
case TelephonyManager.NETWORK_TYPE_EVDO_0:
|
||||
case TelephonyManager.NETWORK_TYPE_EVDO_A:
|
||||
case TelephonyManager.NETWORK_TYPE_EVDO_B:
|
||||
setThreadCount(2);
|
||||
break;
|
||||
case TelephonyManager.NETWORK_TYPE_GPRS: // 2G
|
||||
case TelephonyManager.NETWORK_TYPE_EDGE:
|
||||
setThreadCount(1);
|
||||
break;
|
||||
default:
|
||||
setThreadCount(DEFAULT_THREAD_COUNT);
|
||||
}
|
||||
break;
|
||||
default:
|
||||
setThreadCount(DEFAULT_THREAD_COUNT);
|
||||
}
|
||||
}
|
||||
|
||||
private void setThreadCount(int threadCount) {
|
||||
setCorePoolSize(threadCount);
|
||||
setMaximumPoolSize(threadCount);
|
||||
}
|
||||
}
|
307
mobile/android/thirdparty/com/squareup/picasso/Request.java
vendored
Normal file
307
mobile/android/thirdparty/com/squareup/picasso/Request.java
vendored
Normal file
@ -0,0 +1,307 @@
|
||||
/*
|
||||
* Copyright (C) 2013 Square, Inc.
|
||||
*
|
||||
* 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.squareup.picasso;
|
||||
|
||||
import android.net.Uri;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
import static java.util.Collections.unmodifiableList;
|
||||
|
||||
/** Immutable data about an image and the transformations that will be applied to it. */
|
||||
public final class Request {
|
||||
/**
|
||||
* The image URI.
|
||||
* <p>
|
||||
* This is mutually exclusive with {@link #resourceId}.
|
||||
*/
|
||||
public final Uri uri;
|
||||
/**
|
||||
* The image resource ID.
|
||||
* <p>
|
||||
* This is mutually exclusive with {@link #uri}.
|
||||
*/
|
||||
public final int resourceId;
|
||||
/** List of custom transformations to be applied after the built-in transformations. */
|
||||
public final List<Transformation> transformations;
|
||||
/** Target image width for resizing. */
|
||||
public final int targetWidth;
|
||||
/** Target image height for resizing. */
|
||||
public final int targetHeight;
|
||||
/**
|
||||
* True if the final image should use the 'centerCrop' scale technique.
|
||||
* <p>
|
||||
* This is mutually exclusive with {@link #centerInside}.
|
||||
*/
|
||||
public final boolean centerCrop;
|
||||
/**
|
||||
* True if the final image should use the 'centerInside' scale technique.
|
||||
* <p>
|
||||
* This is mutually exclusive with {@link #centerCrop}.
|
||||
*/
|
||||
public final boolean centerInside;
|
||||
/** Amount to rotate the image in degrees. */
|
||||
public final float rotationDegrees;
|
||||
/** Rotation pivot on the X axis. */
|
||||
public final float rotationPivotX;
|
||||
/** Rotation pivot on the Y axis. */
|
||||
public final float rotationPivotY;
|
||||
/** Whether or not {@link #rotationPivotX} and {@link #rotationPivotY} are set. */
|
||||
public final boolean hasRotationPivot;
|
||||
|
||||
private Request(Uri uri, int resourceId, List<Transformation> transformations, int targetWidth,
|
||||
int targetHeight, boolean centerCrop, boolean centerInside, float rotationDegrees,
|
||||
float rotationPivotX, float rotationPivotY, boolean hasRotationPivot) {
|
||||
this.uri = uri;
|
||||
this.resourceId = resourceId;
|
||||
if (transformations == null) {
|
||||
this.transformations = null;
|
||||
} else {
|
||||
this.transformations = unmodifiableList(transformations);
|
||||
}
|
||||
this.targetWidth = targetWidth;
|
||||
this.targetHeight = targetHeight;
|
||||
this.centerCrop = centerCrop;
|
||||
this.centerInside = centerInside;
|
||||
this.rotationDegrees = rotationDegrees;
|
||||
this.rotationPivotX = rotationPivotX;
|
||||
this.rotationPivotY = rotationPivotY;
|
||||
this.hasRotationPivot = hasRotationPivot;
|
||||
}
|
||||
|
||||
String getName() {
|
||||
if (uri != null) {
|
||||
return uri.getPath();
|
||||
}
|
||||
return Integer.toHexString(resourceId);
|
||||
}
|
||||
|
||||
public boolean hasSize() {
|
||||
return targetWidth != 0;
|
||||
}
|
||||
|
||||
boolean needsTransformation() {
|
||||
return needsMatrixTransform() || hasCustomTransformations();
|
||||
}
|
||||
|
||||
boolean needsMatrixTransform() {
|
||||
return targetWidth != 0 || rotationDegrees != 0;
|
||||
}
|
||||
|
||||
boolean hasCustomTransformations() {
|
||||
return transformations != null;
|
||||
}
|
||||
|
||||
public Builder buildUpon() {
|
||||
return new Builder(this);
|
||||
}
|
||||
|
||||
/** Builder for creating {@link Request} instances. */
|
||||
public static final class Builder {
|
||||
private Uri uri;
|
||||
private int resourceId;
|
||||
private int targetWidth;
|
||||
private int targetHeight;
|
||||
private boolean centerCrop;
|
||||
private boolean centerInside;
|
||||
private float rotationDegrees;
|
||||
private float rotationPivotX;
|
||||
private float rotationPivotY;
|
||||
private boolean hasRotationPivot;
|
||||
private List<Transformation> transformations;
|
||||
|
||||
/** Start building a request using the specified {@link Uri}. */
|
||||
public Builder(Uri uri) {
|
||||
setUri(uri);
|
||||
}
|
||||
|
||||
/** Start building a request using the specified resource ID. */
|
||||
public Builder(int resourceId) {
|
||||
setResourceId(resourceId);
|
||||
}
|
||||
|
||||
Builder(Uri uri, int resourceId) {
|
||||
this.uri = uri;
|
||||
this.resourceId = resourceId;
|
||||
}
|
||||
|
||||
private Builder(Request request) {
|
||||
uri = request.uri;
|
||||
resourceId = request.resourceId;
|
||||
targetWidth = request.targetWidth;
|
||||
targetHeight = request.targetHeight;
|
||||
centerCrop = request.centerCrop;
|
||||
centerInside = request.centerInside;
|
||||
rotationDegrees = request.rotationDegrees;
|
||||
rotationPivotX = request.rotationPivotX;
|
||||
rotationPivotY = request.rotationPivotY;
|
||||
hasRotationPivot = request.hasRotationPivot;
|
||||
if (request.transformations != null) {
|
||||
transformations = new ArrayList<Transformation>(request.transformations);
|
||||
}
|
||||
}
|
||||
|
||||
boolean hasImage() {
|
||||
return uri != null || resourceId != 0;
|
||||
}
|
||||
|
||||
boolean hasSize() {
|
||||
return targetWidth != 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the target image Uri.
|
||||
* <p>
|
||||
* This will clear an image resource ID if one is set.
|
||||
*/
|
||||
public Builder setUri(Uri uri) {
|
||||
if (uri == null) {
|
||||
throw new IllegalArgumentException("Image URI may not be null.");
|
||||
}
|
||||
this.uri = uri;
|
||||
this.resourceId = 0;
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the target image resource ID.
|
||||
* <p>
|
||||
* This will clear an image Uri if one is set.
|
||||
*/
|
||||
public Builder setResourceId(int resourceId) {
|
||||
if (resourceId == 0) {
|
||||
throw new IllegalArgumentException("Image resource ID may not be 0.");
|
||||
}
|
||||
this.resourceId = resourceId;
|
||||
this.uri = null;
|
||||
return this;
|
||||
}
|
||||
|
||||
/** Resize the image to the specified size in pixels. */
|
||||
public Builder resize(int targetWidth, int targetHeight) {
|
||||
if (targetWidth <= 0) {
|
||||
throw new IllegalArgumentException("Width must be positive number.");
|
||||
}
|
||||
if (targetHeight <= 0) {
|
||||
throw new IllegalArgumentException("Height must be positive number.");
|
||||
}
|
||||
this.targetWidth = targetWidth;
|
||||
this.targetHeight = targetHeight;
|
||||
return this;
|
||||
}
|
||||
|
||||
/** Clear the resize transformation, if any. This will also clear center crop/inside if set. */
|
||||
public Builder clearResize() {
|
||||
targetWidth = 0;
|
||||
targetHeight = 0;
|
||||
centerCrop = false;
|
||||
centerInside = false;
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Crops an image inside of the bounds specified by {@link #resize(int, int)} rather than
|
||||
* distorting the aspect ratio. This cropping technique scales the image so that it fills the
|
||||
* requested bounds and then crops the extra.
|
||||
*/
|
||||
public Builder centerCrop() {
|
||||
if (centerInside) {
|
||||
throw new IllegalStateException("Center crop can not be used after calling centerInside");
|
||||
}
|
||||
centerCrop = true;
|
||||
return this;
|
||||
}
|
||||
|
||||
/** Clear the center crop transformation flag, if set. */
|
||||
public Builder clearCenterCrop() {
|
||||
centerCrop = false;
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Centers an image inside of the bounds specified by {@link #resize(int, int)}. This scales
|
||||
* the image so that both dimensions are equal to or less than the requested bounds.
|
||||
*/
|
||||
public Builder centerInside() {
|
||||
if (centerCrop) {
|
||||
throw new IllegalStateException("Center inside can not be used after calling centerCrop");
|
||||
}
|
||||
centerInside = true;
|
||||
return this;
|
||||
}
|
||||
|
||||
/** Clear the center inside transformation flag, if set. */
|
||||
public Builder clearCenterInside() {
|
||||
centerInside = false;
|
||||
return this;
|
||||
}
|
||||
|
||||
/** Rotate the image by the specified degrees. */
|
||||
public Builder rotate(float degrees) {
|
||||
rotationDegrees = degrees;
|
||||
return this;
|
||||
}
|
||||
|
||||
/** Rotate the image by the specified degrees around a pivot point. */
|
||||
public Builder rotate(float degrees, float pivotX, float pivotY) {
|
||||
rotationDegrees = degrees;
|
||||
rotationPivotX = pivotX;
|
||||
rotationPivotY = pivotY;
|
||||
hasRotationPivot = true;
|
||||
return this;
|
||||
}
|
||||
|
||||
/** Clear the rotation transformation, if any. */
|
||||
public Builder clearRotation() {
|
||||
rotationDegrees = 0;
|
||||
rotationPivotX = 0;
|
||||
rotationPivotY = 0;
|
||||
hasRotationPivot = false;
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Add a custom transformation to be applied to the image.
|
||||
* <p/>
|
||||
* Custom transformations will always be run after the built-in transformations.
|
||||
*/
|
||||
public Builder transform(Transformation transformation) {
|
||||
if (transformation == null) {
|
||||
throw new IllegalArgumentException("Transformation must not be null.");
|
||||
}
|
||||
if (transformations == null) {
|
||||
transformations = new ArrayList<Transformation>(2);
|
||||
}
|
||||
transformations.add(transformation);
|
||||
return this;
|
||||
}
|
||||
|
||||
/** Create the immutable {@link Request} object. */
|
||||
public Request build() {
|
||||
if (centerInside && centerCrop) {
|
||||
throw new IllegalStateException("Center crop and center inside can not be used together.");
|
||||
}
|
||||
if (centerCrop && targetWidth == 0) {
|
||||
throw new IllegalStateException("Center crop requires calling resize.");
|
||||
}
|
||||
if (centerInside && targetWidth == 0) {
|
||||
throw new IllegalStateException("Center inside requires calling resize.");
|
||||
}
|
||||
return new Request(uri, resourceId, transformations, targetWidth, targetHeight, centerCrop,
|
||||
centerInside, rotationDegrees, rotationPivotX, rotationPivotY, hasRotationPivot);
|
||||
}
|
||||
}
|
||||
}
|
374
mobile/android/thirdparty/com/squareup/picasso/RequestCreator.java
vendored
Normal file
374
mobile/android/thirdparty/com/squareup/picasso/RequestCreator.java
vendored
Normal file
@ -0,0 +1,374 @@
|
||||
/*
|
||||
* Copyright (C) 2013 Square, Inc.
|
||||
*
|
||||
* 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.squareup.picasso;
|
||||
|
||||
import android.content.res.Resources;
|
||||
import android.graphics.Bitmap;
|
||||
import android.graphics.drawable.Drawable;
|
||||
import android.net.Uri;
|
||||
import android.widget.ImageView;
|
||||
import java.io.IOException;
|
||||
|
||||
import static com.squareup.picasso.BitmapHunter.forRequest;
|
||||
import static com.squareup.picasso.Picasso.LoadedFrom.MEMORY;
|
||||
import static com.squareup.picasso.Utils.checkNotMain;
|
||||
import static com.squareup.picasso.Utils.createKey;
|
||||
|
||||
/** Fluent API for building an image download request. */
|
||||
@SuppressWarnings("UnusedDeclaration") // Public API.
|
||||
public class RequestCreator {
|
||||
private final Picasso picasso;
|
||||
private final Request.Builder data;
|
||||
|
||||
private boolean skipMemoryCache;
|
||||
private boolean noFade;
|
||||
private boolean deferred;
|
||||
private int placeholderResId;
|
||||
private Drawable placeholderDrawable;
|
||||
private int errorResId;
|
||||
private Drawable errorDrawable;
|
||||
|
||||
RequestCreator(Picasso picasso, Uri uri, int resourceId) {
|
||||
if (picasso.shutdown) {
|
||||
throw new IllegalStateException(
|
||||
"Picasso instance already shut down. Cannot submit new requests.");
|
||||
}
|
||||
this.picasso = picasso;
|
||||
this.data = new Request.Builder(uri, resourceId);
|
||||
}
|
||||
|
||||
/**
|
||||
* A placeholder drawable to be used while the image is being loaded. If the requested image is
|
||||
* not immediately available in the memory cache then this resource will be set on the target
|
||||
* {@link ImageView}.
|
||||
*/
|
||||
public RequestCreator placeholder(int placeholderResId) {
|
||||
if (placeholderResId == 0) {
|
||||
throw new IllegalArgumentException("Placeholder image resource invalid.");
|
||||
}
|
||||
if (placeholderDrawable != null) {
|
||||
throw new IllegalStateException("Placeholder image already set.");
|
||||
}
|
||||
this.placeholderResId = placeholderResId;
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* A placeholder drawable to be used while the image is being loaded. If the requested image is
|
||||
* not immediately available in the memory cache then this resource will be set on the target
|
||||
* {@link ImageView}.
|
||||
* <p>
|
||||
* If you are not using a placeholder image but want to clear an existing image (such as when
|
||||
* used in an {@link android.widget.Adapter adapter}), pass in {@code null}.
|
||||
*/
|
||||
public RequestCreator placeholder(Drawable placeholderDrawable) {
|
||||
if (placeholderResId != 0) {
|
||||
throw new IllegalStateException("Placeholder image already set.");
|
||||
}
|
||||
this.placeholderDrawable = placeholderDrawable;
|
||||
return this;
|
||||
}
|
||||
|
||||
/** An error drawable to be used if the request image could not be loaded. */
|
||||
public RequestCreator error(int errorResId) {
|
||||
if (errorResId == 0) {
|
||||
throw new IllegalArgumentException("Error image resource invalid.");
|
||||
}
|
||||
if (errorDrawable != null) {
|
||||
throw new IllegalStateException("Error image already set.");
|
||||
}
|
||||
this.errorResId = errorResId;
|
||||
return this;
|
||||
}
|
||||
|
||||
/** An error drawable to be used if the request image could not be loaded. */
|
||||
public RequestCreator error(Drawable errorDrawable) {
|
||||
if (errorDrawable == null) {
|
||||
throw new IllegalArgumentException("Error image may not be null.");
|
||||
}
|
||||
if (errorResId != 0) {
|
||||
throw new IllegalStateException("Error image already set.");
|
||||
}
|
||||
this.errorDrawable = errorDrawable;
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Attempt to resize the image to fit exactly into the target {@link ImageView}'s bounds. This
|
||||
* will result in delayed execution of the request until the {@link ImageView} has been measured.
|
||||
* <p/>
|
||||
* <em>Note:</em> This method works only when your target is an {@link ImageView}.
|
||||
*/
|
||||
public RequestCreator fit() {
|
||||
deferred = true;
|
||||
return this;
|
||||
}
|
||||
|
||||
/** Internal use only. Used by {@link DeferredRequestCreator}. */
|
||||
RequestCreator unfit() {
|
||||
deferred = false;
|
||||
return this;
|
||||
}
|
||||
|
||||
/** Resize the image to the specified dimension size. */
|
||||
public RequestCreator resizeDimen(int targetWidthResId, int targetHeightResId) {
|
||||
Resources resources = picasso.context.getResources();
|
||||
int targetWidth = resources.getDimensionPixelSize(targetWidthResId);
|
||||
int targetHeight = resources.getDimensionPixelSize(targetHeightResId);
|
||||
return resize(targetWidth, targetHeight);
|
||||
}
|
||||
|
||||
/** Resize the image to the specified size in pixels. */
|
||||
public RequestCreator resize(int targetWidth, int targetHeight) {
|
||||
data.resize(targetWidth, targetHeight);
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Crops an image inside of the bounds specified by {@link #resize(int, int)} rather than
|
||||
* distorting the aspect ratio. This cropping technique scales the image so that it fills the
|
||||
* requested bounds and then crops the extra.
|
||||
*/
|
||||
public RequestCreator centerCrop() {
|
||||
data.centerCrop();
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Centers an image inside of the bounds specified by {@link #resize(int, int)}. This scales
|
||||
* the image so that both dimensions are equal to or less than the requested bounds.
|
||||
*/
|
||||
public RequestCreator centerInside() {
|
||||
data.centerInside();
|
||||
return this;
|
||||
}
|
||||
|
||||
/** Rotate the image by the specified degrees. */
|
||||
public RequestCreator rotate(float degrees) {
|
||||
data.rotate(degrees);
|
||||
return this;
|
||||
}
|
||||
|
||||
/** Rotate the image by the specified degrees around a pivot point. */
|
||||
public RequestCreator rotate(float degrees, float pivotX, float pivotY) {
|
||||
data.rotate(degrees, pivotX, pivotY);
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Add a custom transformation to be applied to the image.
|
||||
* <p/>
|
||||
* Custom transformations will always be run after the built-in transformations.
|
||||
*/
|
||||
// TODO show example of calling resize after a transform in the javadoc
|
||||
public RequestCreator transform(Transformation transformation) {
|
||||
data.transform(transformation);
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Indicate that this action should not use the memory cache for attempting to load or save the
|
||||
* image. This can be useful when you know an image will only ever be used once (e.g., loading
|
||||
* an image from the filesystem and uploading to a remote server).
|
||||
*/
|
||||
public RequestCreator skipMemoryCache() {
|
||||
skipMemoryCache = true;
|
||||
return this;
|
||||
}
|
||||
|
||||
/** Disable brief fade in of images loaded from the disk cache or network. */
|
||||
public RequestCreator noFade() {
|
||||
noFade = true;
|
||||
return this;
|
||||
}
|
||||
|
||||
/** Synchronously fulfill this request. Must not be called from the main thread. */
|
||||
public Bitmap get() throws IOException {
|
||||
checkNotMain();
|
||||
if (deferred) {
|
||||
throw new IllegalStateException("Fit cannot be used with get.");
|
||||
}
|
||||
if (!data.hasImage()) {
|
||||
return null;
|
||||
}
|
||||
|
||||
Request finalData = picasso.transformRequest(data.build());
|
||||
String key = createKey(finalData);
|
||||
|
||||
Action action = new GetAction(picasso, finalData, skipMemoryCache, key);
|
||||
return forRequest(picasso.context, picasso, picasso.dispatcher, picasso.cache, picasso.stats,
|
||||
action, picasso.dispatcher.downloader).hunt();
|
||||
}
|
||||
|
||||
/**
|
||||
* Asynchronously fulfills the request without a {@link ImageView} or {@link Target}. This is
|
||||
* useful when you want to warm up the cache with an image.
|
||||
*/
|
||||
public void fetch() {
|
||||
if (deferred) {
|
||||
throw new IllegalStateException("Fit cannot be used with fetch.");
|
||||
}
|
||||
if (data.hasImage()) {
|
||||
Request finalData = picasso.transformRequest(data.build());
|
||||
String key = createKey(finalData);
|
||||
|
||||
Action action = new FetchAction(picasso, finalData, skipMemoryCache, key);
|
||||
picasso.enqueueAndSubmit(action);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Asynchronously fulfills the request into the specified {@link Target}. In most cases, you
|
||||
* should use this when you are dealing with a custom {@link android.view.View View} or view
|
||||
* holder which should implement the {@link Target} interface.
|
||||
* <p>
|
||||
* Implementing on a {@link android.view.View View}:
|
||||
* <blockquote><pre>
|
||||
* public class ProfileView extends FrameLayout implements Target {
|
||||
* {@literal @}Override public void onBitmapLoaded(Bitmap bitmap, LoadedFrom from) {
|
||||
* setBackgroundDrawable(new BitmapDrawable(bitmap));
|
||||
* }
|
||||
*
|
||||
* {@literal @}Override public void onBitmapFailed() {
|
||||
* setBackgroundResource(R.drawable.profile_error);
|
||||
* }
|
||||
* }
|
||||
* </pre></blockquote>
|
||||
* Implementing on a view holder object for use inside of an adapter:
|
||||
* <blockquote><pre>
|
||||
* public class ViewHolder implements Target {
|
||||
* public FrameLayout frame;
|
||||
* public TextView name;
|
||||
*
|
||||
* {@literal @}Override public void onBitmapLoaded(Bitmap bitmap, LoadedFrom from) {
|
||||
* frame.setBackgroundDrawable(new BitmapDrawable(bitmap));
|
||||
* }
|
||||
*
|
||||
* {@literal @}Override public void onBitmapFailed() {
|
||||
* frame.setBackgroundResource(R.drawable.profile_error);
|
||||
* }
|
||||
* }
|
||||
* </pre></blockquote>
|
||||
* <p>
|
||||
* <em>Note:</em> This method keeps a weak reference to the {@link Target} instance and will be
|
||||
* garbage collected if you do not keep a strong reference to it. To receive callbacks when an
|
||||
* image is loaded use {@link #into(android.widget.ImageView, Callback)}.
|
||||
*/
|
||||
public void into(Target target) {
|
||||
if (target == null) {
|
||||
throw new IllegalArgumentException("Target must not be null.");
|
||||
}
|
||||
if (deferred) {
|
||||
throw new IllegalStateException("Fit cannot be used with a Target.");
|
||||
}
|
||||
|
||||
Drawable drawable =
|
||||
placeholderResId != 0 ? picasso.context.getResources().getDrawable(placeholderResId)
|
||||
: placeholderDrawable;
|
||||
|
||||
if (!data.hasImage()) {
|
||||
picasso.cancelRequest(target);
|
||||
target.onPrepareLoad(drawable);
|
||||
return;
|
||||
}
|
||||
|
||||
Request finalData = picasso.transformRequest(data.build());
|
||||
String requestKey = createKey(finalData);
|
||||
|
||||
if (!skipMemoryCache) {
|
||||
Bitmap bitmap = picasso.quickMemoryCacheCheck(requestKey);
|
||||
if (bitmap != null) {
|
||||
picasso.cancelRequest(target);
|
||||
target.onBitmapLoaded(bitmap, MEMORY);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
target.onPrepareLoad(drawable);
|
||||
|
||||
Action action = new TargetAction(picasso, target, finalData, skipMemoryCache, requestKey);
|
||||
picasso.enqueueAndSubmit(action);
|
||||
}
|
||||
|
||||
/**
|
||||
* Asynchronously fulfills the request into the specified {@link ImageView}.
|
||||
* <p/>
|
||||
* <em>Note:</em> This method keeps a weak reference to the {@link ImageView} instance and will
|
||||
* automatically support object recycling.
|
||||
*/
|
||||
public void into(ImageView target) {
|
||||
into(target, null);
|
||||
}
|
||||
|
||||
/**
|
||||
* Asynchronously fulfills the request into the specified {@link ImageView} and invokes the
|
||||
* target {@link Callback} if it's not {@code null}.
|
||||
* <p/>
|
||||
* <em>Note:</em> The {@link Callback} param is a strong reference and will prevent your
|
||||
* {@link android.app.Activity} or {@link android.app.Fragment} from being garbage collected. If
|
||||
* you use this method, it is <b>strongly</b> recommended you invoke an adjacent
|
||||
* {@link Picasso#cancelRequest(android.widget.ImageView)} call to prevent temporary leaking.
|
||||
*/
|
||||
public void into(ImageView target, Callback callback) {
|
||||
if (target == null) {
|
||||
throw new IllegalArgumentException("Target must not be null.");
|
||||
}
|
||||
|
||||
if (!data.hasImage()) {
|
||||
picasso.cancelRequest(target);
|
||||
PicassoDrawable.setPlaceholder(target, placeholderResId, placeholderDrawable);
|
||||
return;
|
||||
}
|
||||
|
||||
if (deferred) {
|
||||
if (data.hasSize()) {
|
||||
throw new IllegalStateException("Fit cannot be used with resize.");
|
||||
}
|
||||
int measuredWidth = target.getMeasuredWidth();
|
||||
int measuredHeight = target.getMeasuredHeight();
|
||||
if (measuredWidth == 0 || measuredHeight == 0) {
|
||||
PicassoDrawable.setPlaceholder(target, placeholderResId, placeholderDrawable);
|
||||
picasso.defer(target, new DeferredRequestCreator(this, target, callback));
|
||||
return;
|
||||
}
|
||||
data.resize(measuredWidth, measuredHeight);
|
||||
}
|
||||
|
||||
Request finalData = picasso.transformRequest(data.build());
|
||||
String requestKey = createKey(finalData);
|
||||
|
||||
if (!skipMemoryCache) {
|
||||
Bitmap bitmap = picasso.quickMemoryCacheCheck(requestKey);
|
||||
if (bitmap != null) {
|
||||
picasso.cancelRequest(target);
|
||||
PicassoDrawable.setBitmap(target, picasso.context, bitmap, MEMORY, noFade,
|
||||
picasso.debugging);
|
||||
if (callback != null) {
|
||||
callback.onSuccess();
|
||||
}
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
PicassoDrawable.setPlaceholder(target, placeholderResId, placeholderDrawable);
|
||||
|
||||
Action action =
|
||||
new ImageViewAction(picasso, target, finalData, skipMemoryCache, noFade, errorResId,
|
||||
errorDrawable, requestKey, callback);
|
||||
|
||||
picasso.enqueueAndSubmit(action);
|
||||
}
|
||||
}
|
55
mobile/android/thirdparty/com/squareup/picasso/ResourceBitmapHunter.java
vendored
Normal file
55
mobile/android/thirdparty/com/squareup/picasso/ResourceBitmapHunter.java
vendored
Normal file
@ -0,0 +1,55 @@
|
||||
/*
|
||||
* Copyright (C) 2013 Square, Inc.
|
||||
*
|
||||
* 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.squareup.picasso;
|
||||
|
||||
import android.content.Context;
|
||||
import android.content.res.Resources;
|
||||
import android.graphics.Bitmap;
|
||||
import android.graphics.BitmapFactory;
|
||||
import java.io.IOException;
|
||||
|
||||
import static com.squareup.picasso.Picasso.LoadedFrom.DISK;
|
||||
|
||||
class ResourceBitmapHunter extends BitmapHunter {
|
||||
private final Context context;
|
||||
|
||||
ResourceBitmapHunter(Context context, Picasso picasso, Dispatcher dispatcher, Cache cache,
|
||||
Stats stats, Action action) {
|
||||
super(picasso, dispatcher, cache, stats, action);
|
||||
this.context = context;
|
||||
}
|
||||
|
||||
@Override Bitmap decode(Request data) throws IOException {
|
||||
Resources res = Utils.getResources(context, data);
|
||||
int id = Utils.getResourceId(res, data);
|
||||
return decodeResource(res, id, data);
|
||||
}
|
||||
|
||||
@Override Picasso.LoadedFrom getLoadedFrom() {
|
||||
return DISK;
|
||||
}
|
||||
|
||||
private Bitmap decodeResource(Resources resources, int id, Request data) {
|
||||
BitmapFactory.Options bitmapOptions = null;
|
||||
if (data.hasSize()) {
|
||||
bitmapOptions = new BitmapFactory.Options();
|
||||
bitmapOptions.inJustDecodeBounds = true;
|
||||
BitmapFactory.decodeResource(resources, id, bitmapOptions);
|
||||
calculateInSampleSize(data.targetWidth, data.targetHeight, bitmapOptions);
|
||||
}
|
||||
return BitmapFactory.decodeResource(resources, id, bitmapOptions);
|
||||
}
|
||||
}
|
143
mobile/android/thirdparty/com/squareup/picasso/Stats.java
vendored
Normal file
143
mobile/android/thirdparty/com/squareup/picasso/Stats.java
vendored
Normal file
@ -0,0 +1,143 @@
|
||||
/*
|
||||
* Copyright (C) 2013 Square, Inc.
|
||||
*
|
||||
* 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.squareup.picasso;
|
||||
|
||||
import android.graphics.Bitmap;
|
||||
import android.os.Handler;
|
||||
import android.os.HandlerThread;
|
||||
import android.os.Looper;
|
||||
import android.os.Message;
|
||||
|
||||
import static android.os.Process.THREAD_PRIORITY_BACKGROUND;
|
||||
|
||||
class Stats {
|
||||
private static final int CACHE_HIT = 0;
|
||||
private static final int CACHE_MISS = 1;
|
||||
private static final int BITMAP_DECODE_FINISHED = 2;
|
||||
private static final int BITMAP_TRANSFORMED_FINISHED = 3;
|
||||
|
||||
private static final String STATS_THREAD_NAME = Utils.THREAD_PREFIX + "Stats";
|
||||
|
||||
final HandlerThread statsThread;
|
||||
final Cache cache;
|
||||
final Handler handler;
|
||||
|
||||
long cacheHits;
|
||||
long cacheMisses;
|
||||
long totalOriginalBitmapSize;
|
||||
long totalTransformedBitmapSize;
|
||||
long averageOriginalBitmapSize;
|
||||
long averageTransformedBitmapSize;
|
||||
int originalBitmapCount;
|
||||
int transformedBitmapCount;
|
||||
|
||||
Stats(Cache cache) {
|
||||
this.cache = cache;
|
||||
this.statsThread = new HandlerThread(STATS_THREAD_NAME, THREAD_PRIORITY_BACKGROUND);
|
||||
this.statsThread.start();
|
||||
this.handler = new StatsHandler(statsThread.getLooper(), this);
|
||||
}
|
||||
|
||||
void dispatchBitmapDecoded(Bitmap bitmap) {
|
||||
processBitmap(bitmap, BITMAP_DECODE_FINISHED);
|
||||
}
|
||||
|
||||
void dispatchBitmapTransformed(Bitmap bitmap) {
|
||||
processBitmap(bitmap, BITMAP_TRANSFORMED_FINISHED);
|
||||
}
|
||||
|
||||
void dispatchCacheHit() {
|
||||
handler.sendEmptyMessage(CACHE_HIT);
|
||||
}
|
||||
|
||||
void dispatchCacheMiss() {
|
||||
handler.sendEmptyMessage(CACHE_MISS);
|
||||
}
|
||||
|
||||
void shutdown() {
|
||||
statsThread.quit();
|
||||
}
|
||||
|
||||
void performCacheHit() {
|
||||
cacheHits++;
|
||||
}
|
||||
|
||||
void performCacheMiss() {
|
||||
cacheMisses++;
|
||||
}
|
||||
|
||||
void performBitmapDecoded(long size) {
|
||||
originalBitmapCount++;
|
||||
totalOriginalBitmapSize += size;
|
||||
averageOriginalBitmapSize = getAverage(originalBitmapCount, totalOriginalBitmapSize);
|
||||
}
|
||||
|
||||
void performBitmapTransformed(long size) {
|
||||
transformedBitmapCount++;
|
||||
totalTransformedBitmapSize += size;
|
||||
averageTransformedBitmapSize = getAverage(originalBitmapCount, totalTransformedBitmapSize);
|
||||
}
|
||||
|
||||
synchronized StatsSnapshot createSnapshot() {
|
||||
return new StatsSnapshot(cache.maxSize(), cache.size(), cacheHits, cacheMisses,
|
||||
totalOriginalBitmapSize, totalTransformedBitmapSize, averageOriginalBitmapSize,
|
||||
averageTransformedBitmapSize, originalBitmapCount, transformedBitmapCount,
|
||||
System.currentTimeMillis());
|
||||
}
|
||||
|
||||
private void processBitmap(Bitmap bitmap, int what) {
|
||||
// Never send bitmaps to the handler as they could be recycled before we process them.
|
||||
int bitmapSize = Utils.getBitmapBytes(bitmap);
|
||||
handler.sendMessage(handler.obtainMessage(what, bitmapSize, 0));
|
||||
}
|
||||
|
||||
private static long getAverage(int count, long totalSize) {
|
||||
return totalSize / count;
|
||||
}
|
||||
|
||||
private static class StatsHandler extends Handler {
|
||||
|
||||
private final Stats stats;
|
||||
|
||||
public StatsHandler(Looper looper, Stats stats) {
|
||||
super(looper);
|
||||
this.stats = stats;
|
||||
}
|
||||
|
||||
@Override public void handleMessage(final Message msg) {
|
||||
switch (msg.what) {
|
||||
case CACHE_HIT:
|
||||
stats.performCacheHit();
|
||||
break;
|
||||
case CACHE_MISS:
|
||||
stats.performCacheMiss();
|
||||
break;
|
||||
case BITMAP_DECODE_FINISHED:
|
||||
stats.performBitmapDecoded(msg.arg1);
|
||||
break;
|
||||
case BITMAP_TRANSFORMED_FINISHED:
|
||||
stats.performBitmapTransformed(msg.arg1);
|
||||
break;
|
||||
default:
|
||||
Picasso.HANDLER.post(new Runnable() {
|
||||
@Override public void run() {
|
||||
throw new AssertionError("Unhandled stats message." + msg.what);
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
120
mobile/android/thirdparty/com/squareup/picasso/StatsSnapshot.java
vendored
Normal file
120
mobile/android/thirdparty/com/squareup/picasso/StatsSnapshot.java
vendored
Normal file
@ -0,0 +1,120 @@
|
||||
/*
|
||||
* Copyright (C) 2013 Square, Inc.
|
||||
*
|
||||
* 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.squareup.picasso;
|
||||
|
||||
import android.util.Log;
|
||||
import java.io.PrintWriter;
|
||||
import java.io.StringWriter;
|
||||
|
||||
/** Represents all stats for a {@link Picasso} instance at a single point in time. */
|
||||
public class StatsSnapshot {
|
||||
private static final String TAG = "Picasso";
|
||||
|
||||
public final int maxSize;
|
||||
public final int size;
|
||||
public final long cacheHits;
|
||||
public final long cacheMisses;
|
||||
public final long totalOriginalBitmapSize;
|
||||
public final long totalTransformedBitmapSize;
|
||||
public final long averageOriginalBitmapSize;
|
||||
public final long averageTransformedBitmapSize;
|
||||
public final int originalBitmapCount;
|
||||
public final int transformedBitmapCount;
|
||||
|
||||
public final long timeStamp;
|
||||
|
||||
public StatsSnapshot(int maxSize, int size, long cacheHits, long cacheMisses,
|
||||
long totalOriginalBitmapSize, long totalTransformedBitmapSize, long averageOriginalBitmapSize,
|
||||
long averageTransformedBitmapSize, int originalBitmapCount, int transformedBitmapCount,
|
||||
long timeStamp) {
|
||||
this.maxSize = maxSize;
|
||||
this.size = size;
|
||||
this.cacheHits = cacheHits;
|
||||
this.cacheMisses = cacheMisses;
|
||||
this.totalOriginalBitmapSize = totalOriginalBitmapSize;
|
||||
this.totalTransformedBitmapSize = totalTransformedBitmapSize;
|
||||
this.averageOriginalBitmapSize = averageOriginalBitmapSize;
|
||||
this.averageTransformedBitmapSize = averageTransformedBitmapSize;
|
||||
this.originalBitmapCount = originalBitmapCount;
|
||||
this.transformedBitmapCount = transformedBitmapCount;
|
||||
this.timeStamp = timeStamp;
|
||||
}
|
||||
|
||||
/** Prints out this {@link StatsSnapshot} into log. */
|
||||
public void dump() {
|
||||
StringWriter logWriter = new StringWriter();
|
||||
dump(new PrintWriter(logWriter));
|
||||
Log.i(TAG, logWriter.toString());
|
||||
}
|
||||
|
||||
/** Prints out this {@link StatsSnapshot} with the the provided {@link PrintWriter}. */
|
||||
public void dump(PrintWriter writer) {
|
||||
writer.println("===============BEGIN PICASSO STATS ===============");
|
||||
writer.println("Memory Cache Stats");
|
||||
writer.print(" Max Cache Size: ");
|
||||
writer.println(maxSize);
|
||||
writer.print(" Cache Size: ");
|
||||
writer.println(size);
|
||||
writer.print(" Cache % Full: ");
|
||||
writer.println((int) Math.ceil((float) size / maxSize * 100));
|
||||
writer.print(" Cache Hits: ");
|
||||
writer.println(cacheHits);
|
||||
writer.print(" Cache Misses: ");
|
||||
writer.println(cacheMisses);
|
||||
writer.println("Bitmap Stats");
|
||||
writer.print(" Total Bitmaps Decoded: ");
|
||||
writer.println(originalBitmapCount);
|
||||
writer.print(" Total Bitmap Size: ");
|
||||
writer.println(totalOriginalBitmapSize);
|
||||
writer.print(" Total Transformed Bitmaps: ");
|
||||
writer.println(transformedBitmapCount);
|
||||
writer.print(" Total Transformed Bitmap Size: ");
|
||||
writer.println(totalTransformedBitmapSize);
|
||||
writer.print(" Average Bitmap Size: ");
|
||||
writer.println(averageOriginalBitmapSize);
|
||||
writer.print(" Average Transformed Bitmap Size: ");
|
||||
writer.println(averageTransformedBitmapSize);
|
||||
writer.println("===============END PICASSO STATS ===============");
|
||||
writer.flush();
|
||||
}
|
||||
|
||||
@Override public String toString() {
|
||||
return "StatsSnapshot{"
|
||||
+ "maxSize="
|
||||
+ maxSize
|
||||
+ ", size="
|
||||
+ size
|
||||
+ ", cacheHits="
|
||||
+ cacheHits
|
||||
+ ", cacheMisses="
|
||||
+ cacheMisses
|
||||
+ ", totalOriginalBitmapSize="
|
||||
+ totalOriginalBitmapSize
|
||||
+ ", totalTransformedBitmapSize="
|
||||
+ totalTransformedBitmapSize
|
||||
+ ", averageOriginalBitmapSize="
|
||||
+ averageOriginalBitmapSize
|
||||
+ ", averageTransformedBitmapSize="
|
||||
+ averageTransformedBitmapSize
|
||||
+ ", originalBitmapCount="
|
||||
+ originalBitmapCount
|
||||
+ ", transformedBitmapCount="
|
||||
+ transformedBitmapCount
|
||||
+ ", timeStamp="
|
||||
+ timeStamp
|
||||
+ '}';
|
||||
}
|
||||
}
|
45
mobile/android/thirdparty/com/squareup/picasso/Target.java
vendored
Normal file
45
mobile/android/thirdparty/com/squareup/picasso/Target.java
vendored
Normal file
@ -0,0 +1,45 @@
|
||||
/*
|
||||
* Copyright (C) 2013 Square, Inc.
|
||||
*
|
||||
* 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.squareup.picasso;
|
||||
|
||||
import android.graphics.Bitmap;
|
||||
import android.graphics.drawable.Drawable;
|
||||
|
||||
import static com.squareup.picasso.Picasso.LoadedFrom;
|
||||
|
||||
/**
|
||||
* Represents an arbitrary listener for image loading.
|
||||
* <p/>
|
||||
* Objects implementing this class <strong>must</strong> have a working implementation of
|
||||
* {@link #equals(Object)} and {@link #hashCode()} for proper storage internally. Instances of this
|
||||
* interface will also be compared to determine if view recycling is occurring. It is recommended
|
||||
* that you add this interface directly on to a custom view type when using in an adapter to ensure
|
||||
* correct recycling behavior.
|
||||
*/
|
||||
public interface Target {
|
||||
/**
|
||||
* Callback when an image has been successfully loaded.
|
||||
* <p/>
|
||||
* <strong>Note:</strong> You must not recycle the bitmap.
|
||||
*/
|
||||
void onBitmapLoaded(Bitmap bitmap, LoadedFrom from);
|
||||
|
||||
/** Callback indicating the image could not be successfully loaded. */
|
||||
void onBitmapFailed(Drawable errorDrawable);
|
||||
|
||||
/** Callback invoked right before your request is submitted. */
|
||||
void onPrepareLoad(Drawable placeHolderDrawable);
|
||||
}
|
46
mobile/android/thirdparty/com/squareup/picasso/TargetAction.java
vendored
Normal file
46
mobile/android/thirdparty/com/squareup/picasso/TargetAction.java
vendored
Normal file
@ -0,0 +1,46 @@
|
||||
/*
|
||||
* Copyright (C) 2013 Square, Inc.
|
||||
*
|
||||
* 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.squareup.picasso;
|
||||
|
||||
import android.graphics.Bitmap;
|
||||
|
||||
final class TargetAction extends Action<Target> {
|
||||
|
||||
TargetAction(Picasso picasso, Target target, Request data, boolean skipCache, String key) {
|
||||
super(picasso, target, data, skipCache, false, 0, null, key);
|
||||
}
|
||||
|
||||
@Override void complete(Bitmap result, Picasso.LoadedFrom from) {
|
||||
if (result == null) {
|
||||
throw new AssertionError(
|
||||
String.format("Attempted to complete action with no result!\n%s", this));
|
||||
}
|
||||
Target target = getTarget();
|
||||
if (target != null) {
|
||||
target.onBitmapLoaded(result, from);
|
||||
if (result.isRecycled()) {
|
||||
throw new IllegalStateException("Target callback must not recycle bitmap!");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Override void error() {
|
||||
Target target = getTarget();
|
||||
if (target != null) {
|
||||
target.onBitmapFailed(errorDrawable);
|
||||
}
|
||||
}
|
||||
}
|
34
mobile/android/thirdparty/com/squareup/picasso/Transformation.java
vendored
Normal file
34
mobile/android/thirdparty/com/squareup/picasso/Transformation.java
vendored
Normal file
@ -0,0 +1,34 @@
|
||||
/*
|
||||
* Copyright (C) 2013 Square, Inc.
|
||||
*
|
||||
* 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.squareup.picasso;
|
||||
|
||||
import android.graphics.Bitmap;
|
||||
|
||||
/** Image transformation. */
|
||||
public interface Transformation {
|
||||
/**
|
||||
* Transform the source bitmap into a new bitmap. If you create a new bitmap instance, you must
|
||||
* call {@link android.graphics.Bitmap#recycle()} on {@code source}. You may return the original
|
||||
* if no transformation is required.
|
||||
*/
|
||||
Bitmap transform(Bitmap source);
|
||||
|
||||
/**
|
||||
* Returns a unique key for the transformation, used for caching purposes. If the transformation
|
||||
* has parameters (e.g. size, scale factor, etc) then these should be part of the key.
|
||||
*/
|
||||
String key();
|
||||
}
|
100
mobile/android/thirdparty/com/squareup/picasso/UrlConnectionDownloader.java
vendored
Normal file
100
mobile/android/thirdparty/com/squareup/picasso/UrlConnectionDownloader.java
vendored
Normal file
@ -0,0 +1,100 @@
|
||||
/*
|
||||
* Copyright (C) 2013 Square, Inc.
|
||||
*
|
||||
* 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.squareup.picasso;
|
||||
|
||||
import android.content.Context;
|
||||
import android.net.Uri;
|
||||
import android.net.http.HttpResponseCache;
|
||||
import android.os.Build;
|
||||
import java.io.File;
|
||||
import java.io.IOException;
|
||||
import java.net.HttpURLConnection;
|
||||
import java.net.URL;
|
||||
|
||||
import static com.squareup.picasso.Utils.parseResponseSourceHeader;
|
||||
|
||||
/**
|
||||
* A {@link Downloader} which uses {@link HttpURLConnection} to download images. A disk cache of 2%
|
||||
* of the total available space will be used (capped at 50MB) will automatically be installed in the
|
||||
* application's cache directory, when available.
|
||||
*/
|
||||
public class UrlConnectionDownloader implements Downloader {
|
||||
static final String RESPONSE_SOURCE = "X-Android-Response-Source";
|
||||
|
||||
private static final Object lock = new Object();
|
||||
static volatile Object cache;
|
||||
|
||||
private final Context context;
|
||||
|
||||
public UrlConnectionDownloader(Context context) {
|
||||
this.context = context.getApplicationContext();
|
||||
}
|
||||
|
||||
protected HttpURLConnection openConnection(Uri path) throws IOException {
|
||||
HttpURLConnection connection = (HttpURLConnection) new URL(path.toString()).openConnection();
|
||||
connection.setConnectTimeout(Utils.DEFAULT_CONNECT_TIMEOUT);
|
||||
connection.setReadTimeout(Utils.DEFAULT_READ_TIMEOUT);
|
||||
return connection;
|
||||
}
|
||||
|
||||
@Override public Response load(Uri uri, boolean localCacheOnly) throws IOException {
|
||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.ICE_CREAM_SANDWICH) {
|
||||
installCacheIfNeeded(context);
|
||||
}
|
||||
|
||||
HttpURLConnection connection = openConnection(uri);
|
||||
connection.setUseCaches(true);
|
||||
if (localCacheOnly) {
|
||||
connection.setRequestProperty("Cache-Control", "only-if-cached,max-age=" + Integer.MAX_VALUE);
|
||||
}
|
||||
|
||||
int responseCode = connection.getResponseCode();
|
||||
if (responseCode >= 300) {
|
||||
connection.disconnect();
|
||||
throw new ResponseException(responseCode + " " + connection.getResponseMessage());
|
||||
}
|
||||
|
||||
boolean fromCache = parseResponseSourceHeader(connection.getHeaderField(RESPONSE_SOURCE));
|
||||
|
||||
return new Response(connection.getInputStream(), fromCache);
|
||||
}
|
||||
|
||||
private static void installCacheIfNeeded(Context context) {
|
||||
// DCL + volatile should be safe after Java 5.
|
||||
if (cache == null) {
|
||||
try {
|
||||
synchronized (lock) {
|
||||
if (cache == null) {
|
||||
cache = ResponseCacheIcs.install(context);
|
||||
}
|
||||
}
|
||||
} catch (IOException ignored) {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private static class ResponseCacheIcs {
|
||||
static Object install(Context context) throws IOException {
|
||||
File cacheDir = Utils.createDefaultCacheDir(context);
|
||||
HttpResponseCache cache = HttpResponseCache.getInstalled();
|
||||
if (cache == null) {
|
||||
long maxSize = Utils.calculateDiskCacheSize(cacheDir);
|
||||
cache = HttpResponseCache.install(cacheDir, maxSize);
|
||||
}
|
||||
return cache;
|
||||
}
|
||||
}
|
||||
}
|
304
mobile/android/thirdparty/com/squareup/picasso/Utils.java
vendored
Normal file
304
mobile/android/thirdparty/com/squareup/picasso/Utils.java
vendored
Normal file
@ -0,0 +1,304 @@
|
||||
/*
|
||||
* Copyright (C) 2013 Square, Inc.
|
||||
*
|
||||
* 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.squareup.picasso;
|
||||
|
||||
import android.annotation.TargetApi;
|
||||
import android.app.ActivityManager;
|
||||
import android.content.ContentResolver;
|
||||
import android.content.Context;
|
||||
import android.content.pm.PackageManager;
|
||||
import android.content.res.Resources;
|
||||
import android.graphics.Bitmap;
|
||||
import android.os.Looper;
|
||||
import android.os.Process;
|
||||
import android.os.StatFs;
|
||||
import android.provider.Settings;
|
||||
import java.io.ByteArrayOutputStream;
|
||||
import java.io.File;
|
||||
import java.io.FileNotFoundException;
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.util.List;
|
||||
import java.util.concurrent.ThreadFactory;
|
||||
|
||||
import static android.content.Context.ACTIVITY_SERVICE;
|
||||
import static android.content.pm.ApplicationInfo.FLAG_LARGE_HEAP;
|
||||
import static android.os.Build.VERSION.SDK_INT;
|
||||
import static android.os.Build.VERSION_CODES.HONEYCOMB;
|
||||
import static android.os.Build.VERSION_CODES.HONEYCOMB_MR1;
|
||||
import static android.os.Process.THREAD_PRIORITY_BACKGROUND;
|
||||
import static android.provider.Settings.System.AIRPLANE_MODE_ON;
|
||||
|
||||
final class Utils {
|
||||
static final String THREAD_PREFIX = "Picasso-";
|
||||
static final String THREAD_IDLE_NAME = THREAD_PREFIX + "Idle";
|
||||
static final int DEFAULT_READ_TIMEOUT = 20 * 1000; // 20s
|
||||
static final int DEFAULT_CONNECT_TIMEOUT = 15 * 1000; // 15s
|
||||
private static final String PICASSO_CACHE = "picasso-cache";
|
||||
private static final int KEY_PADDING = 50; // Determined by exact science.
|
||||
private static final int MIN_DISK_CACHE_SIZE = 5 * 1024 * 1024; // 5MB
|
||||
private static final int MAX_DISK_CACHE_SIZE = 50 * 1024 * 1024; // 50MB
|
||||
|
||||
/* WebP file header
|
||||
0 1 2 3
|
||||
0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
|
||||
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|
||||
| 'R' | 'I' | 'F' | 'F' |
|
||||
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|
||||
| File Size |
|
||||
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|
||||
| 'W' | 'E' | 'B' | 'P' |
|
||||
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|
||||
*/
|
||||
private static final int WEBP_FILE_HEADER_SIZE = 12;
|
||||
private static final String WEBP_FILE_HEADER_RIFF = "RIFF";
|
||||
private static final String WEBP_FILE_HEADER_WEBP = "WEBP";
|
||||
|
||||
private Utils() {
|
||||
// No instances.
|
||||
}
|
||||
|
||||
static int getBitmapBytes(Bitmap bitmap) {
|
||||
int result;
|
||||
if (SDK_INT >= HONEYCOMB_MR1) {
|
||||
result = BitmapHoneycombMR1.getByteCount(bitmap);
|
||||
} else {
|
||||
result = bitmap.getRowBytes() * bitmap.getHeight();
|
||||
}
|
||||
if (result < 0) {
|
||||
throw new IllegalStateException("Negative size: " + bitmap);
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
static void checkNotMain() {
|
||||
if (Looper.getMainLooper().getThread() == Thread.currentThread()) {
|
||||
throw new IllegalStateException("Method call should not happen from the main thread.");
|
||||
}
|
||||
}
|
||||
|
||||
static String createKey(Request data) {
|
||||
StringBuilder builder;
|
||||
|
||||
if (data.uri != null) {
|
||||
String path = data.uri.toString();
|
||||
builder = new StringBuilder(path.length() + KEY_PADDING);
|
||||
builder.append(path);
|
||||
} else {
|
||||
builder = new StringBuilder(KEY_PADDING);
|
||||
builder.append(data.resourceId);
|
||||
}
|
||||
builder.append('\n');
|
||||
|
||||
if (data.rotationDegrees != 0) {
|
||||
builder.append("rotation:").append(data.rotationDegrees);
|
||||
if (data.hasRotationPivot) {
|
||||
builder.append('@').append(data.rotationPivotX).append('x').append(data.rotationPivotY);
|
||||
}
|
||||
builder.append('\n');
|
||||
}
|
||||
if (data.targetWidth != 0) {
|
||||
builder.append("resize:").append(data.targetWidth).append('x').append(data.targetHeight);
|
||||
builder.append('\n');
|
||||
}
|
||||
if (data.centerCrop) {
|
||||
builder.append("centerCrop\n");
|
||||
} else if (data.centerInside) {
|
||||
builder.append("centerInside\n");
|
||||
}
|
||||
|
||||
if (data.transformations != null) {
|
||||
//noinspection ForLoopReplaceableByForEach
|
||||
for (int i = 0, count = data.transformations.size(); i < count; i++) {
|
||||
builder.append(data.transformations.get(i).key());
|
||||
builder.append('\n');
|
||||
}
|
||||
}
|
||||
|
||||
return builder.toString();
|
||||
}
|
||||
|
||||
static void closeQuietly(InputStream is) {
|
||||
if (is == null) return;
|
||||
try {
|
||||
is.close();
|
||||
} catch (IOException ignored) {
|
||||
}
|
||||
}
|
||||
|
||||
/** Returns {@code true} if header indicates the response body was loaded from the disk cache. */
|
||||
static boolean parseResponseSourceHeader(String header) {
|
||||
if (header == null) {
|
||||
return false;
|
||||
}
|
||||
String[] parts = header.split(" ", 2);
|
||||
if ("CACHE".equals(parts[0])) {
|
||||
return true;
|
||||
}
|
||||
if (parts.length == 1) {
|
||||
return false;
|
||||
}
|
||||
try {
|
||||
return "CONDITIONAL_CACHE".equals(parts[0]) && Integer.parseInt(parts[1]) == 304;
|
||||
} catch (NumberFormatException e) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
static Downloader createDefaultDownloader(Context context) {
|
||||
return new UrlConnectionDownloader(context);
|
||||
}
|
||||
|
||||
static File createDefaultCacheDir(Context context) {
|
||||
File cache = new File(context.getApplicationContext().getCacheDir(), PICASSO_CACHE);
|
||||
if (!cache.exists()) {
|
||||
cache.mkdirs();
|
||||
}
|
||||
return cache;
|
||||
}
|
||||
|
||||
static long calculateDiskCacheSize(File dir) {
|
||||
long size = MIN_DISK_CACHE_SIZE;
|
||||
|
||||
try {
|
||||
StatFs statFs = new StatFs(dir.getAbsolutePath());
|
||||
long available = ((long) statFs.getBlockCount()) * statFs.getBlockSize();
|
||||
// Target 2% of the total space.
|
||||
size = available / 50;
|
||||
} catch (IllegalArgumentException ignored) {
|
||||
}
|
||||
|
||||
// Bound inside min/max size for disk cache.
|
||||
return Math.max(Math.min(size, MAX_DISK_CACHE_SIZE), MIN_DISK_CACHE_SIZE);
|
||||
}
|
||||
|
||||
static int calculateMemoryCacheSize(Context context) {
|
||||
ActivityManager am = (ActivityManager) context.getSystemService(ACTIVITY_SERVICE);
|
||||
boolean largeHeap = (context.getApplicationInfo().flags & FLAG_LARGE_HEAP) != 0;
|
||||
int memoryClass = am.getMemoryClass();
|
||||
if (largeHeap && SDK_INT >= HONEYCOMB) {
|
||||
memoryClass = ActivityManagerHoneycomb.getLargeMemoryClass(am);
|
||||
}
|
||||
// Target ~15% of the available heap.
|
||||
return 1024 * 1024 * memoryClass / 7;
|
||||
}
|
||||
|
||||
static boolean isAirplaneModeOn(Context context) {
|
||||
ContentResolver contentResolver = context.getContentResolver();
|
||||
return Settings.System.getInt(contentResolver, AIRPLANE_MODE_ON, 0) != 0;
|
||||
}
|
||||
|
||||
static boolean hasPermission(Context context, String permission) {
|
||||
return context.checkCallingOrSelfPermission(permission) == PackageManager.PERMISSION_GRANTED;
|
||||
}
|
||||
|
||||
static byte[] toByteArray(InputStream input) throws IOException {
|
||||
ByteArrayOutputStream byteArrayOutputStream = new ByteArrayOutputStream();
|
||||
byte[] buffer = new byte[1024 * 4];
|
||||
int n = 0;
|
||||
while (-1 != (n = input.read(buffer))) {
|
||||
byteArrayOutputStream.write(buffer, 0, n);
|
||||
}
|
||||
return byteArrayOutputStream.toByteArray();
|
||||
}
|
||||
|
||||
static boolean isWebPFile(InputStream stream) throws IOException {
|
||||
byte[] fileHeaderBytes = new byte[WEBP_FILE_HEADER_SIZE];
|
||||
boolean isWebPFile = false;
|
||||
if (stream.read(fileHeaderBytes, 0, WEBP_FILE_HEADER_SIZE) == WEBP_FILE_HEADER_SIZE) {
|
||||
// If a file's header starts with RIFF and end with WEBP, the file is a WebP file
|
||||
isWebPFile = WEBP_FILE_HEADER_RIFF.equals(new String(fileHeaderBytes, 0, 4, "US-ASCII"))
|
||||
&& WEBP_FILE_HEADER_WEBP.equals(new String(fileHeaderBytes, 8, 4, "US-ASCII"));
|
||||
}
|
||||
return isWebPFile;
|
||||
}
|
||||
|
||||
static int getResourceId(Resources resources, Request data) throws FileNotFoundException {
|
||||
if (data.resourceId != 0 || data.uri == null) {
|
||||
return data.resourceId;
|
||||
}
|
||||
|
||||
String pkg = data.uri.getAuthority();
|
||||
if (pkg == null) throw new FileNotFoundException("No package provided: " + data.uri);
|
||||
|
||||
int id;
|
||||
List<String> segments = data.uri.getPathSegments();
|
||||
if (segments == null || segments.isEmpty()) {
|
||||
throw new FileNotFoundException("No path segments: " + data.uri);
|
||||
} else if (segments.size() == 1) {
|
||||
try {
|
||||
id = Integer.parseInt(segments.get(0));
|
||||
} catch (NumberFormatException e) {
|
||||
throw new FileNotFoundException("Last path segment is not a resource ID: " + data.uri);
|
||||
}
|
||||
} else if (segments.size() == 2) {
|
||||
String type = segments.get(0);
|
||||
String name = segments.get(1);
|
||||
|
||||
id = resources.getIdentifier(name, type, pkg);
|
||||
} else {
|
||||
throw new FileNotFoundException("More than two path segments: " + data.uri);
|
||||
}
|
||||
return id;
|
||||
}
|
||||
|
||||
static Resources getResources(Context context, Request data) throws FileNotFoundException {
|
||||
if (data.resourceId != 0 || data.uri == null) {
|
||||
return context.getResources();
|
||||
}
|
||||
|
||||
String pkg = data.uri.getAuthority();
|
||||
if (pkg == null) throw new FileNotFoundException("No package provided: " + data.uri);
|
||||
try {
|
||||
PackageManager pm = context.getPackageManager();
|
||||
return pm.getResourcesForApplication(pkg);
|
||||
} catch (PackageManager.NameNotFoundException e) {
|
||||
throw new FileNotFoundException("Unable to obtain resources for package: " + data.uri);
|
||||
}
|
||||
}
|
||||
|
||||
@TargetApi(HONEYCOMB)
|
||||
private static class ActivityManagerHoneycomb {
|
||||
static int getLargeMemoryClass(ActivityManager activityManager) {
|
||||
return activityManager.getLargeMemoryClass();
|
||||
}
|
||||
}
|
||||
|
||||
static class PicassoThreadFactory implements ThreadFactory {
|
||||
@SuppressWarnings("NullableProblems")
|
||||
public Thread newThread(Runnable r) {
|
||||
return new PicassoThread(r);
|
||||
}
|
||||
}
|
||||
|
||||
private static class PicassoThread extends Thread {
|
||||
public PicassoThread(Runnable r) {
|
||||
super(r);
|
||||
}
|
||||
|
||||
@Override public void run() {
|
||||
Process.setThreadPriority(THREAD_PRIORITY_BACKGROUND);
|
||||
super.run();
|
||||
}
|
||||
}
|
||||
|
||||
@TargetApi(HONEYCOMB_MR1)
|
||||
private static class BitmapHoneycombMR1 {
|
||||
static int getByteCount(Bitmap bitmap) {
|
||||
return bitmap.getByteCount();
|
||||
}
|
||||
}
|
||||
}
|
Loading…
Reference in New Issue
Block a user