================================================
FILE: .idea/vcs.xml
================================================
================================================
FILE: README.md
================================================
NineGridLayout
--------------
1. 简介
这是一个用于实现像微信朋友圈和微博的类似的九宫格图片展示控件,通过自定义viewgroup实现,是用方便,只需要很简单的就可嵌入到项目中去。
----------
2. 使用方法
在项目的layout文件中添加如下xml即可加入到布局文件
因为大部分这类控件都是在listview里面用的,所以针对Listview的复用有针对性的优化,只需要在项目中调用NineGridlayout.setImagesData就可以实现图片的加载和显示。同时为了解耦合和,重写了一个CustomImageView嵌入了picasso图片加载库来加载图片。
----------
3. 效果展示
由于我用markdown嵌入图片发现大小不能调整,所以如果想要看跟多详情,可以去我的CSDN博客,地址:[自定义九宫格控件NineGridLayout ,实现微信朋友圈图片九宫格显示][1]
![效果1][2]
![效果2][3]
[1]: http://blog.csdn.net/u012650948/article/details/43638427
[3]: http://img.blog.csdn.net/20150208195246244?watermark/2/text/aHR0cDovL2Jsb2cuY3Nkbi5uZXQvdTAxMjY1MDk0OA==/font/5a6L5L2T/fontsize/400/fill/I0JBQkFCMA==/dissolve/70/gravity/Center
----------
4. 协议
> /*
* Copyright (C) 2015 panyihong
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
================================================
FILE: WeixinNineGridLayout.iml
================================================
================================================
FILE: app/build.gradle
================================================
apply plugin: 'com.android.application'
android {
compileSdkVersion 21
buildToolsVersion "21.1.0"
defaultConfig {
applicationId "com.weixinninegridlayout"
minSdkVersion 9
targetSdkVersion 21
versionCode 1
versionName "1.0"
}
buildTypes {
release {
minifyEnabled false
proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
}
}
}
dependencies {
compile fileTree(dir: 'libs', include: ['*.jar'])
compile 'com.android.support:appcompat-v7:21.0.0'
compile files('libs/picasso-2.3.4.jar')
}
================================================
FILE: app/proguard-rules.pro
================================================
# Add project specific ProGuard rules here.
# By default, the flags in this file are appended to flags specified
# in D:\android statio\sdk/tools/proguard/proguard-android.txt
# You can edit the include path and order by changing the proguardFiles
# directive in build.gradle.
#
# For more details, see
# http://developer.android.com/guide/developing/tools/proguard.html
# Add any project specific keep options here:
# If your project uses WebView with JS, uncomment the following
# and specify the fully qualified class name to the JavaScript interface
# class:
#-keepclassmembers class fqcn.of.javascript.interface.for.webview {
# public *;
#}
================================================
FILE: app/src/androidTest/java/com/example/administrator/weixinninegridlayout/ApplicationTest.java
================================================
package com.example.administrator.weixinninegridlayout;
import android.app.Application;
import android.test.ApplicationTestCase;
/**
* Testing Fundamentals
*/
public class ApplicationTest extends ApplicationTestCase {
public ApplicationTest() {
super(Application.class);
}
}
================================================
FILE: app/src/main/AndroidManifest.xml
================================================
================================================
FILE: app/src/main/java/com/weixinninegridlayout/CustomImageView.java
================================================
package com.weixinninegridlayout;
import android.content.Context;
import android.graphics.Color;
import android.graphics.PorterDuff;
import android.graphics.drawable.ColorDrawable;
import android.graphics.drawable.Drawable;
import android.text.TextUtils;
import android.util.AttributeSet;
import android.view.MotionEvent;
import android.widget.ImageView;
import com.squareup.picasso.Picasso;
/**
* Created by Pan_ on 2015/2/2.
*/
public class CustomImageView extends ImageView {
private String url;
private boolean isAttachedToWindow;
public CustomImageView(Context context, AttributeSet attrs) {
super(context, attrs);
}
public CustomImageView(Context context) {
super(context);
}
@Override
public boolean onTouchEvent(MotionEvent event) {
switch (event.getAction()) {
case MotionEvent.ACTION_DOWN:
Drawable drawable=getDrawable();
if(drawable!=null) {
drawable.mutate().setColorFilter(Color.GRAY,
PorterDuff.Mode.MULTIPLY);
}
break;
case MotionEvent.ACTION_MOVE:
break;
case MotionEvent.ACTION_CANCEL:
case MotionEvent.ACTION_UP:
Drawable drawableUp=getDrawable();
if(drawableUp!=null) {
drawableUp.mutate().clearColorFilter();
}
break;
}
return super.onTouchEvent(event);
}
@Override
public void onAttachedToWindow() {
isAttachedToWindow = true;
setImageUrl(url);
super.onAttachedToWindow();
}
@Override
public void onDetachedFromWindow() {
Picasso.with(getContext()).cancelRequest(this);
isAttachedToWindow = false;
setImageBitmap(null);
super.onDetachedFromWindow();
}
public void setImageUrl(String url) {
if (!TextUtils.isEmpty(url)) {
this.url = url;
if (isAttachedToWindow) {
Picasso.with(getContext()).load(url).placeholder(new ColorDrawable(Color.parseColor("#f5f5f5"))).into(this);
}
}
}
}
================================================
FILE: app/src/main/java/com/weixinninegridlayout/Image.java
================================================
package com.weixinninegridlayout;
/**
* Created by Pan_ on 2015/2/3.
*/
public class Image {
private String url;
private int width;
private int height;
public Image(String url, int width, int height) {
this.url = url;
this.width = width;
this.height = height;
L.i(toString());
}
public String getUrl() {
return url;
}
public void setUrl(String url) {
this.url = url;
}
public int getWidth() {
return width;
}
public void setWidth(int width) {
this.width = width;
}
public int getHeight() {
return height;
}
public void setHeight(int height) {
this.height = height;
}
@Override
public String toString() {
return "image---->>url="+url+"width="+width+"height"+height;
}
}
================================================
FILE: app/src/main/java/com/weixinninegridlayout/L.java
================================================
package com.weixinninegridlayout;
import android.util.Log;
public final class L {
private L() {
}
public static void v(Throwable t) {
log(Log.VERBOSE, t, null);
}
public static void v(Object format, Object... args) {
log(Log.VERBOSE, null, format, args);
}
public static void v(Throwable t, Object format, Object... args) {
log(Log.VERBOSE, t, format, args);
}
public static void d(Throwable t) {
log(Log.DEBUG, t, null);
}
public static void d(Object format, Object... args) {
log(Log.DEBUG, null, format, args);
}
public static void d(Throwable t, Object format, Object... args) {
log(Log.DEBUG, t, format, args);
}
public static void i(Throwable t) {
log(Log.INFO, t, null);
}
public static void i(Object format, Object... args) {
log(Log.INFO, null, format, args);
}
public static void i(Throwable t, Object format, Object... args) {
log(Log.INFO, t, format, args);
}
public static void w(Throwable t) {
log(Log.WARN, t, null);
}
public static void w(Object format, Object... args) {
log(Log.WARN, null, format, args);
}
public static void w(Throwable t, Object format, Object... args) {
log(Log.WARN, t, format, args);
}
public static void e(Throwable t) {
log(Log.ERROR, t, null);
}
public static void e(Object format, Object... args) {
log(Log.ERROR, null, format, args);
}
public static void e(Throwable t, Object format, Object... args) {
log(Log.ERROR, t, format, args);
}
private static void log(final int pType, final Throwable t, final Object format,
final Object... args) {
final StackTraceElement stackTraceElement = Thread.currentThread().getStackTrace()[4];
final String fullClassName = stackTraceElement.getClassName();
final String className = fullClassName.substring(fullClassName.lastIndexOf(".") + 1);
final int lineNumber = stackTraceElement.getLineNumber();
final String method = stackTraceElement.getMethodName();
final String tag = new StringBuilder("[").append(Thread.currentThread().getId()).append("]")
.append(className).append("<").append(lineNumber).append(">").toString();
final StringBuilder stringBuilder = new StringBuilder();
stringBuilder.append(method);
stringBuilder.append("(): ");
if (format != null) {
try {
final String message = (args == null) ? format.toString() : String.format(
(String) format, args);
stringBuilder.append(message);
} catch (Exception e) {
stringBuilder.append("日志格式化异常:");
stringBuilder.append(format.toString());
e.printStackTrace();
}
}
switch (pType) {
case Log.VERBOSE:
if (t != null) {
Log.v(tag, stringBuilder.toString(), t);
} else {
Log.v(tag, stringBuilder.toString());
}
break;
case Log.DEBUG:
if (t != null) {
Log.d(tag, stringBuilder.toString(), t);
} else {
Log.d(tag, stringBuilder.toString());
}
break;
case Log.INFO:
if (t != null) {
Log.i(tag, stringBuilder.toString(), t);
} else {
Log.i(tag, stringBuilder.toString());
}
break;
case Log.WARN:
if (t != null) {
Log.w(tag, stringBuilder.toString(), t);
} else {
Log.w(tag, stringBuilder.toString());
}
break;
case Log.ERROR:
if (t != null) {
Log.e(tag, stringBuilder.toString(), t);
} else {
Log.e(tag, stringBuilder.toString());
}
break;
}
// }
}
}
================================================
FILE: app/src/main/java/com/weixinninegridlayout/MainActivity.java
================================================
package com.weixinninegridlayout;
import android.support.v7.app.ActionBarActivity;
import android.os.Bundle;
import android.view.Menu;
import android.view.MenuItem;
import android.widget.ListView;
import java.util.ArrayList;
import java.util.List;
public class MainActivity extends ActionBarActivity {
private ListView listView;
private List> imagesList;
private String[][] images=new String[][]{{
"http://img4.duitang.com/uploads/item/201209/25/20120925201555_eUHEU.jpeg","640","960"}
,{"file:///android_asset/img2.jpg","250","250"}
,{"file:///android_asset/img3.jpg","250","250"}
,{"file:///android_asset/img4.jpg","250","250"}
,{"file:///android_asset/img5.jpg","250","250"}
,{"file:///android_asset/img6.jpg","250","250"}
,{"file:///android_asset/img7.jpg","250","250"}
,{"file:///android_asset/img8.jpg","250","250"}
,{"http://img3.douban.com/view/photo/raw/public/p1708880537.jpg","1280","800"}
};
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
listView= (ListView) findViewById(R.id.lv_main);
initData();
listView.setAdapter(new MainAdapter(MainActivity.this,imagesList));
}
private void initData() {
imagesList=new ArrayList<>();
//这里单独添加一条单条的测试数据,用来测试单张的时候横竖图片的效果
ArrayList singleList=new ArrayList<>();
singleList.add(new Image(images[8][0],Integer.parseInt(images[8][1]),Integer.parseInt(images[8][2])));
imagesList.add(singleList);
//从一到9生成9条朋友圈内容,分别是1~9张图片
for(int i=0;i<9;i++){
ArrayList itemList=new ArrayList<>();
for(int j=0;j<=i;j++){
itemList.add(new Image(images[j][0],Integer.parseInt(images[j][1]),Integer.parseInt(images[j][2])));
}
imagesList.add(itemList);
}
}
}
================================================
FILE: app/src/main/java/com/weixinninegridlayout/MainAdapter.java
================================================
package com.weixinninegridlayout;
import android.content.Context;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.BaseAdapter;
import java.util.List;
/**
* Created by Pan_ on 2015/2/3.
*/
public class MainAdapter extends BaseAdapter {
private Context context;
private List> datalist;
public MainAdapter(Context context, List> datalist) {
this.context = context;
this.datalist = datalist;
}
@Override
public int getCount() {
return datalist.size();
}
@Override
public Object getItem(int position) {
return datalist.get(position);
}
@Override
public long getItemId(int position) {
return position;
}
@Override
public View getView(int position, View convertView, ViewGroup parent) {
ViewHolder viewHolder;
List itemList = datalist.get(position);
if (convertView == null) {
convertView = LayoutInflater.from(context).inflate(R.layout.item_ninegridlayout, parent, false);
viewHolder = new ViewHolder();
viewHolder.ivMore = (NineGridlayout) convertView.findViewById(R.id.iv_ngrid_layout);
viewHolder.ivOne = (CustomImageView) convertView.findViewById(R.id.iv_oneimage);
convertView.setTag(viewHolder);
} else {
viewHolder = (ViewHolder) convertView.getTag();
}
if (itemList.isEmpty() || itemList.isEmpty()) {
viewHolder.ivMore.setVisibility(View.GONE);
viewHolder.ivOne.setVisibility(View.GONE);
} else if (itemList.size() == 1) {
viewHolder.ivMore.setVisibility(View.GONE);
viewHolder.ivOne.setVisibility(View.VISIBLE);
handlerOneImage(viewHolder, itemList.get(0));
} else {
viewHolder.ivMore.setVisibility(View.VISIBLE);
viewHolder.ivOne.setVisibility(View.GONE);
viewHolder.ivMore.setImagesData(itemList);
}
return convertView;
}
private void handlerOneImage(ViewHolder viewHolder, Image image) {
int totalWidth;
int imageWidth;
int imageHeight;
ScreenTools screentools = ScreenTools.instance(context);
totalWidth = screentools.getScreenWidth() - screentools.dip2px(80);
imageWidth = screentools.dip2px(image.getWidth());
imageHeight = screentools.dip2px(image.getHeight());
if (image.getWidth() <= image.getHeight()) {
if (imageHeight > totalWidth) {
imageHeight = totalWidth;
imageWidth = (imageHeight * image.getWidth()) / image.getHeight();
}
} else {
if (imageWidth > totalWidth) {
imageWidth = totalWidth;
imageHeight = (imageWidth * image.getHeight()) / image.getWidth();
}
}
ViewGroup.LayoutParams layoutparams = viewHolder.ivOne.getLayoutParams();
layoutparams.height = imageHeight;
layoutparams.width = imageWidth;
viewHolder.ivOne.setLayoutParams(layoutparams);
viewHolder.ivOne.setClickable(true);
viewHolder.ivOne.setScaleType(android.widget.ImageView.ScaleType.FIT_XY);
viewHolder.ivOne.setImageUrl(image.getUrl());
}
class ViewHolder {
public NineGridlayout ivMore;
public CustomImageView ivOne;
}
}
================================================
FILE: app/src/main/java/com/weixinninegridlayout/NineGridlayout.java
================================================
package com.weixinninegridlayout;
import android.content.Context;
import android.graphics.Color;
import android.graphics.drawable.ColorDrawable;
import android.util.AttributeSet;
import android.view.View;
import android.view.ViewGroup;
import android.widget.ImageView;
import java.util.List;
/**
* Created by Pan_ on 2015/2/2.
*/
public class NineGridlayout extends ViewGroup {
/**
* 图片之间的间隔
*/
private int gap = 5;
private int columns;//
private int rows;//
private List listData;
private int totalWidth;
public NineGridlayout(Context context) {
super(context);
}
public NineGridlayout(Context context, AttributeSet attrs) {
super(context, attrs);
ScreenTools screenTools=ScreenTools.instance(getContext());
totalWidth=screenTools.getScreenWidth()-screenTools.dip2px(80);
}
@Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
super.onMeasure(widthMeasureSpec, heightMeasureSpec);
}
@Override
protected void onLayout(boolean changed, int l, int t, int r, int b) {
}
private void layoutChildrenView(){
int childrenCount = listData.size();
int singleWidth = (totalWidth - gap * (3 - 1)) / 3;
int singleHeight = singleWidth;
//根据子view数量确定高度
ViewGroup.LayoutParams params = getLayoutParams();
params.height = singleHeight * rows + gap * (rows - 1);
setLayoutParams(params);
for (int i = 0; i < childrenCount; i++) {
CustomImageView childrenView = (CustomImageView) getChildAt(i);
childrenView.setImageUrl(((Image) listData.get(i)).getUrl());
int[] position = findPosition(i);
int left = (singleWidth + gap) * position[1];
int top = (singleHeight + gap) * position[0];
int right = left + singleWidth;
int bottom = top + singleHeight;
childrenView.layout(left, top, right, bottom);
}
}
private int[] findPosition(int childNum) {
int[] position = new int[2];
for (int i = 0; i < rows; i++) {
for (int j = 0; j < columns; j++) {
if ((i * columns + j) == childNum) {
position[0] = i;//行
position[1] = j;//列
break;
}
}
}
return position;
}
public int getGap() {
return gap;
}
public void setGap(int gap) {
this.gap = gap;
}
public void setImagesData(List lists) {
if (lists == null || lists.isEmpty()) {
return;
}
//初始化布局
generateChildrenLayout(lists.size());
//这里做一个重用view的处理
if (listData == null) {
int i = 0;
while (i < lists.size()) {
CustomImageView iv = generateImageView();
addView(iv,generateDefaultLayoutParams());
i++;
}
} else {
int oldViewCount = listData.size();
int newViewCount = lists.size();
if (oldViewCount > newViewCount) {
removeViews(newViewCount - 1, oldViewCount - newViewCount);
} else if (oldViewCount < newViewCount) {
for (int i = 0; i < newViewCount - oldViewCount; i++) {
CustomImageView iv = generateImageView();
addView(iv,generateDefaultLayoutParams());
}
}
}
listData = lists;
layoutChildrenView();
}
/**
* 根据图片个数确定行列数量
* 对应关系如下
* num row column
* 1 1 1
* 2 1 2
* 3 1 3
* 4 2 2
* 5 2 3
* 6 2 3
* 7 3 3
* 8 3 3
* 9 3 3
*
* @param length
*/
private void generateChildrenLayout(int length) {
if (length <= 3) {
rows = 1;
columns = length;
} else if (length <= 6) {
rows = 2;
columns = 3;
if (length == 4) {
columns = 2;
}
} else {
rows = 3;
columns = 3;
}
}
private CustomImageView generateImageView() {
CustomImageView iv = new CustomImageView(getContext());
iv.setScaleType(ImageView.ScaleType.CENTER_CROP);
iv.setOnClickListener(new OnClickListener() {
@Override
public void onClick(View v) {
}
});
iv.setBackgroundColor(Color.parseColor("#f5f5f5"));
return iv;
}
}
================================================
FILE: app/src/main/java/com/weixinninegridlayout/ScreenTools.java
================================================
package com.weixinninegridlayout;
import android.content.Context;
public class ScreenTools {
private static ScreenTools mScreenTools;
private Context context;
private ScreenTools(Context context) {
this.context = context.getApplicationContext();
}
public static ScreenTools instance(Context context) {
if (mScreenTools == null)
mScreenTools = new ScreenTools(context);
return mScreenTools;
}
public int dip2px(float f) {
return (int) (0.5D + (double) (f * getDensity(context)));
}
public int dip2px(int i) {
return (int) (0.5D + (double) (getDensity(context) * (float) i));
}
public int get480Height(int i) {
return (i * getScreenWidth()) / 480;
}
public float getDensity(Context context) {
return context.getResources().getDisplayMetrics().density;
}
public int getScal() {
return (100 * getScreenWidth()) / 480;
}
public int getScreenDensityDpi() {
return context.getResources().getDisplayMetrics().densityDpi;
}
public int getScreenHeight() {
return context.getResources().getDisplayMetrics().heightPixels;
}
public int getScreenWidth() {
return context.getResources().getDisplayMetrics().widthPixels;
}
public float getXdpi() {
return context.getResources().getDisplayMetrics().xdpi;
}
public float getYdpi() {
return context.getResources().getDisplayMetrics().ydpi;
}
public int px2dip(float f) {
float f1 = getDensity(context);
return (int) (((double) f - 0.5D) / (double) f1);
}
public int px2dip(int i) {
float f = getDensity(context);
return (int) (((double) i - 0.5D) / (double) f);
}
}
================================================
FILE: app/src/main/res/layout/activity_main.xml
================================================
================================================
FILE: app/src/main/res/layout/item_ninegridlayout.xml
================================================
================================================
FILE: app/src/main/res/menu/menu_main.xml
================================================
================================================
FILE: app/src/main/res/values/dimens.xml
================================================
16dp16dp
================================================
FILE: app/src/main/res/values/strings.xml
================================================
WeixinNineGridLayoutHello world!Settings
================================================
FILE: app/src/main/res/values/styles.xml
================================================
================================================
FILE: app/src/main/res/values-w820dp/dimens.xml
================================================
64dp
================================================
FILE: build.gradle
================================================
// Top-level build file where you can add configuration options common to all sub-projects/modules.
buildscript {
repositories {
jcenter()
}
dependencies {
classpath 'com.android.tools.build:gradle:1.0.0'
// NOTE: Do not place your application dependencies here; they belong
// in the individual module build.gradle files
}
}
allprojects {
repositories {
jcenter()
}
}
================================================
FILE: gradle/wrapper/gradle-wrapper.properties
================================================
#Wed Apr 10 15:27:10 PDT 2013
distributionBase=GRADLE_USER_HOME
distributionPath=wrapper/dists
zipStoreBase=GRADLE_USER_HOME
zipStorePath=wrapper/dists
distributionUrl=https\://services.gradle.org/distributions/gradle-2.2.1-all.zip
================================================
FILE: gradle.properties
================================================
# Project-wide Gradle settings.
# IDE (e.g. Android Studio) users:
# Gradle settings configured through the IDE *will override*
# any settings specified in this file.
# For more details on how to configure your build environment visit
# http://www.gradle.org/docs/current/userguide/build_environment.html
# Specifies the JVM arguments used for the daemon process.
# The setting is particularly useful for tweaking memory settings.
# Default value: -Xmx10248m -XX:MaxPermSize=256m
# org.gradle.jvmargs=-Xmx2048m -XX:MaxPermSize=512m -XX:+HeapDumpOnOutOfMemoryError -Dfile.encoding=UTF-8
# When configured, Gradle will run in incubating parallel mode.
# This option should only be used with decoupled projects. More details, visit
# http://www.gradle.org/docs/current/userguide/multi_project_builds.html#sec:decoupled_projects
# org.gradle.parallel=true
================================================
FILE: gradlew
================================================
#!/usr/bin/env bash
##############################################################################
##
## Gradle start up script for UN*X
##
##############################################################################
# Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
DEFAULT_JVM_OPTS=""
APP_NAME="Gradle"
APP_BASE_NAME=`basename "$0"`
# Use the maximum available, or set MAX_FD != -1 to use that value.
MAX_FD="maximum"
warn ( ) {
echo "$*"
}
die ( ) {
echo
echo "$*"
echo
exit 1
}
# OS specific support (must be 'true' or 'false').
cygwin=false
msys=false
darwin=false
case "`uname`" in
CYGWIN* )
cygwin=true
;;
Darwin* )
darwin=true
;;
MINGW* )
msys=true
;;
esac
# For Cygwin, ensure paths are in UNIX format before anything is touched.
if $cygwin ; then
[ -n "$JAVA_HOME" ] && JAVA_HOME=`cygpath --unix "$JAVA_HOME"`
fi
# Attempt to set APP_HOME
# Resolve links: $0 may be a link
PRG="$0"
# Need this for relative symlinks.
while [ -h "$PRG" ] ; do
ls=`ls -ld "$PRG"`
link=`expr "$ls" : '.*-> \(.*\)$'`
if expr "$link" : '/.*' > /dev/null; then
PRG="$link"
else
PRG=`dirname "$PRG"`"/$link"
fi
done
SAVED="`pwd`"
cd "`dirname \"$PRG\"`/" >&-
APP_HOME="`pwd -P`"
cd "$SAVED" >&-
CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar
# Determine the Java command to use to start the JVM.
if [ -n "$JAVA_HOME" ] ; then
if [ -x "$JAVA_HOME/jre/sh/java" ] ; then
# IBM's JDK on AIX uses strange locations for the executables
JAVACMD="$JAVA_HOME/jre/sh/java"
else
JAVACMD="$JAVA_HOME/bin/java"
fi
if [ ! -x "$JAVACMD" ] ; then
die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME
Please set the JAVA_HOME variable in your environment to match the
location of your Java installation."
fi
else
JAVACMD="java"
which java >/dev/null 2>&1 || die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
Please set the JAVA_HOME variable in your environment to match the
location of your Java installation."
fi
# Increase the maximum file descriptors if we can.
if [ "$cygwin" = "false" -a "$darwin" = "false" ] ; then
MAX_FD_LIMIT=`ulimit -H -n`
if [ $? -eq 0 ] ; then
if [ "$MAX_FD" = "maximum" -o "$MAX_FD" = "max" ] ; then
MAX_FD="$MAX_FD_LIMIT"
fi
ulimit -n $MAX_FD
if [ $? -ne 0 ] ; then
warn "Could not set maximum file descriptor limit: $MAX_FD"
fi
else
warn "Could not query maximum file descriptor limit: $MAX_FD_LIMIT"
fi
fi
# For Darwin, add options to specify how the application appears in the dock
if $darwin; then
GRADLE_OPTS="$GRADLE_OPTS \"-Xdock:name=$APP_NAME\" \"-Xdock:icon=$APP_HOME/media/gradle.icns\""
fi
# For Cygwin, switch paths to Windows format before running java
if $cygwin ; then
APP_HOME=`cygpath --path --mixed "$APP_HOME"`
CLASSPATH=`cygpath --path --mixed "$CLASSPATH"`
# We build the pattern for arguments to be converted via cygpath
ROOTDIRSRAW=`find -L / -maxdepth 1 -mindepth 1 -type d 2>/dev/null`
SEP=""
for dir in $ROOTDIRSRAW ; do
ROOTDIRS="$ROOTDIRS$SEP$dir"
SEP="|"
done
OURCYGPATTERN="(^($ROOTDIRS))"
# Add a user-defined pattern to the cygpath arguments
if [ "$GRADLE_CYGPATTERN" != "" ] ; then
OURCYGPATTERN="$OURCYGPATTERN|($GRADLE_CYGPATTERN)"
fi
# Now convert the arguments - kludge to limit ourselves to /bin/sh
i=0
for arg in "$@" ; do
CHECK=`echo "$arg"|egrep -c "$OURCYGPATTERN" -`
CHECK2=`echo "$arg"|egrep -c "^-"` ### Determine if an option
if [ $CHECK -ne 0 ] && [ $CHECK2 -eq 0 ] ; then ### Added a condition
eval `echo args$i`=`cygpath --path --ignore --mixed "$arg"`
else
eval `echo args$i`="\"$arg\""
fi
i=$((i+1))
done
case $i in
(0) set -- ;;
(1) set -- "$args0" ;;
(2) set -- "$args0" "$args1" ;;
(3) set -- "$args0" "$args1" "$args2" ;;
(4) set -- "$args0" "$args1" "$args2" "$args3" ;;
(5) set -- "$args0" "$args1" "$args2" "$args3" "$args4" ;;
(6) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" ;;
(7) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" ;;
(8) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" ;;
(9) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" "$args8" ;;
esac
fi
# Split up the JVM_OPTS And GRADLE_OPTS values into an array, following the shell quoting and substitution rules
function splitJvmOpts() {
JVM_OPTS=("$@")
}
eval splitJvmOpts $DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS
JVM_OPTS[${#JVM_OPTS[*]}]="-Dorg.gradle.appname=$APP_BASE_NAME"
exec "$JAVACMD" "${JVM_OPTS[@]}" -classpath "$CLASSPATH" org.gradle.wrapper.GradleWrapperMain "$@"
================================================
FILE: gradlew.bat
================================================
@if "%DEBUG%" == "" @echo off
@rem ##########################################################################
@rem
@rem Gradle startup script for Windows
@rem
@rem ##########################################################################
@rem Set local scope for the variables with windows NT shell
if "%OS%"=="Windows_NT" setlocal
@rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
set DEFAULT_JVM_OPTS=
set DIRNAME=%~dp0
if "%DIRNAME%" == "" set DIRNAME=.
set APP_BASE_NAME=%~n0
set APP_HOME=%DIRNAME%
@rem Find java.exe
if defined JAVA_HOME goto findJavaFromJavaHome
set JAVA_EXE=java.exe
%JAVA_EXE% -version >NUL 2>&1
if "%ERRORLEVEL%" == "0" goto init
echo.
echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
echo.
echo Please set the JAVA_HOME variable in your environment to match the
echo location of your Java installation.
goto fail
:findJavaFromJavaHome
set JAVA_HOME=%JAVA_HOME:"=%
set JAVA_EXE=%JAVA_HOME%/bin/java.exe
if exist "%JAVA_EXE%" goto init
echo.
echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME%
echo.
echo Please set the JAVA_HOME variable in your environment to match the
echo location of your Java installation.
goto fail
:init
@rem Get command-line arguments, handling Windowz variants
if not "%OS%" == "Windows_NT" goto win9xME_args
if "%@eval[2+2]" == "4" goto 4NT_args
:win9xME_args
@rem Slurp the command line arguments.
set CMD_LINE_ARGS=
set _SKIP=2
:win9xME_args_slurp
if "x%~1" == "x" goto execute
set CMD_LINE_ARGS=%*
goto execute
:4NT_args
@rem Get arguments from the 4NT Shell from JP Software
set CMD_LINE_ARGS=%$
:execute
@rem Setup the command line
set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar
@rem Execute Gradle
"%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %CMD_LINE_ARGS%
:end
@rem End local scope for the variables with windows NT shell
if "%ERRORLEVEL%"=="0" goto mainEnd
:fail
rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of
rem the _cmd.exe /c_ return code!
if not "" == "%GRADLE_EXIT_CONSOLE%" exit 1
exit /b 1
:mainEnd
if "%OS%"=="Windows_NT" endlocal
:omega
================================================
FILE: picasso/Action.java
================================================
/*
* Copyright (C) 2013 Square, Inc.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.squareup.picasso;
import android.graphics.Bitmap;
import android.graphics.drawable.Drawable;
import java.lang.ref.ReferenceQueue;
import java.lang.ref.WeakReference;
abstract class Action {
static class RequestWeakReference extends WeakReference {
final Action action;
public RequestWeakReference(Action action, T referent, ReferenceQueue super T> q) {
super(referent, q);
this.action = action;
}
}
final Picasso picasso;
final Request request;
final WeakReference target;
final boolean skipCache;
final boolean noFade;
final int errorResId;
final Drawable errorDrawable;
final String key;
boolean willReplay;
boolean cancelled;
Action(Picasso picasso, T target, Request request, boolean skipCache, boolean noFade,
int errorResId, Drawable errorDrawable, String key) {
this.picasso = picasso;
this.request = request;
this.target = new RequestWeakReference(this, target, picasso.referenceQueue);
this.skipCache = skipCache;
this.noFade = noFade;
this.errorResId = errorResId;
this.errorDrawable = errorDrawable;
this.key = key;
}
abstract void complete(Bitmap result, Picasso.LoadedFrom from);
abstract void error();
void cancel() {
cancelled = true;
}
Request getRequest() {
return request;
}
T getTarget() {
return target.get();
}
String getKey() {
return key;
}
boolean isCancelled() {
return cancelled;
}
boolean willReplay() {
return willReplay;
}
Picasso getPicasso() {
return picasso;
}
}
================================================
FILE: picasso/AssetRequestHandler.java
================================================
/*
* Copyright (C) 2013 Square, Inc.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.squareup.picasso;
import android.content.Context;
import android.content.res.AssetManager;
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.net.Uri;
import java.io.IOException;
import java.io.InputStream;
import static android.content.ContentResolver.SCHEME_FILE;
import static com.squareup.picasso.Picasso.LoadedFrom.DISK;
class AssetRequestHandler extends RequestHandler {
protected static final String ANDROID_ASSET = "android_asset";
private static final int ASSET_PREFIX_LENGTH =
(SCHEME_FILE + ":///" + ANDROID_ASSET + "/").length();
private final AssetManager assetManager;
public AssetRequestHandler(Context context) {
assetManager = context.getAssets();
}
@Override public boolean canHandleRequest(Request data) {
Uri uri = data.uri;
return (SCHEME_FILE.equals(uri.getScheme())
&& !uri.getPathSegments().isEmpty() && ANDROID_ASSET.equals(uri.getPathSegments().get(0)));
}
@Override public Result load(Request data) throws IOException {
String filePath = data.uri.toString().substring(ASSET_PREFIX_LENGTH);
return new Result(decodeAsset(data, filePath), DISK);
}
Bitmap decodeAsset(Request data, String filePath) throws IOException {
final BitmapFactory.Options options = createBitmapOptions(data);
if (requiresInSampleSize(options)) {
InputStream is = null;
try {
is = assetManager.open(filePath);
BitmapFactory.decodeStream(is, null, options);
} finally {
Utils.closeQuietly(is);
}
calculateInSampleSize(data.targetWidth, data.targetHeight, options);
}
InputStream is = assetManager.open(filePath);
try {
return BitmapFactory.decodeStream(is, null, options);
} finally {
Utils.closeQuietly(is);
}
}
}
================================================
FILE: picasso/BitmapHunter.java
================================================
/*
* Copyright (C) 2013 Square, Inc.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.squareup.picasso;
import android.graphics.Bitmap;
import android.graphics.Matrix;
import android.net.NetworkInfo;
import java.io.IOException;
import java.io.PrintWriter;
import java.io.StringWriter;
import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.Future;
import static com.squareup.picasso.Picasso.LoadedFrom.MEMORY;
import static com.squareup.picasso.Utils.OWNER_HUNTER;
import static com.squareup.picasso.Utils.VERB_DECODED;
import static com.squareup.picasso.Utils.VERB_EXECUTING;
import static com.squareup.picasso.Utils.VERB_JOINED;
import static com.squareup.picasso.Utils.VERB_REMOVED;
import static com.squareup.picasso.Utils.VERB_TRANSFORMED;
import static com.squareup.picasso.Utils.getLogIdsForHunter;
import static com.squareup.picasso.Utils.log;
class BitmapHunter implements Runnable {
/**
* Global lock for bitmap decoding to ensure that we are only are decoding one at a time. Since
* this will only ever happen in background threads we help avoid excessive memory thrashing as
* well as potential OOMs. Shamelessly stolen from Volley.
*/
private static final Object DECODE_LOCK = new Object();
private static final ThreadLocal NAME_BUILDER = new ThreadLocal() {
@Override protected StringBuilder initialValue() {
return new StringBuilder(Utils.THREAD_PREFIX);
}
};
final Picasso picasso;
final Dispatcher dispatcher;
final Cache cache;
final Stats stats;
final String key;
final Request data;
final boolean skipMemoryCache;
final RequestHandler requestHandler;
Action action;
List actions;
Bitmap result;
Future> future;
Picasso.LoadedFrom loadedFrom;
Exception exception;
int exifRotation; // Determined during decoding of original resource.
int retryCount;
BitmapHunter(Picasso picasso, Dispatcher dispatcher, Cache cache, Stats stats, Action action,
RequestHandler requestHandler) {
this.picasso = picasso;
this.dispatcher = dispatcher;
this.cache = cache;
this.stats = stats;
this.key = action.getKey();
this.data = action.getRequest();
this.skipMemoryCache = action.skipCache;
this.requestHandler = requestHandler;
this.retryCount = requestHandler.getRetryCount();
this.action = action;
}
@Override public void run() {
try {
updateThreadName(data);
if (picasso.loggingEnabled) {
log(OWNER_HUNTER, VERB_EXECUTING, getLogIdsForHunter(this));
}
result = hunt();
if (result == null) {
dispatcher.dispatchFailed(this);
} else {
dispatcher.dispatchComplete(this);
}
} catch (Downloader.ResponseException e) {
exception = e;
dispatcher.dispatchFailed(this);
} catch (IOException e) {
exception = e;
dispatcher.dispatchRetry(this);
} catch (OutOfMemoryError e) {
StringWriter writer = new StringWriter();
stats.createSnapshot().dump(new PrintWriter(writer));
exception = new RuntimeException(writer.toString(), e);
dispatcher.dispatchFailed(this);
} catch (Exception e) {
exception = e;
dispatcher.dispatchFailed(this);
} finally {
Thread.currentThread().setName(Utils.THREAD_IDLE_NAME);
}
}
Bitmap hunt() throws IOException {
Bitmap bitmap = null;
if (!skipMemoryCache) {
bitmap = cache.get(key);
if (bitmap != null) {
stats.dispatchCacheHit();
loadedFrom = MEMORY;
if (picasso.loggingEnabled) {
log(OWNER_HUNTER, VERB_DECODED, data.logId(), "from cache");
}
return bitmap;
}
}
data.loadFromLocalCacheOnly = (retryCount == 0);
RequestHandler.Result result = requestHandler.load(data);
if (result != null) {
bitmap = result.getBitmap();
loadedFrom = result.getLoadedFrom();
exifRotation = result.getExifOrientation();
}
if (bitmap != null) {
if (picasso.loggingEnabled) {
log(OWNER_HUNTER, VERB_DECODED, data.logId());
}
stats.dispatchBitmapDecoded(bitmap);
if (data.needsTransformation() || exifRotation != 0) {
synchronized (DECODE_LOCK) {
if (data.needsMatrixTransform() || exifRotation != 0) {
bitmap = transformResult(data, bitmap, exifRotation);
if (picasso.loggingEnabled) {
log(OWNER_HUNTER, VERB_TRANSFORMED, data.logId());
}
}
if (data.hasCustomTransformations()) {
bitmap = applyCustomTransformations(data.transformations, bitmap);
if (picasso.loggingEnabled) {
log(OWNER_HUNTER, VERB_TRANSFORMED, data.logId(), "from custom transformations");
}
}
}
if (bitmap != null) {
stats.dispatchBitmapTransformed(bitmap);
}
}
}
return bitmap;
}
void attach(Action action) {
boolean loggingEnabled = picasso.loggingEnabled;
Request request = action.request;
if (this.action == null) {
this.action = action;
if (loggingEnabled) {
if (actions == null || actions.isEmpty()) {
log(OWNER_HUNTER, VERB_JOINED, request.logId(), "to empty hunter");
} else {
log(OWNER_HUNTER, VERB_JOINED, request.logId(), getLogIdsForHunter(this, "to "));
}
}
return;
}
if (actions == null) {
actions = new ArrayList(3);
}
actions.add(action);
if (loggingEnabled) {
log(OWNER_HUNTER, VERB_JOINED, request.logId(), getLogIdsForHunter(this, "to "));
}
}
void detach(Action action) {
if (this.action == action) {
this.action = null;
} else if (actions != null) {
actions.remove(action);
}
if (picasso.loggingEnabled) {
log(OWNER_HUNTER, VERB_REMOVED, action.request.logId(), getLogIdsForHunter(this, "from "));
}
}
boolean cancel() {
return action == null
&& (actions == null || actions.isEmpty())
&& future != null
&& future.cancel(false);
}
boolean isCancelled() {
return future != null && future.isCancelled();
}
boolean shouldSkipMemoryCache() {
return skipMemoryCache;
}
boolean shouldRetry(boolean airplaneMode, NetworkInfo info) {
boolean hasRetries = retryCount > 0;
if (!hasRetries) {
return false;
}
retryCount--;
return requestHandler.shouldRetry(airplaneMode, info);
}
boolean supportsReplay() {
return requestHandler.supportsReplay();
}
Bitmap getResult() {
return result;
}
String getKey() {
return key;
}
Request getData() {
return data;
}
Action getAction() {
return action;
}
Picasso getPicasso() {
return picasso;
}
List getActions() {
return actions;
}
Exception getException() {
return exception;
}
Picasso.LoadedFrom getLoadedFrom() {
return loadedFrom;
}
static void updateThreadName(Request data) {
String name = data.getName();
StringBuilder builder = NAME_BUILDER.get();
builder.ensureCapacity(Utils.THREAD_PREFIX.length() + name.length());
builder.replace(Utils.THREAD_PREFIX.length(), builder.length(), name);
Thread.currentThread().setName(builder.toString());
}
static BitmapHunter forRequest(Picasso picasso, Dispatcher dispatcher,
Cache cache, Stats stats, Action action) {
Request request = action.getRequest();
List requestHandlers = picasso.getRequestHandlers();
final int count = requestHandlers.size();
for (int i = 0; i < count; i++) {
RequestHandler requestHandler = requestHandlers.get(i);
if (requestHandler.canHandleRequest(request)) {
return new BitmapHunter(picasso, dispatcher, cache, stats, action, requestHandler);
}
}
throw new IllegalStateException("Unrecognized type of request: " + request);
}
static Bitmap applyCustomTransformations(List transformations, Bitmap result) {
for (int i = 0, count = transformations.size(); i < count; i++) {
final Transformation transformation = transformations.get(i);
Bitmap newResult = transformation.transform(result);
if (newResult == null) {
final StringBuilder builder = new StringBuilder() //
.append("Transformation ")
.append(transformation.key())
.append(" returned null after ")
.append(i)
.append(" previous transformation(s).\n\nTransformation list:\n");
for (Transformation t : transformations) {
builder.append(t.key()).append('\n');
}
Picasso.HANDLER.post(new Runnable() {
@Override public void run() {
throw new NullPointerException(builder.toString());
}
});
return null;
}
if (newResult == result && result.isRecycled()) {
Picasso.HANDLER.post(new Runnable() {
@Override public void run() {
throw new IllegalStateException("Transformation "
+ transformation.key()
+ " returned input Bitmap but recycled it.");
}
});
return null;
}
// If the transformation returned a new bitmap ensure they recycled the original.
if (newResult != result && !result.isRecycled()) {
Picasso.HANDLER.post(new Runnable() {
@Override public void run() {
throw new IllegalStateException("Transformation "
+ transformation.key()
+ " mutated input Bitmap but failed to recycle the original.");
}
});
return null;
}
result = newResult;
}
return result;
}
static Bitmap transformResult(Request data, Bitmap result, int exifRotation) {
int inWidth = result.getWidth();
int inHeight = result.getHeight();
int drawX = 0;
int drawY = 0;
int drawWidth = inWidth;
int drawHeight = inHeight;
Matrix matrix = new Matrix();
if (data.needsMatrixTransform()) {
int targetWidth = data.targetWidth;
int targetHeight = data.targetHeight;
float targetRotation = data.rotationDegrees;
if (targetRotation != 0) {
if (data.hasRotationPivot) {
matrix.setRotate(targetRotation, data.rotationPivotX, data.rotationPivotY);
} else {
matrix.setRotate(targetRotation);
}
}
if (data.centerCrop) {
float widthRatio = targetWidth / (float) inWidth;
float heightRatio = targetHeight / (float) inHeight;
float scale;
if (widthRatio > heightRatio) {
scale = widthRatio;
int newSize = (int) Math.ceil(inHeight * (heightRatio / widthRatio));
drawY = (inHeight - newSize) / 2;
drawHeight = newSize;
} else {
scale = heightRatio;
int newSize = (int) Math.ceil(inWidth * (widthRatio / heightRatio));
drawX = (inWidth - newSize) / 2;
drawWidth = newSize;
}
matrix.preScale(scale, scale);
} else if (data.centerInside) {
float widthRatio = targetWidth / (float) inWidth;
float heightRatio = targetHeight / (float) inHeight;
float scale = widthRatio < heightRatio ? widthRatio : heightRatio;
matrix.preScale(scale, scale);
} else if (targetWidth != 0 && targetHeight != 0 //
&& (targetWidth != inWidth || targetHeight != inHeight)) {
// If an explicit target size has been specified and they do not match the results bounds,
// pre-scale the existing matrix appropriately.
float sx = targetWidth / (float) inWidth;
float sy = targetHeight / (float) inHeight;
matrix.preScale(sx, sy);
}
}
if (exifRotation != 0) {
matrix.preRotate(exifRotation);
}
Bitmap newResult =
Bitmap.createBitmap(result, drawX, drawY, drawWidth, drawHeight, matrix, true);
if (newResult != result) {
result.recycle();
result = newResult;
}
return result;
}
}
================================================
FILE: picasso/Cache.java
================================================
/*
* Copyright (C) 2013 Square, Inc.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.squareup.picasso;
import android.graphics.Bitmap;
/**
* A memory cache for storing the most recently used images.
*
* Note: The {@link com.squareup.picasso.Cache} is accessed by multiple threads. You must ensure
* your {@link com.squareup.picasso.Cache} implementation is thread safe when {@link com.squareup.picasso.Cache#get(String)} or {@link
* com.squareup.picasso.Cache#set(String, android.graphics.Bitmap)} is called.
*/
public interface Cache {
/** Retrieve an image for the specified {@code key} or {@code null}. */
Bitmap get(String key);
/** Store an image in the cache for the specified {@code key}. */
void set(String key, Bitmap bitmap);
/** Returns the current size of the cache in bytes. */
int size();
/** Returns the maximum size in bytes that the cache can hold. */
int maxSize();
/** Clears the cache. */
void clear();
/** A cache which does not store any values. */
Cache NONE = new Cache() {
@Override public Bitmap get(String key) {
return null;
}
@Override public void set(String key, Bitmap bitmap) {
// Ignore.
}
@Override public int size() {
return 0;
}
@Override public int maxSize() {
return 0;
}
@Override public void clear() {
}
};
}
================================================
FILE: picasso/Callback.java
================================================
/*
* Copyright (C) 2013 Square, Inc.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.squareup.picasso;
public interface Callback {
void onSuccess();
void onError();
public static class EmptyCallback implements Callback {
@Override public void onSuccess() {
}
@Override public void onError() {
}
}
}
================================================
FILE: picasso/ContactsPhotoRequestHandler.java
================================================
/*
* Copyright (C) 2013 Square, Inc.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.squareup.picasso;
import android.annotation.TargetApi;
import android.content.ContentResolver;
import android.content.Context;
import android.content.UriMatcher;
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.net.Uri;
import android.provider.ContactsContract;
import java.io.IOException;
import java.io.InputStream;
import static android.content.ContentResolver.SCHEME_CONTENT;
import static android.os.Build.VERSION.SDK_INT;
import static android.os.Build.VERSION_CODES.ICE_CREAM_SANDWICH;
import static android.provider.ContactsContract.Contacts.openContactPhotoInputStream;
import static com.squareup.picasso.Picasso.LoadedFrom.DISK;
class ContactsPhotoRequestHandler extends RequestHandler {
/** A lookup uri (e.g. content://com.android.contacts/contacts/lookup/3570i61d948d30808e537) */
private static final int ID_LOOKUP = 1;
/** A contact thumbnail uri (e.g. content://com.android.contacts/contacts/38/photo) */
private static final int ID_THUMBNAIL = 2;
/** A contact uri (e.g. content://com.android.contacts/contacts/38) */
private static final int ID_CONTACT = 3;
/**
* A contact display photo (high resolution) uri
* (e.g. content://com.android.contacts/display_photo/5)
*/
private static final int ID_DISPLAY_PHOTO = 4;
private static final UriMatcher matcher;
static {
matcher = new UriMatcher(UriMatcher.NO_MATCH);
matcher.addURI(ContactsContract.AUTHORITY, "contacts/lookup/*/#", ID_LOOKUP);
matcher.addURI(ContactsContract.AUTHORITY, "contacts/lookup/*", ID_LOOKUP);
matcher.addURI(ContactsContract.AUTHORITY, "contacts/#/photo", ID_THUMBNAIL);
matcher.addURI(ContactsContract.AUTHORITY, "contacts/#", ID_CONTACT);
matcher.addURI(ContactsContract.AUTHORITY, "display_photo/#", ID_DISPLAY_PHOTO);
}
final Context context;
ContactsPhotoRequestHandler(Context context) {
this.context = context;
}
@Override public boolean canHandleRequest(Request data) {
final Uri uri = data.uri;
return (SCHEME_CONTENT.equals(uri.getScheme())
&& ContactsContract.Contacts.CONTENT_URI.getHost().equals(uri.getHost())
&& !uri.getPathSegments().contains(ContactsContract.Contacts.Photo.CONTENT_DIRECTORY));
}
@Override public Result load(Request data) throws IOException {
InputStream is = null;
try {
is = getInputStream(data);
return new Result(decodeStream(is, data), DISK);
} finally {
Utils.closeQuietly(is);
}
}
private InputStream getInputStream(Request data) throws IOException {
ContentResolver contentResolver = context.getContentResolver();
Uri uri = data.uri;
switch (matcher.match(uri)) {
case ID_LOOKUP:
uri = ContactsContract.Contacts.lookupContact(contentResolver, uri);
if (uri == null) {
return null;
}
// Resolved the uri to a contact uri, intentionally fall through to process the resolved uri
case ID_CONTACT:
if (SDK_INT < ICE_CREAM_SANDWICH) {
return openContactPhotoInputStream(contentResolver, uri);
} else {
return ContactPhotoStreamIcs.get(contentResolver, uri);
}
case ID_THUMBNAIL:
case ID_DISPLAY_PHOTO:
return contentResolver.openInputStream(uri);
default:
throw new IllegalStateException("Invalid uri: " + uri);
}
}
private Bitmap decodeStream(InputStream stream, Request data) throws IOException {
if (stream == null) {
return null;
}
final BitmapFactory.Options options = createBitmapOptions(data);
if (requiresInSampleSize(options)) {
InputStream is = getInputStream(data);
try {
BitmapFactory.decodeStream(is, null, options);
} finally {
Utils.closeQuietly(is);
}
calculateInSampleSize(data.targetWidth, data.targetHeight, options);
}
return BitmapFactory.decodeStream(stream, null, options);
}
@TargetApi(ICE_CREAM_SANDWICH)
private static class ContactPhotoStreamIcs {
static InputStream get(ContentResolver contentResolver, Uri uri) {
return openContactPhotoInputStream(contentResolver, uri, true);
}
}
}
================================================
FILE: picasso/ContentStreamRequestHandler.java
================================================
/*
* Copyright (C) 2013 Square, Inc.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.squareup.picasso;
import android.content.ContentResolver;
import android.content.Context;
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import java.io.IOException;
import java.io.InputStream;
import static android.content.ContentResolver.SCHEME_CONTENT;
import static com.squareup.picasso.Picasso.LoadedFrom.DISK;
class ContentStreamRequestHandler extends RequestHandler {
final Context context;
ContentStreamRequestHandler(Context context) {
this.context = context;
}
@Override public boolean canHandleRequest(Request data) {
return SCHEME_CONTENT.equals(data.uri.getScheme());
}
@Override public Result load(Request data) throws IOException {
return new Result(decodeContentStream(data), DISK);
}
protected Bitmap decodeContentStream(Request data) throws IOException {
ContentResolver contentResolver = context.getContentResolver();
final BitmapFactory.Options options = createBitmapOptions(data);
if (requiresInSampleSize(options)) {
InputStream is = null;
try {
is = contentResolver.openInputStream(data.uri);
BitmapFactory.decodeStream(is, null, options);
} finally {
Utils.closeQuietly(is);
}
calculateInSampleSize(data.targetWidth, data.targetHeight, options);
}
InputStream is = contentResolver.openInputStream(data.uri);
try {
return BitmapFactory.decodeStream(is, null, options);
} finally {
Utils.closeQuietly(is);
}
}
}
================================================
FILE: picasso/DeferredRequestCreator.java
================================================
/*
* Copyright (C) 2013 Square, Inc.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.squareup.picasso;
import android.view.ViewTreeObserver;
import android.widget.ImageView;
import java.lang.ref.WeakReference;
class DeferredRequestCreator implements ViewTreeObserver.OnPreDrawListener {
final RequestCreator creator;
final WeakReference target;
Callback callback;
DeferredRequestCreator(RequestCreator creator, ImageView target) {
this(creator, target, null);
}
DeferredRequestCreator(RequestCreator creator, ImageView target, Callback callback) {
this.creator = creator;
this.target = new WeakReference(target);
this.callback = callback;
target.getViewTreeObserver().addOnPreDrawListener(this);
}
@Override public boolean onPreDraw() {
ImageView target = this.target.get();
if (target == null) {
return true;
}
ViewTreeObserver vto = target.getViewTreeObserver();
if (!vto.isAlive()) {
return true;
}
int width = target.getWidth();
int height = target.getHeight();
if (width <= 0 || height <= 0) {
return true;
}
vto.removeOnPreDrawListener(this);
this.creator.unfit().resize(width, height).into(target, callback);
return true;
}
void cancel() {
callback = null;
ImageView target = this.target.get();
if (target == null) {
return;
}
ViewTreeObserver vto = target.getViewTreeObserver();
if (!vto.isAlive()) {
return;
}
vto.removeOnPreDrawListener(this);
}
}
================================================
FILE: picasso/Dispatcher.java
================================================
/*
* Copyright (C) 2013 Square, Inc.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.squareup.picasso;
import android.Manifest;
import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.Intent;
import android.content.IntentFilter;
import android.net.ConnectivityManager;
import android.net.NetworkInfo;
import android.os.Handler;
import android.os.HandlerThread;
import android.os.Looper;
import android.os.Message;
import java.util.ArrayList;
import java.util.Iterator;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
import java.util.WeakHashMap;
import java.util.concurrent.ExecutorService;
import static android.content.Context.CONNECTIVITY_SERVICE;
import static android.content.Intent.ACTION_AIRPLANE_MODE_CHANGED;
import static android.net.ConnectivityManager.CONNECTIVITY_ACTION;
import static android.os.Process.THREAD_PRIORITY_BACKGROUND;
import static com.squareup.picasso.BitmapHunter.forRequest;
import static com.squareup.picasso.Utils.OWNER_DISPATCHER;
import static com.squareup.picasso.Utils.VERB_BATCHED;
import static com.squareup.picasso.Utils.VERB_CANCELED;
import static com.squareup.picasso.Utils.VERB_DELIVERED;
import static com.squareup.picasso.Utils.VERB_ENQUEUED;
import static com.squareup.picasso.Utils.VERB_IGNORED;
import static com.squareup.picasso.Utils.VERB_REPLAYING;
import static com.squareup.picasso.Utils.VERB_RETRYING;
import static com.squareup.picasso.Utils.getLogIdsForHunter;
import static com.squareup.picasso.Utils.getService;
import static com.squareup.picasso.Utils.hasPermission;
import static com.squareup.picasso.Utils.log;
class Dispatcher {
private static final int RETRY_DELAY = 500;
private static final int AIRPLANE_MODE_ON = 1;
private static final int AIRPLANE_MODE_OFF = 0;
static final int REQUEST_SUBMIT = 1;
static final int REQUEST_CANCEL = 2;
static final int REQUEST_GCED = 3;
static final int HUNTER_COMPLETE = 4;
static final int HUNTER_RETRY = 5;
static final int HUNTER_DECODE_FAILED = 6;
static final int HUNTER_DELAY_NEXT_BATCH = 7;
static final int HUNTER_BATCH_COMPLETE = 8;
static final int NETWORK_STATE_CHANGE = 9;
static final int AIRPLANE_MODE_CHANGE = 10;
private static final String DISPATCHER_THREAD_NAME = "Dispatcher";
private static final int BATCH_DELAY = 200; // ms
final DispatcherThread dispatcherThread;
final Context context;
final ExecutorService service;
final Downloader downloader;
final Map hunterMap;
final Map