2 |
3 |
7 |
12 |
23 |
28 |
35 |
43 |
44 |
45 |
--------------------------------------------------------------------------------
/picasso/src/main/java/com/squareup/picasso/ContentStreamRequestHandler.java:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright (C) 2013 Square, Inc.
3 | *
4 | * Licensed under the Apache License, Version 2.0 (the "License");
5 | * you may not use this file except in compliance with the License.
6 | * You may obtain a copy of the License at
7 | *
8 | * http://www.apache.org/licenses/LICENSE-2.0
9 | *
10 | * Unless required by applicable law or agreed to in writing, software
11 | * distributed under the License is distributed on an "AS IS" BASIS,
12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | * See the License for the specific language governing permissions and
14 | * limitations under the License.
15 | */
16 | package com.squareup.picasso;
17 |
18 | import android.content.ContentResolver;
19 | import android.content.Context;
20 | import java.io.FileNotFoundException;
21 | import java.io.IOException;
22 | import java.io.InputStream;
23 |
24 | import static android.content.ContentResolver.SCHEME_CONTENT;
25 | import static com.squareup.picasso.Picasso.LoadedFrom.DISK;
26 |
27 | class ContentStreamRequestHandler extends RequestHandler {
28 | final Context context;
29 |
30 | ContentStreamRequestHandler(Context context) {
31 | this.context = context;
32 | }
33 |
34 | @Override public boolean canHandleRequest(Request data) {
35 | return SCHEME_CONTENT.equals(data.uri.getScheme());
36 | }
37 |
38 | @Override public Result load(Request request, int networkPolicy) throws IOException {
39 | return new Result(getInputStream(request), DISK);
40 | }
41 |
42 | InputStream getInputStream(Request request) throws FileNotFoundException {
43 | ContentResolver contentResolver = context.getContentResolver();
44 | return contentResolver.openInputStream(request.uri);
45 | }
46 | }
47 |
--------------------------------------------------------------------------------
/picasso/src/main/java/com/squareup/picasso/FileRequestHandler.java:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright (C) 2013 Square, Inc.
3 | *
4 | * Licensed under the Apache License, Version 2.0 (the "License");
5 | * you may not use this file except in compliance with the License.
6 | * You may obtain a copy of the License at
7 | *
8 | * http://www.apache.org/licenses/LICENSE-2.0
9 | *
10 | * Unless required by applicable law or agreed to in writing, software
11 | * distributed under the License is distributed on an "AS IS" BASIS,
12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | * See the License for the specific language governing permissions and
14 | * limitations under the License.
15 | */
16 | package com.squareup.picasso;
17 |
18 | import android.content.Context;
19 | import android.media.ExifInterface;
20 | import android.net.Uri;
21 | import java.io.IOException;
22 |
23 | import static android.content.ContentResolver.SCHEME_FILE;
24 | import static android.media.ExifInterface.ORIENTATION_NORMAL;
25 | import static android.media.ExifInterface.TAG_ORIENTATION;
26 | import static com.squareup.picasso.Picasso.LoadedFrom.DISK;
27 |
28 | class FileRequestHandler extends ContentStreamRequestHandler {
29 |
30 | FileRequestHandler(Context context) {
31 | super(context);
32 | }
33 |
34 | @Override public boolean canHandleRequest(Request data) {
35 | return SCHEME_FILE.equals(data.uri.getScheme());
36 | }
37 |
38 | @Override public Result load(Request request, int networkPolicy) throws IOException {
39 | return new Result(null, getInputStream(request), DISK, getFileExifRotation(request.uri));
40 | }
41 |
42 | static int getFileExifRotation(Uri uri) throws IOException {
43 | ExifInterface exifInterface = new ExifInterface(uri.getPath());
44 | return exifInterface.getAttributeInt(TAG_ORIENTATION, ORIENTATION_NORMAL);
45 | }
46 | }
47 |
--------------------------------------------------------------------------------
/picasso/src/main/java/com/squareup/picasso/NetworkPolicy.java:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright (C) 2015 Square, Inc.
3 | *
4 | * Licensed under the Apache License, Version 2.0 (the "License");
5 | * you may not use this file except in compliance with the License.
6 | * You may obtain a copy of the License at
7 | *
8 | * http://www.apache.org/licenses/LICENSE-2.0
9 | *
10 | * Unless required by applicable law or agreed to in writing, software
11 | * distributed under the License is distributed on an "AS IS" BASIS,
12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | * See the License for the specific language governing permissions and
14 | * limitations under the License.
15 | */
16 | package com.squareup.picasso;
17 |
18 | /** Designates the policy to use for network requests. */
19 | @SuppressWarnings("PointlessBitwiseExpression")
20 | public enum NetworkPolicy {
21 |
22 | /** Skips checking the disk cache and forces loading through the network. */
23 | NO_CACHE(1 << 0),
24 |
25 | /**
26 | * Skips storing the result into the disk cache.
27 | *
28 | * Note: At this time this is only supported if you are using OkHttp.
29 | */
30 | NO_STORE(1 << 1),
31 |
32 | /** Forces the request through the disk cache only, skipping network. */
33 | OFFLINE(1 << 2);
34 |
35 | public static boolean shouldReadFromDiskCache(int networkPolicy) {
36 | return (networkPolicy & NetworkPolicy.NO_CACHE.index) == 0;
37 | }
38 |
39 | public static boolean shouldWriteToDiskCache(int networkPolicy) {
40 | return (networkPolicy & NetworkPolicy.NO_STORE.index) == 0;
41 | }
42 |
43 | public static boolean isOfflineOnly(int networkPolicy) {
44 | return (networkPolicy & NetworkPolicy.OFFLINE.index) != 0;
45 | }
46 |
47 | final int index;
48 |
49 | NetworkPolicy(int index) {
50 | this.index = index;
51 | }
52 | }
53 |
--------------------------------------------------------------------------------
/picasso-sample/src/main/java/com/example/picasso/SampleGridViewAdapter.java:
--------------------------------------------------------------------------------
1 | package com.example.picasso;
2 |
3 | import android.content.Context;
4 | import android.view.View;
5 | import android.view.ViewGroup;
6 | import android.widget.BaseAdapter;
7 | import com.squareup.picasso.Picasso;
8 | import java.util.ArrayList;
9 | import java.util.Collections;
10 | import java.util.List;
11 |
12 | import static android.widget.ImageView.ScaleType.CENTER_CROP;
13 |
14 | final class SampleGridViewAdapter extends BaseAdapter {
15 | private final Context context;
16 | private final List urls = new ArrayList<>();
17 |
18 | public SampleGridViewAdapter(Context context) {
19 | this.context = context;
20 |
21 | // Ensure we get a different ordering of images on each run.
22 | Collections.addAll(urls, Data.URLS);
23 | Collections.shuffle(urls);
24 |
25 | // Triple up the list.
26 | ArrayList copy = new ArrayList<>(urls);
27 | urls.addAll(copy);
28 | urls.addAll(copy);
29 | }
30 |
31 | @Override public View getView(int position, View convertView, ViewGroup parent) {
32 | SquaredImageView view = (SquaredImageView) convertView;
33 | if (view == null) {
34 | view = new SquaredImageView(context);
35 | view.setScaleType(CENTER_CROP);
36 | }
37 |
38 | // Get the image URL for the current position.
39 | String url = getItem(position);
40 |
41 | // Trigger the download of the URL asynchronously into the image view.
42 | Picasso.with(context) //
43 | .load(url) //
44 | .placeholder(R.drawable.placeholder) //
45 | .error(R.drawable.error) //
46 | .fit() //
47 | .tag(context) //
48 | .into(view);
49 |
50 | return view;
51 | }
52 |
53 | @Override public int getCount() {
54 | return urls.size();
55 | }
56 |
57 | @Override public String getItem(int position) {
58 | return urls.get(position);
59 | }
60 |
61 | @Override public long getItemId(int position) {
62 | return position;
63 | }
64 | }
65 |
--------------------------------------------------------------------------------
/picasso/src/main/java/com/squareup/picasso/TargetAction.java:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright (C) 2013 Square, Inc.
3 | *
4 | * Licensed under the Apache License, Version 2.0 (the "License");
5 | * you may not use this file except in compliance with the License.
6 | * You may obtain a copy of the License at
7 | *
8 | * http://www.apache.org/licenses/LICENSE-2.0
9 | *
10 | * Unless required by applicable law or agreed to in writing, software
11 | * distributed under the License is distributed on an "AS IS" BASIS,
12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | * See the License for the specific language governing permissions and
14 | * limitations under the License.
15 | */
16 | package com.squareup.picasso;
17 |
18 | import android.graphics.Bitmap;
19 | import android.graphics.drawable.Drawable;
20 |
21 | final class TargetAction extends Action {
22 |
23 | TargetAction(Picasso picasso, Target target, Request data, int memoryPolicy, int networkPolicy,
24 | Drawable errorDrawable, String key, Object tag, int errorResId) {
25 | super(picasso, target, data, memoryPolicy, networkPolicy, errorResId, errorDrawable, key, tag,
26 | false);
27 | }
28 |
29 | @Override void complete(Bitmap result, Picasso.LoadedFrom from) {
30 | if (result == null) {
31 | throw new AssertionError(
32 | String.format("Attempted to complete action with no result!\n%s", this));
33 | }
34 | Target target = getTarget();
35 | if (target != null) {
36 | target.onBitmapLoaded(result, from);
37 | if (result.isRecycled()) {
38 | throw new IllegalStateException("Target callback must not recycle bitmap!");
39 | }
40 | }
41 | }
42 |
43 | @Override void error(Exception e) {
44 | Target target = getTarget();
45 | if (target != null) {
46 | if (errorResId != 0) {
47 | target.onBitmapFailed(e, picasso.context.getResources().getDrawable(errorResId));
48 | } else {
49 | target.onBitmapFailed(e, errorDrawable);
50 | }
51 | }
52 | }
53 | }
54 |
--------------------------------------------------------------------------------
/picasso-sample/src/main/AndroidManifest.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
26 |
27 |
30 |
31 |
32 |
33 |
34 |
35 |
36 |
37 |
38 |
39 |
40 |
41 |
42 |
43 |
44 |
45 |
47 |
48 |
49 |
50 |
51 |
--------------------------------------------------------------------------------
/picasso/src/main/java/com/squareup/picasso/ResourceRequestHandler.java:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright (C) 2013 Square, Inc.
3 | *
4 | * Licensed under the Apache License, Version 2.0 (the "License");
5 | * you may not use this file except in compliance with the License.
6 | * You may obtain a copy of the License at
7 | *
8 | * http://www.apache.org/licenses/LICENSE-2.0
9 | *
10 | * Unless required by applicable law or agreed to in writing, software
11 | * distributed under the License is distributed on an "AS IS" BASIS,
12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | * See the License for the specific language governing permissions and
14 | * limitations under the License.
15 | */
16 | package com.squareup.picasso;
17 |
18 | import android.content.Context;
19 | import android.content.res.Resources;
20 | import android.graphics.Bitmap;
21 | import android.graphics.BitmapFactory;
22 | import java.io.IOException;
23 |
24 | import static android.content.ContentResolver.SCHEME_ANDROID_RESOURCE;
25 | import static com.squareup.picasso.Picasso.LoadedFrom.DISK;
26 |
27 | class ResourceRequestHandler extends RequestHandler {
28 | private final Context context;
29 |
30 | ResourceRequestHandler(Context context) {
31 | this.context = context;
32 | }
33 |
34 | @Override public boolean canHandleRequest(Request data) {
35 | if (data.resourceId != 0) {
36 | return true;
37 | }
38 |
39 | return SCHEME_ANDROID_RESOURCE.equals(data.uri.getScheme());
40 | }
41 |
42 | @Override public Result load(Request request, int networkPolicy) throws IOException {
43 | Resources res = Utils.getResources(context, request);
44 | int id = Utils.getResourceId(res, request);
45 | return new Result(decodeResource(res, id, request), DISK);
46 | }
47 |
48 | private static Bitmap decodeResource(Resources resources, int id, Request data) {
49 | final BitmapFactory.Options options = createBitmapOptions(data);
50 | if (requiresInSampleSize(options)) {
51 | BitmapFactory.decodeResource(resources, id, options);
52 | calculateInSampleSize(data.targetWidth, data.targetHeight, options, data);
53 | }
54 | return BitmapFactory.decodeResource(resources, id, options);
55 | }
56 | }
57 |
--------------------------------------------------------------------------------
/picasso-sample/src/main/java/com/example/picasso/SampleListDetailAdapter.java:
--------------------------------------------------------------------------------
1 | package com.example.picasso;
2 |
3 | import android.content.Context;
4 | import android.view.LayoutInflater;
5 | import android.view.View;
6 | import android.view.ViewGroup;
7 | import android.widget.BaseAdapter;
8 | import android.widget.ImageView;
9 | import android.widget.TextView;
10 | import com.squareup.picasso.Picasso;
11 | import java.util.ArrayList;
12 | import java.util.Collections;
13 | import java.util.List;
14 |
15 | final class SampleListDetailAdapter extends BaseAdapter {
16 | private final Context context;
17 | private final List urls = new ArrayList<>();
18 |
19 | public SampleListDetailAdapter(Context context) {
20 | this.context = context;
21 | Collections.addAll(urls, Data.URLS);
22 | }
23 |
24 | @Override public View getView(int position, View view, ViewGroup parent) {
25 | ViewHolder holder;
26 | if (view == null) {
27 | view = LayoutInflater.from(context).inflate(R.layout.sample_list_detail_item, parent, false);
28 | holder = new ViewHolder();
29 | holder.image = (ImageView) view.findViewById(R.id.photo);
30 | holder.text = (TextView) view.findViewById(R.id.url);
31 | view.setTag(holder);
32 | } else {
33 | holder = (ViewHolder) view.getTag();
34 | }
35 |
36 | // Get the image URL for the current position.
37 | String url = getItem(position);
38 |
39 | holder.text.setText(url);
40 |
41 | // Trigger the download of the URL asynchronously into the image view.
42 | Picasso.with(context)
43 | .load(url)
44 | .placeholder(R.drawable.placeholder)
45 | .error(R.drawable.error)
46 | .resizeDimen(R.dimen.list_detail_image_size, R.dimen.list_detail_image_size)
47 | .centerInside()
48 | .tag(context)
49 | .into(holder.image);
50 |
51 | return view;
52 | }
53 |
54 | @Override public int getCount() {
55 | return urls.size();
56 | }
57 |
58 | @Override public String getItem(int position) {
59 | return urls.get(position);
60 | }
61 |
62 | @Override public long getItemId(int position) {
63 | return position;
64 | }
65 |
66 | static class ViewHolder {
67 | ImageView image;
68 | TextView text;
69 | }
70 | }
71 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # 很简单的android图片加载框架Picasso
2 | 开源项目地址:[https://github.com/open-android/Picasso](https://github.com/open-android/Picasso)
3 |
4 | PS:如果觉得文章太长,你也可观看该课程的[视频](https://www.boxuegu.com/web/html/video.html?courseId=172§ionId=8a2c9bed5a3a4c7e015a3bbffc6107ed&chapterId=8a2c9bed5a3a4c7e015a3affe39a046a&vId=8a2c9bed5a3a4c7e015a3b0451f105b8&videoId=B33E67E868CDB1D19C33DC5901307461),亲,里面还有高清,无码的福利喔
5 |
6 | # 运行效果
7 | 
8 |
9 | * 爱生活,爱学习,更爱做代码的搬运工,分类查找更方便请下载黑马助手app
10 |
11 |
12 | 
13 |
14 | ## 使用步骤
15 | ### 1. 在project的build.gradle添加如下代码(如下图)
16 |
17 | allprojects {
18 | repositories {
19 | ...
20 | maven { url "https://jitpack.io" }
21 | }
22 | }
23 |
24 | ### 2. 在Module的build.gradle添加依赖
25 |
26 | compile 'com.github.open-android:Picasso:0.1.0'
27 | ### 3. Imageview加载图片
28 |
29 | Picasso.with(context) //设置context
30 | .load(url) //图片url地址
31 | .placeholder(R.drawable.placeholder) //加载时显示的图片
32 | .error(R.drawable.error) //加载错误显示的图片
33 | .fit() //自动按照图片尺寸进行压缩
34 | .tag("image") //图片tag,便于控制图片加载和暂停加载
35 | .into(view);//图片显示的imageview
36 | ### 4. 控制图片加载和停止加载
37 |
38 | 我们在列表或者是gradview中显示图片时,滑动列表时可以暂停加载图片,等滑动结束后再加载
39 | listView.setOnScrollListener(new AbsListView.OnScrollListener() {
40 | @Override
41 | public void onScrollStateChanged(AbsListView view, int scrollState) {
42 | final Picasso picasso = Picasso.with(SampleGridViewActivity.this);
43 | if (scrollState == SCROLL_STATE_IDLE || scrollState == SCROLL_STATE_TOUCH_SCROLL) {
44 | picasso.resumeTag("image");//开始加载所有tag为image的imageview
45 | } else {
46 | picasso.pauseTag("image");//开始加载所有tag为image的imageview
47 | }
48 | }
49 |
50 | ### 在AndroidManifest.xml中配置网络权限
51 |
52 |
53 |
54 | * 详细的使用方法在DEMO里面都演示啦,如果你觉得这个库还不错,请赏我一颗star吧~~~
55 |
56 | * 欢迎关注微信公众号
57 |
58 | 
59 |
--------------------------------------------------------------------------------
/picasso/src/main/java/com/squareup/picasso/Cache.java:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright (C) 2013 Square, Inc.
3 | *
4 | * Licensed under the Apache License, Version 2.0 (the "License");
5 | * you may not use this file except in compliance with the License.
6 | * You may obtain a copy of the License at
7 | *
8 | * http://www.apache.org/licenses/LICENSE-2.0
9 | *
10 | * Unless required by applicable law or agreed to in writing, software
11 | * distributed under the License is distributed on an "AS IS" BASIS,
12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | * See the License for the specific language governing permissions and
14 | * limitations under the License.
15 | */
16 | package com.squareup.picasso;
17 |
18 | import android.graphics.Bitmap;
19 |
20 | /**
21 | * A memory cache for storing the most recently used images.
22 | *
23 | * Note: The {@link Cache} is accessed by multiple threads. You must ensure
24 | * your {@link Cache} implementation is thread safe when {@link Cache#get(String)} or {@link
25 | * Cache#set(String, android.graphics.Bitmap)} is called.
26 | */
27 | public interface Cache {
28 | /** Retrieve an image for the specified {@code key} or {@code null}. */
29 | Bitmap get(String key);
30 |
31 | /** Store an image in the cache for the specified {@code key}. */
32 | void set(String key, Bitmap bitmap);
33 |
34 | /** Returns the current size of the cache in bytes. */
35 | int size();
36 |
37 | /** Returns the maximum size in bytes that the cache can hold. */
38 | int maxSize();
39 |
40 | /** Clears the cache. */
41 | void clear();
42 |
43 | /** Remove items whose key is prefixed with {@code keyPrefix}. */
44 | void clearKeyUri(String keyPrefix);
45 |
46 | /** A cache which does not store any values. */
47 | Cache NONE = new Cache() {
48 | @Override public Bitmap get(String key) {
49 | return null;
50 | }
51 |
52 | @Override public void set(String key, Bitmap bitmap) {
53 | // Ignore.
54 | }
55 |
56 | @Override public int size() {
57 | return 0;
58 | }
59 |
60 | @Override public int maxSize() {
61 | return 0;
62 | }
63 |
64 | @Override public void clear() {
65 | }
66 |
67 | @Override public void clearKeyUri(String keyPrefix) {
68 | }
69 | };
70 | }
71 |
--------------------------------------------------------------------------------
/picasso/src/main/java/com/squareup/picasso/AssetRequestHandler.java:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright (C) 2013 Square, Inc.
3 | *
4 | * Licensed under the Apache License, Version 2.0 (the "License");
5 | * you may not use this file except in compliance with the License.
6 | * You may obtain a copy of the License at
7 | *
8 | * http://www.apache.org/licenses/LICENSE-2.0
9 | *
10 | * Unless required by applicable law or agreed to in writing, software
11 | * distributed under the License is distributed on an "AS IS" BASIS,
12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | * See the License for the specific language governing permissions and
14 | * limitations under the License.
15 | */
16 | package com.squareup.picasso;
17 |
18 | import android.content.Context;
19 | import android.content.res.AssetManager;
20 | import android.net.Uri;
21 | import java.io.IOException;
22 | import java.io.InputStream;
23 |
24 | import static android.content.ContentResolver.SCHEME_FILE;
25 | import static com.squareup.picasso.Picasso.LoadedFrom.DISK;
26 |
27 | class AssetRequestHandler extends RequestHandler {
28 | protected static final String ANDROID_ASSET = "android_asset";
29 | private static final int ASSET_PREFIX_LENGTH =
30 | (SCHEME_FILE + ":///" + ANDROID_ASSET + "/").length();
31 |
32 | private final Context context;
33 | private final Object lock = new Object();
34 | private AssetManager assetManager;
35 |
36 | public AssetRequestHandler(Context context) {
37 | this.context = context;
38 | }
39 |
40 | @Override public boolean canHandleRequest(Request data) {
41 | Uri uri = data.uri;
42 | return (SCHEME_FILE.equals(uri.getScheme())
43 | && !uri.getPathSegments().isEmpty() && ANDROID_ASSET.equals(uri.getPathSegments().get(0)));
44 | }
45 |
46 | @Override public Result load(Request request, int networkPolicy) throws IOException {
47 | if (assetManager == null) {
48 | synchronized (lock) {
49 | if (assetManager == null) {
50 | assetManager = context.getAssets();
51 | }
52 | }
53 | }
54 | InputStream is = assetManager.open(getFilePath(request));
55 | return new Result(is, DISK);
56 | }
57 |
58 | static String getFilePath(Request request) {
59 | return request.uri.toString().substring(ASSET_PREFIX_LENGTH);
60 | }
61 | }
62 |
--------------------------------------------------------------------------------
/website/static/html5shiv.min.js:
--------------------------------------------------------------------------------
1 | /*
2 | HTML5 Shiv v3.6.2pre | @afarkas @jdalton @jon_neal @rem | MIT/GPL2 Licensed
3 | */
4 | (function(l,f){function m(){var a=e.elements;return"string"==typeof a?a.split(" "):a}function i(a){var b=n[a[o]];b||(b={},h++,a[o]=h,n[h]=b);return b}function p(a,b,c){b||(b=f);if(g)return b.createElement(a);c||(c=i(b));b=c.cache[a]?c.cache[a].cloneNode():r.test(a)?(c.cache[a]=c.createElem(a)).cloneNode():c.createElem(a);return b.canHaveChildren&&!s.test(a)?c.frag.appendChild(b):b}function t(a,b){if(!b.cache)b.cache={},b.createElem=a.createElement,b.createFrag=a.createDocumentFragment,b.frag=b.createFrag();
5 | a.createElement=function(c){return!e.shivMethods?b.createElem(c):p(c,a,b)};a.createDocumentFragment=Function("h,f","return function(){var n=f.cloneNode(),c=n.createElement;h.shivMethods&&("+m().join().replace(/\w+/g,function(a){b.createElem(a);b.frag.createElement(a);return'c("'+a+'")'})+");return n}")(e,b.frag)}function q(a){a||(a=f);var b=i(a);if(e.shivCSS&&!j&&!b.hasCSS){var c,d=a;c=d.createElement("p");d=d.getElementsByTagName("head")[0]||d.documentElement;c.innerHTML="x";
6 | c=d.insertBefore(c.lastChild,d.firstChild);b.hasCSS=!!c}g||t(a,b);return a}var k=l.html5||{},s=/^<|^(?:button|map|select|textarea|object|iframe|option|optgroup)$/i,r=/^(?:a|b|code|div|fieldset|h1|h2|h3|h4|h5|h6|i|label|li|ol|p|q|span|strong|style|table|tbody|td|th|tr|ul)$/i,j,o="_html5shiv",h=0,n={},g;(function(){try{var a=f.createElement("a");a.innerHTML="";j="hidden"in a;var b;if(!(b=1==a.childNodes.length)){f.createElement("a");var c=f.createDocumentFragment();b="undefined"==typeof c.cloneNode||
7 | "undefined"==typeof c.createDocumentFragment||"undefined"==typeof c.createElement}g=b}catch(d){g=j=!0}})();var e={elements:k.elements||"abbr article aside audio bdi canvas data datalist details figcaption figure footer header hgroup mark meter nav output progress section summary time video",version:"3.6.2pre",shivCSS:!1!==k.shivCSS,supportsUnknownElements:g,shivMethods:!1!==k.shivMethods,type:"default",shivDocument:q,createElement:p,createDocumentFragment:function(a,b){a||(a=f);if(g)return a.createDocumentFragment();
8 | for(var b=b||i(a),c=b.frag.cloneNode(),d=0,e=m(),h=e.length;d
26 | * Objects implementing this class must have a working implementation of
27 | * {@link Object#equals(Object)} and {@link Object#hashCode()} for proper storage internally.
28 | * Instances of this interface will also be compared to determine if view recycling is occurring.
29 | * It is recommended that you add this interface directly on to a custom view type when using in an
30 | * adapter to ensure correct recycling behavior.
31 | */
32 | public interface Target {
33 | /**
34 | * Callback when an image has been successfully loaded.
35 | *
36 | * Note: You must not recycle the bitmap.
37 | */
38 | void onBitmapLoaded(Bitmap bitmap, LoadedFrom from);
39 |
40 | /**
41 | * Callback indicating the image could not be successfully loaded.
42 | *
43 | * Note: The passed {@link Drawable} may be {@code null} if none has been
44 | * specified via {@link RequestCreator#error(android.graphics.drawable.Drawable)}
45 | * or {@link RequestCreator#error(int)}.
46 | */
47 | void onBitmapFailed(Exception e, Drawable errorDrawable);
48 |
49 | /**
50 | * Callback invoked right before your request is submitted.
51 | *
52 | * Note: The passed {@link Drawable} may be {@code null} if none has been
53 | * specified via {@link RequestCreator#placeholder(android.graphics.drawable.Drawable)}
54 | * or {@link RequestCreator#placeholder(int)}.
55 | */
56 | void onPrepareLoad(Drawable placeHolderDrawable);
57 | }
58 |
--------------------------------------------------------------------------------
/picasso-sample/src/main/java/com/example/picasso/PicassoSampleActivity.java:
--------------------------------------------------------------------------------
1 | package com.example.picasso;
2 |
3 | import android.os.Bundle;
4 | import android.support.v4.app.FragmentActivity;
5 | import android.view.View;
6 | import android.view.ViewGroup;
7 | import android.widget.AdapterView;
8 | import android.widget.CompoundButton;
9 | import android.widget.FrameLayout;
10 | import android.widget.ListView;
11 | import android.widget.ToggleButton;
12 |
13 | import com.squareup.picasso.Picasso;
14 |
15 | import static android.view.View.GONE;
16 | import static android.view.View.VISIBLE;
17 |
18 | abstract class PicassoSampleActivity extends FragmentActivity {
19 | private ToggleButton showHide;
20 | private FrameLayout sampleContent;
21 |
22 | @Override protected void onCreate(Bundle savedInstanceState) {
23 | super.onCreate(savedInstanceState);
24 | super.setContentView(R.layout.picasso_sample_activity);
25 | sampleContent = (FrameLayout) findViewById(R.id.sample_content);
26 |
27 | final ListView activityList = (ListView) findViewById(R.id.activity_list);
28 | final PicassoSampleAdapter adapter = new PicassoSampleAdapter(this);
29 | activityList.setAdapter(adapter);
30 | activityList.setOnItemClickListener(new AdapterView.OnItemClickListener() {
31 | @Override
32 | public void onItemClick(AdapterView> adapterView, View view, int position, long id) {
33 | adapter.getItem(position).launch(PicassoSampleActivity.this);
34 | }
35 | });
36 |
37 | showHide = (ToggleButton) findViewById(R.id.faux_action_bar_control);
38 | showHide.setOnCheckedChangeListener(new CompoundButton.OnCheckedChangeListener() {
39 | @Override public void onCheckedChanged(CompoundButton compoundButton, boolean checked) {
40 | activityList.setVisibility(checked ? VISIBLE : GONE);
41 | }
42 | });
43 | }
44 |
45 | @Override
46 | protected void onDestroy() {
47 | super.onDestroy();
48 | Picasso.with(this).cancelTag(this);
49 | }
50 |
51 | @Override public void onBackPressed() {
52 | if (showHide.isChecked()) {
53 | showHide.setChecked(false);
54 | } else {
55 | super.onBackPressed();
56 | }
57 | }
58 |
59 | @Override public void setContentView(int layoutResID) {
60 | getLayoutInflater().inflate(layoutResID, sampleContent);
61 | }
62 |
63 | @Override public void setContentView(View view) {
64 | sampleContent.addView(view);
65 | }
66 |
67 | @Override public void setContentView(View view, ViewGroup.LayoutParams params) {
68 | sampleContent.addView(view, params);
69 | }
70 | }
71 |
--------------------------------------------------------------------------------
/picasso/src/test/java/com/squareup/picasso/PicassoDrawableTest.java:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright (C) 2013 Square, Inc.
3 | *
4 | * Licensed under the Apache License, Version 2.0 (the "License");
5 | * you may not use this file except in compliance with the License.
6 | * You may obtain a copy of the License at
7 | *
8 | * http://www.apache.org/licenses/LICENSE-2.0
9 | *
10 | * Unless required by applicable law or agreed to in writing, software
11 | * distributed under the License is distributed on an "AS IS" BASIS,
12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | * See the License for the specific language governing permissions and
14 | * limitations under the License.
15 | */
16 | package com.squareup.picasso;
17 |
18 | import android.content.Context;
19 | import android.graphics.Bitmap;
20 | import android.graphics.drawable.ColorDrawable;
21 | import android.graphics.drawable.Drawable;
22 | import org.junit.Test;
23 | import org.junit.runner.RunWith;
24 | import org.robolectric.RobolectricGradleTestRunner;
25 | import org.robolectric.RuntimeEnvironment;
26 |
27 | import static android.graphics.Color.RED;
28 | import static com.squareup.picasso.Picasso.LoadedFrom.DISK;
29 | import static com.squareup.picasso.Picasso.LoadedFrom.MEMORY;
30 | import static com.squareup.picasso.TestUtils.makeBitmap;
31 | import static org.fest.assertions.api.Assertions.assertThat;
32 |
33 | @RunWith(RobolectricGradleTestRunner.class)
34 | public class PicassoDrawableTest {
35 | private final Context context = RuntimeEnvironment.application;
36 | private final Drawable placeholder = new ColorDrawable(RED);
37 | private final Bitmap bitmap = makeBitmap();
38 |
39 | @Test public void createWithNoPlaceholderAnimation() {
40 | PicassoDrawable pd = new PicassoDrawable(context, bitmap, null, DISK, false, false);
41 | assertThat(pd.getBitmap()).isSameAs(bitmap);
42 | assertThat(pd.placeholder).isNull();
43 | assertThat(pd.animating).isTrue();
44 | }
45 |
46 | @Test public void createWithPlaceholderAnimation() {
47 | PicassoDrawable pd = new PicassoDrawable(context, bitmap, placeholder, DISK, false, false);
48 | assertThat(pd.getBitmap()).isSameAs(bitmap);
49 | assertThat(pd.placeholder).isSameAs(placeholder);
50 | assertThat(pd.animating).isTrue();
51 | }
52 |
53 | @Test public void createWithBitmapCacheHit() {
54 | PicassoDrawable pd = new PicassoDrawable(context, bitmap, placeholder, MEMORY, false, false);
55 | assertThat(pd.getBitmap()).isSameAs(bitmap);
56 | assertThat(pd.placeholder).isNull();
57 | assertThat(pd.animating).isFalse();
58 | }
59 | }
60 |
--------------------------------------------------------------------------------
/website/static/jquery.smooth-scroll.min.js:
--------------------------------------------------------------------------------
1 | /*!
2 | * Smooth Scroll - v1.4.10 - 2013-03-02
3 | * https://github.com/kswedberg/jquery-smooth-scroll
4 | * Copyright (c) 2013 Karl Swedberg
5 | * Licensed MIT (https://github.com/kswedberg/jquery-smooth-scroll/blob/master/LICENSE-MIT)
6 | */
7 | (function(l){function t(l){return l.replace(/(:|\.)/g,"\\$1")}var e="1.4.10",o={exclude:[],excludeWithin:[],offset:0,direction:"top",scrollElement:null,scrollTarget:null,beforeScroll:function(){},afterScroll:function(){},easing:"swing",speed:400,autoCoefficent:2},r=function(t){var e=[],o=!1,r=t.dir&&"left"==t.dir?"scrollLeft":"scrollTop";return this.each(function(){if(this!=document&&this!=window){var t=l(this);t[r]()>0?e.push(this):(t[r](1),o=t[r]()>0,o&&e.push(this),t[r](0))}}),e.length||this.each(function(){"BODY"===this.nodeName&&(e=[this])}),"first"===t.el&&e.length>1&&(e=[e[0]]),e};l.fn.extend({scrollable:function(l){var t=r.call(this,{dir:l});return this.pushStack(t)},firstScrollable:function(l){var t=r.call(this,{el:"first",dir:l});return this.pushStack(t)},smoothScroll:function(e){e=e||{};var o=l.extend({},l.fn.smoothScroll.defaults,e),r=l.smoothScroll.filterPath(location.pathname);return this.unbind("click.smoothscroll").bind("click.smoothscroll",function(e){var n=this,s=l(this),c=o.exclude,i=o.excludeWithin,a=0,f=0,h=!0,u={},d=location.hostname===n.hostname||!n.hostname,m=o.scrollTarget||(l.smoothScroll.filterPath(n.pathname)||r)===r,p=t(n.hash);if(o.scrollTarget||d&&m&&p){for(;h&&c.length>a;)s.is(t(c[a++]))&&(h=!1);for(;h&&i.length>f;)s.closest(i[f++]).length&&(h=!1)}else h=!1;h&&(e.preventDefault(),l.extend(u,o,{scrollTarget:o.scrollTarget||p,link:n}),l.smoothScroll(u))}),this}}),l.smoothScroll=function(t,e){var o,r,n,s,c=0,i="offset",a="scrollTop",f={},h={};"number"==typeof t?(o=l.fn.smoothScroll.defaults,n=t):(o=l.extend({},l.fn.smoothScroll.defaults,t||{}),o.scrollElement&&(i="position","static"==o.scrollElement.css("position")&&o.scrollElement.css("position","relative"))),o=l.extend({link:null},o),a="left"==o.direction?"scrollLeft":a,o.scrollElement?(r=o.scrollElement,c=r[a]()):r=l("html, body").firstScrollable(),o.beforeScroll.call(r,o),n="number"==typeof t?t:e||l(o.scrollTarget)[i]()&&l(o.scrollTarget)[i]()[o.direction]||0,f[a]=n+c+o.offset,s=o.speed,"auto"===s&&(s=f[a]||r.scrollTop(),s/=o.autoCoefficent),h={duration:s,easing:o.easing,complete:function(){o.afterScroll.call(o.link,o)}},o.step&&(h.step=o.step),r.length?r.stop().animate(f,h):o.afterScroll.call(o.link,o)},l.smoothScroll.version=e,l.smoothScroll.filterPath=function(l){return l.replace(/^\//,"").replace(/(index|default).[a-zA-Z]{3,4}$/,"").replace(/\/$/,"")},l.fn.smoothScroll.defaults=o})(jQuery);
--------------------------------------------------------------------------------
/gradlew.bat:
--------------------------------------------------------------------------------
1 | @if "%DEBUG%" == "" @echo off
2 | @rem ##########################################################################
3 | @rem
4 | @rem Gradle startup script for Windows
5 | @rem
6 | @rem ##########################################################################
7 |
8 | @rem Set local scope for the variables with windows NT shell
9 | if "%OS%"=="Windows_NT" setlocal
10 |
11 | @rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
12 | set DEFAULT_JVM_OPTS=
13 |
14 | set DIRNAME=%~dp0
15 | if "%DIRNAME%" == "" set DIRNAME=.
16 | set APP_BASE_NAME=%~n0
17 | set APP_HOME=%DIRNAME%
18 |
19 | @rem Find java.exe
20 | if defined JAVA_HOME goto findJavaFromJavaHome
21 |
22 | set JAVA_EXE=java.exe
23 | %JAVA_EXE% -version >NUL 2>&1
24 | if "%ERRORLEVEL%" == "0" goto init
25 |
26 | echo.
27 | echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
28 | echo.
29 | echo Please set the JAVA_HOME variable in your environment to match the
30 | echo location of your Java installation.
31 |
32 | goto fail
33 |
34 | :findJavaFromJavaHome
35 | set JAVA_HOME=%JAVA_HOME:"=%
36 | set JAVA_EXE=%JAVA_HOME%/bin/java.exe
37 |
38 | if exist "%JAVA_EXE%" goto init
39 |
40 | echo.
41 | echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME%
42 | echo.
43 | echo Please set the JAVA_HOME variable in your environment to match the
44 | echo location of your Java installation.
45 |
46 | goto fail
47 |
48 | :init
49 | @rem Get command-line arguments, handling Windowz variants
50 |
51 | if not "%OS%" == "Windows_NT" goto win9xME_args
52 | if "%@eval[2+2]" == "4" goto 4NT_args
53 |
54 | :win9xME_args
55 | @rem Slurp the command line arguments.
56 | set CMD_LINE_ARGS=
57 | set _SKIP=2
58 |
59 | :win9xME_args_slurp
60 | if "x%~1" == "x" goto execute
61 |
62 | set CMD_LINE_ARGS=%*
63 | goto execute
64 |
65 | :4NT_args
66 | @rem Get arguments from the 4NT Shell from JP Software
67 | set CMD_LINE_ARGS=%$
68 |
69 | :execute
70 | @rem Setup the command line
71 |
72 | set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar
73 |
74 | @rem Execute Gradle
75 | "%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %CMD_LINE_ARGS%
76 |
77 | :end
78 | @rem End local scope for the variables with windows NT shell
79 | if "%ERRORLEVEL%"=="0" goto mainEnd
80 |
81 | :fail
82 | rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of
83 | rem the _cmd.exe /c_ return code!
84 | if not "" == "%GRADLE_EXIT_CONSOLE%" exit 1
85 | exit /b 1
86 |
87 | :mainEnd
88 | if "%OS%"=="Windows_NT" endlocal
89 |
90 | :omega
91 |
--------------------------------------------------------------------------------
/picasso-sample/src/main/res/values/styles.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
9 |
10 |
13 |
14 |
26 |
27 |
36 |
37 |
41 |
42 |
49 |
50 |
53 |
54 |
57 |
58 |
--------------------------------------------------------------------------------
/picasso/src/main/java/com/squareup/picasso/ImageViewAction.java:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright (C) 2013 Square, Inc.
3 | *
4 | * Licensed under the Apache License, Version 2.0 (the "License");
5 | * you may not use this file except in compliance with the License.
6 | * You may obtain a copy of the License at
7 | *
8 | * http://www.apache.org/licenses/LICENSE-2.0
9 | *
10 | * Unless required by applicable law or agreed to in writing, software
11 | * distributed under the License is distributed on an "AS IS" BASIS,
12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | * See the License for the specific language governing permissions and
14 | * limitations under the License.
15 | */
16 | package com.squareup.picasso;
17 |
18 | import android.content.Context;
19 | import android.graphics.Bitmap;
20 | import android.graphics.drawable.AnimationDrawable;
21 | import android.graphics.drawable.Drawable;
22 | import android.widget.ImageView;
23 |
24 | class ImageViewAction extends Action {
25 |
26 | Callback callback;
27 |
28 | ImageViewAction(Picasso picasso, ImageView imageView, Request data, int memoryPolicy,
29 | int networkPolicy, int errorResId, Drawable errorDrawable, String key, Object tag,
30 | Callback callback, boolean noFade) {
31 | super(picasso, imageView, data, memoryPolicy, networkPolicy, errorResId, errorDrawable, key,
32 | tag, noFade);
33 | this.callback = callback;
34 | }
35 |
36 | @Override public void complete(Bitmap result, Picasso.LoadedFrom from) {
37 | if (result == null) {
38 | throw new AssertionError(
39 | String.format("Attempted to complete action with no result!\n%s", this));
40 | }
41 |
42 | ImageView target = this.target.get();
43 | if (target == null) {
44 | return;
45 | }
46 |
47 | Context context = picasso.context;
48 | boolean indicatorsEnabled = picasso.indicatorsEnabled;
49 | PicassoDrawable.setBitmap(target, context, result, from, noFade, indicatorsEnabled);
50 |
51 | if (callback != null) {
52 | callback.onSuccess();
53 | }
54 | }
55 |
56 | @Override public void error(Exception e) {
57 | ImageView target = this.target.get();
58 | if (target == null) {
59 | return;
60 | }
61 | Drawable placeholder = target.getDrawable();
62 | if (placeholder instanceof AnimationDrawable) {
63 | ((AnimationDrawable) placeholder).stop();
64 | }
65 | if (errorResId != 0) {
66 | target.setImageResource(errorResId);
67 | } else if (errorDrawable != null) {
68 | target.setImageDrawable(errorDrawable);
69 | }
70 |
71 | if (callback != null) {
72 | callback.onError(e);
73 | }
74 | }
75 |
76 | @Override void cancel() {
77 | super.cancel();
78 | if (callback != null) {
79 | callback = null;
80 | }
81 | }
82 | }
83 |
--------------------------------------------------------------------------------
/picasso-sample/src/main/java/com/example/picasso/GrayscaleTransformation.java:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright (C) 2014 Square, Inc.
3 | *
4 | * Licensed under the Apache License, Version 2.0 (the "License");
5 | * you may not use this file except in compliance with the License.
6 | * You may obtain a copy of the License at
7 | *
8 | * http://www.apache.org/licenses/LICENSE-2.0
9 | *
10 | * Unless required by applicable law or agreed to in writing, software
11 | * distributed under the License is distributed on an "AS IS" BASIS,
12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | * See the License for the specific language governing permissions and
14 | * limitations under the License.
15 | */
16 | package com.example.picasso;
17 |
18 | import android.graphics.Bitmap;
19 | import android.graphics.BitmapShader;
20 | import android.graphics.Canvas;
21 | import android.graphics.ColorMatrix;
22 | import android.graphics.ColorMatrixColorFilter;
23 | import android.graphics.Paint;
24 | import android.graphics.PorterDuff;
25 | import android.graphics.PorterDuffXfermode;
26 | import com.squareup.picasso.Picasso;
27 | import com.squareup.picasso.Transformation;
28 | import java.io.IOException;
29 |
30 | import static android.graphics.Bitmap.createBitmap;
31 | import static android.graphics.Paint.ANTI_ALIAS_FLAG;
32 | import static android.graphics.Shader.TileMode.REPEAT;
33 |
34 | public class GrayscaleTransformation implements Transformation {
35 |
36 | private final Picasso picasso;
37 |
38 | public GrayscaleTransformation(Picasso picasso) {
39 | this.picasso = picasso;
40 | }
41 |
42 | @Override public Bitmap transform(Bitmap source) {
43 | Bitmap result = createBitmap(source.getWidth(), source.getHeight(), source.getConfig());
44 | Bitmap noise;
45 | try {
46 | noise = picasso.load(R.drawable.noise).get();
47 | } catch (IOException e) {
48 | throw new RuntimeException("Failed to apply transformation! Missing resource.");
49 | }
50 |
51 | BitmapShader shader = new BitmapShader(noise, REPEAT, REPEAT);
52 |
53 | ColorMatrix colorMatrix = new ColorMatrix();
54 | colorMatrix.setSaturation(0);
55 | ColorMatrixColorFilter filter = new ColorMatrixColorFilter(colorMatrix);
56 |
57 | Paint paint = new Paint(ANTI_ALIAS_FLAG);
58 | paint.setColorFilter(filter);
59 |
60 | Canvas canvas = new Canvas(result);
61 | canvas.drawBitmap(source, 0, 0, paint);
62 |
63 | paint.setColorFilter(null);
64 | paint.setShader(shader);
65 | paint.setXfermode(new PorterDuffXfermode(PorterDuff.Mode.MULTIPLY));
66 |
67 | canvas.drawRect(0, 0, canvas.getWidth(), canvas.getHeight(), paint);
68 |
69 | source.recycle();
70 | noise.recycle();
71 |
72 | return result;
73 | }
74 |
75 | @Override public String key() {
76 | return "grayscaleTransformation()";
77 | }
78 | }
79 |
--------------------------------------------------------------------------------
/picasso-sample/src/main/java/com/example/picasso/SampleContactsAdapter.java:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright (C) 2013 The Android Open Source Project
3 | * Copyright (C) 2013 Square, Inc.
4 | *
5 | * Licensed under the Apache License, Version 2.0 (the "License");
6 | * you may not use this file except in compliance with the License.
7 | * You may obtain a copy of the License at
8 | *
9 | * http://www.apache.org/licenses/LICENSE-2.0
10 | *
11 | * Unless required by applicable law or agreed to in writing, software
12 | * distributed under the License is distributed on an "AS IS" BASIS,
13 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14 | * See the License for the specific language governing permissions and
15 | * limitations under the License.
16 | */
17 | package com.example.picasso;
18 |
19 | import android.content.Context;
20 | import android.database.Cursor;
21 | import android.net.Uri;
22 | import android.support.v4.widget.CursorAdapter;
23 | import android.view.LayoutInflater;
24 | import android.view.View;
25 | import android.view.ViewGroup;
26 | import android.widget.QuickContactBadge;
27 | import android.widget.TextView;
28 | import com.squareup.picasso.Picasso;
29 |
30 | import static android.provider.ContactsContract.Contacts;
31 | import static com.example.picasso.SampleContactsActivity.ContactsQuery;
32 |
33 | class SampleContactsAdapter extends CursorAdapter {
34 | private final LayoutInflater inflater;
35 |
36 | public SampleContactsAdapter(Context context) {
37 | super(context, null, 0);
38 | inflater = LayoutInflater.from(context);
39 | }
40 |
41 | @Override public View newView(Context context, Cursor cursor, ViewGroup viewGroup) {
42 | View itemLayout = inflater.inflate(R.layout.sample_contacts_activity_item, viewGroup, false);
43 |
44 | ViewHolder holder = new ViewHolder();
45 | holder.text1 = (TextView) itemLayout.findViewById(android.R.id.text1);
46 | holder.icon = (QuickContactBadge) itemLayout.findViewById(android.R.id.icon);
47 |
48 | itemLayout.setTag(holder);
49 |
50 | return itemLayout;
51 | }
52 |
53 | @Override public void bindView(View view, Context context, Cursor cursor) {
54 | Uri contactUri = Contacts.getLookupUri(cursor.getLong(ContactsQuery.ID),
55 | cursor.getString(ContactsQuery.LOOKUP_KEY));
56 |
57 | ViewHolder holder = (ViewHolder) view.getTag();
58 | holder.text1.setText(cursor.getString(ContactsQuery.DISPLAY_NAME));
59 | holder.icon.assignContactUri(contactUri);
60 |
61 | Picasso.with(context)
62 | .load(contactUri)
63 | .placeholder(R.drawable.contact_picture_placeholder)
64 | .tag(context)
65 | .into(holder.icon);
66 | }
67 |
68 | @Override public int getCount() {
69 | return getCursor() == null ? 0 : super.getCount();
70 | }
71 |
72 | private static class ViewHolder {
73 | TextView text1;
74 | QuickContactBadge icon;
75 | }
76 | }
77 |
--------------------------------------------------------------------------------
/picasso-sample/src/main/java/com/example/picasso/SampleGalleryActivity.java:
--------------------------------------------------------------------------------
1 | package com.example.picasso;
2 |
3 | import android.content.Intent;
4 | import android.os.Bundle;
5 | import android.view.View;
6 | import android.widget.ImageView;
7 | import android.widget.ViewAnimator;
8 | import com.squareup.picasso.Picasso;
9 |
10 | import static android.content.Intent.ACTION_PICK;
11 | import static android.provider.MediaStore.Images.Media.EXTERNAL_CONTENT_URI;
12 | import static com.squareup.picasso.Callback.EmptyCallback;
13 |
14 | public class SampleGalleryActivity extends PicassoSampleActivity {
15 | private static final int GALLERY_REQUEST = 9391;
16 | private static final String KEY_IMAGE = "com.example.picasso:image";
17 |
18 | private ImageView imageView;
19 | private ViewAnimator animator;
20 | private String image;
21 |
22 | @Override protected void onCreate(Bundle savedInstanceState) {
23 | super.onCreate(savedInstanceState);
24 |
25 | setContentView(R.layout.sample_gallery_activity);
26 |
27 | animator = (ViewAnimator) findViewById(R.id.animator);
28 | imageView = (ImageView) findViewById(R.id.image);
29 |
30 | findViewById(R.id.go).setOnClickListener(new View.OnClickListener() {
31 | @Override public void onClick(View view) {
32 | Intent gallery = new Intent(ACTION_PICK, EXTERNAL_CONTENT_URI);
33 | startActivityForResult(gallery, GALLERY_REQUEST);
34 | }
35 | });
36 |
37 | if (savedInstanceState != null) {
38 | image = savedInstanceState.getString(KEY_IMAGE);
39 | if (image != null) {
40 | loadImage();
41 | }
42 | }
43 | }
44 |
45 | @Override protected void onPause() {
46 | super.onPause();
47 | if (isFinishing()) {
48 | // Always cancel the request here, this is safe to call even if the image has been loaded.
49 | // This ensures that the anonymous callback we have does not prevent the activity from
50 | // being garbage collected. It also prevents our callback from getting invoked even after the
51 | // activity has finished.
52 | Picasso.with(this).cancelRequest(imageView);
53 | }
54 | }
55 |
56 | @Override protected void onSaveInstanceState(Bundle outState) {
57 | super.onSaveInstanceState(outState);
58 | outState.putString(KEY_IMAGE, image);
59 | }
60 |
61 | @Override protected void onActivityResult(int requestCode, int resultCode, Intent data) {
62 | if (requestCode == GALLERY_REQUEST && resultCode == RESULT_OK && data != null) {
63 | image = data.getData().toString();
64 | loadImage();
65 | } else {
66 | super.onActivityResult(requestCode, resultCode, data);
67 | }
68 | }
69 |
70 | private void loadImage() {
71 | // Index 1 is the progress bar. Show it while we're loading the image.
72 | animator.setDisplayedChild(1);
73 |
74 | Picasso.with(this).load(image).fit().centerInside().into(imageView, new EmptyCallback() {
75 | @Override public void onSuccess() {
76 | // Index 0 is the image view.
77 | animator.setDisplayedChild(0);
78 | }
79 | });
80 | }
81 | }
82 |
--------------------------------------------------------------------------------
/picasso-sample/src/main/java/com/example/picasso/SampleContactsActivity.java:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright (C) 2013 The Android Open Source Project
3 | * Copyright (C) 2013 Square, Inc.
4 | *
5 | * Licensed under the Apache License, Version 2.0 (the "License");
6 | * you may not use this file except in compliance with the License.
7 | * You may obtain a copy of the License at
8 | *
9 | * http://www.apache.org/licenses/LICENSE-2.0
10 | *
11 | * Unless required by applicable law or agreed to in writing, software
12 | * distributed under the License is distributed on an "AS IS" BASIS,
13 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14 | * See the License for the specific language governing permissions and
15 | * limitations under the License.
16 | */
17 | package com.example.picasso;
18 |
19 | import android.database.Cursor;
20 | import android.net.Uri;
21 | import android.os.Bundle;
22 | import android.support.v4.app.LoaderManager;
23 | import android.support.v4.content.CursorLoader;
24 | import android.support.v4.content.Loader;
25 | import android.widget.ListView;
26 |
27 | import static android.provider.ContactsContract.Contacts;
28 |
29 | public class SampleContactsActivity extends PicassoSampleActivity
30 | implements LoaderManager.LoaderCallbacks {
31 | private SampleContactsAdapter adapter;
32 |
33 | @Override protected void onCreate(Bundle savedInstanceState) {
34 | super.onCreate(savedInstanceState);
35 | setContentView(R.layout.sample_contacts_activity);
36 |
37 | adapter = new SampleContactsAdapter(this);
38 |
39 | ListView lv = (ListView) findViewById(android.R.id.list);
40 | lv.setAdapter(adapter);
41 | lv.setOnScrollListener(new SampleScrollListener(this));
42 |
43 | getSupportLoaderManager().initLoader(ContactsQuery.QUERY_ID, null, this);
44 | }
45 |
46 | @Override public Loader onCreateLoader(int id, Bundle args) {
47 | if (id == ContactsQuery.QUERY_ID) {
48 | return new CursorLoader(this, //
49 | ContactsQuery.CONTENT_URI, //
50 | ContactsQuery.PROJECTION, //
51 | ContactsQuery.SELECTION, //
52 | null, //
53 | ContactsQuery.SORT_ORDER);
54 | }
55 | return null;
56 | }
57 |
58 | @Override public void onLoadFinished(Loader loader, Cursor data) {
59 | adapter.swapCursor(data);
60 | }
61 |
62 | @Override public void onLoaderReset(Loader loader) {
63 | adapter.swapCursor(null);
64 | }
65 |
66 | interface ContactsQuery {
67 | int QUERY_ID = 1;
68 |
69 | Uri CONTENT_URI = Contacts.CONTENT_URI;
70 |
71 | String SELECTION = Contacts.DISPLAY_NAME_PRIMARY
72 | + "<>''"
73 | + " AND "
74 | + Contacts.IN_VISIBLE_GROUP
75 | + "=1";
76 |
77 | String SORT_ORDER = Contacts.SORT_KEY_PRIMARY;
78 |
79 | String[] PROJECTION = {
80 | Contacts._ID, //
81 | Contacts.LOOKUP_KEY, //
82 | Contacts.DISPLAY_NAME_PRIMARY, //
83 | Contacts.PHOTO_THUMBNAIL_URI, //
84 | SORT_ORDER
85 | };
86 |
87 | int ID = 0;
88 | int LOOKUP_KEY = 1;
89 | int DISPLAY_NAME = 2;
90 | }
91 | }
92 |
--------------------------------------------------------------------------------
/picasso/src/main/java/com/squareup/picasso/NetworkRequestHandler.java:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright (C) 2013 Square, Inc.
3 | *
4 | * Licensed under the Apache License, Version 2.0 (the "License");
5 | * you may not use this file except in compliance with the License.
6 | * You may obtain a copy of the License at
7 | *
8 | * http://www.apache.org/licenses/LICENSE-2.0
9 | *
10 | * Unless required by applicable law or agreed to in writing, software
11 | * distributed under the License is distributed on an "AS IS" BASIS,
12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | * See the License for the specific language governing permissions and
14 | * limitations under the License.
15 | */
16 | package com.squareup.picasso;
17 |
18 | import android.net.NetworkInfo;
19 | import android.support.annotation.Nullable;
20 | import java.io.IOException;
21 | import java.io.InputStream;
22 |
23 | import static com.squareup.picasso.Downloader.Response;
24 | import static com.squareup.picasso.Picasso.LoadedFrom.DISK;
25 | import static com.squareup.picasso.Picasso.LoadedFrom.NETWORK;
26 |
27 | class NetworkRequestHandler extends RequestHandler {
28 | static final int RETRY_COUNT = 2;
29 |
30 | private static final String SCHEME_HTTP = "http";
31 | private static final String SCHEME_HTTPS = "https";
32 |
33 | private final Downloader downloader;
34 | private final Stats stats;
35 |
36 | public NetworkRequestHandler(Downloader downloader, Stats stats) {
37 | this.downloader = downloader;
38 | this.stats = stats;
39 | }
40 |
41 | @Override public boolean canHandleRequest(Request data) {
42 | String scheme = data.uri.getScheme();
43 | return (SCHEME_HTTP.equals(scheme) || SCHEME_HTTPS.equals(scheme));
44 | }
45 |
46 | @Override @Nullable public Result load(Request request, int networkPolicy) throws IOException {
47 | Response response = downloader.load(request.uri, request.networkPolicy);
48 | if (response == null) {
49 | return null;
50 | }
51 |
52 | Picasso.LoadedFrom loadedFrom = response.cached ? DISK : NETWORK;
53 |
54 | InputStream is = response.getInputStream();
55 | // Sometimes response content length is zero when requests are being replayed. Haven't found
56 | // root cause to this but retrying the request seems safe to do so.
57 | if (loadedFrom == DISK && response.getContentLength() == 0) {
58 | Utils.closeQuietly(is);
59 | throw new ContentLengthException("Received response with 0 content-length header.");
60 | }
61 | if (loadedFrom == NETWORK && response.getContentLength() > 0) {
62 | stats.dispatchDownloadFinished(response.getContentLength());
63 | }
64 | return new Result(is, loadedFrom);
65 | }
66 |
67 | @Override int getRetryCount() {
68 | return RETRY_COUNT;
69 | }
70 |
71 | @Override boolean shouldRetry(boolean airplaneMode, NetworkInfo info) {
72 | return info == null || info.isConnected();
73 | }
74 |
75 | @Override boolean supportsReplay() {
76 | return true;
77 | }
78 |
79 | static class ContentLengthException extends IOException {
80 | public ContentLengthException(String message) {
81 | super(message);
82 | }
83 | }
84 | }
85 |
--------------------------------------------------------------------------------
/picasso/src/main/java/com/squareup/picasso/Action.java:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright (C) 2013 Square, Inc.
3 | *
4 | * Licensed under the Apache License, Version 2.0 (the "License");
5 | * you may not use this file except in compliance with the License.
6 | * You may obtain a copy of the License at
7 | *
8 | * http://www.apache.org/licenses/LICENSE-2.0
9 | *
10 | * Unless required by applicable law or agreed to in writing, software
11 | * distributed under the License is distributed on an "AS IS" BASIS,
12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | * See the License for the specific language governing permissions and
14 | * limitations under the License.
15 | */
16 | package com.squareup.picasso;
17 |
18 | import android.graphics.Bitmap;
19 | import android.graphics.drawable.Drawable;
20 | import java.lang.ref.ReferenceQueue;
21 | import java.lang.ref.WeakReference;
22 |
23 | import static com.squareup.picasso.Picasso.Priority;
24 |
25 | abstract class Action {
26 | static class RequestWeakReference extends WeakReference {
27 | final Action action;
28 |
29 | public RequestWeakReference(Action action, M referent, ReferenceQueue super M> q) {
30 | super(referent, q);
31 | this.action = action;
32 | }
33 | }
34 |
35 | final Picasso picasso;
36 | final Request request;
37 | final WeakReference target;
38 | final boolean noFade;
39 | final int memoryPolicy;
40 | final int networkPolicy;
41 | final int errorResId;
42 | final Drawable errorDrawable;
43 | final String key;
44 | final Object tag;
45 |
46 | boolean willReplay;
47 | boolean cancelled;
48 |
49 | Action(Picasso picasso, T target, Request request, int memoryPolicy, int networkPolicy,
50 | int errorResId, Drawable errorDrawable, String key, Object tag, boolean noFade) {
51 | this.picasso = picasso;
52 | this.request = request;
53 | this.target =
54 | target == null ? null : new RequestWeakReference<>(this, target, picasso.referenceQueue);
55 | this.memoryPolicy = memoryPolicy;
56 | this.networkPolicy = networkPolicy;
57 | this.noFade = noFade;
58 | this.errorResId = errorResId;
59 | this.errorDrawable = errorDrawable;
60 | this.key = key;
61 | this.tag = (tag != null ? tag : this);
62 | }
63 |
64 | abstract void complete(Bitmap result, Picasso.LoadedFrom from);
65 |
66 | abstract void error(Exception e);
67 |
68 | void cancel() {
69 | cancelled = true;
70 | }
71 |
72 | Request getRequest() {
73 | return request;
74 | }
75 |
76 | T getTarget() {
77 | return target == null ? null : target.get();
78 | }
79 |
80 | String getKey() {
81 | return key;
82 | }
83 |
84 | boolean isCancelled() {
85 | return cancelled;
86 | }
87 |
88 | boolean willReplay() {
89 | return willReplay;
90 | }
91 |
92 | int getMemoryPolicy() {
93 | return memoryPolicy;
94 | }
95 |
96 | int getNetworkPolicy() {
97 | return networkPolicy;
98 | }
99 |
100 | Picasso getPicasso() {
101 | return picasso;
102 | }
103 |
104 | Priority getPriority() {
105 | return request.priority;
106 | }
107 |
108 | Object getTag() {
109 | return tag;
110 | }
111 | }
112 |
--------------------------------------------------------------------------------
/picasso/src/test/java/com/squareup/picasso/MarkableInputStreamTest.java:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright (C) 2013 Square, Inc.
3 | *
4 | * Licensed under the Apache License, Version 2.0 (the "License");
5 | * you may not use this file except in compliance with the License.
6 | * You may obtain a copy of the License at
7 | *
8 | * http://www.apache.org/licenses/LICENSE-2.0
9 | *
10 | * Unless required by applicable law or agreed to in writing, software
11 | * distributed under the License is distributed on an "AS IS" BASIS,
12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | * See the License for the specific language governing permissions and
14 | * limitations under the License.
15 | */
16 | package com.squareup.picasso;
17 |
18 | import java.io.ByteArrayInputStream;
19 | import java.io.IOException;
20 | import java.io.InputStream;
21 | import java.nio.charset.Charset;
22 | import org.junit.Test;
23 |
24 | import static org.fest.assertions.api.Assertions.assertThat;
25 | import static org.junit.Assert.fail;
26 |
27 | public class MarkableInputStreamTest {
28 | @Test
29 | public void test() throws Exception {
30 | MarkableInputStream in = new MarkableInputStream(new ByteArrayInputStream(
31 | "ABCDEFGHIJKLMNOPQRSTUVWXYZ".getBytes(Charset.forName("US-ASCII"))));
32 | assertThat(readBytes(in, 3)).isEqualTo("ABC");
33 | long posA = in.savePosition(7);// DEFGHIJ
34 | assertThat(readBytes(in, 4)).isEqualTo("DEFG");
35 | in.mark(5); // HIJKL
36 | assertThat(readBytes(in, 4)).isEqualTo("HIJK");
37 | in.reset(); // Back to 'H'
38 | assertThat(readBytes(in, 3)).isEqualTo("HIJ");
39 | in.reset(posA); // Back to 'D'
40 | assertThat(readBytes(in, 7)).isEqualTo("DEFGHIJ");
41 | in.reset(); // Back to 'H' again.
42 | assertThat(readBytes(in, 6)).isEqualTo("HIJKLM");
43 | try {
44 | in.reset();
45 | fail();
46 | } catch (IOException expected) {
47 | }
48 | try {
49 | in.reset(posA);
50 | fail();
51 | } catch (IOException expected) {
52 | }
53 | }
54 |
55 | @Test
56 | public void exceedLimitTest() throws Exception {
57 | MarkableInputStream in = new MarkableInputStream(new ByteArrayInputStream(
58 | "ABCDEFGHIJKLMNOPQRSTUVWXYZ".getBytes(Charset.forName("US-ASCII"))));
59 | in.allowMarksToExpire(false);
60 | assertThat(readBytes(in, 3)).isEqualTo("ABC");
61 | long posA = in.savePosition(7);// DEFGHIJ
62 | assertThat(readBytes(in, 4)).isEqualTo("DEFG");
63 | in.mark(5); // HIJKL
64 | assertThat(readBytes(in, 4)).isEqualTo("HIJK");
65 | in.reset(); // Back to 'H'
66 | assertThat(readBytes(in, 3)).isEqualTo("HIJ");
67 | in.reset(posA); // Back to 'D'
68 | assertThat(readBytes(in, 7)).isEqualTo("DEFGHIJ");
69 | in.reset(); // Back to 'H' again.
70 | assertThat(readBytes(in, 6)).isEqualTo("HIJKLM");
71 |
72 | in.reset(); // Back to 'H' again despite the original limit being exceeded
73 | assertThat(readBytes(in, 2)).isEqualTo("HI"); // confirm we're really back at H
74 |
75 | in.reset(posA); // Back to 'D' again despite the original limit being exceeded
76 | assertThat(readBytes(in, 2)).isEqualTo("DE"); // confirm that we're really back at D
77 | }
78 |
79 | private String readBytes(InputStream in, int count) throws IOException {
80 | byte[] result = new byte[count];
81 | assertThat(in.read(result)).isEqualTo(count);
82 | return new String(result, "US-ASCII");
83 | }
84 | }
85 |
--------------------------------------------------------------------------------
/picasso-sample/src/main/java/com/example/picasso/SampleListDetailActivity.java:
--------------------------------------------------------------------------------
1 | package com.example.picasso;
2 |
3 | import android.app.Activity;
4 | import android.os.Bundle;
5 | import android.support.v4.app.Fragment;
6 | import android.view.LayoutInflater;
7 | import android.view.View;
8 | import android.view.ViewGroup;
9 | import android.widget.AdapterView;
10 | import android.widget.ImageView;
11 | import android.widget.ListView;
12 | import android.widget.TextView;
13 | import com.squareup.picasso.Picasso;
14 |
15 | public class SampleListDetailActivity extends PicassoSampleActivity {
16 | @Override protected void onCreate(Bundle savedInstanceState) {
17 | super.onCreate(savedInstanceState);
18 |
19 | if (savedInstanceState == null) {
20 | getSupportFragmentManager().beginTransaction()
21 | .add(R.id.sample_content, ListFragment.newInstance())
22 | .commit();
23 | }
24 | }
25 |
26 | void showDetails(String url) {
27 | getSupportFragmentManager().beginTransaction()
28 | .replace(R.id.sample_content, DetailFragment.newInstance(url))
29 | .addToBackStack(null)
30 | .commit();
31 | }
32 |
33 | public static class ListFragment extends Fragment {
34 | public static ListFragment newInstance() {
35 | return new ListFragment();
36 | }
37 |
38 | @Override public View onCreateView(LayoutInflater inflater, ViewGroup container,
39 | Bundle savedInstanceState) {
40 | final SampleListDetailActivity activity = (SampleListDetailActivity) getActivity();
41 | final SampleListDetailAdapter adapter = new SampleListDetailAdapter(activity);
42 |
43 | ListView listView = (ListView) LayoutInflater.from(activity)
44 | .inflate(R.layout.sample_list_detail_list, container, false);
45 | listView.setAdapter(adapter);
46 | listView.setOnScrollListener(new SampleScrollListener(activity));
47 | listView.setOnItemClickListener(new AdapterView.OnItemClickListener() {
48 | @Override
49 | public void onItemClick(AdapterView> adapterView, View view, int position, long id) {
50 | String url = adapter.getItem(position);
51 | activity.showDetails(url);
52 | }
53 | });
54 | return listView;
55 | }
56 | }
57 |
58 | public static class DetailFragment extends Fragment {
59 | private static final String KEY_URL = "picasso:url";
60 |
61 | public static DetailFragment newInstance(String url) {
62 | Bundle arguments = new Bundle();
63 | arguments.putString(KEY_URL, url);
64 |
65 | DetailFragment fragment = new DetailFragment();
66 | fragment.setArguments(arguments);
67 | return fragment;
68 | }
69 |
70 | @Override public View onCreateView(LayoutInflater inflater, ViewGroup container,
71 | Bundle savedInstanceState) {
72 | Activity activity = getActivity();
73 |
74 | View view = LayoutInflater.from(activity)
75 | .inflate(R.layout.sample_list_detail_detail, container, false);
76 |
77 | TextView urlView = (TextView) view.findViewById(R.id.url);
78 | ImageView imageView = (ImageView) view.findViewById(R.id.photo);
79 |
80 | Bundle arguments = getArguments();
81 | String url = arguments.getString(KEY_URL);
82 |
83 | urlView.setText(url);
84 | Picasso.with(activity)
85 | .load(url)
86 | .fit()
87 | .tag(activity)
88 | .into(imageView);
89 |
90 | return view;
91 | }
92 | }
93 | }
94 |
--------------------------------------------------------------------------------
/picasso/src/main/java/com/squareup/picasso/Downloader.java:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright (C) 2013 Square, Inc.
3 | *
4 | * Licensed under the Apache License, Version 2.0 (the "License");
5 | * you may not use this file except in compliance with the License.
6 | * You may obtain a copy of the License at
7 | *
8 | * http://www.apache.org/licenses/LICENSE-2.0
9 | *
10 | * Unless required by applicable law or agreed to in writing, software
11 | * distributed under the License is distributed on an "AS IS" BASIS,
12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | * See the License for the specific language governing permissions and
14 | * limitations under the License.
15 | */
16 | package com.squareup.picasso;
17 |
18 | import android.graphics.Bitmap;
19 | import android.net.Uri;
20 | import android.support.annotation.NonNull;
21 | import android.support.annotation.Nullable;
22 |
23 | import java.io.IOException;
24 | import java.io.InputStream;
25 |
26 | /** A mechanism to load images from external resources such as a disk cache and/or the internet. */
27 | public interface Downloader {
28 | /**
29 | * Download the specified image {@code url} from the internet.
30 | *
31 | * @param uri Remote image URL.
32 | * @param networkPolicy The {@link NetworkPolicy} used for this request.
33 | * @return {@link Response} containing either a {@link Bitmap} representation of the request or an
34 | * {@link InputStream} for the image data. {@code null} can be returned to indicate a problem
35 | * loading the bitmap.
36 | * @throws IOException if the requested URL cannot successfully be loaded.
37 | */
38 | @Nullable Response load(@NonNull Uri uri, int networkPolicy) throws IOException;
39 |
40 | /**
41 | * Allows to perform a clean up for this {@link Downloader} including closing the disk cache and
42 | * other resources.
43 | */
44 | void shutdown();
45 |
46 | /** Thrown for non-2XX responses. */
47 | class ResponseException extends IOException {
48 | final boolean localCacheOnly;
49 | final int responseCode;
50 |
51 | public ResponseException(String message, int networkPolicy, int responseCode) {
52 | super(message);
53 | this.localCacheOnly = NetworkPolicy.isOfflineOnly(networkPolicy);
54 | this.responseCode = responseCode;
55 | }
56 | }
57 |
58 | /** Response stream or bitmap and info. */
59 | class Response {
60 | final InputStream stream;
61 | final boolean cached;
62 | final long contentLength;
63 |
64 | /**
65 | * Response stream and info.
66 | *
67 | * @param stream Image data stream.
68 | * @param loadedFromCache {@code true} if the source of the stream is from a local disk cache.
69 | * @param contentLength The content length of the response, typically derived by the
70 | * {@code Content-Length} HTTP header.
71 | */
72 | public Response(InputStream stream, boolean loadedFromCache, long contentLength) {
73 | if (stream == null) {
74 | throw new IllegalArgumentException("Stream may not be null.");
75 | }
76 | this.stream = stream;
77 | this.cached = loadedFromCache;
78 | this.contentLength = contentLength;
79 | }
80 |
81 | /**
82 | * Input stream containing image data.
83 | */
84 | public InputStream getInputStream() {
85 | return stream;
86 | }
87 |
88 | /** Content length of the response. Only valid when used with {@link #getInputStream()}. */
89 | public long getContentLength() {
90 | return contentLength;
91 | }
92 | }
93 | }
94 |
--------------------------------------------------------------------------------
/picasso-sample/src/main/java/com/example/picasso/PicassoSampleAdapter.java:
--------------------------------------------------------------------------------
1 | package com.example.picasso;
2 |
3 | import android.app.Activity;
4 | import android.app.Notification;
5 | import android.app.NotificationManager;
6 | import android.app.PendingIntent;
7 | import android.content.Context;
8 | import android.content.Intent;
9 | import android.support.v4.app.NotificationCompat;
10 | import android.view.LayoutInflater;
11 | import android.view.View;
12 | import android.view.ViewGroup;
13 | import android.widget.BaseAdapter;
14 | import android.widget.RemoteViews;
15 | import android.widget.TextView;
16 | import com.squareup.picasso.Picasso;
17 | import java.util.Random;
18 |
19 | final class PicassoSampleAdapter extends BaseAdapter {
20 |
21 | private static final int NOTIFICATION_ID = 666;
22 |
23 | enum Sample {
24 | GRID_VIEW("Image Grid View", SampleGridViewActivity.class),
25 | GALLERY("Load from Gallery", SampleGalleryActivity.class),
26 | CONTACTS("Contact Photos", SampleContactsActivity.class),
27 | LIST_DETAIL("List / Detail View", SampleListDetailActivity.class),
28 | SHOW_NOTIFICATION("Sample Notification", null) {
29 | @Override public void launch(Activity activity) {
30 | RemoteViews remoteViews =
31 | new RemoteViews(activity.getPackageName(), R.layout.notification_view);
32 |
33 | Intent intent = new Intent(activity, SampleGridViewActivity.class);
34 |
35 | NotificationCompat.Builder builder =
36 | new NotificationCompat.Builder(activity).setSmallIcon(R.drawable.icon)
37 | .setContentIntent(PendingIntent.getActivity(activity, -1, intent, 0))
38 | .setContent(remoteViews);
39 |
40 | Notification notification = builder.getNotification();
41 |
42 | NotificationManager notificationManager =
43 | (NotificationManager) activity.getSystemService(Context.NOTIFICATION_SERVICE);
44 | notificationManager.notify(NOTIFICATION_ID, notification);
45 |
46 | // Now load an image for this notification.
47 | Picasso.with(activity) //
48 | .load(Data.URLS[new Random().nextInt(Data.URLS.length)]) //
49 | .resizeDimen(R.dimen.notification_icon_width_height,
50 | R.dimen.notification_icon_width_height) //
51 | .into(remoteViews, R.id.photo, NOTIFICATION_ID, notification);
52 | }
53 | };
54 |
55 | private final Class extends Activity> activityClass;
56 | private final String name;
57 |
58 | Sample(String name, Class extends Activity> activityClass) {
59 | this.activityClass = activityClass;
60 | this.name = name;
61 | }
62 |
63 | public void launch(Activity activity) {
64 | activity.startActivity(new Intent(activity, activityClass));
65 | activity.finish();
66 | }
67 | }
68 |
69 | private final LayoutInflater inflater;
70 |
71 | public PicassoSampleAdapter(Context context) {
72 | inflater = LayoutInflater.from(context);
73 | }
74 |
75 | @Override public int getCount() {
76 | return Sample.values().length;
77 | }
78 |
79 | @Override public Sample getItem(int position) {
80 | return Sample.values()[position];
81 | }
82 |
83 | @Override public long getItemId(int position) {
84 | return position;
85 | }
86 |
87 | @Override public View getView(int position, View convertView, ViewGroup parent) {
88 | TextView view = (TextView) convertView;
89 | if (view == null) {
90 | view = (TextView) inflater.inflate(R.layout.picasso_sample_activity_item, parent, false);
91 | }
92 |
93 | view.setText(getItem(position).name);
94 |
95 | return view;
96 | }
97 | }
98 |
--------------------------------------------------------------------------------
/website/static/app.css:
--------------------------------------------------------------------------------
1 | html, body {
2 | font-family: 'Roboto', sans-serif;
3 | font-size: 15px;
4 | }
5 | body {
6 | background-color: #f6f6f6;
7 | padding-bottom: 50px;
8 | padding-top: 80px;
9 | }
10 |
11 | header {
12 | min-height: 80px;
13 | color: #f6f6f6;
14 | position: fixed;
15 | top: 0;
16 | left: 0;
17 | width: 100%;
18 | z-index: 99;
19 | }
20 | header h1 {
21 | margin: 10px 0;
22 | font-size: 50px;
23 | line-height: 60px;
24 | font-weight: 100;
25 | text-rendering: auto;
26 | }
27 | header menu {
28 | margin: 20px 0 0;
29 | padding: 0;
30 | height: 40px;
31 | }
32 | header menu ul {
33 | margin: 0;
34 | padding: 0;
35 | float: right;
36 | }
37 | header menu li {
38 | list-style: none;
39 | float: left;
40 | margin: 0;
41 | padding: 0;
42 | }
43 | header menu li a {
44 | display: inline-block;
45 | height: 40px;
46 | font-size: 17px;
47 | line-height: 40px;
48 | padding: 0 20px;
49 | color: #f6f6f6;
50 | }
51 | header menu li a:hover {
52 | color: #f6f6f6;
53 | text-decoration: none;
54 | }
55 | header menu li a img {
56 | margin: 0;
57 | padding: 5px 0;
58 | vertical-align: bottom;
59 | width: 30px;
60 | height: 30px;
61 | }
62 |
63 | #subtitle {
64 | position: absolute;
65 | top: 80px;
66 | left: 0;
67 | width: 100%;
68 | }
69 | h2 {
70 | font-weight: 200;
71 | font-size: 26px;
72 | line-height: 30px;
73 | padding: 15px 0;
74 | margin: 0;
75 | color: #eee;
76 | }
77 | h2 strong {
78 | font-weight: 300;
79 | }
80 |
81 | a.dl {
82 | font-weight: 300;
83 | font-size: 30px;
84 | line-height: 40px;
85 | padding: 3px 10px;
86 | display: inline-block;
87 | border-radius: 6px;
88 | color: #f0f0f0;
89 | margin: 5px 0;
90 | }
91 | a.dl:hover {
92 | color: #f0f0f0;
93 | text-decoration: none;
94 | }
95 |
96 | .content-nav {
97 | margin-top: 130px;
98 | width: 220px;
99 | }
100 | .content-nav.affix {
101 | top: 0;
102 | }
103 | .content-nav li.active a, .content-nav li.active a:hover {
104 | background-color: transparent;
105 | color: #555;
106 | border-left-width: 2px;
107 | }
108 | .content-nav .secondary a {
109 | color: #aaa;
110 | }
111 | .content-nav .secondary a:hover {
112 | color: #888;
113 | }
114 |
115 | h3 {
116 | font-weight: 300;
117 | font-style: italic;
118 | color: #888;
119 | font-size: 20px;
120 | padding-top: 115px;
121 | margin-top: 0;
122 | }
123 |
124 | h4 {
125 | font-weight: 400;
126 | text-transform: uppercase;
127 | color: #888;
128 | font-size: 15px;
129 | padding-top: 20px;
130 | }
131 |
132 | p.license {
133 | font-family: fixed-width;
134 | }
135 |
136 | .row .logo {
137 | text-align: center;
138 | margin-top: 150px;
139 | }
140 | .row .logo img {
141 | height: 30px;
142 | }
143 |
144 | pre, code {
145 | color: #666;
146 | }
147 | code {
148 | border: 0;
149 | background-color: transparent;
150 | }
151 |
152 | .screenshot {
153 | text-align: center;
154 | }
155 |
156 | /* Widescreen desktop. */
157 | @media (min-width: 1200px) {
158 | .content-nav {
159 | width: 270px;
160 | }
161 | }
162 |
163 | /* Smaller width browser, tablets. */
164 | @media (max-width: 979px) {
165 | .content-nav {
166 | width: 166px;
167 | }
168 | }
169 |
170 | /* One-column mobile display. */
171 | @media (max-width: 767px) {
172 | header {
173 | position: absolute;
174 | top: 0;
175 | left: 0;
176 | width: 100%;
177 | padding-left: 20px;
178 | }
179 | header menu {
180 | display: none;
181 | }
182 | #subtitle {
183 | position: absolute;
184 | top: 80px;
185 | left: 0;
186 | width: 100%;
187 | padding-left: 20px;
188 | }
189 | .content-nav {
190 | display: none;
191 | }
192 | }
--------------------------------------------------------------------------------
/picasso/src/main/java/com/squareup/picasso/ContactsPhotoRequestHandler.java:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright (C) 2013 Square, Inc.
3 | *
4 | * Licensed under the Apache License, Version 2.0 (the "License");
5 | * you may not use this file except in compliance with the License.
6 | * You may obtain a copy of the License at
7 | *
8 | * http://www.apache.org/licenses/LICENSE-2.0
9 | *
10 | * Unless required by applicable law or agreed to in writing, software
11 | * distributed under the License is distributed on an "AS IS" BASIS,
12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | * See the License for the specific language governing permissions and
14 | * limitations under the License.
15 | */
16 | package com.squareup.picasso;
17 |
18 | import android.content.ContentResolver;
19 | import android.content.Context;
20 | import android.content.UriMatcher;
21 | import android.net.Uri;
22 | import android.provider.ContactsContract;
23 | import java.io.IOException;
24 | import java.io.InputStream;
25 |
26 | import static android.content.ContentResolver.SCHEME_CONTENT;
27 | import static android.provider.ContactsContract.Contacts.openContactPhotoInputStream;
28 | import static com.squareup.picasso.Picasso.LoadedFrom.DISK;
29 |
30 | class ContactsPhotoRequestHandler extends RequestHandler {
31 | /** A lookup uri (e.g. content://com.android.contacts/contacts/lookup/3570i61d948d30808e537) */
32 | private static final int ID_LOOKUP = 1;
33 | /** A contact thumbnail uri (e.g. content://com.android.contacts/contacts/38/photo) */
34 | private static final int ID_THUMBNAIL = 2;
35 | /** A contact uri (e.g. content://com.android.contacts/contacts/38) */
36 | private static final int ID_CONTACT = 3;
37 | /**
38 | * A contact display photo (high resolution) uri
39 | * (e.g. content://com.android.contacts/display_photo/5)
40 | */
41 | private static final int ID_DISPLAY_PHOTO = 4;
42 |
43 | private static final UriMatcher matcher;
44 |
45 | static {
46 | matcher = new UriMatcher(UriMatcher.NO_MATCH);
47 | matcher.addURI(ContactsContract.AUTHORITY, "contacts/lookup/*/#", ID_LOOKUP);
48 | matcher.addURI(ContactsContract.AUTHORITY, "contacts/lookup/*", ID_LOOKUP);
49 | matcher.addURI(ContactsContract.AUTHORITY, "contacts/#/photo", ID_THUMBNAIL);
50 | matcher.addURI(ContactsContract.AUTHORITY, "contacts/#", ID_CONTACT);
51 | matcher.addURI(ContactsContract.AUTHORITY, "display_photo/#", ID_DISPLAY_PHOTO);
52 | }
53 |
54 | private final Context context;
55 |
56 | ContactsPhotoRequestHandler(Context context) {
57 | this.context = context;
58 | }
59 |
60 | @Override public boolean canHandleRequest(Request data) {
61 | final Uri uri = data.uri;
62 | return (SCHEME_CONTENT.equals(uri.getScheme())
63 | && ContactsContract.Contacts.CONTENT_URI.getHost().equals(uri.getHost())
64 | && matcher.match(data.uri) != UriMatcher.NO_MATCH);
65 | }
66 |
67 | @Override public Result load(Request request, int networkPolicy) throws IOException {
68 | InputStream is = getInputStream(request);
69 | return is != null ? new Result(is, DISK) : null;
70 | }
71 |
72 | private InputStream getInputStream(Request data) throws IOException {
73 | ContentResolver contentResolver = context.getContentResolver();
74 | Uri uri = data.uri;
75 | switch (matcher.match(uri)) {
76 | case ID_LOOKUP:
77 | uri = ContactsContract.Contacts.lookupContact(contentResolver, uri);
78 | if (uri == null) {
79 | return null;
80 | }
81 | // Resolved the uri to a contact uri, intentionally fall through to process the resolved uri
82 | case ID_CONTACT:
83 | return openContactPhotoInputStream(contentResolver, uri, true);
84 | case ID_THUMBNAIL:
85 | case ID_DISPLAY_PHOTO:
86 | return contentResolver.openInputStream(uri);
87 | default:
88 | throw new IllegalStateException("Invalid uri: " + uri);
89 | }
90 | }
91 | }
92 |
--------------------------------------------------------------------------------
/picasso/src/main/java/com/squareup/picasso/DeferredRequestCreator.java:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright (C) 2013 Square, Inc.
3 | *
4 | * Licensed under the Apache License, Version 2.0 (the "License");
5 | * you may not use this file except in compliance with the License.
6 | * You may obtain a copy of the License at
7 | *
8 | * http://www.apache.org/licenses/LICENSE-2.0
9 | *
10 | * Unless required by applicable law or agreed to in writing, software
11 | * distributed under the License is distributed on an "AS IS" BASIS,
12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | * See the License for the specific language governing permissions and
14 | * limitations under the License.
15 | */
16 | package com.squareup.picasso;
17 |
18 | import android.support.annotation.VisibleForTesting;
19 | import android.view.View;
20 | import android.view.View.OnAttachStateChangeListener;
21 | import android.view.ViewTreeObserver;
22 | import android.widget.ImageView;
23 | import java.lang.ref.WeakReference;
24 |
25 | class DeferredRequestCreator implements ViewTreeObserver.OnPreDrawListener {
26 |
27 | final RequestCreator creator;
28 | final WeakReference target;
29 | OnAttachStateChangeListener attachListener;
30 | Callback callback;
31 |
32 | @VisibleForTesting DeferredRequestCreator(RequestCreator creator, ImageView target) {
33 | this(creator, target, null);
34 | }
35 |
36 | DeferredRequestCreator(RequestCreator creator, ImageView target, Callback callback) {
37 | this.creator = creator;
38 | this.target = new WeakReference<>(target);
39 | this.callback = callback;
40 |
41 | // Since the view on which an image is being requested might not be attached to a hierarchy,
42 | // defer adding the pre-draw listener until the view is attached. This works around a platform
43 | // behavior where a global, dummy VTO is used until a real one is available on attach.
44 | // See: https://github.com/square/picasso/issues/1321
45 | if (target.getWindowToken() == null) {
46 | defer(target);
47 | } else {
48 | target.getViewTreeObserver().addOnPreDrawListener(this);
49 | }
50 | }
51 |
52 | @Override public boolean onPreDraw() {
53 | ImageView target = this.target.get();
54 | if (target == null) {
55 | return true;
56 | }
57 | ViewTreeObserver vto = target.getViewTreeObserver();
58 | if (!vto.isAlive()) {
59 | return true;
60 | }
61 |
62 | int width = target.getWidth();
63 | int height = target.getHeight();
64 |
65 | if (width <= 0 || height <= 0 || target.isLayoutRequested()) {
66 | return true;
67 | }
68 |
69 | vto.removeOnPreDrawListener(this);
70 | this.target.clear();
71 |
72 | this.creator.unfit().resize(width, height).into(target, callback);
73 | return true;
74 | }
75 |
76 | void cancel() {
77 | creator.clearTag();
78 | callback = null;
79 |
80 | ImageView target = this.target.get();
81 | if (target == null) {
82 | return;
83 | }
84 | this.target.clear();
85 |
86 | if (attachListener != null) {
87 | target.removeOnAttachStateChangeListener(attachListener);
88 | attachListener = null;
89 | } else {
90 | ViewTreeObserver vto = target.getViewTreeObserver();
91 | if (!vto.isAlive()) {
92 | return;
93 | }
94 | vto.removeOnPreDrawListener(this);
95 | }
96 | }
97 |
98 | Object getTag() {
99 | return creator.getTag();
100 | }
101 |
102 | private void defer(View view) {
103 | attachListener = new OnAttachStateChangeListener() {
104 | @Override public void onViewAttachedToWindow(View view) {
105 | view.removeOnAttachStateChangeListener(this);
106 | view.getViewTreeObserver().addOnPreDrawListener(DeferredRequestCreator.this);
107 |
108 | attachListener = null;
109 | }
110 |
111 | @Override public void onViewDetachedFromWindow(View view) {
112 | }
113 | };
114 | view.addOnAttachStateChangeListener(attachListener);
115 | }
116 | }
117 |
--------------------------------------------------------------------------------
/picasso/src/main/java/com/squareup/picasso/PicassoExecutorService.java:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright (C) 2013 Square, Inc.
3 | *
4 | * Licensed under the Apache License, Version 2.0 (the "License");
5 | * you may not use this file except in compliance with the License.
6 | * You may obtain a copy of the License at
7 | *
8 | * http://www.apache.org/licenses/LICENSE-2.0
9 | *
10 | * Unless required by applicable law or agreed to in writing, software
11 | * distributed under the License is distributed on an "AS IS" BASIS,
12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | * See the License for the specific language governing permissions and
14 | * limitations under the License.
15 | */
16 | package com.squareup.picasso;
17 |
18 | import android.net.ConnectivityManager;
19 | import android.net.NetworkInfo;
20 | import android.telephony.TelephonyManager;
21 | import java.util.concurrent.Future;
22 | import java.util.concurrent.FutureTask;
23 | import java.util.concurrent.PriorityBlockingQueue;
24 | import java.util.concurrent.ThreadPoolExecutor;
25 | import java.util.concurrent.TimeUnit;
26 |
27 | /**
28 | * The default {@link java.util.concurrent.ExecutorService} used for new {@link Picasso} instances.
29 | *
30 | * Exists as a custom type so that we can differentiate the use of defaults versus a user-supplied
31 | * instance.
32 | */
33 | class PicassoExecutorService extends ThreadPoolExecutor {
34 | private static final int DEFAULT_THREAD_COUNT = 3;
35 |
36 | PicassoExecutorService() {
37 | super(DEFAULT_THREAD_COUNT, DEFAULT_THREAD_COUNT, 0, TimeUnit.MILLISECONDS,
38 | new PriorityBlockingQueue(), new Utils.PicassoThreadFactory());
39 | }
40 |
41 | void adjustThreadCount(NetworkInfo info) {
42 | if (info == null || !info.isConnectedOrConnecting()) {
43 | setThreadCount(DEFAULT_THREAD_COUNT);
44 | return;
45 | }
46 | switch (info.getType()) {
47 | case ConnectivityManager.TYPE_WIFI:
48 | case ConnectivityManager.TYPE_WIMAX:
49 | case ConnectivityManager.TYPE_ETHERNET:
50 | setThreadCount(4);
51 | break;
52 | case ConnectivityManager.TYPE_MOBILE:
53 | switch (info.getSubtype()) {
54 | case TelephonyManager.NETWORK_TYPE_LTE: // 4G
55 | case TelephonyManager.NETWORK_TYPE_HSPAP:
56 | case TelephonyManager.NETWORK_TYPE_EHRPD:
57 | setThreadCount(3);
58 | break;
59 | case TelephonyManager.NETWORK_TYPE_UMTS: // 3G
60 | case TelephonyManager.NETWORK_TYPE_CDMA:
61 | case TelephonyManager.NETWORK_TYPE_EVDO_0:
62 | case TelephonyManager.NETWORK_TYPE_EVDO_A:
63 | case TelephonyManager.NETWORK_TYPE_EVDO_B:
64 | setThreadCount(2);
65 | break;
66 | case TelephonyManager.NETWORK_TYPE_GPRS: // 2G
67 | case TelephonyManager.NETWORK_TYPE_EDGE:
68 | setThreadCount(1);
69 | break;
70 | default:
71 | setThreadCount(DEFAULT_THREAD_COUNT);
72 | }
73 | break;
74 | default:
75 | setThreadCount(DEFAULT_THREAD_COUNT);
76 | }
77 | }
78 |
79 | private void setThreadCount(int threadCount) {
80 | setCorePoolSize(threadCount);
81 | setMaximumPoolSize(threadCount);
82 | }
83 |
84 | @Override
85 | public Future> submit(Runnable task) {
86 | PicassoFutureTask ftask = new PicassoFutureTask((BitmapHunter) task);
87 | execute(ftask);
88 | return ftask;
89 | }
90 |
91 | private static final class PicassoFutureTask extends FutureTask
92 | implements Comparable {
93 | private final BitmapHunter hunter;
94 |
95 | public PicassoFutureTask(BitmapHunter hunter) {
96 | super(hunter, null);
97 | this.hunter = hunter;
98 | }
99 |
100 | @Override
101 | public int compareTo(PicassoFutureTask other) {
102 | Picasso.Priority p1 = hunter.getPriority();
103 | Picasso.Priority p2 = other.hunter.getPriority();
104 |
105 | // High-priority requests are "lesser" so they are sorted to the front.
106 | // Equal priorities are sorted by sequence number to provide FIFO ordering.
107 | return (p1 == p2 ? hunter.sequence - other.hunter.sequence : p2.ordinal() - p1.ordinal());
108 | }
109 | }
110 | }
111 |
--------------------------------------------------------------------------------
/picasso/src/test/java/com/squareup/picasso/TargetActionTest.java:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright (C) 2013 Square, Inc.
3 | *
4 | * Licensed under the Apache License, Version 2.0 (the "License");
5 | * you may not use this file except in compliance with the License.
6 | * You may obtain a copy of the License at
7 | *
8 | * http://www.apache.org/licenses/LICENSE-2.0
9 | *
10 | * Unless required by applicable law or agreed to in writing, software
11 | * distributed under the License is distributed on an "AS IS" BASIS,
12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | * See the License for the specific language governing permissions and
14 | * limitations under the License.
15 | */
16 | package com.squareup.picasso;
17 |
18 | import android.content.Context;
19 | import android.content.res.Resources;
20 | import android.graphics.Bitmap;
21 | import android.graphics.drawable.Drawable;
22 | import org.junit.Test;
23 | import org.junit.runner.RunWith;
24 | import org.robolectric.RobolectricGradleTestRunner;
25 |
26 | import static android.graphics.Bitmap.Config.ARGB_8888;
27 | import static com.squareup.picasso.Picasso.LoadedFrom.MEMORY;
28 | import static com.squareup.picasso.Picasso.RequestTransformer.IDENTITY;
29 | import static com.squareup.picasso.TestUtils.RESOURCE_ID_1;
30 | import static com.squareup.picasso.TestUtils.URI_KEY_1;
31 | import static com.squareup.picasso.TestUtils.makeBitmap;
32 | import static com.squareup.picasso.TestUtils.mockTarget;
33 | import static org.junit.Assert.fail;
34 | import static org.mockito.Mockito.mock;
35 | import static org.mockito.Mockito.verify;
36 | import static org.mockito.Mockito.when;
37 |
38 | @RunWith(RobolectricGradleTestRunner.class)
39 | public class TargetActionTest {
40 |
41 | @Test(expected = AssertionError.class)
42 | public void throwsErrorWithNullResult() throws Exception {
43 | TargetAction request =
44 | new TargetAction(mock(Picasso.class), mockTarget(), null, 0, 0, null, URI_KEY_1, null, 0);
45 | request.complete(null, MEMORY);
46 | }
47 |
48 | @Test
49 | public void invokesSuccessIfTargetIsNotNull() throws Exception {
50 | Bitmap bitmap = makeBitmap();
51 | Target target = mockTarget();
52 | TargetAction request =
53 | new TargetAction(mock(Picasso.class), target, null, 0, 0, null, URI_KEY_1, null, 0);
54 | request.complete(bitmap, MEMORY);
55 | verify(target).onBitmapLoaded(bitmap, MEMORY);
56 | }
57 |
58 | @Test
59 | public void invokesOnBitmapFailedIfTargetIsNotNullWithErrorDrawable() throws Exception {
60 | Drawable errorDrawable = mock(Drawable.class);
61 | Target target = mockTarget();
62 | TargetAction request =
63 | new TargetAction(mock(Picasso.class), target, null, 0, 0, errorDrawable, URI_KEY_1, null,
64 | 0);
65 | Exception e = new RuntimeException();
66 | request.error(e);
67 | verify(target).onBitmapFailed(e, errorDrawable);
68 | }
69 |
70 | @Test
71 | public void invokesOnBitmapFailedIfTargetIsNotNullWithErrorResourceId() throws Exception {
72 | Drawable errorDrawable = mock(Drawable.class);
73 | Target target = mockTarget();
74 | Context context = mock(Context.class);
75 | Picasso picasso =
76 | new Picasso(context, mock(Dispatcher.class), Cache.NONE, null, IDENTITY, null,
77 | mock(Stats.class), ARGB_8888, false, false);
78 | Resources res = mock(Resources.class);
79 | TargetAction request =
80 | new TargetAction(picasso, target, null, 0, 0, null, URI_KEY_1, null, RESOURCE_ID_1);
81 |
82 | when(context.getResources()).thenReturn(res);
83 | when(res.getDrawable(RESOURCE_ID_1)).thenReturn(errorDrawable);
84 | Exception e = new RuntimeException();
85 | request.error(e);
86 | verify(target).onBitmapFailed(e, errorDrawable);
87 | }
88 |
89 | @Test public void recyclingInSuccessThrowsException() {
90 | Target bad = new Target() {
91 | @Override public void onBitmapLoaded(Bitmap bitmap, Picasso.LoadedFrom from) {
92 | bitmap.recycle();
93 | }
94 |
95 | @Override public void onBitmapFailed(Exception e, Drawable errorDrawable) {
96 | throw new AssertionError();
97 | }
98 |
99 | @Override public void onPrepareLoad(Drawable placeHolderDrawable) {
100 | throw new AssertionError();
101 | }
102 | };
103 | Picasso picasso = mock(Picasso.class);
104 |
105 | Bitmap bitmap = makeBitmap();
106 | TargetAction tr = new TargetAction(picasso, bad, null, 0, 0, null, URI_KEY_1, null, 0);
107 | try {
108 | tr.complete(bitmap, MEMORY);
109 | fail();
110 | } catch (IllegalStateException ignored) {
111 | }
112 | }
113 | }
114 |
--------------------------------------------------------------------------------
/picasso/src/test/java/com/squareup/picasso/MediaStoreRequestHandlerTest.java:
--------------------------------------------------------------------------------
1 | package com.squareup.picasso;
2 |
3 | import android.content.ContentResolver;
4 | import android.content.Context;
5 | import android.graphics.Bitmap;
6 | import android.net.Uri;
7 | import org.junit.Before;
8 | import org.junit.Test;
9 | import org.junit.runner.RunWith;
10 | import org.mockito.Mock;
11 | import org.robolectric.RobolectricGradleTestRunner;
12 | import org.robolectric.annotation.Config;
13 | import org.robolectric.shadows.ShadowBitmap;
14 |
15 | import static android.graphics.Bitmap.Config.ARGB_8888;
16 | import static com.squareup.picasso.MediaStoreRequestHandler.PicassoKind.FULL;
17 | import static com.squareup.picasso.MediaStoreRequestHandler.PicassoKind.MICRO;
18 | import static com.squareup.picasso.MediaStoreRequestHandler.PicassoKind.MINI;
19 | import static com.squareup.picasso.MediaStoreRequestHandler.getPicassoKind;
20 | import static com.squareup.picasso.TestUtils.MEDIA_STORE_CONTENT_1_URL;
21 | import static com.squareup.picasso.TestUtils.MEDIA_STORE_CONTENT_KEY_1;
22 | import static com.squareup.picasso.TestUtils.makeBitmap;
23 | import static com.squareup.picasso.TestUtils.mockAction;
24 | import static org.fest.assertions.api.Assertions.assertThat;
25 | import static org.junit.Assert.fail;
26 | import static org.mockito.Matchers.any;
27 | import static org.mockito.Mockito.mock;
28 | import static org.mockito.Mockito.when;
29 | import static org.mockito.MockitoAnnotations.initMocks;
30 | import static org.robolectric.Shadows.shadowOf;
31 |
32 | @RunWith(RobolectricGradleTestRunner.class)
33 | @Config(shadows = { Shadows.ShadowVideoThumbnails.class, Shadows.ShadowImageThumbnails.class })
34 | public class MediaStoreRequestHandlerTest {
35 |
36 | @Mock Context context;
37 |
38 | @Before public void setUp() {
39 | initMocks(this);
40 | }
41 |
42 | @Test public void decodesVideoThumbnailWithVideoMimeType() throws Exception {
43 | Bitmap bitmap = makeBitmap();
44 | Request request =
45 | new Request.Builder(MEDIA_STORE_CONTENT_1_URL, 0, ARGB_8888).resize(100, 100).build();
46 | Action action = mockAction(MEDIA_STORE_CONTENT_KEY_1, request);
47 | MediaStoreRequestHandler requestHandler = create("video/");
48 | Bitmap result = requestHandler.load(action.getRequest(), 0).getBitmap();
49 | assertBitmapsEqual(result, bitmap);
50 | }
51 |
52 | @Test public void decodesImageThumbnailWithImageMimeType() throws Exception {
53 | Bitmap bitmap = makeBitmap(20, 20);
54 | Request request =
55 | new Request.Builder(MEDIA_STORE_CONTENT_1_URL, 0, ARGB_8888).resize(100, 100).build();
56 | Action action = mockAction(MEDIA_STORE_CONTENT_KEY_1, request);
57 | MediaStoreRequestHandler requestHandler = create("image/png");
58 | Bitmap result = requestHandler.load(action.getRequest(), 0).getBitmap();
59 | assertBitmapsEqual(result, bitmap);
60 | }
61 |
62 | @Test public void getPicassoKindMicro() throws Exception {
63 | assertThat(getPicassoKind(96, 96)).isEqualTo(MICRO);
64 | assertThat(getPicassoKind(95, 95)).isEqualTo(MICRO);
65 | }
66 |
67 | @Test public void getPicassoKindMini() throws Exception {
68 | assertThat(getPicassoKind(512, 384)).isEqualTo(MINI);
69 | assertThat(getPicassoKind(100, 100)).isEqualTo(MINI);
70 | }
71 |
72 | @Test public void getPicassoKindFull() throws Exception {
73 | assertThat(getPicassoKind(513, 385)).isEqualTo(FULL);
74 | assertThat(getPicassoKind(1000, 1000)).isEqualTo(FULL);
75 | assertThat(getPicassoKind(1000, 384)).isEqualTo(FULL);
76 | assertThat(getPicassoKind(1000, 96)).isEqualTo(FULL);
77 | assertThat(getPicassoKind(96, 1000)).isEqualTo(FULL);
78 | }
79 |
80 | private MediaStoreRequestHandler create(String mimeType) {
81 | ContentResolver contentResolver = mock(ContentResolver.class);
82 | when(contentResolver.getType(any(Uri.class))).thenReturn(mimeType);
83 | return create(contentResolver);
84 | }
85 |
86 | private MediaStoreRequestHandler create(ContentResolver contentResolver) {
87 | when(context.getContentResolver()).thenReturn(contentResolver);
88 | return new MediaStoreRequestHandler(context);
89 | }
90 |
91 | private static void assertBitmapsEqual(Bitmap a, Bitmap b) {
92 | ShadowBitmap shadowA = shadowOf(a);
93 | ShadowBitmap shadowB = shadowOf(b);
94 |
95 | if (shadowA.getHeight() != shadowB.getHeight()) {
96 | fail();
97 | }
98 | if (shadowA.getWidth() != shadowB.getWidth()) {
99 | fail();
100 | }
101 | if (shadowA.getDescription() != null ? !shadowA.getDescription().equals(shadowB.getDescription()) : shadowB.getDescription() != null) {
102 | fail();
103 | }
104 | }
105 | }
106 |
--------------------------------------------------------------------------------
/picasso/src/test/java/com/squareup/picasso/RemoteViewsActionTest.java:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright (C) 2014 Square, Inc.
3 | *
4 | * Licensed under the Apache License, Version 2.0 (the "License");
5 | * you may not use this file except in compliance with the License.
6 | * You may obtain a copy of the License at
7 | *
8 | * http://www.apache.org/licenses/LICENSE-2.0
9 | *
10 | * Unless required by applicable law or agreed to in writing, software
11 | * distributed under the License is distributed on an "AS IS" BASIS,
12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | * See the License for the specific language governing permissions and
14 | * limitations under the License.
15 | */
16 | package com.squareup.picasso;
17 |
18 | import android.graphics.Bitmap;
19 | import android.widget.ImageView;
20 | import android.widget.RemoteViews;
21 | import org.junit.Before;
22 | import org.junit.Test;
23 | import org.junit.runner.RunWith;
24 | import org.robolectric.RobolectricGradleTestRunner;
25 | import org.robolectric.RuntimeEnvironment;
26 |
27 | import static android.graphics.Bitmap.Config.ARGB_8888;
28 | import static com.squareup.picasso.Picasso.LoadedFrom.NETWORK;
29 | import static com.squareup.picasso.Picasso.RequestTransformer.IDENTITY;
30 | import static com.squareup.picasso.TestUtils.URI_KEY_1;
31 | import static com.squareup.picasso.TestUtils.makeBitmap;
32 | import static com.squareup.picasso.TestUtils.mockCallback;
33 | import static com.squareup.picasso.TestUtils.mockImageViewTarget;
34 | import static org.fest.assertions.api.Assertions.assertThat;
35 | import static org.mockito.Mockito.mock;
36 | import static org.mockito.Mockito.verify;
37 | import static org.mockito.Mockito.verifyZeroInteractions;
38 | import static org.mockito.Mockito.when;
39 |
40 | @RunWith(RobolectricGradleTestRunner.class) public class RemoteViewsActionTest {
41 |
42 | private Picasso picasso;
43 | private RemoteViews remoteViews;
44 |
45 | @Before public void setUp() {
46 | picasso = createPicasso();
47 | remoteViews = mock(RemoteViews.class);
48 | when(remoteViews.getLayoutId()).thenReturn(android.R.layout.list_content);
49 | }
50 |
51 | @Test public void completeSetsBitmapOnRemoteViews() throws Exception {
52 | Callback callback = mockCallback();
53 | Bitmap bitmap = makeBitmap();
54 | RemoteViewsAction action = createAction(callback);
55 | action.complete(bitmap, NETWORK);
56 | verify(remoteViews).setImageViewBitmap(1, bitmap);
57 | verify(callback).onSuccess();
58 | }
59 |
60 | @Test public void errorWithNoResourceIsNoop() throws Exception {
61 | Callback callback = mockCallback();
62 | RemoteViewsAction action = createAction(callback);
63 | Exception e = new RuntimeException();
64 | action.error(e);
65 | verifyZeroInteractions(remoteViews);
66 | verify(callback).onError(e);
67 | }
68 |
69 | @Test public void errorWithResourceSetsResource() throws Exception {
70 | Callback callback = mockCallback();
71 | RemoteViewsAction action = createAction(1, callback);
72 | Exception e = new RuntimeException();
73 | action.error(e);
74 | verify(remoteViews).setImageViewResource(1, 1);
75 | verify(callback).onError(e);
76 | }
77 |
78 | @Test public void clearsCallbackOnCancel() throws Exception {
79 | Picasso picasso = mock(Picasso.class);
80 | ImageView target = mockImageViewTarget();
81 | Callback callback = mockCallback();
82 | ImageViewAction request =
83 | new ImageViewAction(picasso, target, null, 0, 0, 0, null, URI_KEY_1, null, callback, false);
84 | request.cancel();
85 | assertThat(request.callback).isNull();
86 | }
87 |
88 | private TestableRemoteViewsAction createAction(Callback callback) {
89 | return createAction(0, callback);
90 | }
91 |
92 | private TestableRemoteViewsAction createAction(int errorResId, Callback callback) {
93 | return new TestableRemoteViewsAction(picasso, null, remoteViews, 1, errorResId, 0, 0, null,
94 | URI_KEY_1, callback);
95 | }
96 |
97 | private Picasso createPicasso() {
98 | return new Picasso(RuntimeEnvironment.application, mock(Dispatcher.class), Cache.NONE, null,
99 | IDENTITY, null, mock(Stats.class), ARGB_8888, false, false);
100 | }
101 |
102 | static class TestableRemoteViewsAction extends RemoteViewsAction {
103 | TestableRemoteViewsAction(Picasso picasso, Request data, RemoteViews remoteViews, int viewId,
104 | int errorResId, int memoryPolicy, int networkPolicy, String tag, String key,
105 | Callback callback) {
106 | super(picasso, data, remoteViews, viewId, errorResId, memoryPolicy, networkPolicy, tag, key,
107 | callback);
108 | }
109 |
110 | @Override void update() {
111 | }
112 | }
113 | }
114 |
--------------------------------------------------------------------------------
/picasso/src/test/java/com/squareup/picasso/UtilsTest.java:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright (C) 2013 Square, Inc.
3 | *
4 | * Licensed under the Apache License, Version 2.0 (the "License");
5 | * you may not use this file except in compliance with the License.
6 | * You may obtain a copy of the License at
7 | *
8 | * http://www.apache.org/licenses/LICENSE-2.0
9 | *
10 | * Unless required by applicable law or agreed to in writing, software
11 | * distributed under the License is distributed on an "AS IS" BASIS,
12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | * See the License for the specific language governing permissions and
14 | * limitations under the License.
15 | */
16 | package com.squareup.picasso;
17 |
18 | import android.content.res.Resources;
19 | import java.io.ByteArrayInputStream;
20 | import java.io.IOException;
21 | import org.junit.Test;
22 | import org.junit.runner.RunWith;
23 | import org.robolectric.RobolectricGradleTestRunner;
24 |
25 | import static com.squareup.picasso.TestUtils.RESOURCE_ID_1;
26 | import static com.squareup.picasso.TestUtils.RESOURCE_ID_URI;
27 | import static com.squareup.picasso.TestUtils.RESOURCE_TYPE_URI;
28 | import static com.squareup.picasso.TestUtils.URI_1;
29 | import static com.squareup.picasso.TestUtils.mockPackageResourceContext;
30 | import static com.squareup.picasso.Utils.createKey;
31 | import static com.squareup.picasso.Utils.isWebPFile;
32 | import static org.fest.assertions.api.Assertions.assertThat;
33 |
34 | @RunWith(RobolectricGradleTestRunner.class)
35 | public class UtilsTest {
36 |
37 | @Test public void matchingRequestsHaveSameKey() throws Exception {
38 | Request request = new Request.Builder(URI_1).build();
39 | String key1 = createKey(request);
40 | String key2 = createKey(request);
41 | assertThat(key1).isEqualTo(key2);
42 |
43 | Transformation t1 = new TestTransformation("foo", null);
44 | Transformation t2 = new TestTransformation("foo", null);
45 |
46 | Request requestTransform1 = new Request.Builder(URI_1).transform(t1).build();
47 | Request requestTransform2 = new Request.Builder(URI_1).transform(t2).build();
48 |
49 | String single1 = createKey(requestTransform1);
50 | String single2 = createKey(requestTransform2);
51 | assertThat(single1).isEqualTo(single2);
52 |
53 | Transformation t3 = new TestTransformation("foo", null);
54 | Transformation t4 = new TestTransformation("bar", null);
55 |
56 | Request requestTransform3 = new Request.Builder(URI_1).transform(t3).transform(t4).build();
57 | Request requestTransform4 = new Request.Builder(URI_1).transform(t3).transform(t4).build();
58 |
59 | String double1 = createKey(requestTransform3);
60 | String double2 = createKey(requestTransform4);
61 | assertThat(double1).isEqualTo(double2);
62 |
63 | Transformation t5 = new TestTransformation("foo", null);
64 | Transformation t6 = new TestTransformation("bar", null);
65 |
66 | Request requestTransform5 = new Request.Builder(URI_1).transform(t5).transform(t6).build();
67 | Request requestTransform6 = new Request.Builder(URI_1).transform(t6).transform(t5).build();
68 |
69 | String order1 = createKey(requestTransform5);
70 | String order2 = createKey(requestTransform6);
71 | assertThat(order1).isNotEqualTo(order2);
72 | }
73 |
74 | @Test public void detectedWebPFile() throws Exception {
75 | assertThat(isWebPFile(new ByteArrayInputStream("RIFFxxxxWEBP".getBytes("US-ASCII")))).isTrue();
76 | assertThat(
77 | isWebPFile(new ByteArrayInputStream("RIFFxxxxxWEBP".getBytes("US-ASCII")))).isFalse();
78 | assertThat(isWebPFile(new ByteArrayInputStream("ABCDxxxxWEBP".getBytes("US-ASCII")))).isFalse();
79 | assertThat(isWebPFile(new ByteArrayInputStream("RIFFxxxxABCD".getBytes("US-ASCII")))).isFalse();
80 | assertThat(isWebPFile(new ByteArrayInputStream("RIFFxxWEBP".getBytes("US-ASCII")))).isFalse();
81 | }
82 |
83 | @Test public void ensureBuilderIsCleared() throws Exception {
84 | Request request1 = new Request.Builder(RESOURCE_ID_URI).build();
85 | Request request2 = new Request.Builder(URI_1).build();
86 | Utils.createKey(request1);
87 | assertThat(Utils.MAIN_THREAD_KEY_BUILDER.length()).isEqualTo(0);
88 | Utils.createKey(request2);
89 | assertThat(Utils.MAIN_THREAD_KEY_BUILDER.length()).isEqualTo(0);
90 | }
91 |
92 | @Test public void getResourceById() throws IOException {
93 | Request request = new Request.Builder(RESOURCE_ID_URI).build();
94 | Resources res = Utils.getResources(mockPackageResourceContext(), request);
95 | int id = Utils.getResourceId(res, request);
96 | assertThat(id).isEqualTo(RESOURCE_ID_1);
97 | }
98 |
99 | @Test public void getResourceByTypeAndName() throws IOException {
100 | Request request = new Request.Builder(RESOURCE_TYPE_URI).build();
101 | Resources res = Utils.getResources(mockPackageResourceContext(), request);
102 | int id = Utils.getResourceId(res, request);
103 | assertThat(id).isEqualTo(RESOURCE_ID_1);
104 | }
105 | }
106 |
--------------------------------------------------------------------------------
/picasso/src/main/java/com/squareup/picasso/RemoteViewsAction.java:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright (C) 2014 Square, Inc.
3 | *
4 | * Licensed under the Apache License, Version 2.0 (the "License");
5 | * you may not use this file except in compliance with the License.
6 | * You may obtain a copy of the License at
7 | *
8 | * http://www.apache.org/licenses/LICENSE-2.0
9 | *
10 | * Unless required by applicable law or agreed to in writing, software
11 | * distributed under the License is distributed on an "AS IS" BASIS,
12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | * See the License for the specific language governing permissions and
14 | * limitations under the License.
15 | */
16 | package com.squareup.picasso;
17 |
18 | import android.app.Notification;
19 | import android.app.NotificationManager;
20 | import android.appwidget.AppWidgetManager;
21 | import android.graphics.Bitmap;
22 | import android.widget.RemoteViews;
23 |
24 | import static android.content.Context.NOTIFICATION_SERVICE;
25 | import static com.squareup.picasso.Utils.getService;
26 |
27 | abstract class RemoteViewsAction extends Action {
28 | final RemoteViews remoteViews;
29 | final int viewId;
30 | Callback callback;
31 |
32 | private RemoteViewsTarget target;
33 |
34 | RemoteViewsAction(Picasso picasso, Request data, RemoteViews remoteViews, int viewId,
35 | int errorResId, int memoryPolicy, int networkPolicy, Object tag, String key,
36 | Callback callback) {
37 | super(picasso, null, data, memoryPolicy, networkPolicy, errorResId, null, key, tag, false);
38 | this.remoteViews = remoteViews;
39 | this.viewId = viewId;
40 | this.callback = callback;
41 | }
42 |
43 | @Override void complete(Bitmap result, Picasso.LoadedFrom from) {
44 | remoteViews.setImageViewBitmap(viewId, result);
45 | update();
46 | if (callback != null) {
47 | callback.onSuccess();
48 | }
49 | }
50 |
51 | @Override void cancel() {
52 | super.cancel();
53 | if (callback != null) {
54 | callback = null;
55 | }
56 | }
57 |
58 | @Override public void error(Exception e) {
59 | if (errorResId != 0) {
60 | setImageResource(errorResId);
61 | }
62 | if (callback != null) {
63 | callback.onError(e);
64 | }
65 | }
66 |
67 | @Override RemoteViewsTarget getTarget() {
68 | if (target == null) {
69 | target = new RemoteViewsTarget(remoteViews, viewId);
70 | }
71 | return target;
72 | }
73 |
74 | void setImageResource(int resId) {
75 | remoteViews.setImageViewResource(viewId, resId);
76 | update();
77 | }
78 |
79 | abstract void update();
80 |
81 | static class RemoteViewsTarget {
82 | final RemoteViews remoteViews;
83 | final int viewId;
84 |
85 | RemoteViewsTarget(RemoteViews remoteViews, int viewId) {
86 | this.remoteViews = remoteViews;
87 | this.viewId = viewId;
88 | }
89 |
90 | @Override public boolean equals(Object o) {
91 | if (this == o) return true;
92 | if (o == null || getClass() != o.getClass()) return false;
93 | RemoteViewsTarget remoteViewsTarget = (RemoteViewsTarget) o;
94 | return viewId == remoteViewsTarget.viewId && remoteViews.equals(
95 | remoteViewsTarget.remoteViews);
96 | }
97 |
98 | @Override public int hashCode() {
99 | return 31 * remoteViews.hashCode() + viewId;
100 | }
101 | }
102 |
103 | static class AppWidgetAction extends RemoteViewsAction {
104 | private final int[] appWidgetIds;
105 |
106 | AppWidgetAction(Picasso picasso, Request data, RemoteViews remoteViews, int viewId,
107 | int[] appWidgetIds, int memoryPolicy, int networkPolicy, String key, Object tag,
108 | int errorResId, Callback callback) {
109 | super(picasso, data, remoteViews, viewId, errorResId, memoryPolicy, networkPolicy, tag, key,
110 | callback);
111 | this.appWidgetIds = appWidgetIds;
112 | }
113 |
114 | @Override void update() {
115 | AppWidgetManager manager = AppWidgetManager.getInstance(picasso.context);
116 | manager.updateAppWidget(appWidgetIds, remoteViews);
117 | }
118 | }
119 |
120 | static class NotificationAction extends RemoteViewsAction {
121 | private final int notificationId;
122 | private final String notificationTag;
123 | private final Notification notification;
124 |
125 | NotificationAction(Picasso picasso, Request data, RemoteViews remoteViews, int viewId,
126 | int notificationId, Notification notification, String notificationTag, int memoryPolicy,
127 | int networkPolicy, String key, Object tag, int errorResId, Callback callback) {
128 | super(picasso, data, remoteViews, viewId, errorResId, memoryPolicy, networkPolicy, tag, key,
129 | callback);
130 | this.notificationId = notificationId;
131 | this.notificationTag = notificationTag;
132 | this.notification = notification;
133 | }
134 |
135 | @Override void update() {
136 | NotificationManager manager = getService(picasso.context, NOTIFICATION_SERVICE);
137 | manager.notify(notificationTag, notificationId, notification);
138 | }
139 | }
140 | }
141 |
--------------------------------------------------------------------------------
/picasso/src/main/java/com/squareup/picasso/OkHttp3Downloader.java:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright (C) 2013 Square, Inc.
3 | *
4 | * Licensed under the Apache License, Version 2.0 (the "License");
5 | * you may not use this file except in compliance with the License.
6 | * You may obtain a copy of the License at
7 | *
8 | * http://www.apache.org/licenses/LICENSE-2.0
9 | *
10 | * Unless required by applicable law or agreed to in writing, software
11 | * distributed under the License is distributed on an "AS IS" BASIS,
12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | * See the License for the specific language governing permissions and
14 | * limitations under the License.
15 | */
16 | package com.squareup.picasso;
17 |
18 | import android.content.Context;
19 | import android.net.Uri;
20 | import android.support.annotation.NonNull;
21 | import android.support.annotation.VisibleForTesting;
22 |
23 | import java.io.File;
24 | import java.io.IOException;
25 | import okhttp3.Cache;
26 | import okhttp3.CacheControl;
27 | import okhttp3.Call;
28 | import okhttp3.OkHttpClient;
29 | import okhttp3.Request;
30 | import okhttp3.ResponseBody;
31 |
32 | /** A {@link Downloader} which uses OkHttp to download images. */
33 | public final class OkHttp3Downloader implements Downloader {
34 | private final Call.Factory client;
35 | private final Cache cache;
36 | private boolean sharedClient = true;
37 |
38 | /**
39 | * Create new downloader that uses OkHttp. This will install an image cache into your application
40 | * cache directory.
41 | */
42 | public OkHttp3Downloader(final Context context) {
43 | this(Utils.createDefaultCacheDir(context));
44 | }
45 |
46 | /**
47 | * Create new downloader that uses OkHttp. This will install an image cache into the specified
48 | * directory.
49 | *
50 | * @param cacheDir The directory in which the cache should be stored
51 | */
52 | public OkHttp3Downloader(final File cacheDir) {
53 | this(cacheDir, Utils.calculateDiskCacheSize(cacheDir));
54 | }
55 |
56 | /**
57 | * Create new downloader that uses OkHttp. This will install an image cache into your application
58 | * cache directory.
59 | *
60 | * @param maxSize The size limit for the cache.
61 | */
62 | public OkHttp3Downloader(final Context context, final long maxSize) {
63 | this(Utils.createDefaultCacheDir(context), maxSize);
64 | }
65 |
66 | /**
67 | * Create new downloader that uses OkHttp. This will install an image cache into the specified
68 | * directory.
69 | *
70 | * @param cacheDir The directory in which the cache should be stored
71 | * @param maxSize The size limit for the cache.
72 | */
73 | public OkHttp3Downloader(final File cacheDir, final long maxSize) {
74 | this(new OkHttpClient.Builder().cache(new Cache(cacheDir, maxSize)).build());
75 | sharedClient = false;
76 | }
77 |
78 | /**
79 | * Create a new downloader that uses the specified OkHttp instance. A response cache will not be
80 | * automatically configured.
81 | */
82 | public OkHttp3Downloader(OkHttpClient client) {
83 | this.client = client;
84 | this.cache = client.cache();
85 | }
86 |
87 | /** Create a new downloader that uses the specified {@link Call.Factory} instance. */
88 | public OkHttp3Downloader(Call.Factory client) {
89 | this.client = client;
90 | this.cache = null;
91 | }
92 |
93 | @VisibleForTesting Cache getCache() {
94 | return ((OkHttpClient) client).cache();
95 | }
96 |
97 | @Override public Response load(@NonNull Uri uri, int networkPolicy) throws IOException {
98 | CacheControl cacheControl = null;
99 | if (networkPolicy != 0) {
100 | if (NetworkPolicy.isOfflineOnly(networkPolicy)) {
101 | cacheControl = CacheControl.FORCE_CACHE;
102 | } else {
103 | CacheControl.Builder builder = new CacheControl.Builder();
104 | if (!NetworkPolicy.shouldReadFromDiskCache(networkPolicy)) {
105 | builder.noCache();
106 | }
107 | if (!NetworkPolicy.shouldWriteToDiskCache(networkPolicy)) {
108 | builder.noStore();
109 | }
110 | cacheControl = builder.build();
111 | }
112 | }
113 |
114 | Request.Builder builder = new okhttp3.Request.Builder().url(uri.toString());
115 | if (cacheControl != null) {
116 | builder.cacheControl(cacheControl);
117 | }
118 |
119 | okhttp3.Response response = client.newCall(builder.build()).execute();
120 | int responseCode = response.code();
121 | if (responseCode >= 300) {
122 | response.body().close();
123 | throw new ResponseException(responseCode + " " + response.message(), networkPolicy,
124 | responseCode);
125 | }
126 |
127 | boolean fromCache = response.cacheResponse() != null;
128 |
129 | ResponseBody responseBody = response.body();
130 | return new Response(responseBody.byteStream(), fromCache, responseBody.contentLength());
131 | }
132 |
133 | @Override public void shutdown() {
134 | if (!sharedClient) {
135 | if (cache != null) {
136 | try {
137 | cache.close();
138 | } catch (IOException ignored) {
139 | }
140 | }
141 | }
142 | }
143 | }
144 |
--------------------------------------------------------------------------------
/checkstyle.xml:
--------------------------------------------------------------------------------
1 |
2 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
24 |
25 |
26 |
27 |
28 |
29 |
30 |
31 |
32 |
33 |
34 |
35 |
36 |
37 |
38 |
39 |
40 |
41 |
42 |
43 |
44 |
45 |
46 |
47 |
48 |
49 |
50 |
51 |
52 |
53 |
54 |
55 |
56 |
57 |
58 |
59 |
60 |
61 |
62 |
63 |
64 |
65 |
66 |
67 |
68 |
69 |
70 |
71 |
72 |
73 |
74 |
75 |
76 |
77 |
78 |
79 |
80 |
81 |
82 |
83 |
84 |
85 |
86 |
87 |
88 |
89 |
90 |
91 |
92 |
93 |
94 |
95 |
96 |
97 |
98 |
99 |
100 |
101 |
102 |
103 |
104 |
105 |
106 |
107 |
108 |
109 |
110 |
111 |
112 |
113 |
114 |
115 |
116 |
117 |
118 |
119 |
120 |
121 |
122 |
123 |
124 |
125 |
126 |
127 |
128 |
129 |
130 |
131 |
132 |
133 |
134 |
135 |
136 |
137 |
138 |
--------------------------------------------------------------------------------
/picasso/src/main/java/com/squareup/picasso/LruCache.java:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright (C) 2011 The Android Open Source Project
3 | *
4 | * Licensed under the Apache License, Version 2.0 (the "License");
5 | * you may not use this file except in compliance with the License.
6 | * You may obtain a copy of the License at
7 | *
8 | * http://www.apache.org/licenses/LICENSE-2.0
9 | *
10 | * Unless required by applicable law or agreed to in writing, software
11 | * distributed under the License is distributed on an "AS IS" BASIS,
12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | * See the License for the specific language governing permissions and
14 | * limitations under the License.
15 | */
16 | package com.squareup.picasso;
17 |
18 | import android.content.Context;
19 | import android.graphics.Bitmap;
20 | import android.support.annotation.NonNull;
21 |
22 | import java.util.Iterator;
23 | import java.util.LinkedHashMap;
24 | import java.util.Map;
25 |
26 | import static com.squareup.picasso.Utils.KEY_SEPARATOR;
27 |
28 | /** A memory cache which uses a least-recently used eviction policy. */
29 | public class LruCache implements Cache {
30 | final LinkedHashMap map;
31 | private final int maxSize;
32 |
33 | private int size;
34 | private int putCount;
35 | private int evictionCount;
36 | private int hitCount;
37 | private int missCount;
38 |
39 | /** Create a cache using an appropriate portion of the available RAM as the maximum size. */
40 | public LruCache(@NonNull Context context) {
41 | this(Utils.calculateMemoryCacheSize(context));
42 | }
43 |
44 | /** Create a cache with a given maximum size in bytes. */
45 | public LruCache(int maxSize) {
46 | if (maxSize <= 0) {
47 | throw new IllegalArgumentException("Max size must be positive.");
48 | }
49 | this.maxSize = maxSize;
50 | this.map = new LinkedHashMap<>(0, 0.75f, true);
51 | }
52 |
53 | @Override public Bitmap get(@NonNull String key) {
54 | if (key == null) {
55 | throw new NullPointerException("key == null");
56 | }
57 |
58 | Bitmap mapValue;
59 | synchronized (this) {
60 | mapValue = map.get(key);
61 | if (mapValue != null) {
62 | hitCount++;
63 | return mapValue;
64 | }
65 | missCount++;
66 | }
67 |
68 | return null;
69 | }
70 |
71 | @Override public void set(@NonNull String key, @NonNull Bitmap bitmap) {
72 | if (key == null || bitmap == null) {
73 | throw new NullPointerException("key == null || bitmap == null");
74 | }
75 |
76 | int addedSize = Utils.getBitmapBytes(bitmap);
77 | if (addedSize > maxSize) {
78 | return;
79 | }
80 |
81 | synchronized (this) {
82 | putCount++;
83 | size += addedSize;
84 | Bitmap previous = map.put(key, bitmap);
85 | if (previous != null) {
86 | size -= Utils.getBitmapBytes(previous);
87 | }
88 | }
89 |
90 | trimToSize(maxSize);
91 | }
92 |
93 | private void trimToSize(int maxSize) {
94 | while (true) {
95 | String key;
96 | Bitmap value;
97 | synchronized (this) {
98 | if (size < 0 || (map.isEmpty() && size != 0)) {
99 | throw new IllegalStateException(
100 | getClass().getName() + ".sizeOf() is reporting inconsistent results!");
101 | }
102 |
103 | if (size <= maxSize || map.isEmpty()) {
104 | break;
105 | }
106 |
107 | Map.Entry toEvict = map.entrySet().iterator().next();
108 | key = toEvict.getKey();
109 | value = toEvict.getValue();
110 | map.remove(key);
111 | size -= Utils.getBitmapBytes(value);
112 | evictionCount++;
113 | }
114 | }
115 | }
116 |
117 | /** Clear the cache. */
118 | public final void evictAll() {
119 | trimToSize(-1); // -1 will evict 0-sized elements
120 | }
121 |
122 | @Override public final synchronized int size() {
123 | return size;
124 | }
125 |
126 | @Override public final synchronized int maxSize() {
127 | return maxSize;
128 | }
129 |
130 | @Override public final synchronized void clear() {
131 | evictAll();
132 | }
133 |
134 | @Override public final synchronized void clearKeyUri(String uri) {
135 | int uriLength = uri.length();
136 | for (Iterator> i = map.entrySet().iterator(); i.hasNext();) {
137 | Map.Entry entry = i.next();
138 | String key = entry.getKey();
139 | Bitmap value = entry.getValue();
140 | int newlineIndex = key.indexOf(KEY_SEPARATOR);
141 | if (newlineIndex == uriLength && key.substring(0, newlineIndex).equals(uri)) {
142 | i.remove();
143 | size -= Utils.getBitmapBytes(value);
144 | }
145 | }
146 | }
147 |
148 | /** Returns the number of times {@link #get} returned a value. */
149 | public final synchronized int hitCount() {
150 | return hitCount;
151 | }
152 |
153 | /** Returns the number of times {@link #get} returned {@code null}. */
154 | public final synchronized int missCount() {
155 | return missCount;
156 | }
157 |
158 | /** Returns the number of times {@link #set(String, Bitmap)} was called. */
159 | public final synchronized int putCount() {
160 | return putCount;
161 | }
162 |
163 | /** Returns the number of values that have been evicted. */
164 | public final synchronized int evictionCount() {
165 | return evictionCount;
166 | }
167 | }
168 |
--------------------------------------------------------------------------------
/picasso/src/main/java/com/squareup/picasso/MediaStoreRequestHandler.java:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright (C) 2014 Square, Inc.
3 | *
4 | * Licensed under the Apache License, Version 2.0 (the "License");
5 | * you may not use this file except in compliance with the License.
6 | * You may obtain a copy of the License at
7 | *
8 | * http://www.apache.org/licenses/LICENSE-2.0
9 | *
10 | * Unless required by applicable law or agreed to in writing, software
11 | * distributed under the License is distributed on an "AS IS" BASIS,
12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | * See the License for the specific language governing permissions and
14 | * limitations under the License.
15 | */
16 | package com.squareup.picasso;
17 |
18 | import android.content.ContentResolver;
19 | import android.content.Context;
20 | import android.database.Cursor;
21 | import android.graphics.Bitmap;
22 | import android.graphics.BitmapFactory;
23 | import android.net.Uri;
24 | import android.provider.MediaStore;
25 | import java.io.IOException;
26 |
27 | import static android.content.ContentResolver.SCHEME_CONTENT;
28 | import static android.content.ContentUris.parseId;
29 | import static android.provider.MediaStore.Images;
30 | import static android.provider.MediaStore.Video;
31 | import static android.provider.MediaStore.Images.Thumbnails.FULL_SCREEN_KIND;
32 | import static android.provider.MediaStore.Images.Thumbnails.MICRO_KIND;
33 | import static android.provider.MediaStore.Images.Thumbnails.MINI_KIND;
34 | import static com.squareup.picasso.MediaStoreRequestHandler.PicassoKind.FULL;
35 | import static com.squareup.picasso.MediaStoreRequestHandler.PicassoKind.MICRO;
36 | import static com.squareup.picasso.MediaStoreRequestHandler.PicassoKind.MINI;
37 | import static com.squareup.picasso.Picasso.LoadedFrom.DISK;
38 |
39 | class MediaStoreRequestHandler extends ContentStreamRequestHandler {
40 | private static final String[] CONTENT_ORIENTATION = new String[] {
41 | Images.ImageColumns.ORIENTATION
42 | };
43 |
44 | MediaStoreRequestHandler(Context context) {
45 | super(context);
46 | }
47 |
48 | @Override public boolean canHandleRequest(Request data) {
49 | final Uri uri = data.uri;
50 | return (SCHEME_CONTENT.equals(uri.getScheme())
51 | && MediaStore.AUTHORITY.equals(uri.getAuthority()));
52 | }
53 |
54 | @Override public Result load(Request request, int networkPolicy) throws IOException {
55 | ContentResolver contentResolver = context.getContentResolver();
56 | int exifOrientation = getExifOrientation(contentResolver, request.uri);
57 |
58 | String mimeType = contentResolver.getType(request.uri);
59 | boolean isVideo = mimeType != null && mimeType.startsWith("video/");
60 |
61 | if (request.hasSize()) {
62 | PicassoKind picassoKind = getPicassoKind(request.targetWidth, request.targetHeight);
63 | if (!isVideo && picassoKind == FULL) {
64 | return new Result(null, getInputStream(request), DISK, exifOrientation);
65 | }
66 |
67 | long id = parseId(request.uri);
68 |
69 | BitmapFactory.Options options = createBitmapOptions(request);
70 | options.inJustDecodeBounds = true;
71 |
72 | calculateInSampleSize(request.targetWidth, request.targetHeight, picassoKind.width,
73 | picassoKind.height, options, request);
74 |
75 | Bitmap bitmap;
76 |
77 | if (isVideo) {
78 | // Since MediaStore doesn't provide the full screen kind thumbnail, we use the mini kind
79 | // instead which is the largest thumbnail size can be fetched from MediaStore.
80 | int kind = (picassoKind == FULL) ? Video.Thumbnails.MINI_KIND : picassoKind.androidKind;
81 | bitmap = Video.Thumbnails.getThumbnail(contentResolver, id, kind, options);
82 | } else {
83 | bitmap =
84 | Images.Thumbnails.getThumbnail(contentResolver, id, picassoKind.androidKind, options);
85 | }
86 |
87 | if (bitmap != null) {
88 | return new Result(bitmap, null, DISK, exifOrientation);
89 | }
90 | }
91 |
92 | return new Result(null, getInputStream(request), DISK, exifOrientation);
93 | }
94 |
95 | static PicassoKind getPicassoKind(int targetWidth, int targetHeight) {
96 | if (targetWidth <= MICRO.width && targetHeight <= MICRO.height) {
97 | return MICRO;
98 | } else if (targetWidth <= MINI.width && targetHeight <= MINI.height) {
99 | return MINI;
100 | }
101 | return FULL;
102 | }
103 |
104 | static int getExifOrientation(ContentResolver contentResolver, Uri uri) {
105 | Cursor cursor = null;
106 | try {
107 | cursor = contentResolver.query(uri, CONTENT_ORIENTATION, null, null, null);
108 | if (cursor == null || !cursor.moveToFirst()) {
109 | return 0;
110 | }
111 | return cursor.getInt(0);
112 | } catch (RuntimeException ignored) {
113 | // If the orientation column doesn't exist, assume no rotation.
114 | return 0;
115 | } finally {
116 | if (cursor != null) {
117 | cursor.close();
118 | }
119 | }
120 | }
121 |
122 | enum PicassoKind {
123 | MICRO(MICRO_KIND, 96, 96),
124 | MINI(MINI_KIND, 512, 384),
125 | FULL(FULL_SCREEN_KIND, -1, -1);
126 |
127 | final int androidKind;
128 | final int width;
129 | final int height;
130 |
131 | PicassoKind(int androidKind, int width, int height) {
132 | this.androidKind = androidKind;
133 | this.width = width;
134 | this.height = height;
135 | }
136 | }
137 | }
138 |
--------------------------------------------------------------------------------
/picasso/src/main/java/com/squareup/picasso/PicassoDrawable.java:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright (C) 2013 Square, Inc.
3 | *
4 | * Licensed under the Apache License, Version 2.0 (the "License");
5 | * you may not use this file except in compliance with the License.
6 | * You may obtain a copy of the License at
7 | *
8 | * http://www.apache.org/licenses/LICENSE-2.0
9 | *
10 | * Unless required by applicable law or agreed to in writing, software
11 | * distributed under the License is distributed on an "AS IS" BASIS,
12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | * See the License for the specific language governing permissions and
14 | * limitations under the License.
15 | */
16 | package com.squareup.picasso;
17 |
18 | import android.content.Context;
19 | import android.graphics.Bitmap;
20 | import android.graphics.Canvas;
21 | import android.graphics.ColorFilter;
22 | import android.graphics.Paint;
23 | import android.graphics.Path;
24 | import android.graphics.Rect;
25 | import android.graphics.drawable.AnimationDrawable;
26 | import android.graphics.drawable.BitmapDrawable;
27 | import android.graphics.drawable.Drawable;
28 | import android.os.SystemClock;
29 | import android.widget.ImageView;
30 |
31 | import static android.graphics.Color.WHITE;
32 | import static com.squareup.picasso.Picasso.LoadedFrom.MEMORY;
33 |
34 | final class PicassoDrawable extends BitmapDrawable {
35 | // Only accessed from main thread.
36 | private static final Paint DEBUG_PAINT = new Paint();
37 | private static final float FADE_DURATION = 200f; //ms
38 |
39 | /**
40 | * Create or update the drawable on the target {@link ImageView} to display the supplied bitmap
41 | * image.
42 | */
43 | static void setBitmap(ImageView target, Context context, Bitmap bitmap,
44 | Picasso.LoadedFrom loadedFrom, boolean noFade, boolean debugging) {
45 | Drawable placeholder = target.getDrawable();
46 | if (placeholder instanceof AnimationDrawable) {
47 | ((AnimationDrawable) placeholder).stop();
48 | }
49 | PicassoDrawable drawable =
50 | new PicassoDrawable(context, bitmap, placeholder, loadedFrom, noFade, debugging);
51 | target.setImageDrawable(drawable);
52 | }
53 |
54 | /**
55 | * Create or update the drawable on the target {@link ImageView} to display the supplied
56 | * placeholder image.
57 | */
58 | static void setPlaceholder(ImageView target, Drawable placeholderDrawable) {
59 | target.setImageDrawable(placeholderDrawable);
60 | if (target.getDrawable() instanceof AnimationDrawable) {
61 | ((AnimationDrawable) target.getDrawable()).start();
62 | }
63 | }
64 |
65 | private final boolean debugging;
66 | private final float density;
67 | private final Picasso.LoadedFrom loadedFrom;
68 |
69 | Drawable placeholder;
70 |
71 | long startTimeMillis;
72 | boolean animating;
73 | int alpha = 0xFF;
74 |
75 | PicassoDrawable(Context context, Bitmap bitmap, Drawable placeholder,
76 | Picasso.LoadedFrom loadedFrom, boolean noFade, boolean debugging) {
77 | super(context.getResources(), bitmap);
78 |
79 | this.debugging = debugging;
80 | this.density = context.getResources().getDisplayMetrics().density;
81 |
82 | this.loadedFrom = loadedFrom;
83 |
84 | boolean fade = loadedFrom != MEMORY && !noFade;
85 | if (fade) {
86 | this.placeholder = placeholder;
87 | animating = true;
88 | startTimeMillis = SystemClock.uptimeMillis();
89 | }
90 | }
91 |
92 | @Override public void draw(Canvas canvas) {
93 | if (!animating) {
94 | super.draw(canvas);
95 | } else {
96 | float normalized = (SystemClock.uptimeMillis() - startTimeMillis) / FADE_DURATION;
97 | if (normalized >= 1f) {
98 | animating = false;
99 | placeholder = null;
100 | super.draw(canvas);
101 | } else {
102 | if (placeholder != null) {
103 | placeholder.draw(canvas);
104 | }
105 |
106 | int partialAlpha = (int) (alpha * normalized);
107 | super.setAlpha(partialAlpha);
108 | super.draw(canvas);
109 | super.setAlpha(alpha);
110 | }
111 | }
112 |
113 | if (debugging) {
114 | drawDebugIndicator(canvas);
115 | }
116 | }
117 |
118 | @Override public void setAlpha(int alpha) {
119 | this.alpha = alpha;
120 | if (placeholder != null) {
121 | placeholder.setAlpha(alpha);
122 | }
123 | super.setAlpha(alpha);
124 | }
125 |
126 | @Override public void setColorFilter(ColorFilter cf) {
127 | if (placeholder != null) {
128 | placeholder.setColorFilter(cf);
129 | }
130 | super.setColorFilter(cf);
131 | }
132 |
133 | @Override protected void onBoundsChange(Rect bounds) {
134 | if (placeholder != null) {
135 | placeholder.setBounds(bounds);
136 | }
137 | super.onBoundsChange(bounds);
138 | }
139 |
140 | private void drawDebugIndicator(Canvas canvas) {
141 | DEBUG_PAINT.setColor(WHITE);
142 | Path path = getTrianglePath(0, 0, (int) (16 * density));
143 | canvas.drawPath(path, DEBUG_PAINT);
144 |
145 | DEBUG_PAINT.setColor(loadedFrom.debugColor);
146 | path = getTrianglePath(0, 0, (int) (15 * density));
147 | canvas.drawPath(path, DEBUG_PAINT);
148 | }
149 |
150 | private static Path getTrianglePath(int x1, int y1, int width) {
151 | final Path path = new Path();
152 | path.moveTo(x1, y1);
153 | path.lineTo(x1 + width, y1);
154 | path.lineTo(x1, y1 + width);
155 |
156 | return path;
157 | }
158 | }
159 |
--------------------------------------------------------------------------------
/picasso/src/main/java/com/squareup/picasso/Stats.java:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright (C) 2013 Square, Inc.
3 | *
4 | * Licensed under the Apache License, Version 2.0 (the "License");
5 | * you may not use this file except in compliance with the License.
6 | * You may obtain a copy of the License at
7 | *
8 | * http://www.apache.org/licenses/LICENSE-2.0
9 | *
10 | * Unless required by applicable law or agreed to in writing, software
11 | * distributed under the License is distributed on an "AS IS" BASIS,
12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | * See the License for the specific language governing permissions and
14 | * limitations under the License.
15 | */
16 | package com.squareup.picasso;
17 |
18 | import android.graphics.Bitmap;
19 | import android.os.Handler;
20 | import android.os.HandlerThread;
21 | import android.os.Looper;
22 | import android.os.Message;
23 |
24 | import static android.os.Process.THREAD_PRIORITY_BACKGROUND;
25 |
26 | class Stats {
27 | private static final int CACHE_HIT = 0;
28 | private static final int CACHE_MISS = 1;
29 | private static final int BITMAP_DECODE_FINISHED = 2;
30 | private static final int BITMAP_TRANSFORMED_FINISHED = 3;
31 | private static final int DOWNLOAD_FINISHED = 4;
32 |
33 | private static final String STATS_THREAD_NAME = Utils.THREAD_PREFIX + "Stats";
34 |
35 | final HandlerThread statsThread;
36 | final Cache cache;
37 | final Handler handler;
38 |
39 | long cacheHits;
40 | long cacheMisses;
41 | long totalDownloadSize;
42 | long totalOriginalBitmapSize;
43 | long totalTransformedBitmapSize;
44 | long averageDownloadSize;
45 | long averageOriginalBitmapSize;
46 | long averageTransformedBitmapSize;
47 | int downloadCount;
48 | int originalBitmapCount;
49 | int transformedBitmapCount;
50 |
51 | Stats(Cache cache) {
52 | this.cache = cache;
53 | this.statsThread = new HandlerThread(STATS_THREAD_NAME, THREAD_PRIORITY_BACKGROUND);
54 | this.statsThread.start();
55 | Utils.flushStackLocalLeaks(statsThread.getLooper());
56 | this.handler = new StatsHandler(statsThread.getLooper(), this);
57 | }
58 |
59 | void dispatchBitmapDecoded(Bitmap bitmap) {
60 | processBitmap(bitmap, BITMAP_DECODE_FINISHED);
61 | }
62 |
63 | void dispatchBitmapTransformed(Bitmap bitmap) {
64 | processBitmap(bitmap, BITMAP_TRANSFORMED_FINISHED);
65 | }
66 |
67 | void dispatchDownloadFinished(long size) {
68 | handler.sendMessage(handler.obtainMessage(DOWNLOAD_FINISHED, size));
69 | }
70 |
71 | void dispatchCacheHit() {
72 | handler.sendEmptyMessage(CACHE_HIT);
73 | }
74 |
75 | void dispatchCacheMiss() {
76 | handler.sendEmptyMessage(CACHE_MISS);
77 | }
78 |
79 | void shutdown() {
80 | statsThread.quit();
81 | }
82 |
83 | void performCacheHit() {
84 | cacheHits++;
85 | }
86 |
87 | void performCacheMiss() {
88 | cacheMisses++;
89 | }
90 |
91 | void performDownloadFinished(Long size) {
92 | downloadCount++;
93 | totalDownloadSize += size;
94 | averageDownloadSize = getAverage(downloadCount, totalDownloadSize);
95 | }
96 |
97 | void performBitmapDecoded(long size) {
98 | originalBitmapCount++;
99 | totalOriginalBitmapSize += size;
100 | averageOriginalBitmapSize = getAverage(originalBitmapCount, totalOriginalBitmapSize);
101 | }
102 |
103 | void performBitmapTransformed(long size) {
104 | transformedBitmapCount++;
105 | totalTransformedBitmapSize += size;
106 | averageTransformedBitmapSize = getAverage(originalBitmapCount, totalTransformedBitmapSize);
107 | }
108 |
109 | StatsSnapshot createSnapshot() {
110 | return new StatsSnapshot(cache.maxSize(), cache.size(), cacheHits, cacheMisses,
111 | totalDownloadSize, totalOriginalBitmapSize, totalTransformedBitmapSize, averageDownloadSize,
112 | averageOriginalBitmapSize, averageTransformedBitmapSize, downloadCount, originalBitmapCount,
113 | transformedBitmapCount, System.currentTimeMillis());
114 | }
115 |
116 | private void processBitmap(Bitmap bitmap, int what) {
117 | // Never send bitmaps to the handler as they could be recycled before we process them.
118 | int bitmapSize = Utils.getBitmapBytes(bitmap);
119 | handler.sendMessage(handler.obtainMessage(what, bitmapSize, 0));
120 | }
121 |
122 | private static long getAverage(int count, long totalSize) {
123 | return totalSize / count;
124 | }
125 |
126 | private static class StatsHandler extends Handler {
127 |
128 | private final Stats stats;
129 |
130 | public StatsHandler(Looper looper, Stats stats) {
131 | super(looper);
132 | this.stats = stats;
133 | }
134 |
135 | @Override public void handleMessage(final Message msg) {
136 | switch (msg.what) {
137 | case CACHE_HIT:
138 | stats.performCacheHit();
139 | break;
140 | case CACHE_MISS:
141 | stats.performCacheMiss();
142 | break;
143 | case BITMAP_DECODE_FINISHED:
144 | stats.performBitmapDecoded(msg.arg1);
145 | break;
146 | case BITMAP_TRANSFORMED_FINISHED:
147 | stats.performBitmapTransformed(msg.arg1);
148 | break;
149 | case DOWNLOAD_FINISHED:
150 | stats.performDownloadFinished((Long) msg.obj);
151 | break;
152 | default:
153 | Picasso.HANDLER.post(new Runnable() {
154 | @Override public void run() {
155 | throw new AssertionError("Unhandled stats message." + msg.what);
156 | }
157 | });
158 | }
159 | }
160 | }
161 | }
--------------------------------------------------------------------------------
/picasso/src/test/java/com/squareup/picasso/OkHttp3DownloaderTest.java:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright (C) 2013 Square, Inc.
3 | *
4 | * Licensed under the Apache License, Version 2.0 (the "License");
5 | * you may not use this file except in compliance with the License.
6 | * You may obtain a copy of the License at
7 | *
8 | * http://www.apache.org/licenses/LICENSE-2.0
9 | *
10 | * Unless required by applicable law or agreed to in writing, software
11 | * distributed under the License is distributed on an "AS IS" BASIS,
12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | * See the License for the specific language governing permissions and
14 | * limitations under the License.
15 | */
16 | package com.squareup.picasso;
17 |
18 | import android.net.Uri;
19 | import okhttp3.OkHttpClient;
20 | import okhttp3.mockwebserver.MockResponse;
21 | import okhttp3.mockwebserver.MockWebServer;
22 | import okhttp3.mockwebserver.RecordedRequest;
23 | import okio.Okio;
24 | import org.junit.Before;
25 | import org.junit.Rule;
26 | import org.junit.Test;
27 | import org.junit.rules.TemporaryFolder;
28 | import org.junit.runner.RunWith;
29 | import org.robolectric.RobolectricGradleTestRunner;
30 | import org.robolectric.annotation.Config;
31 |
32 | import static org.fest.assertions.api.Assertions.assertThat;
33 | import static org.junit.Assert.fail;
34 |
35 | @RunWith(RobolectricGradleTestRunner.class)
36 | @Config(shadows = { Shadows.ShadowNetwork.class })
37 | public class OkHttp3DownloaderTest {
38 | @Rule public TemporaryFolder temporaryFolder = new TemporaryFolder();
39 | @Rule public MockWebServer server = new MockWebServer();
40 |
41 | private OkHttp3Downloader downloader;
42 | private Uri uri;
43 |
44 | @Before public void setUp() throws Exception {
45 | downloader = new OkHttp3Downloader(temporaryFolder.getRoot());
46 | uri = Uri.parse(server.url("/").toString());
47 | }
48 |
49 | @Test public void cachedResponse() throws Exception {
50 | server.enqueue(new MockResponse()
51 | .setHeader("Cache-Control", "max-age=31536000")
52 | .setBody("Hi"));
53 |
54 | Downloader.Response response1 = downloader.load(uri, 0);
55 | assertThat(response1.cached).isFalse();
56 | // Exhaust input stream to ensure response is cached.
57 | Okio.buffer(Okio.source(response1.getInputStream())).readByteArray();
58 |
59 | Downloader.Response response2 = downloader.load(uri, NetworkPolicy.OFFLINE.index);
60 | assertThat(response2.cached).isTrue();
61 | }
62 |
63 | @Test public void offlineStaleResponse() throws Exception {
64 | server.enqueue(new MockResponse()
65 | .setHeader("Cache-Control", "max-age=1")
66 | .setHeader("Expires", "Mon, 29 Dec 2014 21:44:55 GMT")
67 | .setBody("Hi"));
68 |
69 | Downloader.Response response1 = downloader.load(uri, 0);
70 | assertThat(response1.cached).isFalse();
71 | // Exhaust input stream to ensure response is cached.
72 | Okio.buffer(Okio.source(response1.getInputStream())).readByteArray();
73 |
74 | Downloader.Response response2 = downloader.load(uri, NetworkPolicy.OFFLINE.index);
75 | assertThat(response2.cached).isTrue();
76 | }
77 |
78 | @Test public void networkPolicyNoCache() throws Exception {
79 | MockResponse response =
80 | new MockResponse().setHeader("Cache-Control", "max-age=31536000").setBody("Hi");
81 | server.enqueue(response);
82 |
83 | Downloader.Response response1 = downloader.load(uri, 0);
84 | assertThat(response1.cached).isFalse();
85 | // Exhaust input stream to ensure response is cached.
86 | Okio.buffer(Okio.source(response1.getInputStream())).readByteArray();
87 |
88 | // Enqueue the same response again but this time use NetworkPolicy.NO_CACHE.
89 | server.enqueue(response);
90 |
91 | Downloader.Response response2 = downloader.load(uri, NetworkPolicy.NO_CACHE.index);
92 | // Response should not be coming from cache even if it was cached previously.
93 | assertThat(response2.cached).isFalse();
94 | }
95 |
96 | @Test public void networkPolicyNoStore() throws Exception {
97 | server.enqueue(new MockResponse());
98 | downloader.load(uri, NetworkPolicy.NO_STORE.index);
99 | RecordedRequest request = server.takeRequest();
100 | assertThat(request.getHeader("Cache-Control")).isEqualTo("no-store");
101 | }
102 |
103 | @Test public void readsContentLengthHeader() throws Exception {
104 | server.enqueue(new MockResponse().addHeader("Content-Length", 1024));
105 |
106 | Downloader.Response response = downloader.load(uri, 0);
107 | assertThat(response.contentLength).isEqualTo(1024);
108 | }
109 |
110 | @Test public void throwsResponseException() throws Exception {
111 | server.enqueue(new MockResponse().setStatus("HTTP/1.1 401 Not Authorized"));
112 |
113 | try {
114 | downloader.load(uri, 0);
115 | fail("Expected ResponseException.");
116 | } catch (Downloader.ResponseException e) {
117 | assertThat(e).hasMessage("401 Not Authorized");
118 | }
119 | }
120 |
121 | @Test public void shutdownClosesCacheIfNotShared() throws Exception {
122 | OkHttp3Downloader downloader = new OkHttp3Downloader(temporaryFolder.getRoot());
123 | okhttp3.Cache cache = downloader.getCache();
124 | downloader.shutdown();
125 | assertThat(cache.isClosed()).isTrue();
126 | }
127 |
128 | @Test public void shutdownDoesNotCloseCacheIfSharedClient() throws Exception {
129 | okhttp3.Cache cache = new okhttp3.Cache(temporaryFolder.getRoot(), 100);
130 | OkHttpClient client = new OkHttpClient.Builder().cache(cache).build();
131 | new OkHttp3Downloader(client).shutdown();
132 | assertThat(cache.isClosed()).isFalse();
133 | }
134 | }
135 |
--------------------------------------------------------------------------------
/gradlew:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env bash
2 |
3 | ##############################################################################
4 | ##
5 | ## Gradle start up script for UN*X
6 | ##
7 | ##############################################################################
8 |
9 | # Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
10 | DEFAULT_JVM_OPTS=""
11 |
12 | APP_NAME="Gradle"
13 | APP_BASE_NAME=`basename "$0"`
14 |
15 | # Use the maximum available, or set MAX_FD != -1 to use that value.
16 | MAX_FD="maximum"
17 |
18 | warn ( ) {
19 | echo "$*"
20 | }
21 |
22 | die ( ) {
23 | echo
24 | echo "$*"
25 | echo
26 | exit 1
27 | }
28 |
29 | # OS specific support (must be 'true' or 'false').
30 | cygwin=false
31 | msys=false
32 | darwin=false
33 | case "`uname`" in
34 | CYGWIN* )
35 | cygwin=true
36 | ;;
37 | Darwin* )
38 | darwin=true
39 | ;;
40 | MINGW* )
41 | msys=true
42 | ;;
43 | esac
44 |
45 | # For Cygwin, ensure paths are in UNIX format before anything is touched.
46 | if $cygwin ; then
47 | [ -n "$JAVA_HOME" ] && JAVA_HOME=`cygpath --unix "$JAVA_HOME"`
48 | fi
49 |
50 | # Attempt to set APP_HOME
51 | # Resolve links: $0 may be a link
52 | PRG="$0"
53 | # Need this for relative symlinks.
54 | while [ -h "$PRG" ] ; do
55 | ls=`ls -ld "$PRG"`
56 | link=`expr "$ls" : '.*-> \(.*\)$'`
57 | if expr "$link" : '/.*' > /dev/null; then
58 | PRG="$link"
59 | else
60 | PRG=`dirname "$PRG"`"/$link"
61 | fi
62 | done
63 | SAVED="`pwd`"
64 | cd "`dirname \"$PRG\"`/" >&-
65 | APP_HOME="`pwd -P`"
66 | cd "$SAVED" >&-
67 |
68 | CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar
69 |
70 | # Determine the Java command to use to start the JVM.
71 | if [ -n "$JAVA_HOME" ] ; then
72 | if [ -x "$JAVA_HOME/jre/sh/java" ] ; then
73 | # IBM's JDK on AIX uses strange locations for the executables
74 | JAVACMD="$JAVA_HOME/jre/sh/java"
75 | else
76 | JAVACMD="$JAVA_HOME/bin/java"
77 | fi
78 | if [ ! -x "$JAVACMD" ] ; then
79 | die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME
80 |
81 | Please set the JAVA_HOME variable in your environment to match the
82 | location of your Java installation."
83 | fi
84 | else
85 | JAVACMD="java"
86 | which java >/dev/null 2>&1 || die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
87 |
88 | Please set the JAVA_HOME variable in your environment to match the
89 | location of your Java installation."
90 | fi
91 |
92 | # Increase the maximum file descriptors if we can.
93 | if [ "$cygwin" = "false" -a "$darwin" = "false" ] ; then
94 | MAX_FD_LIMIT=`ulimit -H -n`
95 | if [ $? -eq 0 ] ; then
96 | if [ "$MAX_FD" = "maximum" -o "$MAX_FD" = "max" ] ; then
97 | MAX_FD="$MAX_FD_LIMIT"
98 | fi
99 | ulimit -n $MAX_FD
100 | if [ $? -ne 0 ] ; then
101 | warn "Could not set maximum file descriptor limit: $MAX_FD"
102 | fi
103 | else
104 | warn "Could not query maximum file descriptor limit: $MAX_FD_LIMIT"
105 | fi
106 | fi
107 |
108 | # For Darwin, add options to specify how the application appears in the dock
109 | if $darwin; then
110 | GRADLE_OPTS="$GRADLE_OPTS \"-Xdock:name=$APP_NAME\" \"-Xdock:icon=$APP_HOME/media/gradle.icns\""
111 | fi
112 |
113 | # For Cygwin, switch paths to Windows format before running java
114 | if $cygwin ; then
115 | APP_HOME=`cygpath --path --mixed "$APP_HOME"`
116 | CLASSPATH=`cygpath --path --mixed "$CLASSPATH"`
117 |
118 | # We build the pattern for arguments to be converted via cygpath
119 | ROOTDIRSRAW=`find -L / -maxdepth 1 -mindepth 1 -type d 2>/dev/null`
120 | SEP=""
121 | for dir in $ROOTDIRSRAW ; do
122 | ROOTDIRS="$ROOTDIRS$SEP$dir"
123 | SEP="|"
124 | done
125 | OURCYGPATTERN="(^($ROOTDIRS))"
126 | # Add a user-defined pattern to the cygpath arguments
127 | if [ "$GRADLE_CYGPATTERN" != "" ] ; then
128 | OURCYGPATTERN="$OURCYGPATTERN|($GRADLE_CYGPATTERN)"
129 | fi
130 | # Now convert the arguments - kludge to limit ourselves to /bin/sh
131 | i=0
132 | for arg in "$@" ; do
133 | CHECK=`echo "$arg"|egrep -c "$OURCYGPATTERN" -`
134 | CHECK2=`echo "$arg"|egrep -c "^-"` ### Determine if an option
135 |
136 | if [ $CHECK -ne 0 ] && [ $CHECK2 -eq 0 ] ; then ### Added a condition
137 | eval `echo args$i`=`cygpath --path --ignore --mixed "$arg"`
138 | else
139 | eval `echo args$i`="\"$arg\""
140 | fi
141 | i=$((i+1))
142 | done
143 | case $i in
144 | (0) set -- ;;
145 | (1) set -- "$args0" ;;
146 | (2) set -- "$args0" "$args1" ;;
147 | (3) set -- "$args0" "$args1" "$args2" ;;
148 | (4) set -- "$args0" "$args1" "$args2" "$args3" ;;
149 | (5) set -- "$args0" "$args1" "$args2" "$args3" "$args4" ;;
150 | (6) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" ;;
151 | (7) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" ;;
152 | (8) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" ;;
153 | (9) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" "$args8" ;;
154 | esac
155 | fi
156 |
157 | # Split up the JVM_OPTS And GRADLE_OPTS values into an array, following the shell quoting and substitution rules
158 | function splitJvmOpts() {
159 | JVM_OPTS=("$@")
160 | }
161 | eval splitJvmOpts $DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS
162 | JVM_OPTS[${#JVM_OPTS[*]}]="-Dorg.gradle.appname=$APP_BASE_NAME"
163 |
164 | exec "$JAVACMD" "${JVM_OPTS[@]}" -classpath "$CLASSPATH" org.gradle.wrapper.GradleWrapperMain "$@"
165 |
--------------------------------------------------------------------------------