Full Code of panyiho/NineGridView for AI

master b9603957d85d cached
73 files
223.6 KB
54.5k tokens
501 symbols
1 requests
Download .txt
Showing preview only (243K chars total). Download the full file or copy to clipboard to get everything.
Repository: panyiho/NineGridView
Branch: master
Commit: b9603957d85d
Files: 73
Total size: 223.6 KB

Directory structure:
gitextract_9vah1spq/

├── .gitignore
├── .idea/
│   ├── .name
│   ├── compiler.xml
│   ├── copyright/
│   │   └── profiles_settings.xml
│   ├── encodings.xml
│   ├── gradle.xml
│   ├── misc.xml
│   ├── modules.xml
│   ├── scopes/
│   │   └── scope_settings.xml
│   └── vcs.xml
├── README.md
├── WeixinNineGridLayout.iml
├── app/
│   ├── .gitignore
│   ├── app.iml
│   ├── build.gradle
│   ├── libs/
│   │   └── picasso-2.3.4.jar
│   ├── proguard-rules.pro
│   └── src/
│       ├── androidTest/
│       │   └── java/
│       │       └── com/
│       │           └── example/
│       │               └── administrator/
│       │                   └── weixinninegridlayout/
│       │                       └── ApplicationTest.java
│       └── main/
│           ├── AndroidManifest.xml
│           ├── java/
│           │   └── com/
│           │       └── weixinninegridlayout/
│           │           ├── CustomImageView.java
│           │           ├── Image.java
│           │           ├── L.java
│           │           ├── MainActivity.java
│           │           ├── MainAdapter.java
│           │           ├── NineGridlayout.java
│           │           └── ScreenTools.java
│           └── res/
│               ├── layout/
│               │   ├── activity_main.xml
│               │   └── item_ninegridlayout.xml
│               ├── menu/
│               │   └── menu_main.xml
│               ├── values/
│               │   ├── dimens.xml
│               │   ├── strings.xml
│               │   └── styles.xml
│               └── values-w820dp/
│                   └── dimens.xml
├── build.gradle
├── gradle/
│   └── wrapper/
│       ├── gradle-wrapper.jar
│       └── gradle-wrapper.properties
├── gradle.properties
├── gradlew
├── gradlew.bat
├── picasso/
│   ├── Action.java
│   ├── AssetRequestHandler.java
│   ├── BitmapHunter.java
│   ├── Cache.java
│   ├── Callback.java
│   ├── ContactsPhotoRequestHandler.java
│   ├── ContentStreamRequestHandler.java
│   ├── DeferredRequestCreator.java
│   ├── Dispatcher.java
│   ├── Downloader.java
│   ├── FetchAction.java
│   ├── FileRequestHandler.java
│   ├── GetAction.java
│   ├── ImageViewAction.java
│   ├── LruCache.java
│   ├── MarkableInputStream.java
│   ├── MediaStoreRequestHandler.java
│   ├── NetworkRequestHandler.java
│   ├── Picasso.java
│   ├── PicassoDrawable.java
│   ├── PicassoExecutorService.java
│   ├── RemoteViewsAction.java
│   ├── Request.java
│   ├── RequestCreator.java
│   ├── RequestHandler.java
│   ├── ResourceRequestHandler.java
│   ├── Stats.java
│   ├── StatsSnapshot.java
│   ├── Target.java
│   ├── TargetAction.java
│   ├── Transformation.java
│   ├── UrlConnectionDownloader.java
│   └── Utils.java
└── settings.gradle

================================================
FILE CONTENTS
================================================

================================================
FILE: .gitignore
================================================
.gradle
/local.properties
/.idea/workspace.xml
/.idea/libraries
.DS_Store
/build


================================================
FILE: .idea/.name
================================================
WeixinNineGridLayout

================================================
FILE: .idea/compiler.xml
================================================
<?xml version="1.0" encoding="UTF-8"?>
<project version="4">
  <component name="CompilerConfiguration">
    <option name="DEFAULT_COMPILER" value="Javac" />
    <resourceExtensions />
    <wildcardResourcePatterns>
      <entry name="!?*.java" />
      <entry name="!?*.form" />
      <entry name="!?*.class" />
      <entry name="!?*.groovy" />
      <entry name="!?*.scala" />
      <entry name="!?*.flex" />
      <entry name="!?*.kt" />
      <entry name="!?*.clj" />
    </wildcardResourcePatterns>
    <annotationProcessing>
      <profile default="true" name="Default" enabled="false">
        <processorPath useClasspath="true" />
      </profile>
    </annotationProcessing>
  </component>
</project>



================================================
FILE: .idea/copyright/profiles_settings.xml
================================================
<component name="CopyrightManager">
  <settings default="" />
</component>

================================================
FILE: .idea/encodings.xml
================================================
<?xml version="1.0" encoding="UTF-8"?>
<project version="4">
  <component name="Encoding" useUTFGuessing="true" native2AsciiForPropertiesFiles="false" />
</project>



================================================
FILE: .idea/gradle.xml
================================================
<?xml version="1.0" encoding="UTF-8"?>
<project version="4">
  <component name="GradleSettings">
    <option name="linkedExternalProjectsSettings">
      <GradleProjectSettings>
        <option name="distributionType" value="LOCAL" />
        <option name="externalProjectPath" value="$PROJECT_DIR$" />
        <option name="gradleHome" value="D:/常用软件/gradle-2.2.1" />
        <option name="modules">
          <set>
            <option value="$PROJECT_DIR$" />
            <option value="$PROJECT_DIR$/app" />
          </set>
        </option>
      </GradleProjectSettings>
    </option>
  </component>
</project>



================================================
FILE: .idea/misc.xml
================================================
<?xml version="1.0" encoding="UTF-8"?>
<project version="4">
  <component name="DaemonCodeAnalyzer">
    <disable_hints />
  </component>
  <component name="ProjectLevelVcsManager" settingsEditedManually="false">
    <OptionsSetting value="true" id="Add" />
    <OptionsSetting value="true" id="Remove" />
    <OptionsSetting value="true" id="Checkout" />
    <OptionsSetting value="true" id="Update" />
    <OptionsSetting value="true" id="Status" />
    <OptionsSetting value="true" id="Edit" />
    <ConfirmationsSetting value="0" id="Add" />
    <ConfirmationsSetting value="0" id="Remove" />
  </component>
  <component name="ProjectRootManager" version="2" languageLevel="JDK_1_6" assert-keyword="true" jdk-15="true">
    <output url="file://$PROJECT_DIR$/build/classes" />
  </component>
  <component name="RunManager">
    <configuration default="true" type="Remote" factoryName="Remote">
      <option name="USE_SOCKET_TRANSPORT" value="true" />
      <option name="SERVER_MODE" value="false" />
      <option name="SHMEM_ADDRESS" value="javadebug" />
      <option name="HOST" value="localhost" />
      <option name="PORT" value="5005" />
      <method />
    </configuration>
    <configuration default="true" type="JUnit" factoryName="JUnit">
      <module name="" />
      <option name="ALTERNATIVE_JRE_PATH_ENABLED" value="false" />
      <option name="ALTERNATIVE_JRE_PATH" />
      <option name="PACKAGE_NAME" />
      <option name="MAIN_CLASS_NAME" />
      <option name="METHOD_NAME" />
      <option name="TEST_OBJECT" value="class" />
      <option name="VM_PARAMETERS" value="-ea" />
      <option name="PARAMETERS" />
      <option name="WORKING_DIRECTORY" value="$PROJECT_DIR$" />
      <option name="ENV_VARIABLES" />
      <option name="PASS_PARENT_ENVS" value="true" />
      <option name="TEST_SEARCH_SCOPE">
        <value defaultName="moduleWithDependencies" />
      </option>
      <envs />
      <patterns />
      <method />
    </configuration>
    <configuration default="true" type="TestNG" factoryName="TestNG">
      <module name="" />
      <option name="ALTERNATIVE_JRE_PATH_ENABLED" value="false" />
      <option name="ALTERNATIVE_JRE_PATH" />
      <option name="SUITE_NAME" />
      <option name="PACKAGE_NAME" />
      <option name="MAIN_CLASS_NAME" />
      <option name="METHOD_NAME" />
      <option name="GROUP_NAME" />
      <option name="TEST_OBJECT" value="CLASS" />
      <option name="VM_PARAMETERS" value="-ea" />
      <option name="PARAMETERS" />
      <option name="WORKING_DIRECTORY" value="$PROJECT_DIR$" />
      <option name="OUTPUT_DIRECTORY" />
      <option name="ANNOTATION_TYPE" />
      <option name="ENV_VARIABLES" />
      <option name="PASS_PARENT_ENVS" value="true" />
      <option name="TEST_SEARCH_SCOPE">
        <value defaultName="moduleWithDependencies" />
      </option>
      <option name="USE_DEFAULT_REPORTERS" value="false" />
      <option name="PROPERTIES_FILE" />
      <envs />
      <properties />
      <listeners />
      <method />
    </configuration>
    <configuration default="true" type="Application" factoryName="Application">
      <option name="MAIN_CLASS_NAME" />
      <option name="VM_PARAMETERS" />
      <option name="PROGRAM_PARAMETERS" />
      <option name="WORKING_DIRECTORY" value="$PROJECT_DIR$" />
      <option name="ALTERNATIVE_JRE_PATH_ENABLED" value="false" />
      <option name="ALTERNATIVE_JRE_PATH" />
      <option name="ENABLE_SWING_INSPECTOR" value="false" />
      <option name="ENV_VARIABLES" />
      <option name="PASS_PARENT_ENVS" value="true" />
      <module name="" />
      <envs />
      <method />
    </configuration>
    <list size="0" />
    <configuration name="&lt;template&gt;" type="#org.jetbrains.idea.devkit.run.PluginConfigurationType" default="true" selected="false">
      <option name="VM_PARAMETERS" value="-Xmx512m -Xms256m -XX:MaxPermSize=250m -ea" />
    </configuration>
    <configuration name="&lt;template&gt;" type="WebApp" default="true" selected="false">
      <Host>localhost</Host>
      <Port>5050</Port>
    </configuration>
  </component>
  <component name="SvnBranchConfigurationManager">
    <option name="mySupportsUserInfoFilter" value="true" />
  </component>
</project>



================================================
FILE: .idea/modules.xml
================================================
<?xml version="1.0" encoding="UTF-8"?>
<project version="4">
  <component name="ProjectModuleManager">
    <modules>
      <module fileurl="file://$PROJECT_DIR$/WeixinNineGridLayout.iml" filepath="$PROJECT_DIR$/WeixinNineGridLayout.iml" />
    </modules>
  </component>
</project>



================================================
FILE: .idea/scopes/scope_settings.xml
================================================
<component name="DependencyValidationManager">
  <state>
    <option name="SKIP_IMPORT_STATEMENTS" value="false" />
  </state>
</component>

================================================
FILE: .idea/vcs.xml
================================================
<?xml version="1.0" encoding="UTF-8"?>
<project version="4">
  <component name="VcsDirectoryMappings">
    <mapping directory="" vcs="svn" />
  </component>
</project>



================================================
FILE: README.md
================================================
NineGridLayout
--------------

 1. 简介

  这是一个用于实现像微信朋友圈和微博的类似的九宫格图片展示控件,通过自定义viewgroup实现,是用方便,只需要很简单的就可嵌入到项目中去。
  
  


----------


 2. 使用方法
 在项目的layout文件中添加如下xml即可加入到布局文件

                <com.weixinninegridlayout.NineGridlayout
                android:layout_marginTop="8dp"
                android:id="@+id/iv_ngrid_layout"
                android:layout_height="wrap_content"
                android:layout_width="match_parent" />

  因为大部分这类控件都是在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
================================================
<?xml version="1.0" encoding="UTF-8"?>
<module external.linked.project.path="$MODULE_DIR$" external.root.project.path="$MODULE_DIR$" external.system.id="GRADLE" type="JAVA_MODULE" version="4">
  <component name="FacetManager">
    <facet type="java-gradle" name="Java-Gradle">
      <configuration>
        <option name="BUILD_FOLDER_PATH" value="$MODULE_DIR$/build" />
      </configuration>
    </facet>
    <facet type="android-gradle" name="Android-Gradle">
      <configuration>
        <option name="GRADLE_PROJECT_PATH" value=":" />
      </configuration>
    </facet>
    <facet type="android" name="Android">
      <configuration>
        <option name="ALLOW_USER_CONFIGURATION" value="false" />
      </configuration>
    </facet>
  </component>
  <component name="NewModuleRootManager" inherit-compiler-output="false">
    <output url="file://$MODULE_DIR$/build/classes/main" />
    <output-test url="file://$MODULE_DIR$/build/classes/test" />
    <exclude-output />
    <content url="file://$MODULE_DIR$">
      <excludeFolder url="file://$MODULE_DIR$/.gradle" />
    </content>
    <orderEntry type="inheritedJdk" />
    <orderEntry type="sourceFolder" forTests="false" />
  </component>
</module>



================================================
FILE: app/.gitignore
================================================
/build


================================================
FILE: app/app.iml
================================================
<?xml version="1.0" encoding="UTF-8"?>
<module external.linked.project.path="$MODULE_DIR$" external.root.project.path="$MODULE_DIR$/.." external.system.id="GRADLE" type="JAVA_MODULE" version="4">
  <component name="FacetManager">
    <facet type="android-gradle" name="Android-Gradle">
      <configuration>
        <option name="GRADLE_PROJECT_PATH" value=":app" />
      </configuration>
    </facet>
    <facet type="android" name="Android">
      <configuration>
        <option name="SELECTED_BUILD_VARIANT" value="debug" />
        <option name="ASSEMBLE_TASK_NAME" value="assembleDebug" />
        <option name="COMPILE_JAVA_TASK_NAME" value="compileDebugSources" />
        <option name="ASSEMBLE_TEST_TASK_NAME" value="assembleDebugTest" />
        <option name="SOURCE_GEN_TASK_NAME" value="generateDebugSources" />
        <option name="TEST_SOURCE_GEN_TASK_NAME" value="generateDebugTestSources" />
        <option name="ALLOW_USER_CONFIGURATION" value="false" />
        <option name="MANIFEST_FILE_RELATIVE_PATH" value="/src/main/AndroidManifest.xml" />
        <option name="RES_FOLDER_RELATIVE_PATH" value="/src/main/res" />
        <option name="RES_FOLDERS_RELATIVE_PATH" value="file://$MODULE_DIR$/src/main/res" />
        <option name="ASSETS_FOLDER_RELATIVE_PATH" value="/src/main/assets" />
      </configuration>
    </facet>
  </component>
  <component name="NewModuleRootManager" inherit-compiler-output="false">
    <output url="file://$MODULE_DIR$/build/intermediates/classes/debug" />
    <exclude-output />
    <content url="file://$MODULE_DIR$">
      <sourceFolder url="file://$MODULE_DIR$/build/generated/source/r/debug" isTestSource="false" generated="true" />
      <sourceFolder url="file://$MODULE_DIR$/build/generated/source/aidl/debug" isTestSource="false" generated="true" />
      <sourceFolder url="file://$MODULE_DIR$/build/generated/source/buildConfig/debug" isTestSource="false" generated="true" />
      <sourceFolder url="file://$MODULE_DIR$/build/generated/source/rs/debug" isTestSource="false" generated="true" />
      <sourceFolder url="file://$MODULE_DIR$/build/generated/res/rs/debug" type="java-resource" />
      <sourceFolder url="file://$MODULE_DIR$/build/generated/res/generated/debug" type="java-resource" />
      <sourceFolder url="file://$MODULE_DIR$/build/generated/source/r/test/debug" isTestSource="true" generated="true" />
      <sourceFolder url="file://$MODULE_DIR$/build/generated/source/aidl/test/debug" isTestSource="true" generated="true" />
      <sourceFolder url="file://$MODULE_DIR$/build/generated/source/buildConfig/test/debug" isTestSource="true" generated="true" />
      <sourceFolder url="file://$MODULE_DIR$/build/generated/source/rs/test/debug" isTestSource="true" generated="true" />
      <sourceFolder url="file://$MODULE_DIR$/build/generated/res/rs/test/debug" type="java-test-resource" />
      <sourceFolder url="file://$MODULE_DIR$/build/generated/res/generated/test/debug" type="java-test-resource" />
      <sourceFolder url="file://$MODULE_DIR$/src/debug/res" type="java-resource" />
      <sourceFolder url="file://$MODULE_DIR$/src/debug/resources" type="java-resource" />
      <sourceFolder url="file://$MODULE_DIR$/src/debug/assets" type="java-resource" />
      <sourceFolder url="file://$MODULE_DIR$/src/debug/aidl" isTestSource="false" />
      <sourceFolder url="file://$MODULE_DIR$/src/debug/java" isTestSource="false" />
      <sourceFolder url="file://$MODULE_DIR$/src/debug/jni" isTestSource="false" />
      <sourceFolder url="file://$MODULE_DIR$/src/debug/rs" isTestSource="false" />
      <sourceFolder url="file://$MODULE_DIR$/src/main/res" type="java-resource" />
      <sourceFolder url="file://$MODULE_DIR$/src/main/resources" type="java-resource" />
      <sourceFolder url="file://$MODULE_DIR$/src/main/assets" type="java-resource" />
      <sourceFolder url="file://$MODULE_DIR$/src/main/aidl" isTestSource="false" />
      <sourceFolder url="file://$MODULE_DIR$/src/main/java" isTestSource="false" />
      <sourceFolder url="file://$MODULE_DIR$/src/main/jni" isTestSource="false" />
      <sourceFolder url="file://$MODULE_DIR$/src/main/rs" isTestSource="false" />
      <sourceFolder url="file://$MODULE_DIR$/src/androidTest/res" type="java-test-resource" />
      <sourceFolder url="file://$MODULE_DIR$/src/androidTest/resources" type="java-test-resource" />
      <sourceFolder url="file://$MODULE_DIR$/src/androidTest/assets" type="java-test-resource" />
      <sourceFolder url="file://$MODULE_DIR$/src/androidTest/aidl" isTestSource="true" />
      <sourceFolder url="file://$MODULE_DIR$/src/androidTest/java" isTestSource="true" />
      <sourceFolder url="file://$MODULE_DIR$/src/androidTest/jni" isTestSource="true" />
      <sourceFolder url="file://$MODULE_DIR$/src/androidTest/rs" isTestSource="true" />
      <excludeFolder url="file://$MODULE_DIR$/build/intermediates/assets" />
      <excludeFolder url="file://$MODULE_DIR$/build/intermediates/bundles" />
      <excludeFolder url="file://$MODULE_DIR$/build/intermediates/classes" />
      <excludeFolder url="file://$MODULE_DIR$/build/intermediates/coverage-instrumented-classes" />
      <excludeFolder url="file://$MODULE_DIR$/build/intermediates/dependency-cache" />
      <excludeFolder url="file://$MODULE_DIR$/build/intermediates/dex" />
      <excludeFolder url="file://$MODULE_DIR$/build/intermediates/dex-cache" />
      <excludeFolder url="file://$MODULE_DIR$/build/intermediates/incremental" />
      <excludeFolder url="file://$MODULE_DIR$/build/intermediates/jacoco" />
      <excludeFolder url="file://$MODULE_DIR$/build/intermediates/javaResources" />
      <excludeFolder url="file://$MODULE_DIR$/build/intermediates/libs" />
      <excludeFolder url="file://$MODULE_DIR$/build/intermediates/lint" />
      <excludeFolder url="file://$MODULE_DIR$/build/intermediates/manifests" />
      <excludeFolder url="file://$MODULE_DIR$/build/intermediates/ndk" />
      <excludeFolder url="file://$MODULE_DIR$/build/intermediates/pre-dexed" />
      <excludeFolder url="file://$MODULE_DIR$/build/intermediates/proguard" />
      <excludeFolder url="file://$MODULE_DIR$/build/intermediates/res" />
      <excludeFolder url="file://$MODULE_DIR$/build/intermediates/rs" />
      <excludeFolder url="file://$MODULE_DIR$/build/intermediates/symbols" />
      <excludeFolder url="file://$MODULE_DIR$/build/outputs" />
      <excludeFolder url="file://$MODULE_DIR$/build/tmp" />
    </content>
    <orderEntry type="jdk" jdkName="Android API 21 Platform" jdkType="Android SDK" />
    <orderEntry type="sourceFolder" forTests="false" />
    <orderEntry type="library" exported="" name="support-annotations-21.0.0" level="project" />
    <orderEntry type="library" exported="" name="picasso-2.3.4" level="project" />
    <orderEntry type="library" exported="" name="support-v4-21.0.0" level="project" />
    <orderEntry type="library" exported="" name="appcompat-v7-21.0.0" level="project" />
  </component>
</module>



================================================
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;

/**
 * <a href="http://d.android.com/tools/testing/testing_android.html">Testing Fundamentals</a>
 */
public class ApplicationTest extends ApplicationTestCase<Application> {
    public ApplicationTest() {
        super(Application.class);
    }
}

================================================
FILE: app/src/main/AndroidManifest.xml
================================================
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
    package="com.weixinninegridlayout">

    <uses-permission android:name="android.permission.INTERNET" />
    <uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
    <uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE"/>

    <application
        android:allowBackup="true"
        android:icon="@drawable/ic_launcher"
        android:label="@string/app_name"
        android:theme="@style/AppTheme">
        <activity
            android:name="com.weixinninegridlayout.MainActivity"
            android:label="@string/app_name">
            <intent-filter>
                <action android:name="android.intent.action.MAIN" />

                <category android:name="android.intent.category.LAUNCHER" />
            </intent-filter>
        </activity>
    </application>

</manifest>


================================================
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<List<Image>> 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<Image> 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<Image> 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<List<Image>> datalist;

    public MainAdapter(Context context, List<List<Image>> 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<Image> 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<Image> 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
================================================
    <LinearLayout
        xmlns:android="http://schemas.android.com/apk/res/android"
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:orientation="vertical"
       >

     <ListView android:layout_width="fill_parent"
         android:layout_height="fill_parent"
         android:listSelector="@android:color/transparent"
         android:id="@+id/lv_main"
         />

    </LinearLayout>


================================================
FILE: app/src/main/res/layout/item_ninegridlayout.xml
================================================
<?xml version="1.0" encoding="utf-8"?>
<FrameLayout
    android:layout_height="fill_parent"
    android:layout_width="fill_parent"
    xmlns:android="http://schemas.android.com/apk/res/android">

    <LinearLayout
        android:layout_marginTop="10dp"
        android:layout_marginBottom="10dp"
        android:orientation="horizontal"
        android:layout_width="match_parent"
        android:layout_height="match_parent">

        <ImageView
            android:layout_width="50dp"
            android:background="@drawable/ic_launcher"
            android:layout_height="50dp" />

        <LinearLayout
            android:layout_marginLeft="5dp"
            android:layout_width="fill_parent"
            android:orientation="vertical"
            android:layout_height="wrap_content">

            <TextView
                android:text="小明"
                android:layout_width="wrap_content"
                android:layout_height="wrap_content" />

            <TextView
                android:layout_marginTop="5dp"
                android:text="今天天气真好,好开心!!!!!!!!!!!!!!!!!"
                android:layout_width="wrap_content"
                android:layout_height="wrap_content" />

            <com.weixinninegridlayout.NineGridlayout
                android:layout_marginTop="8dp"
                android:id="@+id/iv_ngrid_layout"
                android:layout_height="wrap_content"
                android:layout_width="match_parent" />

            <com.weixinninegridlayout.CustomImageView
                android:layout_marginTop="8dp"
                android:id="@+id/iv_oneimage"
                android:layout_width="fill_parent"
                android:layout_height="wrap_content" />

            <TextView
                android:layout_marginTop="8dp"
                android:text="3分钟前"
                android:layout_width="wrap_content"
                android:layout_height="wrap_content" />
        </LinearLayout>
    </LinearLayout>
</FrameLayout>

================================================
FILE: app/src/main/res/menu/menu_main.xml
================================================
<menu xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    xmlns:tools="http://schemas.android.com/tools" tools:context=".MainActivity">
    <item android:id="@+id/action_settings" android:title="@string/action_settings"
        android:orderInCategory="100" app:showAsAction="never" />
</menu>


================================================
FILE: app/src/main/res/values/dimens.xml
================================================
<resources>
    <!-- Default screen margins, per the Android Design guidelines. -->
    <dimen name="activity_horizontal_margin">16dp</dimen>
    <dimen name="activity_vertical_margin">16dp</dimen>
</resources>


================================================
FILE: app/src/main/res/values/strings.xml
================================================
<?xml version="1.0" encoding="utf-8"?>
<resources>

    <string name="app_name">WeixinNineGridLayout</string>
    <string name="hello_world">Hello world!</string>
    <string name="action_settings">Settings</string>

</resources>


================================================
FILE: app/src/main/res/values/styles.xml
================================================
<resources>

    <!-- Base application theme. -->
    <style name="AppTheme" parent="Theme.AppCompat.Light.DarkActionBar">
        <!-- Customize your theme here. -->
    </style>

</resources>


================================================
FILE: app/src/main/res/values-w820dp/dimens.xml
================================================
<resources>
    <!-- Example customization of dimensions originally defined in res/values/dimens.xml
         (such as screen margins) for screens with more than 820dp of available width. This
         would include 7" and 10" devices in landscape (~960dp and ~1280dp respectively). -->
    <dimen name="activity_horizontal_margin">64dp</dimen>
</resources>


================================================
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<T> {
  static class RequestWeakReference<T> extends WeakReference<T> {
    final Action action;

    public RequestWeakReference(Action action, T referent, ReferenceQueue<? super T> q) {
      super(referent, q);
      this.action = action;
    }
  }

  final Picasso picasso;
  final Request request;
  final WeakReference<T> 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<T>(this, target, picasso.referenceQueue);
    this.skipCache = skipCache;
    this.noFade = noFade;
    this.errorResId = errorResId;
    this.errorDrawable = errorDrawable;
    this.key = key;
  }

  abstract void complete(Bitmap result, Picasso.LoadedFrom from);

  abstract void error();

  void cancel() {
    cancelled = true;
  }

  Request 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<StringBuilder> NAME_BUILDER = new ThreadLocal<StringBuilder>() {
    @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<Action> 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<Action>(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<Action> 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<RequestHandler> 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<Transformation> transformations, Bitmap result) {
    for (int i = 0, count = transformations.size(); i < count; i++) {
      final Transformation transformation = transformations.get(i);
      Bitmap newResult = transformation.transform(result);

      if (newResult == null) {
        final StringBuilder builder = new StringBuilder() //
            .append("Transformation ")
            .append(transformation.key())
            .append(" returned null after ")
            .append(i)
            .append(" previous transformation(s).\n\nTransformation list:\n");
        for (Transformation t : transformations) {
          builder.append(t.key()).append('\n');
        }
        Picasso.HANDLER.post(new Runnable() {
          @Override public void run() {
            throw new NullPointerException(builder.toString());
          }
        });
        return null;
      }

      if (newResult == result && result.isRecycled()) {
        Picasso.HANDLER.post(new Runnable() {
          @Override public void run() {
            throw new IllegalStateException("Transformation "
                + transformation.key()
                + " returned input Bitmap but recycled it.");
          }
        });
        return null;
      }

      // If the transformation returned a new bitmap ensure they recycled the original.
      if (newResult != result && !result.isRecycled()) {
        Picasso.HANDLER.post(new Runnable() {
          @Override public void run() {
            throw new IllegalStateException("Transformation "
                + transformation.key()
                + " mutated input Bitmap but failed to recycle the original.");
          }
        });
        return null;
      }

      result = newResult;
    }
    return result;
  }

  static Bitmap transformResult(Request data, Bitmap result, int exifRotation) {
    int inWidth = result.getWidth();
    int inHeight = result.getHeight();

    int drawX = 0;
    int drawY = 0;
    int drawWidth = inWidth;
    int drawHeight = inHeight;

    Matrix matrix = new Matrix();

    if (data.needsMatrixTransform()) {
      int targetWidth = data.targetWidth;
      int targetHeight = data.targetHeight;

      float targetRotation = data.rotationDegrees;
      if (targetRotation != 0) {
        if (data.hasRotationPivot) {
          matrix.setRotate(targetRotation, data.rotationPivotX, data.rotationPivotY);
        } else {
          matrix.setRotate(targetRotation);
        }
      }

      if (data.centerCrop) {
        float widthRatio = targetWidth / (float) inWidth;
        float heightRatio = targetHeight / (float) inHeight;
        float scale;
        if (widthRatio > heightRatio) {
          scale = widthRatio;
          int newSize = (int) Math.ceil(inHeight * (heightRatio / widthRatio));
          drawY = (inHeight - newSize) / 2;
          drawHeight = newSize;
        } else {
          scale = heightRatio;
          int newSize = (int) Math.ceil(inWidth * (widthRatio / heightRatio));
          drawX = (inWidth - newSize) / 2;
          drawWidth = newSize;
        }
        matrix.preScale(scale, scale);
      } else if (data.centerInside) {
        float widthRatio = targetWidth / (float) inWidth;
        float heightRatio = targetHeight / (float) inHeight;
        float scale = widthRatio < heightRatio ? widthRatio : heightRatio;
        matrix.preScale(scale, scale);
      } else if (targetWidth != 0 && targetHeight != 0 //
          && (targetWidth != inWidth || targetHeight != inHeight)) {
        // If an explicit target size has been specified and they do not match the results bounds,
        // pre-scale the existing matrix appropriately.
        float sx = targetWidth / (float) inWidth;
        float sy = targetHeight / (float) inHeight;
        matrix.preScale(sx, sy);
      }
    }

    if (exifRotation != 0) {
      matrix.preRotate(exifRotation);
    }

    Bitmap newResult =
        Bitmap.createBitmap(result, drawX, drawY, drawWidth, drawHeight, matrix, true);
    if (newResult != result) {
      result.recycle();
      result = newResult;
    }

    return result;
  }
}


================================================
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.
 * <p>
 * <em>Note:</em> 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<ImageView> 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<ImageView>(target);
    this.callback = callback;
    target.getViewTreeObserver().addOnPreDrawListener(this);
  }

  @Override public boolean onPreDraw() {
    ImageView target = this.target.get();
    if (target == null) {
      return true;
    }
    ViewTreeObserver vto = target.getViewTreeObserver();
    if (!vto.isAlive()) {
      return true;
    }

    int width = target.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<String, BitmapHunter> hunterMap;
  final Map<Object, Action> failedActions;
  final Handler handler;
  final Handler mainThreadHandler;
  final Cache cache;
  final Stats stats;
  final List<BitmapHunter> batch;
  final NetworkBroadcastReceiver receiver;
  final boolean scansNetworkChanges;

  boolean airplaneMode;

  Dispatcher(Context context, ExecutorService service, Handler mainThreadHandler,
      Downloader downloader, Cache cache, Stats stats) {
    this.dispatcherThread = new DispatcherThread();
    this.dispatcherThread.start();
    this.context = context;
    this.service = service;
    this.hunterMap = new LinkedHashMap<String, BitmapHunter>();
    this.failedActions = new WeakHashMap<Object, Action>();
    this.handler = new DispatcherHandler(dispatcherThread.getLooper(), this);
    this.downloader = downloader;
    this.mainThreadHandler = mainThreadHandler;
    this.cache = cache;
    this.stats = stats;
    this.batch = new ArrayList<BitmapHunter>(4);
    this.airplaneMode = Utils.isAirplaneModeOn(this.context);
    this.scansNetworkChanges = hasPermission(context, Manifest.permission.ACCESS_NETWORK_STATE);
    this.receiver = new NetworkBroadcastReceiver(this);
    receiver.register();
  }

  void shutdown() {
    service.shutdown();
    dispatcherThread.quit();
    receiver.unregister();
  }

  void dispatchSubmit(Action action) {
    handler.sendMessage(handler.obtainMessage(REQUEST_SUBMIT, action));
  }

  void dispatchCancel(Action action) {
    handler.sendMessage(handler.obtainMessage(REQUEST_CANCEL, action));
  }

  void dispatchComplete(BitmapHunter hunter) {
    handler.sendMessage(handler.obtainMessage(HUNTER_COMPLETE, hunter));
  }

  void dispatchRetry(BitmapHunter hunter) {
    handler.sendMessageDelayed(handler.obtainMessage(HUNTER_RETRY, hunter), RETRY_DELAY);
  }

  void dispatchFailed(BitmapHunter hunter) {
    handler.sendMessage(handler.obtainMessage(HUNTER_DECODE_FAILED, hunter));
  }

  void dispatchNetworkStateChange(NetworkInfo info) {
    handler.sendMessage(handler.obtainMessage(NETWORK_STATE_CHANGE, info));
  }

  void dispatchAirplaneModeChange(boolean airplaneMode) {
    handler.sendMessage(handler.obtainMessage(AIRPLANE_MODE_CHANGE,
        airplaneMode ? AIRPLANE_MODE_ON : AIRPLANE_MODE_OFF, 0));
  }

  void performSubmit(Action action) {
    BitmapHunter hunter = hunterMap.get(action.getKey());
    if (hunter != null) {
      hunter.attach(action);
      return;
    }

    if (service.isShutdown()) {
      if (action.getPicasso().loggingEnabled) {
        log(OWNER_DISPATCHER, VERB_IGNORED, action.request.logId(), "because shut down");
      }
      return;
    }

    hunter = forRequest(action.getPicasso(), this, cache, stats, action);
    hunter.future = service.submit(hunter);
    hunterMap.put(action.getKey(), hunter);
    failedActions.remove(action.getTarget());

    if (action.getPicasso().loggingEnabled) {
      log(OWNER_DISPATCHER, VERB_ENQUEUED, action.request.logId());
    }
  }

  void performCancel(Action action) {
    String key = action.getKey();
    BitmapHunter hunter = hunterMap.get(key);
    if (hunter != null) {
      hunter.detach(action);
      if (hunter.cancel()) {
        hunterMap.remove(key);
        if (action.getPicasso().loggingEnabled) {
          log(OWNER_DISPATCHER, VERB_CANCELED, action.getRequest().logId());
        }
      }
    }
    Action remove = failedActions.remove(action.getTarget());
    if (remove != null && remove.getPicasso().loggingEnabled) {
      log(OWNER_DISPATCHER, VERB_CANCELED, remove.getRequest().logId(), "from replaying");
    }
  }

  void performRetry(BitmapHunter hunter) {
    if (hunter.isCancelled()) return;

    if (service.isShutdown()) {
      performError(hunter, false);
      return;
    }

    NetworkInfo networkInfo = null;
    if (scansNetworkChanges) {
      ConnectivityManager connectivityManager = getService(context, CONNECTIVITY_SERVICE);
      networkInfo = connectivityManager.getActiveNetworkInfo();
    }

    boolean hasConnectivity = networkInfo != null && networkInfo.isConnected();
    boolean shouldRetryHunter = hunter.shouldRetry(airplaneMode, networkInfo);
    boolean supportsReplay = hunter.supportsReplay();

    if (!shouldRetryHunter) {
      // Mark for replay only if we observe network info changes and support replay.
      boolean willReplay = scansNetworkChanges && supportsReplay;
      performError(hunter, willReplay);
      if (willReplay) {
        markForReplay(hunter);
      }
      return;
    }

    // If we don't scan for network changes (missing permission) or if we have connectivity, retry.
    if (!scansNetworkChanges || hasConnectivity) {
      if (hunter.getPicasso().loggingEnabled) {
        log(OWNER_DISPATCHER, VERB_RETRYING, getLogIdsForHunter(hunter));
      }
      hunter.future = service.submit(hunter);
      return;
    }

    performError(hunter, supportsReplay);

    if (supportsReplay) {
      markForReplay(hunter);
    }
  }

  void performComplete(BitmapHunter hunter) {
    if (!hunter.shouldSkipMemoryCache()) {
      cache.set(hunter.getKey(), hunter.getResult());
    }
    hunterMap.remove(hunter.getKey());
    batch(hunter);
    if (hunter.getPicasso().loggingEnabled) {
      log(OWNER_DISPATCHER, VERB_BATCHED, getLogIdsForHunter(hunter), "for completion");
    }
  }

  void performBatchComplete() {
    List<BitmapHunter> copy = new ArrayList<BitmapHunter>(batch);
    batch.clear();
    mainThreadHandler.sendMessage(mainThreadHandler.obtainMessage(HUNTER_BATCH_COMPLETE, copy));
    logBatch(copy);
  }

  void performError(BitmapHunter hunter, boolean willReplay) {
    if (hunter.getPicasso().loggingEnabled) {
      log(OWNER_DISPATCHER, VERB_BATCHED, getLogIdsForHunter(hunter),
          "for error" + (willReplay ? " (will replay)" : ""));
    }
    hunterMap.remove(hunter.getKey());
    batch(hunter);
  }

  void performAirplaneModeChange(boolean airplaneMode) {
    this.airplaneMode = airplaneMode;
  }

  void performNetworkStateChange(NetworkInfo info) {
    if (service instanceof PicassoExecutorService) {
      ((PicassoExecutorService) service).adjustThreadCount(info);
    }
    // Intentionally check only if isConnected() here before we flush out failed actions.
    if (info != null && info.isConnected()) {
      flushFailedActions();
    }
  }

  private void flushFailedActions() {
    if (!failedActions.isEmpty()) {
      Iterator<Action> iterator = failedActions.values().iterator();
      while (iterator.hasNext()) {
        Action action = iterator.next();
        iterator.remove();
        if (action.getPicasso().loggingEnabled) {
          log(OWNER_DISPATCHER, VERB_REPLAYING, action.getRequest().logId());
        }
        performSubmit(action);
      }
    }
  }

  private void markForReplay(BitmapHunter hunter) {
    Action action = hunter.getAction();
    if (action != null) {
      markForReplay(action);
    }
    List<Action> joined = hunter.getActions();
    if (joined != null) {
      //noinspection ForLoopReplaceableByForEach
      for (int i = 0, n = joined.size(); i < n; i++) {
        Action join = joined.get(i);
        markForReplay(join);
      }
    }
  }

  private void markForReplay(Action action) {
    Object target = action.getTarget();
    if (target != null) {
      action.willReplay = true;
      failedActions.put(target, action);
    }
  }

  private void batch(BitmapHunter hunter) {
    if (hunter.isCancelled()) {
      return;
    }
    batch.add(hunter);
    if (!handler.hasMessages(HUNTER_DELAY_NEXT_BATCH)) {
      handler.sendEmptyMessageDelayed(HUNTER_DELAY_NEXT_BATCH, BATCH_DELAY);
    }
  }

  private void logBatch(List<BitmapHunter> copy) {
    if (copy == null || copy.isEmpty()) return;
    BitmapHunter hunter = copy.get(0);
    Picasso picasso = hunter.getPicasso();
    if (picasso.loggingEnabled) {
      StringBuilder builder = new StringBuilder();
      for (BitmapHunter bitmapHunter : copy) {
        if (builder.length() > 0) builder.append(", ");
        builder.append(Utils.getLogIdsForHunter(bitmapHunter));
      }
      log(OWNER_DISPATCHER, VERB_DELIVERED, builder.toString());
    }
  }

  private static class DispatcherHandler extends Handler {
    private final Dispatcher dispatcher;

    public DispatcherHandler(Looper looper, Dispatcher dispatcher) {
      super(looper);
      this.dispatcher = dispatcher;
    }

    @Override public void handleMessage(final Message msg) {
      switch (msg.what) {
        case REQUEST_SUBMIT: {
          Action action = (Action) msg.obj;
          dispatcher.performSubmit(action);
          break;
        }
        case REQUEST_CANCEL: {
          Action action = (Action) msg.obj;
          dispatcher.performCancel(action);
          break;
        }
        case HUNTER_COMPLETE: {
          BitmapHunter hunter = (BitmapHunter) msg.obj;
          dispatcher.performComplete(hunter);
          break;
        }
        case HUNTER_RETRY: {
          BitmapHunter hunter = (BitmapHunter) msg.obj;
          dispatcher.performRetry(hunter);
          break;
        }
        case HUNTER_DECODE_FAILED: {
          BitmapHunter hunter = (BitmapHunter) msg.obj;
          dispatcher.performError(hunter, false);
          break;
        }
        case HUNTER_DELAY_NEXT_BATCH: {
          dispatcher.performBatchComplete();
          break;
        }
        case NETWORK_STATE_CHANGE: {
          NetworkInfo info = (NetworkInfo) msg.obj;
          dispatcher.performNetworkStateChange(info);
          break;
        }
        case AIRPLANE_MODE_CHANGE: {
          dispatcher.performAirplaneModeChange(msg.arg1 == AIRPLANE_MODE_ON);
          break;
        }
        default:
          Picasso.HANDLER.post(new Runnable() {
            @Override public void run() {
              throw new AssertionError("Unknown handler message received: " + msg.what);
            }
          });
      }
    }
  }

  static class DispatcherThread extends HandlerThread {
    DispatcherThread() {
      super(Utils.THREAD_PREFIX + DISPATCHER_THREAD_NAME, THREAD_PRIORITY_BACKGROUND);
    }
  }

  static class NetworkBroadcastReceiver extends BroadcastReceiver {
    static final String EXTRA_AIRPLANE_STATE = "state";

    private final Dispatcher dispatcher;

    NetworkBroadcastReceiver(Dispatcher dispatcher) {
      this.dispatcher = dispatcher;
    }

    void register() {
      IntentFilter filter = new IntentFilter();
      filter.addAction(ACTION_AIRPLANE_MODE_CHANGED);
      if (dispatcher.scansNetworkChanges) {
        filter.addAction(CONNECTIVITY_ACTION);
      }
      dispatcher.context.registerReceiver(this, filter);
    }

    void unregister() {
      dispatcher.context.unregisterReceiver(this);
    }

    @Override public void onReceive(Context context, Intent intent) {
      // On some versions of Android this may be called with a null Intent,
      // also without extras (getExtras() == null), in such case we use defaults.
      if (intent == null) {
        return;
      }
      final String action = intent.getAction();
      if (ACTION_AIRPLANE_MODE_CHANGED.equals(action)) {
        if (!intent.hasExtra(EXTRA_AIRPLANE_STATE)) {
          return; // No airplane state, ignore it. Should we query Utils.isAirplaneModeOn?
        }
        dispatcher.dispatchAirplaneModeChange(intent.getBooleanExtra(EXTRA_AIRPLANE_STATE, false));
      } else if (CONNECTIVITY_ACTION.equals(action)) {
        ConnectivityManager connectivityManager = getService(context, CONNECTIVITY_SERVICE);
        dispatcher.dispatchNetworkStateChange(connectivityManager.getActiveNetworkInfo());
      }
    }
  }
}


================================================
FILE: picasso/Downloader.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.net.Uri;
import java.io.IOException;
import java.io.InputStream;

/** A mechanism to load images from external resources such as a disk cache and/or the internet. */
public interface Downloader {
  /**
   * Download the specified image {@code url} from the internet.
   *
   * @param uri Remote image URL.
   * @param localCacheOnly If {@code true} the URL should only be loaded if available in a local
   * disk cache.
   * @return {@link com.squareup.picasso.Downloader.Response} containing either a {@link android.graphics.Bitmap} representation of the request or an
   * {@link java.io.InputStream} for the image data. {@code null} can be returned to indicate a problem
   * loading the bitmap.
   * @throws java.io.IOException if the requested URL cannot successfully be loaded.
   */
  Response load(Uri uri, boolean localCacheOnly) throws IOException;

  /** Thrown for non-2XX responses. */
  class ResponseException extends IOException {
    public ResponseException(String message) {
      super(message);
    }
  }

  /** Response stream or bitmap and info. */
  class Response {
    final InputStream stream;
    final Bitmap bitmap;
    final boolean cached;
    final long contentLength;

    /**
     * Response image and info.
     *
     * @param bitmap Image.
     * @param loadedFromCache {@code true} if the source of the image is from a local disk cache.
     * @deprecated Use {@link com.squareup.picasso.Downloader.Response#Response(android.graphics.Bitmap, boolean, long)} instead.
     */
    @Deprecated @SuppressWarnings("UnusedDeclaration")
    public Response(Bitmap bitmap, boolean loadedFromCache) {
      this(bitmap, loadedFromCache, -1);
    }

    /**
     * Response stream and info.
     *
     * @param stream Image data stream.
     * @param loadedFromCache {@code true} if the source of the stream is from a local disk cache.
     * @deprecated Use {@link com.squareup.picasso.Downloader.Response#Response(java.io.InputStream, boolean, long)} instead.
     */
    @Deprecated @SuppressWarnings("UnusedDeclaration")
    public Response(InputStream stream, boolean loadedFromCache) {
      this(stream, loadedFromCache, -1);
    }

    /**
     * Response image and info.
     *
     * @param bitmap Image.
     * @param loadedFromCache {@code true} if the source of the image is from a local disk cache.
     * @param contentLength The content length of the response, typically derived by the
     * {@code Content-Length} HTTP header.
     */
    public Response(Bitmap bitmap, boolean loadedFromCache, long contentLength) {
      if (bitmap == null) {
        throw new IllegalArgumentException("Bitmap may not be null.");
      }
      this.stream = null;
      this.bitmap = bitmap;
      this.cached = loadedFromCache;
      this.contentLength = contentLength;
    }

    /**
     * Response stream and info.
     *
     * @param stream Image data stream.
     * @param loadedFromCache {@code true} if the source of the stream is from a local disk cache.
     * @param contentLength The content length of the response, typically derived by the
     * {@code Content-Length} HTTP header.
     */
    public Response(InputStream stream, boolean loadedFromCache, long contentLength) {
      if (stream == null) {
        throw new IllegalArgumentException("Stream may not be null.");
      }
      this.stream = stream;
      this.bitmap = null;
      this.cached = loadedFromCache;
      this.contentLength = contentLength;
    }

    /**
     * Input stream containing image data.
     * <p>
     * If this returns {@code null}, image data will be available via {@link #getBitmap()}.
     */
    public InputStream getInputStream() {
      return stream;
    }

    /**
     * Bitmap representing the image.
     * <p>
     * If this returns {@code null}, image data will be available via {@link #getInputStream()}.
     */
    public Bitmap getBitmap() {
      return bitmap;
    }

    /** Content length of the response. */
    public long getContentLength() {
      return contentLength;
    }
  }
}


================================================
FILE: picasso/FetchAction.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;

class FetchAction extends Action<Void> {
  FetchAction(Picasso picasso, Request data, boolean skipCache, String key) {
    super(picasso, null, data, skipCache, false, 0, null, key);
  }

  @Override void complete(Bitmap result, Picasso.LoadedFrom from) {
  }

  @Override public void error() {
  }
}


================================================
FILE: picasso/FileRequestHandler.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.media.ExifInterface;
import android.net.Uri;
import java.io.IOException;

import static android.content.ContentResolver.SCHEME_FILE;
import static android.media.ExifInterface.ORIENTATION_NORMAL;
import static android.media.ExifInterface.ORIENTATION_ROTATE_180;
import static android.media.ExifInterface.ORIENTATION_ROTATE_270;
import static android.media.ExifInterface.ORIENTATION_ROTATE_90;
import static android.media.ExifInterface.TAG_ORIENTATION;
import static com.squareup.picasso.Picasso.LoadedFrom.DISK;

class FileRequestHandler extends ContentStreamRequestHandler {

  FileRequestHandler(Context context) {
    super(context);
  }

  @Override public boolean canHandleRequest(Request data) {
    return SCHEME_FILE.equals(data.uri.getScheme());
  }

  @Override public Result load(Request data) throws IOException {
    return new Result(decodeContentStream(data), DISK, getFileExifRotation(data.uri));
  }

  static int getFileExifRotation(Uri uri) throws IOException {
    ExifInterface exifInterface = new ExifInterface(uri.getPath());
    int orientation = exifInterface.getAttributeInt(TAG_ORIENTATION, ORIENTATION_NORMAL);
    switch (orientation) {
      case ORIENTATION_ROTATE_90:
        return 90;
      case ORIENTATION_ROTATE_180:
        return 180;
      case ORIENTATION_ROTATE_270:
        return 270;
      default:
        return 0;
    }
  }
}


================================================
FILE: picasso/GetAction.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;

class GetAction extends Action<Void> {
  GetAction(Picasso picasso, Request data, boolean skipCache, String key) {
    super(picasso, null, data, skipCache, false, 0, null, key);
  }

  @Override void complete(Bitmap result, Picasso.LoadedFrom from) {
  }

  @Override public void error() {
  }
}


================================================
FILE: picasso/ImageViewAction.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.graphics.Bitmap;
import android.graphics.drawable.Drawable;
import android.widget.ImageView;

class ImageViewAction extends Action<ImageView> {

  Callback callback;

  ImageViewAction(Picasso picasso, ImageView imageView, Request data, boolean skipCache,
      boolean noFade, int errorResId, Drawable errorDrawable, String key, Callback callback) {
    super(picasso, imageView, data, skipCache, noFade, errorResId, errorDrawable, key);
    this.callback = callback;
  }

  @Override public void complete(Bitmap result, Picasso.LoadedFrom from) {
    if (result == null) {
      throw new AssertionError(
          String.format("Attempted to complete action with no result!\n%s", this));
    }

    ImageView target = this.target.get();
    if (target == null) {
      return;
    }

    Context context = picasso.context;
    boolean indicatorsEnabled = picasso.indicatorsEnabled;
    PicassoDrawable.setBitmap(target, context, result, from, noFade, indicatorsEnabled);

    if (callback != null) {
      callback.onSuccess();
    }
  }

  @Override public void error() {
    ImageView target = this.target.get();
    if (target == null) {
      return;
    }
    if (errorResId != 0) {
      target.setImageResource(errorResId);
    } else if (errorDrawable != null) {
      target.setImageDrawable(errorDrawable);
    }

    if (callback != null) {
      callback.onError();
    }
  }

  @Override void cancel() {
    super.cancel();
    if (callback != null) {
      callback = null;
    }
  }
}


================================================
FILE: picasso/LruCache.java
================================================
/*
 * Copyright (C) 2011 The Android Open Source Project
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
package com.squareup.picasso;

import android.content.Context;
import android.graphics.Bitmap;
import java.util.LinkedHashMap;
import java.util.Map;

/** A memory cache which uses a least-recently used eviction policy. */
public class LruCache implements Cache {
  final LinkedHashMap<String, Bitmap> map;
  private final int maxSize;

  private int size;
  private int putCount;
  private int evictionCount;
  private int hitCount;
  private int missCount;

  /** Create a cache using an appropriate portion of the available RAM as the maximum size. */
  public LruCache(Context context) {
    this(Utils.calculateMemoryCacheSize(context));
  }

  /** Create a cache with a given maximum size in bytes. */
  public LruCache(int maxSize) {
    if (maxSize <= 0) {
      throw new IllegalArgumentException("Max size must be positive.");
    }
    this.maxSize = maxSize;
    this.map = new LinkedHashMap<String, Bitmap>(0, 0.75f, true);
  }

  @Override public Bitmap get(String key) {
    if (key == null) {
      throw new NullPointerException("key == null");
    }

    Bitmap mapValue;
    synchronized (this) {
      mapValue = map.get(key);
      if (mapValue != null) {
        hitCount++;
        return mapValue;
      }
      missCount++;
    }

    return null;
  }

  @Override public void set(String key, Bitmap bitmap) {
    if (key == null || bitmap == null) {
      throw new NullPointerException("key == null || bitmap == null");
    }

    Bitmap previous;
    synchronized (this) {
      putCount++;
      size += Utils.getBitmapBytes(bitmap);
      previous = map.put(key, bitmap);
      if (previous != null) {
        size -= Utils.getBitmapBytes(previous);
      }
    }

    trimToSize(maxSize);
  }

  private void trimToSize(int maxSize) {
    while (true) {
      String key;
      Bitmap value;
      synchronized (this) {
        if (size < 0 || (map.isEmpty() && size != 0)) {
          throw new IllegalStateException(
              getClass().getName() + ".sizeOf() is reporting inconsistent results!");
        }

        if (size <= maxSize || map.isEmpty()) {
          break;
        }

        Map.Entry<String, Bitmap> toEvict = map.entrySet().iterator().next();
        key = toEvict.getKey();
        value = toEvict.getValue();
        map.remove(key);
        size -= Utils.getBitmapBytes(value);
        evictionCount++;
      }
    }
  }

  /** Clear the cache. */
  public final void evictAll() {
    trimToSize(-1); // -1 will evict 0-sized elements
  }

  /** Returns the sum of the sizes of the entries in this cache. */
  public final synchronized int size() {
    return size;
  }

  /** Returns the maximum sum of the sizes of the entries in this cache. */
  public final synchronized int maxSize() {
    return maxSize;
  }

  public final synchronized void clear() {
    evictAll();
  }

  /** Returns the number of times {@link #get} returned a value. */
  public final synchronized int hitCount() {
    return hitCount;
  }

  /** Returns the number of times {@link #get} returned {@code null}. */
  public final synchronized int missCount() {
    return missCount;
  }

  /** Returns the number of times {@link #set(String, android.graphics.Bitmap)} was called. */
  public final synchronized int putCount() {
    return putCount;
  }

  /** Returns the number of values that have been evicted. */
  public final synchronized int evictionCount() {
    return evictionCount;
  }
}


================================================
FILE: picasso/MarkableInputStream.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 java.io.BufferedInputStream;
import java.io.IOException;
import java.io.InputStream;

/**
 * An input stream wrapper that supports unlimited independent cursors for
 * marking and resetting. Each cursor is a token, and it's the caller's
 * responsibility to keep track of these.
 */
final class MarkableInputStream extends InputStream {
  private static final int DEFAULT_BUFFER_SIZE = 4096;

  private final InputStream in;

  private long offset;
  private long reset;
  private long limit;
  private long defaultMark = -1;

  public MarkableInputStream(InputStream in) {
    this(in, DEFAULT_BUFFER_SIZE);
  }

  public MarkableInputStream(InputStream in, int size) {
    if (!in.markSupported()) {
      in = new BufferedInputStream(in, size);
    }
    this.in = in;
  }

  /** Marks this place in the stream so we can reset back to it later. */
  @Override public void mark(int readLimit) {
    defaultMark = savePosition(readLimit);
  }

  /**
   * Returns an opaque token representing the current position in the stream.
   * Call {@link #reset(long)} to return to this position in the stream later.
   * It is an error to call {@link #reset(long)} after consuming more than
   * {@code readLimit} bytes from this stream.
   */
  public long savePosition(int readLimit) {
    long offsetLimit = offset + readLimit;
    if (limit < offsetLimit) {
      setLimit(offsetLimit);
    }
    return offset;
  }

  /**
   * Makes sure that the underlying stream can backtrack the full range from
   * {@code reset} thru {@code limit}. Since we can't call {@code mark()}
   * without also adjusting the reset-to-position on the underlying stream this
   * method resets first and then marks the union of the two byte ranges. On
   * buffered streams this additional cursor motion shouldn't result in any
   * additional I/O.
   */
  private void setLimit(long limit) {
    try {
      if (reset < offset && offset <= this.limit) {
        in.reset();
        in.mark((int) (limit - reset));
        skip(reset, offset);
      } else {
        reset = offset;
        in.mark((int) (limit - offset));
      }
      this.limit = limit;
    } catch (IOException e) {
      throw new IllegalStateException("Unable to mark: " + e);
    }
  }

  /** Resets the stream to the most recent {@link #mark mark}. */
  @Override public void reset() throws IOException {
    reset(defaultMark);
  }

  /** Resets the stream to the position recorded by {@code token}. */
  public void reset(long token) throws IOException {
    if (offset > limit || token < reset) {
      throw new IOException("Cannot reset");
    }
    in.reset();
    skip(reset, token);
    offset = token;
  }

  /** Skips {@code target - current} bytes and returns. */
  private void skip(long current, long target) throws IOException {
    while (current < target) {
      long skipped = in.skip(target - current);
      if (skipped == 0) {
        if (read() == -1) {
          break; // EOF
        } else {
          skipped = 1;
        }
      }
      current += skipped;
    }
  }

  @Override public int read() throws IOException {
    int result = in.read();
    if (result != -1) {
      offset++;
    }
    return result;
  }

  @Override public int read(byte[] buffer) throws IOException {
    int count = in.read(buffer);
    if (count != -1) {
      offset += count;
    }
    return count;
  }

  @Override public int read(byte[] buffer, int offset, int length) throws IOException {
    int count = in.read(buffer, offset, length);
    if (count != -1) {
      this.offset += count;
    }
    return count;
  }

  @Override public long skip(long byteCount) throws IOException {
    long skipped = in.skip(byteCount);
    offset += skipped;
    return skipped;
  }

  @Override public int available() throws IOException {
    return in.available();
  }

  @Override public void close() throws IOException {
    in.close();
  }

  @Override public boolean markSupported() {
    return in.markSupported();
  }
}


================================================
FILE: picasso/MediaStoreRequestHandler.java
================================================
/*
 * Copyright (C) 2014 Square, Inc.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
package com.squareup.picasso;

import android.content.ContentResolver;
import android.content.Context;
import android.database.Cursor;
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.net.Uri;
import android.provider.MediaStore;
import java.io.IOException;

import static android.content.ContentResolver.SCHEME_CONTENT;
import static android.content.ContentUris.parseId;
import static android.provider.MediaStore.Images;
import static android.provider.MediaStore.Video;
import static android.provider.MediaStore.Images.Thumbnails.FULL_SCREEN_KIND;
import static android.provider.MediaStore.Images.Thumbnails.MICRO_KIND;
import static android.provider.MediaStore.Images.Thumbnails.MINI_KIND;
import static com.squareup.picasso.MediaStoreRequestHandler.PicassoKind.FULL;
import static com.squareup.picasso.MediaStoreRequestHandler.PicassoKind.MICRO;
import static com.squareup.picasso.MediaStoreRequestHandler.PicassoKind.MINI;
import static com.squareup.picasso.Picasso.LoadedFrom.DISK;

class MediaStoreRequestHandler extends ContentStreamRequestHandler {
  private static final String[] CONTENT_ORIENTATION = new String[] {
      Images.ImageColumns.ORIENTATION
  };

  MediaStoreRequestHandler(Context context) {
    super(context);
  }

  @Override public boolean canHandleRequest(Request data) {
    final Uri uri = data.uri;
    return (SCHEME_CONTENT.equals(uri.getScheme())
            && MediaStore.AUTHORITY.equals(uri.getAuthority()));
  }

  @Override public Result load(Request data) throws IOException {
    ContentResolver contentResolver = context.getContentResolver();
    int exifOrientation = getExifOrientation(contentResolver, data.uri);

    String mimeType = contentResolver.getType(data.uri);
    boolean isVideo = mimeType != null && mimeType.startsWith("video/");

    if (data.hasSize()) {
      PicassoKind picassoKind = getPicassoKind(data.targetWidth, data.targetHeight);
      if (!isVideo && picassoKind == FULL) {
        return new Result(decodeContentStream(data), DISK, exifOrientation);
      }

      long id = parseId(data.uri);

      BitmapFactory.Options options = createBitmapOptions(data);
      options.inJustDecodeBounds = true;

      calculateInSampleSize(data.targetWidth, data.targetHeight, picassoKind.width,
              picassoKind.height, options);

      Bitmap bitmap;

      if (isVideo) {
        // Since MediaStore doesn't provide the full screen kind thumbnail, we use the mini kind
        // instead which is the largest thumbnail size can be fetched from MediaStore.
        int kind = (picassoKind == FULL) ? Video.Thumbnails.MINI_KIND : picassoKind.androidKind;
        bitmap = Video.Thumbnails.getThumbnail(contentResolver, id, kind, options);
      } else {
        bitmap =
            Images.Thumbnails.getThumbnail(contentResolver, id, picassoKind.androidKind, options);
      }

      if (bitmap != null) {
        return new Result(bitmap, DISK, exifOrientation);
      }
    }

    return new Result(decodeContentStream(data), DISK, exifOrientation);
  }

  static PicassoKind getPicassoKind(int targetWidth, int targetHeight) {
    if (targetWidth <= MICRO.width && targetHeight <= MICRO.height) {
      return MICRO;
    } else if (targetWidth <= MINI.width && targetHeight <= MINI.height) {
      return MINI;
    }
    return FULL;
  }

  static int getExifOrientation(ContentResolver contentResolver, Uri uri) {
    Cursor cursor = null;
    try {
      cursor = contentResolver.query(uri, CONTENT_ORIENTATION, null, null, null);
      if (cursor == null || !cursor.moveToFirst()) {
        return 0;
      }
      return cursor.getInt(0);
    } catch (RuntimeException ignored) {
      // If the orientation column doesn't exist, assume no rotation.
      return 0;
    } finally {
      if (cursor != null) {
        cursor.close();
      }
    }
  }

  enum PicassoKind {
    MICRO(MICRO_KIND, 96, 96),
    MINI(MINI_KIND, 512, 384),
    FULL(FULL_SCREEN_KIND, -1, -1);

    final int androidKind;
    final int width;
    final int height;

    PicassoKind(int androidKind, int width, int height) {
      this.androidKind = androidKind;
      this.width = width;
      this.height = height;
    }
  }
}


================================================
FILE: picasso/NetworkRequestHandler.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.BitmapFactory;
import android.net.NetworkInfo;
import java.io.IOException;
import java.io.InputStream;

import static com.squareup.picasso.Downloader.Response;
import static com.squareup.picasso.Picasso.LoadedFrom.DISK;
import static com.squareup.picasso.Picasso.LoadedFrom.NETWORK;

class NetworkRequestHandler extends RequestHandler {
  static final int RETRY_COUNT = 2;
  private static final int MARKER = 65536;

  private static final String SCHEME_HTTP = "http";
  private static final String SCHEME_HTTPS = "https";

  private final Downloader downloader;
  private final Stats stats;

  public NetworkRequestHandler(Downloader downloader, Stats stats) {
    this.downloader = downloader;
    this.stats = stats;
  }

  @Override public boolean canHandleRequest(Request data) {
    String scheme = data.uri.getScheme();
    return (SCHEME_HTTP.equals(scheme) || SCHEME_HTTPS.equals(scheme));
  }

  @Override public Result load(Request data) throws IOException {
    Response response = downloader.load(data.uri, data.loadFromLocalCacheOnly);
    if (response == null) {
      return null;
    }

    Picasso.LoadedFrom loadedFrom = response.cached ? DISK : NETWORK;

    Bitmap bitmap = response.getBitmap();
    if (bitmap != null) {
      return new Result(bitmap, loadedFrom);
    }

    InputStream is = response.getInputStream();
    if (is == null) {
      return null;
    }
    // Sometimes response content length is zero when requests are being replayed. Haven't found
    // root cause to this but retrying the request seems safe to do so.
    if (response.getContentLength() == 0) {
      Utils.closeQuietly(is);
      throw new IOException("Received response with 0 content-length header.");
    }
    if (loadedFrom == NETWORK && response.getContentLength() > 0) {
      stats.dispatchDownloadFinished(response.getContentLength());
    }
    try {
      return new Result(decodeStream(is, data), loadedFrom);
    } finally {
      Utils.closeQuietly(is);
    }
  }

  @Override int getRetryCount() {
    return RETRY_COUNT;
  }

  @Override boolean shouldRetry(boolean airplaneMode, NetworkInfo info) {
    return info == null || info.isConnected();
  }

  @Override boolean supportsReplay() {
    return true;
  }

  private Bitmap decodeStream(InputStream stream, Request data) throws IOException {
    MarkableInputStream markStream = new MarkableInputStream(stream);
    stream = markStream;

    long mark = markStream.savePosition(MARKER);

    final BitmapFactory.Options options = createBitmapOptions(data);
    final boolean calculateSize = requiresInSampleSize(options);

    boolean isWebPFile = Utils.isWebPFile(stream);
    markStream.reset(mark);
    // When decode WebP network stream, BitmapFactory throw JNI Exception and make app crash.
    // Decode byte array instead
    if (isWebPFile) {
      byte[] bytes = Utils.toByteArray(stream);
      if (calculateSize) {
        BitmapFactory.decodeByteArray(bytes, 0, bytes.length, options);
        calculateInSampleSize(data.targetWidth, data.targetHeight, options);
      }
      return BitmapFactory.decodeByteArray(bytes, 0, bytes.length, options);
    } else {
      if (calculateSize) {
        BitmapFactory.decodeStream(stream, null, options);
        calculateInSampleSize(data.targetWidth, data.targetHeight, options);

        markStream.reset(mark);
      }
      Bitmap bitmap = BitmapFactory.decodeStream(stream, null, options);
      if (bitmap == null) {
        // Treat null as an IO exception, we will eventually retry.
        throw new IOException("Failed to decode stream.");
      }
      return bitmap;
    }
  }
}


================================================
FILE: picasso/Picasso.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.graphics.Bitmap;
import android.graphics.Color;
import android.net.Uri;
import android.os.Handler;
import android.os.Looper;
import android.os.Message;
import android.os.Process;
import android.widget.ImageView;
import android.widget.RemoteViews;
import java.io.File;
import java.lang.ref.ReferenceQueue;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import java.util.Map;
import java.util.WeakHashMap;
import java.util.concurrent.ExecutorService;

import static android.os.Process.THREAD_PRIORITY_BACKGROUND;
import static com.squareup.picasso.Action.RequestWeakReference;
import static com.squareup.picasso.Dispatcher.HUNTER_BATCH_COMPLETE;
import static com.squareup.picasso.Dispatcher.REQUEST_GCED;
import static com.squareup.picasso.Utils.OWNER_MAIN;
import static com.squareup.picasso.Utils.THREAD_PREFIX;
import static com.squareup.picasso.Utils.VERB_COMPLETED;
import static com.squareup.picasso.Utils.VERB_ERRORED;
import static com.squareup.picasso.Utils.checkMain;
import static com.squareup.picasso.Utils.log;

/**
 * Image downloading, transformation, and caching manager.
 * <p>
 * Use {@link #with(android.content.Context)} for the global singleton instance or construct your
 * own instance with {@link com.squareup.picasso.Picasso.Builder}.
 */
public class Picasso {

  /** Callbacks for Picasso events. */
  public interface Listener {
    /**
     * Invoked when an image has failed to load. This is useful for reporting image failures to a
     * remote analytics service, for example.
     */
    void onImageLoadFailed(Picasso picasso, Uri uri, Exception exception);
  }

  /**
   * A transformer that is called immediately before every request is submitted. This can be used to
   * modify any information about a request.
   * <p>
   * For example, if you use a CDN you can change the hostname for the image based on the current
   * location of the user in order to get faster download speeds.
   * <p>
   * <b>NOTE:</b> This is a beta feature. The API is subject to change in a backwards incompatible
   * way at any time.
   */
  public interface RequestTransformer {
    /**
     * Transform a request before it is submitted to be processed.
     *
     * @return The original request or a new request to replace it. Must not be null.
     */
    Request transformRequest(Request request);

    /** A {@link com.squareup.picasso.Picasso.RequestTransformer} which returns the original request. */
    RequestTransformer IDENTITY = new RequestTransformer() {
      @Override public Request transformRequest(Request request) {
        return request;
      }
    };
  }

  static final String TAG = "Picasso";
  static final Handler HANDLER = new Handler(Looper.getMainLooper()) {
    @Override public void handleMessage(Message msg) {
      switch (msg.what) {
        case HUNTER_BATCH_COMPLETE: {
          @SuppressWarnings("unchecked") List<BitmapHunter> batch = (List<BitmapHunter>) msg.obj;
          //noinspection ForLoopReplaceableByForEach
          for (int i = 0, n = batch.size(); i < n; i++) {
            BitmapHunter hunter = batch.get(i);
            hunter.picasso.complete(hunter);
          }
          break;
        }
        case REQUEST_GCED: {
          Action action = (Action) msg.obj;
          action.picasso.cancelExistingRequest(action.getTarget());
          break;
        }
        default:
          throw new AssertionError("Unknown handler message received: " + msg.what);
      }
    }
  };

  static Picasso singleton = null;

  private final Listener listener;
  private final RequestTransformer requestTransformer;
  private final CleanupThread cleanupThread;
  private final List<RequestHandler> requestHandlers;

  final Context context;
  final Dispatcher dispatcher;
  final Cache cache;
  final Stats stats;
  final Map<Object, Action> targetToAction;
  final Map<ImageView, DeferredRequestCreator> targetToDeferredRequestCreator;
  final ReferenceQueue<Object> referenceQueue;

  boolean indicatorsEnabled;
  volatile boolean loggingEnabled;

  boolean shutdown;

  Picasso(Context context, Dispatcher dispatcher, Cache cache, Listener listener,
      RequestTransformer requestTransformer, List<RequestHandler> extraRequestHandlers,
      Stats stats, boolean indicatorsEnabled, boolean loggingEnabled) {
    this.context = context;
    this.dispatcher = dispatcher;
    this.cache = cache;
    this.listener = listener;
    this.requestTransformer = requestTransformer;

    final int extraCount = (extraRequestHandlers != null ? extraRequestHandlers.size() : 0);
    final List<RequestHandler> allRequestHandlers = new ArrayList<RequestHandler>(7 + extraCount);
    // ResourceRequestHandler needs to be the first in the list to avoid
    // forcing other RequestHandlers to perform null checks on request.uri
    // to cover the (request.resourceId != 0) case.
    allRequestHandlers.add(new ResourceRequestHandler(context));
    if (extraRequestHandlers != null) {
      allRequestHandlers.addAll(extraRequestHandlers);
    }
    allRequestHandlers.add(new ContactsPhotoRequestHandler(context));
    allRequestHandlers.add(new MediaStoreRequestHandler(context));
    allRequestHandlers.add(new ContentStreamRequestHandler(context));
    allRequestHandlers.add(new AssetRequestHandler(context));
    allRequestHandlers.add(new FileRequestHandler(context));
    allRequestHandlers.add(new NetworkRequestHandler(dispatcher.downloader, stats));
    requestHandlers = Collections.unmodifiableList(allRequestHandlers);

    this.stats = stats;
    this.targetToAction = new WeakHashMap<Object, Action>();
    this.targetToDeferredRequestCreator = new WeakHashMap<ImageView, DeferredRequestCreator>();
    this.indicatorsEnabled = indicatorsEnabled;
    this.loggingEnabled = loggingEnabled;
    this.referenceQueue = new ReferenceQueue<Object>();
    this.cleanupThread = new CleanupThread(referenceQueue, HANDLER);
    this.cleanupThread.start();
  }

  /** Cancel any existing requests for the specified target {@link android.widget.ImageView}. */
  public void cancelRequest(ImageView view) {
    cancelExistingRequest(view);
  }

  /** Cancel any existing requests for the specified {@link com.squareup.picasso.Target} instance. */
  public void cancelRequest(Target target) {
    cancelExistingRequest(target);
  }

  /**
   * Cancel any existing requests for the specified {@link android.widget.RemoteViews} target with the given {@code
   * viewId}.
   */
  public void cancelRequest(RemoteViews remoteViews, int viewId) {
    cancelExistingRequest(new RemoteViewsAction.RemoteViewsTarget(remoteViews, viewId));
  }

  /**
   * Start an image request using the specified URI.
   * <p>
   * Passing {@code null} as a {@code uri} will not trigger any request but will set a placeholder,
   * if one is specified.
   *
   * @see #load(java.io.File)
   * @see #load(String)
   * @see #load(int)
   */
  public RequestCreator load(Uri uri) {
    return new RequestCreator(this, uri, 0);
  }

  /**
   * Start an image request using the specified path. This is a convenience method for calling
   * {@link #load(android.net.Uri)}.
   * <p>
   * This path may be a remote URL, file resource (prefixed with {@code file:}), content resource
   * (prefixed with {@code content:}), or android resource (prefixed with {@code
   * android.resource:}.
   * <p>
   * Passing {@code null} as a {@code path} will not trigger any request but will set a
   * placeholder, if one is specified.
   *
   * @see #load(android.net.Uri)
   * @see #load(java.io.File)
   * @see #load(int)
   * @throws IllegalArgumentException if {@code path} is empty or blank string.
   */
  public RequestCreator load(String path) {
    if (path == null) {
      return new RequestCreator(this, null, 0);
    }
    if (path.trim().length() == 0) {
      throw new IllegalArgumentException("Path must not be empty.");
    }
    return load(Uri.parse(path));
  }

  /**
   * Start an image request using the specified image file. This is a convenience method for
   * calling {@link #load(android.net.Uri)}.
   * <p>
   * Passing {@code null} as a {@code file} will not trigger any request but will set a
   * placeholder, if one is specified.
   * <p>
   * Equivalent to calling {@link #load(android.net.Uri) load(Uri.fromFile(file))}.
   *
   * @see #load(android.net.Uri)
   * @see #load(String)
   * @see #load(int)
   */
  public RequestCreator load(File file) {
    if (file == null) {
      return new RequestCreator(this, null, 0);
    }
    return load(Uri.fromFile(file));
  }

  /**
   * Start an image request using the specified drawable resource ID.
   *
   * @see #load(android.net.Uri)
   * @see #load(String)
   * @see #load(java.io.File)
   */
  public RequestCreator load(int resourceId) {
    if (resourceId == 0) {
      throw new IllegalArgumentException("Resource ID must not be zero.");
    }
    return new RequestCreator(this, null, resourceId);
  }

  /**
   * {@code true} if debug display, logging, and statistics are enabled.
   * <p>
   * @deprecated Use {@link #areIndicatorsEnabled()} and {@link #isLoggingEnabled()} instead.
   */
  @SuppressWarnings("UnusedDeclaration") @Deprecated public boolean isDebugging() {
    return areIndicatorsEnabled() && isLoggingEnabled();
  }

  /**
   * Toggle whether debug display, logging, and statistics are enabled.
   * <p>
   * @deprecated Use {@link #setIndicatorsEnabled(boolean)} and {@link #setLoggingEnabled(boolean)}
   * instead.
   */
  @SuppressWarnings("UnusedDeclaration") @Deprecated public void setDebugging(boolean debugging) {
    setIndicatorsEnabled(debugging);
  }

  /** Toggle whether to display debug indicators on images. */
  @SuppressWarnings("UnusedDeclaration") public void setIndicatorsEnabled(boolean enabled) {
    indicatorsEnabled = enabled;
  }

  /** {@code true} if debug indicators should are displayed on images. */
  @SuppressWarnings("UnusedDeclaration") public boolean areIndicatorsEnabled() {
    return indicatorsEnabled;
  }

  /**
   * Toggle whether debug logging is enabled.
   * <p>
   * <b>WARNING:</b> Enabling this will result in excessive object allocation. This should be only
   * be used for debugging Picasso behavior. Do NOT pass {@code BuildConfig.DEBUG}.
   */
  public void setLoggingEnabled(boolean enabled) {
    loggingEnabled = enabled;
  }

  /** {@code true} if debug logging is enabled. */
  public boolean isLoggingEnabled() {
    return loggingEnabled;
  }

  /**
   * Creates a {@link com.squareup.picasso.StatsSnapshot} of the current stats for this instance.
   * <p>
   * <b>NOTE:</b> The snapshot may not always be completely up-to-date if requests are still in
   * progress.
   */
  @SuppressWarnings("UnusedDeclaration") public StatsSnapshot getSnapshot() {
    return stats.createSnapshot();
  }

  /** Stops this instance from accepting further requests. */
  public void shutdown() {
    if (this == singleton) {
      throw new UnsupportedOperationException("Default singleton instance cannot be shutdown.");
    }
    if (shutdown) {
      return;
    }
    cache.clear();
    cleanupThread.shutdown();
    stats.shutdown();
    dispatcher.shutdown();
    for (DeferredRequestCreator deferredRequestCreator : targetToDeferredRequestCreator.values()) {
      deferredRequestCreator.cancel();
    }
    targetToDeferredRequestCreator.clear();
    shutdown = true;
  }

  List<RequestHandler> getRequestHandlers() {
    return requestHandlers;
  }

  Request transformRequest(Request request) {
    Request transformed = requestTransformer.transformRequest(request);
    if (transformed == null) {
      throw new IllegalStateException("Request transformer "
          + requestTransformer.getClass().getCanonicalName()
          + " returned null for "
          + request);
    }
    return transformed;
  }

  void defer(ImageView view, DeferredRequestCreator request) {
    targetToDeferredRequestCreator.put(view, request);
  }

  void enqueueAndSubmit(Action action) {
    Object target = action.getTarget();
    if (target != null) {
      // This will also check we are on the main thread.
      cancelExistingRequest(target);
      targetToAction.put(target, action);
    }
    submit(action);
  }

  void submit(Action action) {
    dispatcher.dispatchSubmit(action);
  }

  Bitmap quickMemoryCacheCheck(String key) {
    Bitmap cached = cache.get(key);
    if (cached != null) {
      stats.dispatchCacheHit();
    } else {
      stats.dispatchCacheMiss();
    }
    return cached;
  }

  void complete(BitmapHunter hunter) {
    Action single = hunter.getAction();
    List<Action> joined = hunter.getActions();

    boolean hasMultiple = joined != null && !joined.isEmpty();
    boolean shouldDeliver = single != null || hasMultiple;

    if (!shouldDeliver) {
      return;
    }

    Uri uri = hunter.getData().uri;
    Exception exception = hunter.getException();
    Bitmap result = hunter.getResult();
    LoadedFrom from = hunter.getLoadedFrom();

    if (single != null) {
      deliverAction(result, from, single);
    }

    if (hasMultiple) {
      //noinspection ForLoopReplaceableByForEach
      for (int i = 0, n = joined.size(); i < n; i++) {
        Action join = joined.get(i);
        deliverAction(result, from, join);
      }
    }

    if (listener != null && exception != null) {
      listener.onImageLoadFailed(this, uri, exception);
    }
  }

  private void deliverAction(Bitmap result, LoadedFrom from, Action action) {
    if (action.isCancelled()) {
      return;
    }
    if (!action.willReplay()) {
      targetToAction.remove(action.getTarget());
    }
    if (result != null) {
      if (from == null) {
        throw new AssertionError("LoadedFrom cannot be null.");
      }
      action.complete(result, from);
      if (loggingEnabled) {
        log(OWNER_MAIN, VERB_COMPLETED, action.request.logId(), "from " + from);
      }
    } else {
      action.error();
      if (loggingEnabled) {
        log(OWNER_MAIN, VERB_ERRORED, action.request.logId());
      }
    }
  }

  private void cancelExistingRequest(Object target) {
    checkMain();
    Action action = targetToAction.remove(target);
    if (action != null) {
      action.cancel();
      dispatcher.dispatchCancel(action);
    }
    if (target instanceof ImageView) {
      ImageView targetImageView = (ImageView) target;
      DeferredRequestCreator deferredRequestCreator =
          targetToDeferredRequestCreator.remove(targetImageView);
      if (deferredRequestCreator != null) {
        deferredRequestCreator.cancel();
      }
    }
  }

  private static class CleanupThread extends Thread {
    private final ReferenceQueue<?> referenceQueue;
    private final Handler handler;

    CleanupThread(ReferenceQueue<?> referenceQueue, Handler handler) {
      this.referenceQueue = referenceQueue;
      this.handler = handler;
      setDaemon(true);
      setName(THREAD_PREFIX + "refQueue");
    }

    @Override public void run() {
      Process.setThreadPriority(THREAD_PRIORITY_BACKGROUND);
      while (true) {
        try {
          RequestWeakReference<?> remove = (RequestWeakReference<?>) referenceQueue.remove();
          handler.sendMessage(handler.obtainMessage(REQUEST_GCED, remove.action));
        } catch (InterruptedException e) {
          break;
        } catch (final Exception e) {
          handler.post(new Runnable() {
            @Override public void run() {
              throw new RuntimeException(e);
            }
          });
          break;
        }
      }
    }

    void shutdown() {
      interrupt();
    }
  }

  /**
   * The global default {@link com.squareup.picasso.Picasso} instance.
   * <p>
   * This instance is automatically initialized with defaults that are suitable to most
   * implementations.
   * <ul>
   * <li>LRU memory cache of 15% the available application RAM</li>
   * <li>Disk cache of 2% storage space up to 50MB but no less than 5MB. (Note: this is only
   * available on API 14+ <em>or</em> if you are using a standalone library that provides a disk
   * cache on all API levels like OkHttp)</li>
   * <li>Three download threads for disk and network access.</li>
   * </ul>
   * <p>
   * If these settings do not meet the requirements of your application you can construct your own
   * instance with full control over the configuration by using {@link com.squareup.picasso.Picasso.Builder}.
   */
  public static Picasso with(Context context) {
    if (singleton == null) {
      synchronized (Picasso.class) {
        if (singleton == null) {
          singleton = new Builder(context).build();
        }
      }
    }
    return singleton;
  }

  /** Fluent API for creating {@link com.squareup.picasso.Picasso} instances. */
  @SuppressWarnings("UnusedDeclaration") // Public API.
  public static class Builder {
    private final Context context;
    private Downloader downloader;
    private ExecutorService service;
    private Cache cache;
    private Listener listener;
    private RequestTransformer transformer;
    private List<RequestHandler> requestHandlers;

    private boolean indicatorsEnabled;
    private boolean loggingEnabled;

    /** Start building a new {@link com.squareup.picasso.Picasso} instance. */
    public Builder(Context context) {
      if (context == null) {
        throw new IllegalArgumentException("Context must not be null.");
      }
      this.context = context.getApplicationContext();
    }

    /** Specify the {@link com.squareup.picasso.Downloader} that will be used for downloading images. */
    public Builder downloader(Downloader downloader) {
      if (downloader == null) {
        throw new IllegalArgumentException("Downloader must not be null.");
      }
      if (this.downloader != null) {
        throw new IllegalStateException("Downloader already set.");
      }
      this.downloader = downloader;
      return this;
    }

    /** Specify the executor service for loading images in the background. */
    public Builder executor(ExecutorService executorService) {
      if (executorService == null) {
        throw new IllegalArgumentException("Executor service must not be null.");
      }
      if (this.service != null) {
        throw new IllegalStateException("Executor service already set.");
      }
      this.service = executorService;
      return this;
    }

    /** Specify the memory cache used for the most recent images. */
    public Builder memoryCache(Cache memoryCache) {
      if (memoryCache == null) {
        throw new IllegalArgumentException("Memory cache must not be null.");
      }
      if (this.cache != null) {
        throw new IllegalStateException("Memory cache already set.");
      }
      this.cache = memoryCache;
      return this;
    }

    /** Specify a listener for interesting events. */
    public Builder listener(Listener listener) {
      if (listener == null) {
        throw new IllegalArgumentException("Listener must not be null.");
      }
      if (this.listener != null) {
        throw new IllegalStateException("Listener already set.");
      }
      this.listener = listener;
      return this;
    }

    /**
     * Specify a transformer for all incoming requests.
     * <p>
     * <b>NOTE:</b> This is a beta feature. The API is subject to change in a backwards incompatible
     * way at any time.
     */
    public Builder requestTransformer(RequestTransformer transformer) {
      if (transformer == null) {
        throw new IllegalArgumentException("Transformer must not be null.");
      }
      if (this.transformer != null) {
        throw new IllegalStateException("Transformer already set.");
      }
      this.transformer = transformer;
      return this;
    }

    /** Register a {@link RequestHandler}. */
    public Builder addRequestHandler(RequestHandler requestHandler) {
      if (requestHandler == null) {
        throw new IllegalArgumentException("RequestHandler must not be null.");
      }
      if (requestHandlers == null) {
        requestHandlers = new ArrayList<RequestHandler>();
      }
      if (requestHandlers.contains(requestHandler)) {
        throw new IllegalStateException("RequestHandler already registered.");
      }
      requestHandlers.add(requestHandler);
      return this;
    }

    /**
     * @deprecated Use {@link #indicatorsEnabled(boolean)} instead.
     * Whether debugging is enabled or not.
     */
    @Deprecated public Builder debugging(boolean debugging) {
      return indicatorsEnabled(debugging);
    }

    /** Toggle whether to display debug indicators on images. */
    public Builder indicatorsEnabled(boolean enabled) {
      this.indicatorsEnabled = enabled;
      return this;
    }

    /**
     * Toggle whether debug logging is enabled.
     * <p>
     * <b>WARNING:</b> Enabling this will result in excessive object allocation. This should be only
     * be used for debugging purposes. Do NOT pass {@code BuildConfig.DEBUG}.
     */
    public Builder loggingEnabled(boolean enabled) {
      this.loggingEnabled = enabled;
      return this;
    }

    /** Create the {@link com.squareup.picasso.Picasso} instance. */
    public Picasso build() {
      Context context = this.context;

      if (downloader == null) {
        downloader = Utils.createDefaultDownloader(context);
      }
      if (cache == null) {
        cache = new LruCache(context);
      }
      if (service == null) {
        service = new PicassoExecutorService();
      }
      if (transformer == null) {
        transformer = RequestTransformer.IDENTITY;
      }

      Stats stats = new Stats(cache);

      Dispatcher dispatcher = new Dispatcher(context, service, HANDLER, downloader, cache, stats);

      return new Picasso(context, dispatcher, cache, listener, transformer,
          requestHandlers, stats, indicatorsEnabled, loggingEnabled);
    }
  }

  /** Describes where the image was loaded from. */
  public enum LoadedFrom {
    MEMORY(Color.GREEN),
    DISK(Color.YELLOW),
    NETWORK(Color.RED);

    final int debugColor;

    private LoadedFrom(int debugColor) {
      this.debugColor = debugColor;
    }
  }
}


================================================
FILE: picasso/PicassoDrawable.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.graphics.Bitmap;
import android.graphics.Canvas;
import android.graphics.ColorFilter;
import android.graphics.Paint;
import android.graphics.Path;
import android.graphics.Point;
import android.graphics.Rect;
import android.graphics.drawable.AnimationDrawable;
import android.graphics.drawable.BitmapDrawable;
import android.graphics.drawable.Drawable;
import android.os.Build;
import android.os.SystemClock;
import android.widget.ImageView;

import static android.graphics.Color.WHITE;
import static com.squareup.picasso.Picasso.LoadedFrom.MEMORY;

final class PicassoDrawable extends BitmapDrawable {
  // Only accessed from main thread.
  private static final Paint DEBUG_PAINT = new Paint();
  private static final float FADE_DURATION = 200f; //ms

  /**
   * Create or update the drawable on the target {@link android.widget.ImageView} to display the supplied bitmap
   * image.
   */
  static void setBitmap(ImageView target, Context context, Bitmap bitmap,
      Picasso.LoadedFrom loadedFrom, boolean noFade, boolean debugging) {
    Drawable placeholder = target.getDrawable();
    if (placeholder instanceof AnimationDrawable) {
      ((AnimationDrawable) placeholder).stop();
    }
    PicassoDrawable drawable =
        new PicassoDrawable(context, bitmap, placeholder, loadedFrom, noFade, debugging);
    target.setImageDrawable(drawable);
  }

  /**
   * Create or update the drawable on the target {@link android.widget.ImageView} to display the supplied
   * placeholder image.
   */
  static void setPlaceholder(ImageView target, int placeholderResId, Drawable placeholderDrawable) {
    if (placeholderResId != 0) {
      target.setImageResource(placeholderResId);
    } else {
      target.setImageDrawable(placeholderDrawable);
    }
    if (target.getDrawable() instanceof AnimationDrawable) {
      ((AnimationDrawable) target.getDrawable()).start();
    }
  }

  private final boolean debugging;
  private final float density;
  private final Picasso.LoadedFrom loadedFrom;

  Drawable placeholder;

  long startTimeMillis;
  boolean animating;
  int alpha = 0xFF;

  PicassoDrawable(Context context, Bitmap bitmap, Drawable placeholder,
      Picasso.LoadedFrom loadedFrom, boolean noFade, boolean debugging) {
    super(context.getResources(), bitmap);

    this.debugging = debugging;
    this.density = context.getResources().getDisplayMetrics().density;

    this.loadedFrom = loadedFrom;

    boolean fade = loadedFrom != MEMORY && !noFade;
    if (fade) {
      this.placeholder = placeholder;
      animating = true;
      startTimeMillis = SystemClock.uptimeMillis();
    }
  }

  @Override public void draw(Canvas canvas) {
    if (!animating) {
      super.draw(canvas);
    } else {
      float normalized = (SystemClock.uptimeMillis() - startTimeMillis) / FADE_DURATION;
      if (normalized >= 1f) {
        animating = false;
        placeholder = null;
        super.draw(canvas);
      } else {
        if (placeholder != null) {
          placeholder.draw(canvas);
        }

        int partialAlpha = (int) (alpha * normalized);
        super.setAlpha(partialAlpha);
        super.draw(canvas);
        super.setAlpha(alpha);
        if (Build.VERSION.SDK_INT <= Build.VERSION_CODES.GINGERBREAD_MR1) {
          invalidateSelf();
        }
      }
    }

    if (debugging) {
      drawDebugIndicator(canvas);
    }
  }

  @Override public void setAlpha(int alpha) {
    this.alpha = alpha;
    if (placeholder != null) {
      placeholder.setAlpha(alpha);
    }
    super.setAlpha(alpha);
  }

  @Override public void setColorFilter(ColorFilter cf) {
    if (placeholder != null) {
      placeholder.setColorFilter(cf);
    }
    super.setColorFilter(cf);
  }

  @Override protected void onBoundsChange(Rect bounds) {
    if (placeholder != null) {
      placeholder.setBounds(bounds);
    }
    super.onBoundsChange(bounds);
  }

  private void drawDebugIndicator(Canvas canvas) {
    DEBUG_PAINT.setColor(WHITE);
    Path path = getTrianglePath(new Point(0, 0), (int) (16 * density));
    canvas.drawPath(path, DEBUG_PAINT);

    DEBUG_PAINT.setColor(loadedFrom.debugColor);
    path = getTrianglePath(new Point(0, 0), (int) (15 * density));
    canvas.drawPath(path, DEBUG_PAINT);
  }

  private static Path getTrianglePath(Point p1, int width) {
    Point p2 = new Point(p1.x + width, p1.y);
    Point p3 = new Point(p1.x, p1.y + width);

    Path path = new Path();
    path.moveTo(p1.x, p1.y);
    path.lineTo(p2.x, p2.y);
    path.lineTo(p3.x, p3.y);

    return path;
  }
}


================================================
FILE: picasso/PicassoExecutorService.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.net.ConnectivityManager;
import android.net.NetworkInfo;
import android.telephony.TelephonyManager;
import java.util.concurrent.LinkedBlockingQueue;
import java.util.concurrent.ThreadPoolExecutor;
import java.util.concurrent.TimeUnit;

/**
 * The default {@link java.util.concurrent.ExecutorService} used for new {@link com.squareup.picasso.Picasso} instances.
 * <p>
 * Exists as a custom type so that we can differentiate the use of defaults versus a user-supplied
 * instance.
 */
class PicassoExecutorService extends ThreadPoolExecutor {
  private static final int DEFAULT_THREAD_COUNT = 3;

  PicassoExecutorService() {
    super(DEFAULT_THREAD_COUNT, DEFAULT_THREAD_COUNT, 0, TimeUnit.MILLISECONDS,
        new LinkedBlockingQueue<Runnable>(), new Utils.PicassoThreadFactory());
  }

  void adjustThreadCount(NetworkInfo info) {
    if (info == null || !info.isConnectedOrConnecting()) {
      setThreadCount(DEFAULT_THREAD_COUNT);
      return;
    }
    switch (info.getType()) {
      case ConnectivityManager.TYPE_WIFI:
      case ConnectivityManager.TYPE_WIMAX:
      case ConnectivityManager.TYPE_ETHERNET:
        setThreadCount(4);
        break;
      case ConnectivityManager.TYPE_MOBILE:
        switch (info.getSubtype()) {
          case TelephonyManager.NETWORK_TYPE_LTE:  // 4G
          case TelephonyManager.NETWORK_TYPE_HSPAP:
          case TelephonyManager.NETWORK_TYPE_EHRPD:
            setThreadCount(3);
            break;
          case TelephonyManager.NETWORK_TYPE_UMTS: // 3G
          case TelephonyManager.NETWORK_TYPE_CDMA:
          case TelephonyManager.NETWORK_TYPE_EVDO_0:
          case TelephonyManager.NETWORK_TYPE_EVDO_A:
          case TelephonyManager.NETWORK_TYPE_EVDO_B:
            setThreadCount(2);
            break;
          case TelephonyManager.NETWORK_TYPE_GPRS: // 2G
          case TelephonyManager.NETWORK_TYPE_EDGE:
            setThreadCount(1);
            break;
          default:
            setThreadCount(DEFAULT_THREAD_COUNT);
        }
        break;
      default:
        setThreadCount(DEFAULT_THREAD_COUNT);
    }
  }

  private void setThreadCount(int threadCount) {
    setCorePoolSize(threadCount);
    setMaximumPoolSize(threadCount);
  }
}


================================================
FILE: picasso/RemoteViewsAction.java
================================================
/*
 * Copyright (C) 2014 Square, Inc.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
package com.squareup.picasso;

import android.app.Notification;
import android.app.NotificationManager;
import android.appwidget.AppWidgetManager;
import android.graphics.Bitmap;
import android.widget.RemoteViews;

import static android.content.Context.NOTIFICATION_SERVICE;
import static com.squareup.picasso.Utils.getService;

abstract class RemoteViewsAction extends Action<RemoteViewsAction.RemoteViewsTarget> {
  final RemoteViews remoteViews;
  final int viewId;

  RemoteViewsAction(Picasso picasso, Request data, RemoteViews remoteViews, int viewId,
      int errorResId, boolean skipCache, String key) {
    super(picasso, new RemoteViewsTarget(remoteViews, viewId), data, skipCache, false, errorResId,
        null, key);
    this.remoteViews = remoteViews;
    this.viewId = viewId;
  }

  @Override void complete(Bitmap result, Picasso.LoadedFrom from) {
    remoteViews.setImageViewBitmap(viewId, result);
    update();
  }

  @Override public void error() {
    if (errorResId != 0) {
      setImageResource(errorResId);
    }
  }

  void setImageResource(int resId) {
    remoteViews.setImageViewResource(viewId, resId);
    update();
  }

  abstract void update();

  static class RemoteViewsTarget {
    final RemoteViews remoteViews;
    final int viewId;

    RemoteViewsTarget(RemoteViews remoteViews, int viewId) {
      this.remoteViews = remoteViews;
      this.viewId = viewId;
    }

    @Override public boolean equals(Object o) {
      if (this == o) return true;
      if (o == null || getClass() != o.getClass()) return false;
      RemoteViewsTarget remoteViewsTarget = (RemoteViewsTarget) o;
      return viewId == remoteViewsTarget.viewId && remoteViews.equals(
          remoteViewsTarget.remoteViews);
    }

    @Override public int hashCode() {
      return 31 * remoteViews.hashCode() + viewId;
    }
  }

  static class AppWidgetAction extends RemoteViewsAction {
    private final int[] appWidgetIds;

    AppWidgetAction(Picasso picasso, Request data, RemoteViews remoteViews, int viewId,
        int[] appWidgetIds, boolean skipCache, int errorResId, String key) {
      super(picasso, data, remoteViews, viewId, errorResId, skipCache, key);
      this.appWidgetIds = appWidgetIds;
    }

    @Override void update() {
      AppWidgetManager manager = AppWidgetManager.getInstance(picasso.context);
      manager.updateAppWidget(appWidgetIds, remoteViews);
    }
  }

  static class NotificationAction extends RemoteViewsAction {
    private final int notificationId;
    private final Notification notification;

    NotificationAction(Picasso picasso, Request data, RemoteViews remoteViews, int viewId,
        int notificationId, Notification notification, boolean skipCache, int errorResId,
        String key) {
      super(picasso, data, remoteViews, viewId, errorResId, skipCache, key);
      this.notificationId = notificationId;
      this.notification = notification;
    }

    @Override void update() {
      NotificationManager manager = getService(picasso.context, NOTIFICATION_SERVICE);
      manager.notify(notificationId, notification);
    }
  }
}


================================================
FILE: picasso/Request.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.net.Uri;
import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.TimeUnit;

import static java.util.Collections.unmodifiableList;

/** Immutable data about an image and the transformations that will be applied to it. */
public final class Request {
  private static final long TOO_LONG_LOG = TimeUnit.SECONDS.toNanos(5);

  /** A unique ID for the request. */
  int id;
  /** The time that the request was first submitted (in nanos). */
  long started;
  /** Whether or not this request should only load from local cache. */
  boolean loadFromLocalCacheOnly;

  /**
   * The image URI.
   * <p>
   * This is mutually exclusive with {@link #resourceId}.
   */
  public final Uri uri;
  /**
   * The image resource ID.
   * <p>
   * This is mutually exclusive with {@link #uri}.
   */
  public final int resourceId;
  /** List of custom transformations to be applied after the built-in transformations. */
  public final List<Transformation> transformations;
  /** Target image width for resizing. */
  public final int targetWidth;
  /** Target image height for resizing. */
  public final int targetHeight;
  /**
   * True if the final image should use the 'centerCrop' scale technique.
   * <p>
   * This is mutually exclusive with {@link #centerInside}.
   */
  public final boolean centerCrop;
  /**
   * True if the final image should use the 'centerInside' scale technique.
   * <p>
   * This is mutually exclusive with {@link #centerCrop}.
   */
  public final boolean centerInside;
  /** Amount to rotate the image in degrees. */
  public final float rotationDegrees;
  /** Rotation pivot on the X axis. */
  public final float rotationPivotX;
  /** Rotation pivot on the Y axis. */
  public final float rotationPivotY;
  /** Whether or not {@link #rotationPivotX} and {@link #rotationPivotY} are set. */
  public final boolean hasRotationPivot;
  /** Target image config for decoding. */
  public final Bitmap.Config config;

  private Request(Uri uri, int resourceId, List<Transformation> transformations, int targetWidth,
      int targetHeight, boolean centerCrop, boolean centerInside, float rotationDegrees,
      float rotationPivotX, float rotationPivotY, boolean hasRotationPivot, Bitmap.Config config) {
    this.uri = uri;
    this.resourceId = resourceId;
    if (transformations == null) {
      this.transformations = null;
    } else {
      this.transformations = unmodifiableList(transformations);
    }
    this.targetWidth = targetWidth;
    this.targetHeight = targetHeight;
    this.centerCrop = centerCrop;
    this.centerInside = centerInside;
    this.rotationDegrees = rotationDegrees;
    this.rotationPivotX = rotationPivotX;
    this.rotationPivotY = rotationPivotY;
    this.hasRotationPivot = hasRotationPivot;
    this.config = config;
  }

  @Override public String toString() {
    final StringBuilder sb = new StringBuilder("Request{");
    if (resourceId > 0) {
      sb.append(resourceId);
    } else {
      sb.append(uri);
    }
    if (transformations != null && !transformations.isEmpty()) {
      for (Transformation transformation : transformations) {
        sb.append(' ').append(transformation.key());
      }
    }
    if (targetWidth > 0) {
      sb.append(" resize(").append(targetWidth).append(',').append(targetHeight).append(')');
    }
    if (centerCrop) {
      sb.append(" centerCrop");
    }
    if (centerInside) {
      sb.append(" centerInside");
    }
    if (rotationDegrees != 0) {
      sb.append(" rotation(").append(rotationDegrees);
      if (hasRotationPivot) {
        sb.append(" @ ").append(rotationPivotX).append(',').append(rotationPivotY);
      }
      sb.append(')');
    }
    if (config != null) {
      sb.append(' ').append(config);
    }
    sb.append('}');

    return sb.toString();
  }

  String logId() {
    long delta = System.nanoTime() - started;
    if (delta > TOO_LONG_LOG) {
      return plainId() + '+' + TimeUnit.NANOSECONDS.toSeconds(delta) + 's';
    }
    return plainId() + '+' + TimeUnit.NANOSECONDS.toMillis(delta) + "ms";
  }

  String plainId() {
    return "[R" + id + ']';
  }

  String getName() {
    if (uri != null) {
      return String.valueOf(uri.getPath());
    }
    return Integer.toHexString(resourceId);
  }

  public boolean hasSize() {
    return targetWidth != 0;
  }

  boolean needsTransformation() {
    return needsMatrixTransform() || hasCustomTransformations();
  }

  boolean needsMatrixTransform() {
    return targetWidth != 0 || rotationDegrees != 0;
  }

  boolean hasCustomTransformations() {
    return transformations != null;
  }

  public Builder buildUpon() {
    return new Builder(this);
  }

  /** Builder for creating {@link com.squareup.picasso.Request} instances. */
  public static final class Builder {
    private Uri uri;
    private int resourceId;
    private int targetWidth;
    private int targetHeight;
    private boolean centerCrop;
    private boolean centerInside;
    private float rotationDegrees;
    private float rotationPivotX;
    private float rotationPivotY;
    private boolean hasRotationPivot;
    private List<Transformation> transformations;
    private Bitmap.Config config;

    /** Start building a request using the specified {@link android.net.Uri}. */
    public Builder(Uri uri) {
      setUri(uri);
    }

    /** Start building a request using the specified resource ID. */
    public Builder(int resourceId) {
      setResourceId(resourceId);
    }

    Builder(Uri uri, int resourceId) {
      this.uri = uri;
      this.resourceId = resourceId;
    }

    private Builder(Request request) {
      uri = request.uri;
      resourceId = request.resourceId;
      targetWidth = request.targetWidth;
      targetHeight = request.targetHeight;
      centerCrop = request.centerCrop;
      centerInside = request.centerInside;
      rotationDegrees = request.rotationDegrees;
      rotationPivotX = request.rotationPivotX;
      rotationPivotY = request.rotationPivotY;
      hasRotationPivot = request.hasRotationPivot;
      if (request.transformations != null) {
        transformations = new ArrayList<Transformation>(request.transformations);
      }
      config = request.config;
    }

    boolean hasImage() {
      return uri != null || resourceId != 0;
    }

    boolean hasSize() {
      return targetWidth != 0;
    }

    /**
     * Set the target image Uri.
     * <p>
     * This will clear an image resource ID if one is set.
     */
    public Builder setUri(Uri uri) {
      if (uri == null) {
        throw new IllegalArgumentException("Image URI may not be null.");
      }
      this.uri = uri;
      this.resourceId = 0;
      return this;
    }

    /**
     * Set the target image resource ID.
     * <p>
     * This will clear an image Uri if one is set.
     */
    public Builder setResourceId(int resourceId) {
      if (resourceId == 0) {
        throw new IllegalArgumentException("Image resource ID may not be 0.");
      }
      this.resourceId = resourceId;
      this.uri = null;
      return this;
    }

    /** Resize the image to the specified size in pixels. */
    public Builder resize(int targetWidth, int targetHeight) {
      if (targetWidth <= 0) {
        throw new IllegalArgumentException("Width must be positive number.");
      }
      if (targetHeight <= 0) {
        throw new IllegalArgumentException("Height must be positive number.");
      }
      this.targetWidth = targetWidth;
      this.targetHeight = targetHeight;
      return this;
    }

    /** Clear the resize transformation, if any. This will also clear center crop/inside if set. */
    public Builder clearResize() {
      targetWidth = 0;
      targetHeight = 0;
      centerCrop = false;
      centerInside = false;
      return this;
    }

    /**
     * Crops an image inside of the bounds specified by {@link #resize(int, int)} rather than
     * distorting the aspect ratio. This cropping technique scales the image so that it fills the
     * requested bounds and then crops the extra.
     */
    public Builder centerCrop() {
      if (centerInside) {
        throw new IllegalStateException("Center crop can not be used after calling centerInside");
      }
      centerCrop = true;
      return this;
    }

    /** Clear the center crop transformation flag, if set. */
    public Builder clearCenterCrop() {
      centerCrop = false;
      return this;
    }

    /**
     * Centers an image inside of the bounds specified by {@link #resize(int, int)}. This scales
     * the image so that both dimensions are equal to or less than the requested bounds.
     */
    public Builder centerInside() {
      if (centerCrop) {
        throw new IllegalStateException("Center inside can not be used after calling centerCrop");
      }
      centerInside = true;
      return this;
    }

    /** Clear the center inside transformation flag, if set. */
    public Builder clearCenterInside() {
      centerInside = false;
      return this;
    }

    /** Rotate the image by the specified degrees. */
    public Builder rotate(float degrees) {
      rotationDegrees = degrees;
      return this;
    }

    /** Rotate the image by the specified degrees around a pivot point. */
    public Builder rotate(float degrees, float pivotX, float pivotY) {
      rotationDegrees = degrees;
      rotationPivotX = pivotX;
      rotationPivotY = pivotY;
      hasRotationPivot = true;
      return this;
    }

    /** Clear the rotation transformation, if any. */
    public Builder clearRotation() {
      rotationDegrees = 0;
      rotationPivotX = 0;
      rotationPivotY = 0;
      hasRotationPivot = false;
      return this;
    }

    /** Decode the image using the specified config. */
    public Builder config(Bitmap.Config config) {
      this.config = config;
      return this;
    }

    /**
     * Add a custom transformation to be applied to the image.
     * <p>
     * Custom transformations will always be run after the built-in transformations.
     */
    public Builder transform(Transformation transformation) {
      if (transformation == null) {
        throw new IllegalArgumentException("Transformation must not be null.");
      }
      if (transformations == null) {
        transformations = new ArrayList<Transformation>(2);
      }
      transformations.add(transformation);
      return this;
    }

    /** Create the immutable {@link com.squareup.picasso.Request} object. */
    public Request build() {
      if (centerInside && centerCrop) {
        throw new IllegalStateException("Center crop and center inside can not be used together.");
      }
      if (centerCrop && targetWidth == 0) {
        throw new IllegalStateException("Center crop requires calling resize.");
      }
      if (centerInside && targetWidth == 0) {
        throw new IllegalStateException("Center inside requires calling resize.");
      }
      return new Request(uri, resourceId, transformations, targetWidth, targetHeight, centerCrop,
          centerInside, rotationDegrees, rotationPivotX, rotationPivotY, hasRotationPivot, config);
    }
  }
}


================================================
FILE: picasso/RequestCreator.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.app.Notification;
import android.content.res.Resources;
import android.graphics.Bitmap;
import android.graphics.drawable.Drawable;
import android.net.Uri;
import android.widget.ImageView;
import android.widget.RemoteViews;
import java.io.IOException;
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.atomic.AtomicInteger;

import static com.squareup.picasso.BitmapHunter.forRequest;
import static com.squareup.picasso.Picasso.LoadedFrom.MEMORY;
import static com.squareup.picasso.PicassoDrawable.setBitmap;
import static com.squareup.picasso.PicassoDrawable.setPlaceholder;
import static com.squareup.picasso.RemoteViewsAction.AppWidgetAction;
import static com.squareup.picasso.RemoteViewsAction.NotificationAction;
import static com.squareup.picasso.Utils.OWNER_MAIN;
import static com.squareup.picasso.Utils.VERB_CHANGED;
import static com.squareup.picasso.Utils.VERB_COMPLETED;
import static com.squareup.picasso.Utils.VERB_CREATED;
import static com.squareup.picasso.Utils.checkMain;
import static com.squareup.picasso.Utils.checkNotMain;
import static com.squareup.picasso.Utils.createKey;
import static com.squareup.picasso.Utils.isMain;
import static com.squareup.picasso.Utils.log;

/** Fluent API for building an image download request. */
@SuppressWarnings("UnusedDeclaration") // Public API.
public class RequestCreator {
  private static int nextId = 0;

  private static int getRequestId() {
    if (isMain()) {
      return nextId++;
    }

    final CountDownLatch latch = new CountDownLatch(1);
    final AtomicInteger id = new AtomicInteger();
    Picasso.HANDLER.post(new Runnable() {
      @Override public void run() {
        id.set(getRequestId());
        latch.countDown();
      }
    });
    try {
      latch.await();
    } catch (final InterruptedException e) {
      Picasso.HANDLER.post(new Runnable() {
        @Override public void run() {
          throw new RuntimeException(e);
        }
      });
    }
    return id.get();
  }

  private final Picasso picasso;
  private final Request.Builder data;

  private boolean skipMemoryCache;
  private boolean noFade;
  private boolean deferred;
  private int placeholderResId;
  private int errorResId;
  private Drawable placeholderDrawable;
  private Drawable errorDrawable;

  RequestCreator(Picasso picasso, Uri uri, int resourceId) {
    if (picasso.shutdown) {
      throw new IllegalStateException(
          "Picasso instance already shut down. Cannot submit new requests.");
    }
    this.picasso = picasso;
    this.data = new Request.Builder(uri, resourceId);
  }

   RequestCreator() {
    this.picasso = null;
    this.data = new Request.Builder(null, 0);
  }

  /**
   * A placeholder drawable to be used while the image is being loaded. If the requested image is
   * not immediately available in the memory cache then this resource will be set on the target
   * {@link android.widget.ImageView}.
   */
  public RequestCreator placeholder(int placeholderResId) {
    if (placeholderResId == 0) {
      throw new IllegalArgumentException("Placeholder image resource invalid.");
    }
    if (placeholderDrawable != null) {
      throw new IllegalStateException("Placeholder image already set.");
    }
    this.placeholderResId = placeholderResId;
    return this;
  }

  /**
   * A placeholder drawable to be used while the image is being loaded. If the requested image is
   * not immediately available in the memory cache then this resource will be set on the target
   * {@link android.widget.ImageView}.
   * <p>
   * If you are not using a placeholder image but want to clear an existing image (such as when
   * used in an {@link android.widget.Adapter adapter}), pass in {@code null}.
   */
  public RequestCreator placeholder(Drawable placeholderDrawable) {
    if (placeholderResId != 0) {
      throw new IllegalStateException("Placeholder image already set.");
    }
    this.placeholderDrawable = placeholderDrawable;
    return this;
  }

  /** An error drawable to be used if the request image could not be loaded. */
  public RequestCreator error(int errorResId) {
    if (errorResId == 0) {
      throw new IllegalArgumentException("Error image resource invalid.");
    }
    if (errorDrawable != null) {
      throw new IllegalStateException("Error image already set.");
    }
    this.errorResId = errorResId;
    return this;
  }

  /** An error drawable to be used if the request image could not be loaded. */
  public RequestCreator error(Drawable errorDrawable) {
    if (errorDrawable == null) {
      throw new IllegalArgumentException("Error image may not be null.");
    }
    if (errorResId != 0) {
      throw new IllegalStateException("Error image already set.");
    }
    this.errorDrawable = errorDrawable;
    return this;
  }

  /**
   * Attempt to resize the image to fit exactly into the target {@link android.widget.ImageView}'s bounds. This
   * will result in delayed execution of the request until the {@link android.widget.ImageView} has been laid out.
   * <p>
   * <em>Note:</em> This method works only when your target is an {@link android.widget.ImageView}.
   */
  public RequestCreator fit() {
    deferred = true;
    return this;
  }

  /** Internal use only. Used by {@link com.squareup.picasso.DeferredRequestCreator}. */
  RequestCreator unfit() {
    deferred = false;
    return this;
  }

  /** Resize the image to the specified dimension size. */
  public RequestCreator resizeDimen(int targetWidthResId, int targetHeightResId) {
    Resources resources = picasso.context.getResources();
    int targetWidth = resources.getDimensionPixelSize(targetWidthResId);
    int targetHeight = resources.getDimensionPixelSize(targetHeightResId);
    return resize(targetWidth, targetHeight);
  }

  /** Resize the image to the specified size in pixels. */
  public RequestCreator resize(int targetWidth, int targetHeight) {
    data.resize(targetWidth, targetHeight);
    return this;
  }

  /**
   * Crops an image inside of the bounds specified by {@link #resize(int, int)} rather than
   * distorting the aspect ratio. This cropping technique scales the image so that it fills the
   * requested bounds and then crops the extra.
   */
  public RequestCreator centerCrop() {
    data.centerCrop();
    return this;
  }

  /**
   * Centers an image inside of the bounds specified by {@link #resize(int, int)}. This scales
   * the image so that both dimensions are equal to or less than the requested bounds.
   */
  public RequestCreator centerInside() {
    data.centerInside();
    return this;
  }

  /** Rotate the image by the specified degrees. */
  public RequestCreator rotate(float degrees) {
    data.rotate(degrees);
    return this;
  }

  /** Rotate the image by the specified degrees around a pivot point. */
  public RequestCreator rotate(float degrees, float pivotX, float pivotY) {
    data.rotate(degrees, pivotX, pivotY);
    return this;
  }

  /**
   * Attempt to decode the image using the specified config.
   * <p>
   * Note: This value may be ignored by {@link android.graphics.BitmapFactory}. See
   * {@link android.graphics.BitmapFactory.Options#inPreferredConfig its documentation} for more details.
   */
  public RequestCreator config(Bitmap.Config config) {
    data.config(config);
    return this;
  }

  /**
   * Add a custom transformation to be applied to the image.
   * <p>
   * Custom transformations will always be run after the built-in transformations.
   */
  // TODO show example of calling resize after a transform in the javadoc
  public RequestCreator transform(Transformation transformation) {
    data.transform(transformation);
    return this;
  }

  /**
   * Indicate that this action should not use the memory cache for attempting to load or save the
   * image. This can be useful when you know an image will only ever be used once (e.g., loading
   * an image from the filesystem and uploading to a remote server).
   */
  public RequestCreator skipMemoryCache() {
    skipMemoryCache = true;
    return this;
  }

  /** Disable brief fade in of images loaded from the disk cache or network. */
  public RequestCreator noFade() {
    noFade = true;
    return this;
  }

  /**
   * Synchronously fulfill this request. Must not be called from the main thread.
   * <p>
   * <em>Note</em>: The result of this operation is not cached in memory because the underlying
   * {@link com.squareup.picasso.Cache} implementation is not guaranteed to be thread-safe.
   */
  public Bitmap get() throws IOException {
    long started = System.nanoTime();
    checkNotMain();

    if (deferred) {
      throw new IllegalStateException("Fit cannot be used with get.");
    }
    if (!data.hasImage()) {
      return null;
    }

    Request finalData = createRequest(started);
    String key = createKey(finalData, new StringBuilder());

    Action action = new GetAction(picasso, finalData, skipMemoryCache, key);
    return forRequest(picasso, picasso.dispatcher, picasso.cache, picasso.stats, action).hunt();
  }

  /**
   * Asynchronously fulfills the request without a {@link android.widget.ImageView} or {@link com.squareup.picasso.Target}. This is
   * useful when you want to warm up the cache with an image.
   * <p>
   * <em>Note:</em> It is safe to invoke this method from any thread.
   */
  public void fetch() {
    long started = System.nanoTime();

    if (deferred) {
      throw new IllegalStateException("Fit cannot be used with fetch.");
    }
    if (data.hasImage()) {
      Request request = createRequest(started);
      String key = createKey(request, new StringBuilder());

      Action action = new FetchAction(picasso, request, skipMemoryCache, key);
      picasso.submit(action);
    }
  }

  /**
   * Asynchronously fulfills the request into the specified {@link com.squareup.picasso.Target}. In most cases, you
   * should use this when you are dealing with a custom {@link android.view.View View} or view
   * holder which should implement the {@link com.squareup.picasso.Target} interface.
   * <p>
   * Implementing on a {@link android.view.View View}:
   * <blockquote><pre>
   * public class ProfileView extends FrameLayout implements Target {
   *   {@literal @}Override public void onBitmapLoaded(Bitmap bitmap, LoadedFrom from) {
   *     setBackgroundDrawable(new BitmapDrawable(bitmap));
   *   }
   *
   *   {@literal @}Override public void onBitmapFailed() {
   *     setBackgroundResource(R.drawable.profile_error);
   *   }
   *
   *   {@literal @}Override public void onPrepareLoad(Drawable placeHolderDrawable) {
   *     frame.setBackgroundDrawable(placeHolderDrawable);
   *   }
   * }
   * </pre></blockquote>
   * Implementing on a view holder object for use inside of an adapter:
   * <blockquote><pre>
   * public class ViewHolder implements Target {
   *   public FrameLayout frame;
   *   public TextView name;
   *
   *   {@literal @}Override public void onBitmapLoaded(Bitmap bitmap, LoadedFrom from) {
   *     frame.setBackgroundDrawable(new BitmapDrawable(bitmap));
   *   }
   *
   *   {@literal @}Override public void onBitmapFailed() {
   *     frame.setBackgroundResource(R.drawable.profile_error);
   *   }
   *
   *   {@literal @}Override public void onPrepareLoad(Drawable placeHolderDrawable) {
   *     frame.setBackgroundDrawable(placeHolderDrawable);
   *   }
   * }
   * </pre></blockquote>
   * <p>
   * <em>Note:</em> This method keeps a weak reference to the {@link com.squareup.picasso.Target} instance and will be
   * garbage collected if you do not keep a strong reference to it. To receive callbacks when an
   * image is loaded use {@link #into(android.widget.ImageView, com.squareup.picasso.Callback)}.
   */
  public void into(Target target) {
    long started = System.nanoTime();
    checkMain();

    if (target == null) {
      throw new IllegalArgumentException("Target must not be null.");
    }
    if (deferred) {
      throw new IllegalStateException("Fit cannot be used with a Target.");
    }

    Drawable drawable =
        placeholderResId != 0 ? picasso.context.getResources().getDrawable(placeholderResId)
            : placeholderDrawable;

    if (!data.hasImage()) {
      picasso.cancelRequest(target);
      target.onPrepareLoad(drawable);
      return;
    }

    Request request = createRequest(started);
    String requestKey = createKey(request);

    if (!skipMemoryCache) {
      Bitmap bitmap = picasso.quickMemoryCacheCheck(requestKey);
      if (bitmap != null) {
        picasso.cancelRequest(target);
        target.onBitmapLoaded(bitmap, MEMORY);
        return;
      }
    }

    target.onPrepareLoad(drawable);

    Action action =
        new TargetAction(picasso, target, request, skipMemoryCache, errorResId, errorDrawable,
            requestKey);
    picasso.enqueueAndSubmit(action);
  }

  /**
   * Asynchronously fulfills the request into the specified {@link android.widget.RemoteViews} object with the
   * given {@code viewId}. This is used for loading bitmaps into a {@link android.app.Notification}.
   */
  public void into(RemoteViews remoteViews, int viewId, int notificationId,
      Notification notification) {
    long started = System.nanoTime();
    checkMain();

    if (remoteViews == null) {
      throw new IllegalArgumentException("RemoteViews must not be null.");
    }
    if (notification == null) {
      throw new IllegalArgumentException("Notification must not be null.");
    }
    if (deferred) {
      throw new IllegalStateException("Fit cannot be used with RemoteViews.");
    }
    if (placeholderDrawable != null || errorDrawable != null) {
      throw new IllegalArgumentException(
          "Cannot use placeholder or error drawables with remote views.");
    }

    Request request = createRequest(started);
    String key = createKey(request);

    RemoteViewsAction action =
        new NotificationAction(picasso, request, remoteViews, viewId, notificationId, notification,
            skipMemoryCache, errorResId, key);

    performRemoteViewInto(action);
  }

  /**
   * Asynchronously fulfills the request into the specified {@link android.widget.RemoteViews} object with the
   * given {@code viewId}. This is used for loading bitmaps into all instances of a widget.
   */
  public void into(RemoteViews remoteViews, int viewId, int[] appWidgetIds) {
    long started = System.nanoTime();
    checkMain();

    if (remoteViews == null) {
      throw new IllegalArgumentException("remoteViews must not be null.");
    }
    if (appWidgetIds == null) {
      throw new IllegalArgumentException("appWidgetIds must not be null.");
    }
    if (deferred) {
      throw new IllegalStateException("Fit cannot be used with remote views.");
    }
    if (placeholderDrawable != null || errorDrawable != null) {
      throw new IllegalArgumentException(
          "Cannot use placeholder or error drawables with remote views.");
    }

    Request request = createRequest(started);
    String key = createKey(request);

    RemoteViewsAction action =
        new AppWidgetAction(picasso, request, remoteViews, viewId, appWidgetIds, skipMemoryCache,
            errorResId, key);

    performRemoteViewInto(action);
  }

  /**
   * Asynchronously fulfills the request into the specified {@link android.widget.ImageView}.
   * <p>
   * <em>Note:</em> This method keeps a weak reference to the {@link android.widget.ImageView} instance and will
   * automatically support object recycling.
   */
  public void into(ImageView target) {
    into(target, null);
  }

  /**
   * Asynchronously fulfills the request into the specified {@link android.widget.ImageView} and invokes the
   * target {@link com.squareup.picasso.Callback} if it's not {@code null}.
   * <p>
   * <em>Note:</em> The {@link com.squareup.picasso.Callback} param is a strong reference and will prevent your
   * {@link android.app.Activity} or {@link android.app.Fragment} from being garbage collected. If
   * you use this method, it is <b>strongly</b> recommended you invoke an adjacent
   * {@link com.squareup.picasso.Picasso#cancelRequest(android.widget.ImageView)} call to prevent temporary leaking.
   */
  public void into(ImageView target, Callback callback) {
    long started = System.nanoTime();
    checkMain();

    if (target == null) {
      throw new IllegalArgumentException("Target must not be null.");
    }

    if (!data.hasImage()) {
      picasso.cancelRequest(target);
      setPlaceholder(target, placeholderResId, placeholderDrawable);
      return;
    }

    if (deferred) {
      if (data.hasSize()) {
        throw new IllegalStateException("Fit cannot be used with resize.");
      }
      int width = target.getWidth();
      int height = target.getHeight();
      if (width == 0 || height == 0) {
        setPlaceholder(target, placeholderResId, placeholderDrawable);
        picasso.defer(target, new DeferredRequestCreator(this, target, callback));
        return;
      }
      data.resize(width, height);
    }

    Request request = createRequest(started);
    String requestKey = createKey(request);

    if (!skipMemoryCache) {
      Bitmap bitmap = picasso.quickMemoryCacheCheck(requestKey);
      if (bitmap != null) {
        picasso.cancelRequest(target);
        setBitmap(target, picasso.context, bitmap, MEMORY, noFade, picasso.indicatorsEnabled);
        if (picasso.loggingEnabled) {
          log(OWNER_MAIN, VERB_COMPLETED, request.plainId(), "from " + MEMORY);
        }
        if (callback != null) {
          callback.onSuccess();
        }
        return;
      }
    }

    setPlaceholder(target, placeholderResId, placeholderDrawable);

    Action action =
        new ImageViewAction(picasso, target, request, skipMemoryCache, noFade, errorResId,
            errorDrawable, requestKey, callback);

    picasso.enqueueAndSubmit(action);
  }

  /** Create the request optionally passing it through the request transformer. */
  private Request createRequest(long started) {
    int id = getRequestId();

    Request request = data.build();
    request.id = id;
    request.started = started;

    boolean loggingEnabled = picasso.loggingEnabled;
    if (loggingEnabled) {
      log(OWNER_MAIN, VERB_CREATED, request.plainId(), request.toString());
    }

    Request transformed = picasso.transformRequest(request);
    if (transformed != request) {
      // If the request was changed, copy over the id and timestamp from the original.
      transformed.id = id;
      transformed.started = started;

      if (loggingEnabled) {
        log(OWNER_MAIN, VERB_CHANGED, transformed.logId(), "into " + transformed);
      }
    }

    return transformed;
  }

  private void performRemoteViewInto(RemoteViewsAction action) {
    if (!skipMemoryCache) {
      Bitmap bitmap = picasso.quickMemoryCacheCheck(action.getKey());
      if (bitmap != null) {
        action.complete(bitmap, MEMORY);
        return;
      }
    }

    if (placeholderResId != 0) {
      action.setImageResource(placeholderResId);
    }

    picasso.enqueueAndSubmit(action);
  }
}


================================================
FILE: picasso/RequestHandler.java
================================================
/*
 * Copyright (C) 2014 Square, Inc.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
package com.squareup.picasso;

import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.net.NetworkInfo;

import java.io.IOException;

/**
 * {@link com.squareup.picasso.RequestHandler} allows you to extend Picasso to load images
 * in ways that are not supported by default in the library.
 * <p>
 * <h2>Usage</h2>
 * <p>{@link com.squareup.picasso.RequestHandler} must be subclassed to be used. You will have to
 * override two methods ({@link #canHandleRequest(com.squareup.picasso.Request)} and
 * {@link #load(com.squareup.picasso.Request)}) with your custom logic to load images.</p>
 *
 * <p>You should then register your {@link com.squareup.picasso.RequestHandler} using
 * {@link com.squareup.picasso.Picasso.Builder#addRequestHandler(com.squareup.picasso.RequestHandler)}</p>
 *
 * <b>NOTE:</b> This is a beta feature. The API is subject to change in a backwards
 * incompatible way at any time.
 *
 * @see com.squareup.picasso.Picasso.Builder#addRequestHandler(com.squareup.picasso.RequestHandler)
 */
public abstract class RequestHandler {
  /**
   * {@link com.squareup.picasso.RequestHandler.Result} represents the result of a {@link #load(com.squareup.picasso.Request)} call in a
   * {@link com.squareup.picasso.RequestHandler}.
   *
   * @see com.squareup.picasso.RequestHandler
   * @see #load(com.squareup.picasso.Request)
   */
  public static final class Result {
    private final Picasso.LoadedFrom loadedFrom;
    private final Bitmap bitmap;
    private final int exifOrientation;

    public Result(Bitmap bitmap, Picasso.LoadedFrom loadedFrom) {
      this(bitmap, loadedFrom, 0);
    }

    Result(Bitmap bitmap, Picasso.LoadedFrom loadedFrom, int exifOrientation) {
      this.bitmap = bitmap;
      this.loadedFrom = loadedFrom;
      this.exifOrientation = exifOrientation;
    }

    /**
     * Returns the resulting {@link android.graphics.Bitmap} generated
     * from a {@link #load(com.squareup.picasso.Request)} call.
     */
    public Bitmap getBitmap() {
      return bitmap;
    }

    /**
     * Returns the resulting {@link com.squareup.picasso.Picasso.LoadedFrom} generated
     * from a {@link #load(com.squareup.picasso.Request)} call.
     */
    public Picasso.LoadedFrom getLoadedFrom() {
      return loadedFrom;
    }

    /**
     * Returns the resulting EXIF orientation generated
     * from a {@link #load(com.squareup.picasso.Request)} call. This is only accessible
     * to built-in RequestHandlers.
     */
    int getExifOrientation() {
      
Download .txt
gitextract_9vah1spq/

├── .gitignore
├── .idea/
│   ├── .name
│   ├── compiler.xml
│   ├── copyright/
│   │   └── profiles_settings.xml
│   ├── encodings.xml
│   ├── gradle.xml
│   ├── misc.xml
│   ├── modules.xml
│   ├── scopes/
│   │   └── scope_settings.xml
│   └── vcs.xml
├── README.md
├── WeixinNineGridLayout.iml
├── app/
│   ├── .gitignore
│   ├── app.iml
│   ├── build.gradle
│   ├── libs/
│   │   └── picasso-2.3.4.jar
│   ├── proguard-rules.pro
│   └── src/
│       ├── androidTest/
│       │   └── java/
│       │       └── com/
│       │           └── example/
│       │               └── administrator/
│       │                   └── weixinninegridlayout/
│       │                       └── ApplicationTest.java
│       └── main/
│           ├── AndroidManifest.xml
│           ├── java/
│           │   └── com/
│           │       └── weixinninegridlayout/
│           │           ├── CustomImageView.java
│           │           ├── Image.java
│           │           ├── L.java
│           │           ├── MainActivity.java
│           │           ├── MainAdapter.java
│           │           ├── NineGridlayout.java
│           │           └── ScreenTools.java
│           └── res/
│               ├── layout/
│               │   ├── activity_main.xml
│               │   └── item_ninegridlayout.xml
│               ├── menu/
│               │   └── menu_main.xml
│               ├── values/
│               │   ├── dimens.xml
│               │   ├── strings.xml
│               │   └── styles.xml
│               └── values-w820dp/
│                   └── dimens.xml
├── build.gradle
├── gradle/
│   └── wrapper/
│       ├── gradle-wrapper.jar
│       └── gradle-wrapper.properties
├── gradle.properties
├── gradlew
├── gradlew.bat
├── picasso/
│   ├── Action.java
│   ├── AssetRequestHandler.java
│   ├── BitmapHunter.java
│   ├── Cache.java
│   ├── Callback.java
│   ├── ContactsPhotoRequestHandler.java
│   ├── ContentStreamRequestHandler.java
│   ├── DeferredRequestCreator.java
│   ├── Dispatcher.java
│   ├── Downloader.java
│   ├── FetchAction.java
│   ├── FileRequestHandler.java
│   ├── GetAction.java
│   ├── ImageViewAction.java
│   ├── LruCache.java
│   ├── MarkableInputStream.java
│   ├── MediaStoreRequestHandler.java
│   ├── NetworkRequestHandler.java
│   ├── Picasso.java
│   ├── PicassoDrawable.java
│   ├── PicassoExecutorService.java
│   ├── RemoteViewsAction.java
│   ├── Request.java
│   ├── RequestCreator.java
│   ├── RequestHandler.java
│   ├── ResourceRequestHandler.java
│   ├── Stats.java
│   ├── StatsSnapshot.java
│   ├── Target.java
│   ├── TargetAction.java
│   ├── Transformation.java
│   ├── UrlConnectionDownloader.java
│   └── Utils.java
└── settings.gradle
Download .txt
SYMBOL INDEX (501 symbols across 41 files)

FILE: app/src/androidTest/java/com/example/administrator/weixinninegridlayout/ApplicationTest.java
  class ApplicationTest (line 9) | public class ApplicationTest extends ApplicationTestCase<Application> {
    method ApplicationTest (line 10) | public ApplicationTest() {

FILE: app/src/main/java/com/weixinninegridlayout/CustomImageView.java
  class CustomImageView (line 19) | public class CustomImageView extends ImageView {
    method CustomImageView (line 23) | public CustomImageView(Context context, AttributeSet attrs) {
    method CustomImageView (line 27) | public CustomImageView(Context context) {
    method onTouchEvent (line 32) | @Override
    method onAttachedToWindow (line 57) | @Override
    method onDetachedFromWindow (line 64) | @Override
    method setImageUrl (line 73) | public void setImageUrl(String url) {

FILE: app/src/main/java/com/weixinninegridlayout/Image.java
  class Image (line 6) | public class Image {
    method Image (line 11) | public Image(String url, int width, int height) {
    method getUrl (line 18) | public String getUrl() {
    method setUrl (line 22) | public void setUrl(String url) {
    method getWidth (line 26) | public int getWidth() {
    method setWidth (line 30) | public void setWidth(int width) {
    method getHeight (line 34) | public int getHeight() {
    method setHeight (line 38) | public void setHeight(int height) {
    method toString (line 42) | @Override

FILE: app/src/main/java/com/weixinninegridlayout/L.java
  class L (line 6) | public final class L {
    method L (line 7) | private L() {
    method v (line 11) | public static void v(Throwable t) {
    method v (line 15) | public static void v(Object format, Object... args) {
    method v (line 19) | public static void v(Throwable t, Object format, Object... args) {
    method d (line 23) | public static void d(Throwable t) {
    method d (line 27) | public static void d(Object format, Object... args) {
    method d (line 31) | public static void d(Throwable t, Object format, Object... args) {
    method i (line 35) | public static void i(Throwable t) {
    method i (line 39) | public static void i(Object format, Object... args) {
    method i (line 43) | public static void i(Throwable t, Object format, Object... args) {
    method w (line 47) | public static void w(Throwable t) {
    method w (line 51) | public static void w(Object format, Object... args) {
    method w (line 55) | public static void w(Throwable t, Object format, Object... args) {
    method e (line 59) | public static void e(Throwable t) {
    method e (line 63) | public static void e(Object format, Object... args) {
    method e (line 67) | public static void e(Throwable t, Object format, Object... args) {
    method log (line 71) | private static void log(final int pType, final Throwable t, final Obje...

FILE: app/src/main/java/com/weixinninegridlayout/MainActivity.java
  class MainActivity (line 13) | public class MainActivity extends ActionBarActivity {
    method onCreate (line 29) | @Override
    method initData (line 40) | private void initData() {

FILE: app/src/main/java/com/weixinninegridlayout/MainAdapter.java
  class MainAdapter (line 14) | public class MainAdapter extends BaseAdapter {
    method MainAdapter (line 18) | public MainAdapter(Context context, List<List<Image>> datalist) {
    method getCount (line 23) | @Override
    method getItem (line 28) | @Override
    method getItemId (line 33) | @Override
    method getView (line 38) | @Override
    method handlerOneImage (line 70) | private void handlerOneImage(ViewHolder viewHolder, Image image) {
    class ViewHolder (line 100) | class ViewHolder {

FILE: app/src/main/java/com/weixinninegridlayout/NineGridlayout.java
  class NineGridlayout (line 17) | public class NineGridlayout extends ViewGroup {
    method NineGridlayout (line 28) | public NineGridlayout(Context context) {
    method NineGridlayout (line 32) | public NineGridlayout(Context context, AttributeSet attrs) {
    method onMeasure (line 38) | @Override
    method onLayout (line 43) | @Override
    method layoutChildrenView (line 47) | private void layoutChildrenView(){
    method findPosition (line 73) | private int[] findPosition(int childNum) {
    method getGap (line 87) | public int getGap() {
    method setGap (line 91) | public void setGap(int gap) {
    method setImagesData (line 96) | public void setImagesData(List<Image> lists) {
    method generateChildrenLayout (line 143) | private void generateChildrenLayout(int length) {
    method generateImageView (line 159) | private CustomImageView generateImageView() {

FILE: app/src/main/java/com/weixinninegridlayout/ScreenTools.java
  class ScreenTools (line 6) | public class ScreenTools {
    method ScreenTools (line 11) | private ScreenTools(Context context) {
    method instance (line 15) | public static ScreenTools instance(Context context) {
    method dip2px (line 21) | public int dip2px(float f) {
    method dip2px (line 25) | public int dip2px(int i) {
    method get480Height (line 29) | public int get480Height(int i) {
    method getDensity (line 33) | public float getDensity(Context context) {
    method getScal (line 37) | public int getScal() {
    method getScreenDensityDpi (line 41) | public int getScreenDensityDpi() {
    method getScreenHeight (line 45) | public int getScreenHeight() {
    method getScreenWidth (line 49) | public int getScreenWidth() {
    method getXdpi (line 54) | public float getXdpi() {
    method getYdpi (line 58) | public float getYdpi() {
    method px2dip (line 62) | public int px2dip(float f) {
    method px2dip (line 67) | public int px2dip(int i) {

FILE: picasso/Action.java
  class Action (line 23) | abstract class Action<T> {
    class RequestWeakReference (line 24) | static class RequestWeakReference<T> extends WeakReference<T> {
      method RequestWeakReference (line 27) | public RequestWeakReference(Action action, T referent, ReferenceQueu...
    method Action (line 45) | Action(Picasso picasso, T target, Request request, boolean skipCache, ...
    method complete (line 57) | abstract void complete(Bitmap result, Picasso.LoadedFrom from);
    method error (line 59) | abstract void error();
    method cancel (line 61) | void cancel() {
    method getRequest (line 65) | Request getRequest() {
    method getTarget (line 69) | T getTarget() {
    method getKey (line 73) | String getKey() {
    method isCancelled (line 77) | boolean isCancelled() {
    method willReplay (line 81) | boolean willReplay() {
    method getPicasso (line 85) | Picasso getPicasso() {

FILE: picasso/AssetRequestHandler.java
  class AssetRequestHandler (line 29) | class AssetRequestHandler extends RequestHandler {
    method AssetRequestHandler (line 36) | public AssetRequestHandler(Context context) {
    method canHandleRequest (line 40) | @Override public boolean canHandleRequest(Request data) {
    method load (line 46) | @Override public Result load(Request data) throws IOException {
    method decodeAsset (line 51) | Bitmap decodeAsset(Request data, String filePath) throws IOException {

FILE: picasso/BitmapHunter.java
  class BitmapHunter (line 38) | class BitmapHunter implements Runnable {
    method initialValue (line 47) | @Override protected StringBuilder initialValue() {
    method BitmapHunter (line 70) | BitmapHunter(Picasso picasso, Dispatcher dispatcher, Cache cache, Stat...
    method run (line 84) | @Override public void run() {
    method hunt (line 118) | Bitmap hunt() throws IOException {
    method attach (line 170) | void attach(Action action) {
    method detach (line 197) | void detach(Action action) {
    method cancel (line 209) | boolean cancel() {
    method isCancelled (line 216) | boolean isCancelled() {
    method shouldSkipMemoryCache (line 220) | boolean shouldSkipMemoryCache() {
    method shouldRetry (line 224) | boolean shouldRetry(boolean airplaneMode, NetworkInfo info) {
    method supportsReplay (line 233) | boolean supportsReplay() {
    method getResult (line 237) | Bitmap getResult() {
    method getKey (line 241) | String getKey() {
    method getData (line 245) | Request getData() {
    method getAction (line 249) | Action getAction() {
    method getPicasso (line 253) | Picasso getPicasso() {
    method getActions (line 257) | List<Action> getActions() {
    method getException (line 261) | Exception getException() {
    method getLoadedFrom (line 265) | Picasso.LoadedFrom getLoadedFrom() {
    method updateThreadName (line 269) | static void updateThreadName(Request data) {
    method forRequest (line 279) | static BitmapHunter forRequest(Picasso picasso, Dispatcher dispatcher,
    method applyCustomTransformations (line 295) | static Bitmap applyCustomTransformations(List<Transformation> transfor...
    method transformResult (line 346) | static Bitmap transformResult(Request data, Bitmap result, int exifRot...

FILE: picasso/Cache.java
  type Cache (line 27) | public interface Cache {
    method get (line 29) | Bitmap get(String key);
    method set (line 32) | void set(String key, Bitmap bitmap);
    method size (line 35) | int size();
    method maxSize (line 38) | int maxSize();
    method clear (line 41) | void clear();
    method get (line 45) | @Override public Bitmap get(String key) {
    method set (line 49) | @Override public void set(String key, Bitmap bitmap) {
    method size (line 53) | @Override public int size() {
    method maxSize (line 57) | @Override public int maxSize() {
    method clear (line 61) | @Override public void clear() {

FILE: picasso/Callback.java
  type Callback (line 18) | public interface Callback {
    method onSuccess (line 19) | void onSuccess();
    method onError (line 21) | void onError();
    class EmptyCallback (line 23) | public static class EmptyCallback implements Callback {
      method onSuccess (line 25) | @Override public void onSuccess() {
      method onError (line 28) | @Override public void onError() {

FILE: picasso/ContactsPhotoRequestHandler.java
  class ContactsPhotoRequestHandler (line 36) | class ContactsPhotoRequestHandler extends RequestHandler {
    method ContactsPhotoRequestHandler (line 62) | ContactsPhotoRequestHandler(Context context) {
    method canHandleRequest (line 66) | @Override public boolean canHandleRequest(Request data) {
    method load (line 73) | @Override public Result load(Request data) throws IOException {
    method getInputStream (line 83) | private InputStream getInputStream(Request data) throws IOException {
    method decodeStream (line 107) | private Bitmap decodeStream(InputStream stream, Request data) throws I...
    class ContactPhotoStreamIcs (line 124) | @TargetApi(ICE_CREAM_SANDWICH)
      method get (line 126) | static InputStream get(ContentResolver contentResolver, Uri uri) {

FILE: picasso/ContentStreamRequestHandler.java
  class ContentStreamRequestHandler (line 28) | class ContentStreamRequestHandler extends RequestHandler {
    method ContentStreamRequestHandler (line 31) | ContentStreamRequestHandler(Context context) {
    method canHandleRequest (line 35) | @Override public boolean canHandleRequest(Request data) {
    method load (line 39) | @Override public Result load(Request data) throws IOException {
    method decodeContentStream (line 43) | protected Bitmap decodeContentStream(Request data) throws IOException {

FILE: picasso/DeferredRequestCreator.java
  class DeferredRequestCreator (line 22) | class DeferredRequestCreator implements ViewTreeObserver.OnPreDrawListen...
    method DeferredRequestCreator (line 28) | DeferredRequestCreator(RequestCreator creator, ImageView target) {
    method DeferredRequestCreator (line 32) | DeferredRequestCreator(RequestCreator creator, ImageView target, Callb...
    method onPreDraw (line 39) | @Override public boolean onPreDraw() {
    method cancel (line 62) | void cancel() {

FILE: picasso/Dispatcher.java
  class Dispatcher (line 55) | class Dispatcher {
    method Dispatcher (line 90) | Dispatcher(Context context, ExecutorService service, Handler mainThrea...
    method shutdown (line 110) | void shutdown() {
    method dispatchSubmit (line 116) | void dispatchSubmit(Action action) {
    method dispatchCancel (line 120) | void dispatchCancel(Action action) {
    method dispatchComplete (line 124) | void dispatchComplete(BitmapHunter hunter) {
    method dispatchRetry (line 128) | void dispatchRetry(BitmapHunter hunter) {
    method dispatchFailed (line 132) | void dispatchFailed(BitmapHunter hunter) {
    method dispatchNetworkStateChange (line 136) | void dispatchNetworkStateChange(NetworkInfo info) {
    method dispatchAirplaneModeChange (line 140) | void dispatchAirplaneModeChange(boolean airplaneMode) {
    method performSubmit (line 145) | void performSubmit(Action action) {
    method performCancel (line 169) | void performCancel(Action action) {
    method performRetry (line 187) | void performRetry(BitmapHunter hunter) {
    method performComplete (line 231) | void performComplete(BitmapHunter hunter) {
    method performBatchComplete (line 242) | void performBatchComplete() {
    method performError (line 249) | void performError(BitmapHunter hunter, boolean willReplay) {
    method performAirplaneModeChange (line 258) | void performAirplaneModeChange(boolean airplaneMode) {
    method performNetworkStateChange (line 262) | void performNetworkStateChange(NetworkInfo info) {
    method flushFailedActions (line 272) | private void flushFailedActions() {
    method markForReplay (line 286) | private void markForReplay(BitmapHunter hunter) {
    method markForReplay (line 301) | private void markForReplay(Action action) {
    method batch (line 309) | private void batch(BitmapHunter hunter) {
    method logBatch (line 319) | private void logBatch(List<BitmapHunter> copy) {
    class DispatcherHandler (line 333) | private static class DispatcherHandler extends Handler {
      method DispatcherHandler (line 336) | public DispatcherHandler(Looper looper, Dispatcher dispatcher) {
      method handleMessage (line 341) | @Override public void handleMessage(final Message msg) {
    class DispatcherThread (line 391) | static class DispatcherThread extends HandlerThread {
      method DispatcherThread (line 392) | DispatcherThread() {
    class NetworkBroadcastReceiver (line 397) | static class NetworkBroadcastReceiver extends BroadcastReceiver {
      method NetworkBroadcastReceiver (line 402) | NetworkBroadcastReceiver(Dispatcher dispatcher) {
      method register (line 406) | void register() {
      method unregister (line 415) | void unregister() {
      method onReceive (line 419) | @Override public void onReceive(Context context, Intent intent) {

FILE: picasso/Downloader.java
  type Downloader (line 24) | public interface Downloader {
    method load (line 36) | Response load(Uri uri, boolean localCacheOnly) throws IOException;
    class ResponseException (line 39) | class ResponseException extends IOException {
      method ResponseException (line 40) | public ResponseException(String message) {
    class Response (line 46) | class Response {
      method Response (line 59) | @Deprecated @SuppressWarnings("UnusedDeclaration")
      method Response (line 71) | @Deprecated @SuppressWarnings("UnusedDeclaration")
      method Response (line 84) | public Response(Bitmap bitmap, boolean loadedFromCache, long content...
      method Response (line 102) | public Response(InputStream stream, boolean loadedFromCache, long co...
      method getInputStream (line 117) | public InputStream getInputStream() {
      method getBitmap (line 126) | public Bitmap getBitmap() {
      method getContentLength (line 131) | public long getContentLength() {

FILE: picasso/FetchAction.java
  class FetchAction (line 20) | class FetchAction extends Action<Void> {
    method FetchAction (line 21) | FetchAction(Picasso picasso, Request data, boolean skipCache, String k...
    method complete (line 25) | @Override void complete(Bitmap result, Picasso.LoadedFrom from) {
    method error (line 28) | @Override public void error() {

FILE: picasso/FileRequestHandler.java
  class FileRequestHandler (line 31) | class FileRequestHandler extends ContentStreamRequestHandler {
    method FileRequestHandler (line 33) | FileRequestHandler(Context context) {
    method canHandleRequest (line 37) | @Override public boolean canHandleRequest(Request data) {
    method load (line 41) | @Override public Result load(Request data) throws IOException {
    method getFileExifRotation (line 45) | static int getFileExifRotation(Uri uri) throws IOException {

FILE: picasso/GetAction.java
  class GetAction (line 20) | class GetAction extends Action<Void> {
    method GetAction (line 21) | GetAction(Picasso picasso, Request data, boolean skipCache, String key) {
    method complete (line 25) | @Override void complete(Bitmap result, Picasso.LoadedFrom from) {
    method error (line 28) | @Override public void error() {

FILE: picasso/ImageViewAction.java
  class ImageViewAction (line 23) | class ImageViewAction extends Action<ImageView> {
    method ImageViewAction (line 27) | ImageViewAction(Picasso picasso, ImageView imageView, Request data, bo...
    method complete (line 33) | @Override public void complete(Bitmap result, Picasso.LoadedFrom from) {
    method error (line 53) | @Override public void error() {
    method cancel (line 69) | @Override void cancel() {

FILE: picasso/LruCache.java
  class LruCache (line 24) | public class LruCache implements Cache {
    method LruCache (line 35) | public LruCache(Context context) {
    method LruCache (line 40) | public LruCache(int maxSize) {
    method get (line 48) | @Override public Bitmap get(String key) {
    method set (line 66) | @Override public void set(String key, Bitmap bitmap) {
    method trimToSize (line 84) | private void trimToSize(int maxSize) {
    method evictAll (line 109) | public final void evictAll() {
    method size (line 114) | public final synchronized int size() {
    method maxSize (line 119) | public final synchronized int maxSize() {
    method clear (line 123) | public final synchronized void clear() {
    method hitCount (line 128) | public final synchronized int hitCount() {
    method missCount (line 133) | public final synchronized int missCount() {
    method putCount (line 138) | public final synchronized int putCount() {
    method evictionCount (line 143) | public final synchronized int evictionCount() {

FILE: picasso/MarkableInputStream.java
  class MarkableInputStream (line 27) | final class MarkableInputStream extends InputStream {
    method MarkableInputStream (line 37) | public MarkableInputStream(InputStream in) {
    method MarkableInputStream (line 41) | public MarkableInputStream(InputStream in, int size) {
    method mark (line 49) | @Override public void mark(int readLimit) {
    method savePosition (line 59) | public long savePosition(int readLimit) {
    method setLimit (line 75) | private void setLimit(long limit) {
    method reset (line 92) | @Override public void reset() throws IOException {
    method reset (line 97) | public void reset(long token) throws IOException {
    method skip (line 107) | private void skip(long current, long target) throws IOException {
    method read (line 121) | @Override public int read() throws IOException {
    method read (line 129) | @Override public int read(byte[] buffer) throws IOException {
    method read (line 137) | @Override public int read(byte[] buffer, int offset, int length) throw...
    method skip (line 145) | @Override public long skip(long byteCount) throws IOException {
    method available (line 151) | @Override public int available() throws IOException {
    method close (line 155) | @Override public void close() throws IOException {
    method markSupported (line 159) | @Override public boolean markSupported() {

FILE: picasso/MediaStoreRequestHandler.java
  class MediaStoreRequestHandler (line 39) | class MediaStoreRequestHandler extends ContentStreamRequestHandler {
    method MediaStoreRequestHandler (line 44) | MediaStoreRequestHandler(Context context) {
    method canHandleRequest (line 48) | @Override public boolean canHandleRequest(Request data) {
    method load (line 54) | @Override public Result load(Request data) throws IOException {
    method getPicassoKind (line 95) | static PicassoKind getPicassoKind(int targetWidth, int targetHeight) {
    method getExifOrientation (line 104) | static int getExifOrientation(ContentResolver contentResolver, Uri uri) {
    type PicassoKind (line 122) | enum PicassoKind {
      method PicassoKind (line 131) | PicassoKind(int androidKind, int width, int height) {

FILE: picasso/NetworkRequestHandler.java
  class NetworkRequestHandler (line 28) | class NetworkRequestHandler extends RequestHandler {
    method NetworkRequestHandler (line 38) | public NetworkRequestHandler(Downloader downloader, Stats stats) {
    method canHandleRequest (line 43) | @Override public boolean canHandleRequest(Request data) {
    method load (line 48) | @Override public Result load(Request data) throws IOException {
    method getRetryCount (line 81) | @Override int getRetryCount() {
    method shouldRetry (line 85) | @Override boolean shouldRetry(boolean airplaneMode, NetworkInfo info) {
    method supportsReplay (line 89) | @Override boolean supportsReplay() {
    method decodeStream (line 93) | private Bitmap decodeStream(InputStream stream, Request data) throws I...

FILE: picasso/Picasso.java
  class Picasso (line 54) | public class Picasso {
    type Listener (line 57) | public interface Listener {
      method onImageLoadFailed (line 62) | void onImageLoadFailed(Picasso picasso, Uri uri, Exception exception);
    type RequestTransformer (line 75) | public interface RequestTransformer {
      method transformRequest (line 81) | Request transformRequest(Request request);
      method transformRequest (line 85) | @Override public Request transformRequest(Request request) {
    method handleMessage (line 93) | @Override public void handleMessage(Message msg) {
    method Picasso (line 135) | Picasso(Context context, Dispatcher dispatcher, Cache cache, Listener ...
    method cancelRequest (line 172) | public void cancelRequest(ImageView view) {
    method cancelRequest (line 177) | public void cancelRequest(Target target) {
    method cancelRequest (line 185) | public void cancelRequest(RemoteViews remoteViews, int viewId) {
    method load (line 199) | public RequestCreator load(Uri uri) {
    method load (line 219) | public RequestCreator load(String path) {
    method load (line 242) | public RequestCreator load(File file) {
    method load (line 256) | public RequestCreator load(int resourceId) {
    method isDebugging (line 268) | @SuppressWarnings("UnusedDeclaration") @Deprecated public boolean isDe...
    method setDebugging (line 278) | @SuppressWarnings("UnusedDeclaration") @Deprecated public void setDebu...
    method setIndicatorsEnabled (line 283) | @SuppressWarnings("UnusedDeclaration") public void setIndicatorsEnable...
    method areIndicatorsEnabled (line 288) | @SuppressWarnings("UnusedDeclaration") public boolean areIndicatorsEna...
    method setLoggingEnabled (line 298) | public void setLoggingEnabled(boolean enabled) {
    method isLoggingEnabled (line 303) | public boolean isLoggingEnabled() {
    method getSnapshot (line 313) | @SuppressWarnings("UnusedDeclaration") public StatsSnapshot getSnapsho...
    method shutdown (line 318) | public void shutdown() {
    method getRequestHandlers (line 336) | List<RequestHandler> getRequestHandlers() {
    method transformRequest (line 340) | Request transformRequest(Request request) {
    method defer (line 351) | void defer(ImageView view, DeferredRequestCreator request) {
    method enqueueAndSubmit (line 355) | void enqueueAndSubmit(Action action) {
    method submit (line 365) | void submit(Action action) {
    method quickMemoryCacheCheck (line 369) | Bitmap quickMemoryCacheCheck(String key) {
    method complete (line 379) | void complete(BitmapHunter hunter) {
    method deliverAction (line 412) | private void deliverAction(Bitmap result, LoadedFrom from, Action acti...
    method cancelExistingRequest (line 435) | private void cancelExistingRequest(Object target) {
    class CleanupThread (line 452) | private static class CleanupThread extends Thread {
      method CleanupThread (line 456) | CleanupThread(ReferenceQueue<?> referenceQueue, Handler handler) {
      method run (line 463) | @Override public void run() {
      method shutdown (line 482) | void shutdown() {
    method with (line 503) | public static Picasso with(Context context) {
    class Builder (line 515) | @SuppressWarnings("UnusedDeclaration") // Public API.
      method Builder (line 529) | public Builder(Context context) {
      method downloader (line 537) | public Builder downloader(Downloader downloader) {
      method executor (line 549) | public Builder executor(ExecutorService executorService) {
      method memoryCache (line 561) | public Builder memoryCache(Cache memoryCache) {
      method listener (line 573) | public Builder listener(Listener listener) {
      method requestTransformer (line 590) | public Builder requestTransformer(RequestTransformer transformer) {
      method addRequestHandler (line 602) | public Builder addRequestHandler(RequestHandler requestHandler) {
      method debugging (line 620) | @Deprecated public Builder debugging(boolean debugging) {
      method indicatorsEnabled (line 625) | public Builder indicatorsEnabled(boolean enabled) {
      method loggingEnabled (line 636) | public Builder loggingEnabled(boolean enabled) {
      method build (line 642) | public Picasso build() {
    type LoadedFrom (line 668) | public enum LoadedFrom {
      method LoadedFrom (line 675) | private LoadedFrom(int debugColor) {

FILE: picasso/PicassoDrawable.java
  class PicassoDrawable (line 36) | final class PicassoDrawable extends BitmapDrawable {
    method setBitmap (line 45) | static void setBitmap(ImageView target, Context context, Bitmap bitmap,
    method setPlaceholder (line 60) | static void setPlaceholder(ImageView target, int placeholderResId, Dra...
    method PicassoDrawable (line 81) | PicassoDrawable(Context context, Bitmap bitmap, Drawable placeholder,
    method draw (line 98) | @Override public void draw(Canvas canvas) {
    method setAlpha (line 127) | @Override public void setAlpha(int alpha) {
    method setColorFilter (line 135) | @Override public void setColorFilter(ColorFilter cf) {
    method onBoundsChange (line 142) | @Override protected void onBoundsChange(Rect bounds) {
    method drawDebugIndicator (line 149) | private void drawDebugIndicator(Canvas canvas) {
    method getTrianglePath (line 159) | private static Path getTrianglePath(Point p1, int width) {

FILE: picasso/PicassoExecutorService.java
  class PicassoExecutorService (line 31) | class PicassoExecutorService extends ThreadPoolExecutor {
    method PicassoExecutorService (line 34) | PicassoExecutorService() {
    method adjustThreadCount (line 39) | void adjustThreadCount(NetworkInfo info) {
    method setThreadCount (line 77) | private void setThreadCount(int threadCount) {

FILE: picasso/RemoteViewsAction.java
  class RemoteViewsAction (line 27) | abstract class RemoteViewsAction extends Action<RemoteViewsAction.Remote...
    method RemoteViewsAction (line 31) | RemoteViewsAction(Picasso picasso, Request data, RemoteViews remoteVie...
    method complete (line 39) | @Override void complete(Bitmap result, Picasso.LoadedFrom from) {
    method error (line 44) | @Override public void error() {
    method setImageResource (line 50) | void setImageResource(int resId) {
    method update (line 55) | abstract void update();
    class RemoteViewsTarget (line 57) | static class RemoteViewsTarget {
      method RemoteViewsTarget (line 61) | RemoteViewsTarget(RemoteViews remoteViews, int viewId) {
      method equals (line 66) | @Override public boolean equals(Object o) {
      method hashCode (line 74) | @Override public int hashCode() {
    class AppWidgetAction (line 79) | static class AppWidgetAction extends RemoteViewsAction {
      method AppWidgetAction (line 82) | AppWidgetAction(Picasso picasso, Request data, RemoteViews remoteVie...
      method update (line 88) | @Override void update() {
    class NotificationAction (line 94) | static class NotificationAction extends RemoteViewsAction {
      method NotificationAction (line 98) | NotificationAction(Picasso picasso, Request data, RemoteViews remote...
      method update (line 106) | @Override void update() {

FILE: picasso/Request.java
  class Request (line 27) | public final class Request {
    method Request (line 78) | private Request(Uri uri, int resourceId, List<Transformation> transfor...
    method toString (line 99) | @Override public String toString() {
    method logId (line 135) | String logId() {
    method plainId (line 143) | String plainId() {
    method getName (line 147) | String getName() {
    method hasSize (line 154) | public boolean hasSize() {
    method needsTransformation (line 158) | boolean needsTransformation() {
    method needsMatrixTransform (line 162) | boolean needsMatrixTransform() {
    method hasCustomTransformations (line 166) | boolean hasCustomTransformations() {
    method buildUpon (line 170) | public Builder buildUpon() {
    class Builder (line 175) | public static final class Builder {
      method Builder (line 190) | public Builder(Uri uri) {
      method Builder (line 195) | public Builder(int resourceId) {
      method Builder (line 199) | Builder(Uri uri, int resourceId) {
      method Builder (line 204) | private Builder(Request request) {
      method hasImage (line 221) | boolean hasImage() {
      method hasSize (line 225) | boolean hasSize() {
      method setUri (line 234) | public Builder setUri(Uri uri) {
      method setResourceId (line 248) | public Builder setResourceId(int resourceId) {
      method resize (line 258) | public Builder resize(int targetWidth, int targetHeight) {
      method clearResize (line 271) | public Builder clearResize() {
      method centerCrop (line 284) | public Builder centerCrop() {
      method clearCenterCrop (line 293) | public Builder clearCenterCrop() {
      method centerInside (line 302) | public Builder centerInside() {
      method clearCenterInside (line 311) | public Builder clearCenterInside() {
      method rotate (line 317) | public Builder rotate(float degrees) {
      method rotate (line 323) | public Builder rotate(float degrees, float pivotX, float pivotY) {
      method clearRotation (line 332) | public Builder clearRotation() {
      method config (line 341) | public Builder config(Bitmap.Config config) {
      method transform (line 351) | public Builder transform(Transformation transformation) {
      method build (line 363) | public Request build() {

FILE: picasso/RequestCreator.java
  class RequestCreator (line 46) | @SuppressWarnings("UnusedDeclaration") // Public API.
    method getRequestId (line 50) | private static int getRequestId() {
    method RequestCreator (line 86) | RequestCreator(Picasso picasso, Uri uri, int resourceId) {
    method RequestCreator (line 95) | RequestCreator() {
    method placeholder (line 105) | public RequestCreator placeholder(int placeholderResId) {
    method placeholder (line 124) | public RequestCreator placeholder(Drawable placeholderDrawable) {
    method error (line 133) | public RequestCreator error(int errorResId) {
    method error (line 145) | public RequestCreator error(Drawable errorDrawable) {
    method fit (line 162) | public RequestCreator fit() {
    method unfit (line 168) | RequestCreator unfit() {
    method resizeDimen (line 174) | public RequestCreator resizeDimen(int targetWidthResId, int targetHeig...
    method resize (line 182) | public RequestCreator resize(int targetWidth, int targetHeight) {
    method centerCrop (line 192) | public RequestCreator centerCrop() {
    method centerInside (line 201) | public RequestCreator centerInside() {
    method rotate (line 207) | public RequestCreator rotate(float degrees) {
    method rotate (line 213) | public RequestCreator rotate(float degrees, float pivotX, float pivotY) {
    method config (line 224) | public RequestCreator config(Bitmap.Config config) {
    method transform (line 235) | public RequestCreator transform(Transformation transformation) {
    method skipMemoryCache (line 245) | public RequestCreator skipMemoryCache() {
    method noFade (line 251) | public RequestCreator noFade() {
    method get (line 262) | public Bitmap get() throws IOException {
    method fetch (line 286) | public void fetch() {
    method into (line 346) | public void into(Target target) {
    method into (line 391) | public void into(RemoteViews remoteViews, int viewId, int notificationId,
    method into (line 424) | public void into(RemoteViews remoteViews, int viewId, int[] appWidgetI...
    method into (line 458) | public void into(ImageView target) {
    method into (line 471) | public void into(ImageView target, Callback callback) {
    method createRequest (line 527) | private Request createRequest(long started) {
    method performRemoteViewInto (line 553) | private void performRemoteViewInto(RemoteViewsAction action) {

FILE: picasso/RequestHandler.java
  class RequestHandler (line 41) | public abstract class RequestHandler {
    class Result (line 49) | public static final class Result {
      method Result (line 54) | public Result(Bitmap bitmap, Picasso.LoadedFrom loadedFrom) {
      method Result (line 58) | Result(Bitmap bitmap, Picasso.LoadedFrom loadedFrom, int exifOrienta...
      method getBitmap (line 68) | public Bitmap getBitmap() {
      method getLoadedFrom (line 76) | public Picasso.LoadedFrom getLoadedFrom() {
      method getExifOrientation (line 85) | int getExifOrientation() {
    method canHandleRequest (line 94) | public abstract boolean canHandleRequest(Request data);
    method load (line 102) | public abstract Result load(Request data) throws IOException;
    method getRetryCount (line 104) | int getRetryCount() {
    method shouldRetry (line 108) | boolean shouldRetry(boolean airplaneMode, NetworkInfo info) {
    method supportsReplay (line 112) | boolean supportsReplay() {
    method createBitmapOptions (line 120) | static BitmapFactory.Options createBitmapOptions(Request data) {
    method requiresInSampleSize (line 134) | static boolean requiresInSampleSize(BitmapFactory.Options options) {
    method calculateInSampleSize (line 138) | static void calculateInSampleSize(int reqWidth, int reqHeight, BitmapF...
    method calculateInSampleSize (line 142) | static void calculateInSampleSize(int reqWidth, int reqHeight, int wid...

FILE: picasso/ResourceRequestHandler.java
  class ResourceRequestHandler (line 27) | class ResourceRequestHandler extends RequestHandler {
    method ResourceRequestHandler (line 30) | ResourceRequestHandler(Context context) {
    method canHandleRequest (line 34) | @Override public boolean canHandleRequest(Request data) {
    method load (line 42) | @Override public Result load(Request data) throws IOException {
    method decodeResource (line 48) | private static Bitmap decodeResource(Resources resources, int id, Requ...

FILE: picasso/Stats.java
  class Stats (line 26) | class Stats {
    method Stats (line 51) | Stats(Cache cache) {
    method dispatchBitmapDecoded (line 58) | void dispatchBitmapDecoded(Bitmap bitmap) {
    method dispatchBitmapTransformed (line 62) | void dispatchBitmapTransformed(Bitmap bitmap) {
    method dispatchDownloadFinished (line 66) | void dispatchDownloadFinished(long size) {
    method dispatchCacheHit (line 70) | void dispatchCacheHit() {
    method dispatchCacheMiss (line 74) | void dispatchCacheMiss() {
    method shutdown (line 78) | void shutdown() {
    method performCacheHit (line 82) | void performCacheHit() {
    method performCacheMiss (line 86) | void performCacheMiss() {
    method performDownloadFinished (line 90) | void performDownloadFinished(Long size) {
    method performBitmapDecoded (line 96) | void performBitmapDecoded(long size) {
    method performBitmapTransformed (line 102) | void performBitmapTransformed(long size) {
    method createSnapshot (line 108) | StatsSnapshot createSnapshot() {
    method processBitmap (line 115) | private void processBitmap(Bitmap bitmap, int what) {
    method getAverage (line 121) | private static long getAverage(int count, long totalSize) {
    class StatsHandler (line 125) | private static class StatsHandler extends Handler {
      method StatsHandler (line 129) | public StatsHandler(Looper looper, Stats stats) {
      method handleMessage (line 134) | @Override public void handleMessage(final Message msg) {

FILE: picasso/StatsSnapshot.java
  class StatsSnapshot (line 25) | public class StatsSnapshot {
    method StatsSnapshot (line 42) | public StatsSnapshot(int maxSize, int size, long cacheHits, long cache...
    method dump (line 63) | @SuppressWarnings("UnusedDeclaration") public void dump() {
    method dump (line 70) | public void dump(PrintWriter writer) {
    method toString (line 107) | @Override public String toString() {

FILE: picasso/Target.java
  type Target (line 32) | public interface Target {
    method onBitmapLoaded (line 38) | void onBitmapLoaded(Bitmap bitmap, LoadedFrom from);
    method onBitmapFailed (line 47) | void onBitmapFailed(Drawable errorDrawable);
    method onPrepareLoad (line 56) | void onPrepareLoad(Drawable placeHolderDrawable);

FILE: picasso/TargetAction.java
  class TargetAction (line 21) | final class TargetAction extends Action<Target> {
    method TargetAction (line 23) | TargetAction(Picasso picasso, Target target, Request data, boolean ski...
    method complete (line 28) | @Override void complete(Bitmap result, Picasso.LoadedFrom from) {
    method error (line 42) | @Override void error() {

FILE: picasso/Transformation.java
  type Transformation (line 21) | public interface Transformation {
    method transform (line 27) | Bitmap transform(Bitmap source);
    method key (line 33) | String key();

FILE: picasso/UrlConnectionDownloader.java
  class UrlConnectionDownloader (line 34) | public class UrlConnectionDownloader implements Downloader {
    method UrlConnectionDownloader (line 42) | public UrlConnectionDownloader(Context context) {
    method openConnection (line 46) | protected HttpURLConnection openConnection(Uri path) throws IOException {
    method load (line 53) | @Override public Response load(Uri uri, boolean localCacheOnly) throws...
    method installCacheIfNeeded (line 76) | private static void installCacheIfNeeded(Context context) {
    class ResponseCacheIcs (line 90) | private static class ResponseCacheIcs {
      method install (line 91) | static Object install(Context context) throws IOException {

FILE: picasso/Utils.java
  class Utils (line 48) | final class Utils {
    method Utils (line 97) | private Utils() {
    method getBitmapBytes (line 101) | static int getBitmapBytes(Bitmap bitmap) {
    method checkNotMain (line 114) | static void checkNotMain() {
    method checkMain (line 120) | static void checkMain() {
    method isMain (line 126) | static boolean isMain() {
    method getLogIdsForHunter (line 130) | static String getLogIdsForHunter(BitmapHunter hunter) {
    method getLogIdsForHunter (line 134) | static String getLogIdsForHunter(BitmapHunter hunter, String prefix) {
    method log (line 150) | static void log(String owner, String verb, String logId) {
    method log (line 154) | static void log(String owner, String verb, String logId, String extras) {
    method createKey (line 158) | static String createKey(Request data) {
    method createKey (line 164) | static String createKey(Request data, StringBuilder builder) {
    method closeQuietly (line 203) | static void closeQuietly(InputStream is) {
    method parseResponseSourceHeader (line 212) | static boolean parseResponseSourceHeader(String header) {
    method createDefaultDownloader (line 230) | static Downloader createDefaultDownloader(Context context) {
    method createDefaultCacheDir (line 259) | static File createDefaultCacheDir(Context context) {
    method calculateDiskCacheSize (line 268) | static long calculateDiskCacheSize(File dir) {
    method calculateMemoryCacheSize (line 283) | static int calculateMemoryCacheSize(Context context) {
    method isAirplaneModeOn (line 294) | static boolean isAirplaneModeOn(Context context) {
    method getService (line 299) | @SuppressWarnings("unchecked")
    method hasPermission (line 304) | static boolean hasPermission(Context context, String permission) {
    method toByteArray (line 308) | static byte[] toByteArray(InputStream input) throws IOException {
    method isWebPFile (line 318) | static boolean isWebPFile(InputStream stream) throws IOException {
    method getResourceId (line 329) | static int getResourceId(Resources resources, Request data) throws Fil...
    method getResources (line 358) | static Resources getResources(Context context, Request data) throws Fi...
    class ActivityManagerHoneycomb (line 373) | @TargetApi(HONEYCOMB)
      method getLargeMemoryClass (line 375) | static int getLargeMemoryClass(ActivityManager activityManager) {
    class PicassoThreadFactory (line 380) | static class PicassoThreadFactory implements ThreadFactory {
      method newThread (line 381) | @SuppressWarnings("NullableProblems")
    class PicassoThread (line 387) | private static class PicassoThread extends Thread {
      method PicassoThread (line 388) | public PicassoThread(Runnable r) {
      method run (line 392) | @Override public void run() {
    class BitmapHoneycombMR1 (line 398) | @TargetApi(HONEYCOMB_MR1)
      method getByteCount (line 400) | static int getByteCount(Bitmap bitmap) {
    class OkHttpLoaderCreator (line 405) | private static class OkHttpLoaderCreator {
      method create (line 406) | static Downloader create(Context context) {
Condensed preview — 73 files, each showing path, character count, and a content snippet. Download the .json file or copy for the full structured content (243K chars).
[
  {
    "path": ".gitignore",
    "chars": 81,
    "preview": ".gradle\n/local.properties\n/.idea/workspace.xml\n/.idea/libraries\n.DS_Store\n/build\n"
  },
  {
    "path": ".idea/.name",
    "chars": 20,
    "preview": "WeixinNineGridLayout"
  },
  {
    "path": ".idea/compiler.xml",
    "chars": 711,
    "preview": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<project version=\"4\">\n  <component name=\"CompilerConfiguration\">\n    <option name"
  },
  {
    "path": ".idea/copyright/profiles_settings.xml",
    "chars": 74,
    "preview": "<component name=\"CopyrightManager\">\n  <settings default=\"\" />\n</component>"
  },
  {
    "path": ".idea/encodings.xml",
    "chars": 166,
    "preview": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<project version=\"4\">\n  <component name=\"Encoding\" useUTFGuessing=\"true\" native2A"
  },
  {
    "path": ".idea/gradle.xml",
    "chars": 618,
    "preview": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<project version=\"4\">\n  <component name=\"GradleSettings\">\n    <option name=\"linke"
  },
  {
    "path": ".idea/misc.xml",
    "chars": 4242,
    "preview": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<project version=\"4\">\n  <component name=\"DaemonCodeAnalyzer\">\n    <disable_hints "
  },
  {
    "path": ".idea/modules.xml",
    "chars": 282,
    "preview": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<project version=\"4\">\n  <component name=\"ProjectModuleManager\">\n    <modules>\n   "
  },
  {
    "path": ".idea/scopes/scope_settings.xml",
    "chars": 139,
    "preview": "<component name=\"DependencyValidationManager\">\n  <state>\n    <option name=\"SKIP_IMPORT_STATEMENTS\" value=\"false\" />\n  </"
  },
  {
    "path": ".idea/vcs.xml",
    "chars": 169,
    "preview": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<project version=\"4\">\n  <component name=\"VcsDirectoryMappings\">\n    <mapping dire"
  },
  {
    "path": "README.md",
    "chars": 1617,
    "preview": "NineGridLayout\n--------------\n\n 1. 简介\n\n  这是一个用于实现像微信朋友圈和微博的类似的九宫格图片展示控件,通过自定义viewgroup实现,是用方便,只需要很简单的就可嵌入到项目中去。\n  \n  \n\n\n"
  },
  {
    "path": "WeixinNineGridLayout.iml",
    "chars": 1212,
    "preview": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<module external.linked.project.path=\"$MODULE_DIR$\" external.root.project.path=\"$"
  },
  {
    "path": "app/.gitignore",
    "chars": 7,
    "preview": "/build\n"
  },
  {
    "path": "app/app.iml",
    "chars": 7001,
    "preview": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<module external.linked.project.path=\"$MODULE_DIR$\" external.root.project.path=\"$"
  },
  {
    "path": "app/build.gradle",
    "chars": 633,
    "preview": "apply plugin: 'com.android.application'\n\nandroid {\n    compileSdkVersion 21\n    buildToolsVersion \"21.1.0\"\n\n    defaultC"
  },
  {
    "path": "app/proguard-rules.pro",
    "chars": 652,
    "preview": "# Add project specific ProGuard rules here.\n# By default, the flags in this file are appended to flags specified\n# in D:"
  },
  {
    "path": "app/src/androidTest/java/com/example/administrator/weixinninegridlayout/ApplicationTest.java",
    "chars": 377,
    "preview": "package com.example.administrator.weixinninegridlayout;\n\nimport android.app.Application;\nimport android.test.Application"
  },
  {
    "path": "app/src/main/AndroidManifest.xml",
    "chars": 941,
    "preview": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<manifest xmlns:android=\"http://schemas.android.com/apk/res/android\"\n    package="
  },
  {
    "path": "app/src/main/java/com/weixinninegridlayout/CustomImageView.java",
    "chars": 2210,
    "preview": "package com.weixinninegridlayout;\n\nimport android.content.Context;\nimport android.graphics.Color;\nimport android.graphic"
  },
  {
    "path": "app/src/main/java/com/weixinninegridlayout/Image.java",
    "chars": 849,
    "preview": "package com.weixinninegridlayout;\n\n/**\n * Created by Pan_ on 2015/2/3.\n */\npublic class Image {\n    private String url;\n"
  },
  {
    "path": "app/src/main/java/com/weixinninegridlayout/L.java",
    "chars": 3356,
    "preview": "package com.weixinninegridlayout;\n\nimport android.util.Log;\n\n\npublic final class L {\n\tprivate L() {\n\t}\n\n\n\tpublic static "
  },
  {
    "path": "app/src/main/java/com/weixinninegridlayout/MainActivity.java",
    "chars": 2031,
    "preview": "package com.weixinninegridlayout;\n\nimport android.support.v7.app.ActionBarActivity;\nimport android.os.Bundle;\nimport and"
  },
  {
    "path": "app/src/main/java/com/weixinninegridlayout/MainAdapter.java",
    "chars": 3460,
    "preview": "package com.weixinninegridlayout;\n\nimport android.content.Context;\nimport android.view.LayoutInflater;\nimport android.vi"
  },
  {
    "path": "app/src/main/java/com/weixinninegridlayout/NineGridlayout.java",
    "chars": 4616,
    "preview": "package com.weixinninegridlayout;\n\nimport android.content.Context;\nimport android.graphics.Color;\nimport android.graphic"
  },
  {
    "path": "app/src/main/java/com/weixinninegridlayout/ScreenTools.java",
    "chars": 1785,
    "preview": "\npackage com.weixinninegridlayout;\n\nimport android.content.Context;\n\npublic class ScreenTools {\n\n    private static Scre"
  },
  {
    "path": "app/src/main/res/layout/activity_main.xml",
    "chars": 445,
    "preview": "    <LinearLayout\n        xmlns:android=\"http://schemas.android.com/apk/res/android\"\n        android:layout_width=\"match"
  },
  {
    "path": "app/src/main/res/layout/item_ninegridlayout.xml",
    "chars": 1982,
    "preview": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<FrameLayout\n    android:layout_height=\"fill_parent\"\n    android:layout_width=\"fi"
  },
  {
    "path": "app/src/main/res/menu/menu_main.xml",
    "chars": 361,
    "preview": "<menu xmlns:android=\"http://schemas.android.com/apk/res/android\"\n    xmlns:app=\"http://schemas.android.com/apk/res-auto\""
  },
  {
    "path": "app/src/main/res/values/dimens.xml",
    "chars": 211,
    "preview": "<resources>\n    <!-- Default screen margins, per the Android Design guidelines. -->\n    <dimen name=\"activity_horizontal"
  },
  {
    "path": "app/src/main/res/values/strings.xml",
    "chars": 230,
    "preview": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<resources>\n\n    <string name=\"app_name\">WeixinNineGridLayout</string>\n    <strin"
  },
  {
    "path": "app/src/main/res/values/styles.xml",
    "chars": 194,
    "preview": "<resources>\n\n    <!-- Base application theme. -->\n    <style name=\"AppTheme\" parent=\"Theme.AppCompat.Light.DarkActionBar"
  },
  {
    "path": "app/src/main/res/values-w820dp/dimens.xml",
    "chars": 358,
    "preview": "<resources>\n    <!-- Example customization of dimensions originally defined in res/values/dimens.xml\n         (such as s"
  },
  {
    "path": "build.gradle",
    "chars": 436,
    "preview": "// Top-level build file where you can add configuration options common to all sub-projects/modules.\n\nbuildscript {\n    r"
  },
  {
    "path": "gradle/wrapper/gradle-wrapper.properties",
    "chars": 232,
    "preview": "#Wed Apr 10 15:27:10 PDT 2013\ndistributionBase=GRADLE_USER_HOME\ndistributionPath=wrapper/dists\nzipStoreBase=GRADLE_USER_"
  },
  {
    "path": "gradle.properties",
    "chars": 855,
    "preview": "# Project-wide Gradle settings.\n\n# IDE (e.g. Android Studio) users:\n# Gradle settings configured through the IDE *will o"
  },
  {
    "path": "gradlew",
    "chars": 5080,
    "preview": "#!/usr/bin/env bash\n\n##############################################################################\n##\n##  Gradle start "
  },
  {
    "path": "gradlew.bat",
    "chars": 2314,
    "preview": "@if \"%DEBUG%\" == \"\" @echo off\n@rem ##########################################################################\n@rem\n@rem "
  },
  {
    "path": "picasso/Action.java",
    "chars": 2190,
    "preview": "/*\n * Copyright (C) 2013 Square, Inc.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may n"
  },
  {
    "path": "picasso/AssetRequestHandler.java",
    "chars": 2419,
    "preview": "/*\n * Copyright (C) 2013 Square, Inc.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may n"
  },
  {
    "path": "picasso/BitmapHunter.java",
    "chars": 12687,
    "preview": "/*\n * Copyright (C) 2013 Square, Inc.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may n"
  },
  {
    "path": "picasso/Cache.java",
    "chars": 1897,
    "preview": "/*\n * Copyright (C) 2013 Square, Inc.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may n"
  },
  {
    "path": "picasso/Callback.java",
    "chars": 856,
    "preview": "/*\n * Copyright (C) 2013 Square, Inc.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may n"
  },
  {
    "path": "picasso/ContactsPhotoRequestHandler.java",
    "chars": 4787,
    "preview": "/*\n * Copyright (C) 2013 Square, Inc.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may n"
  },
  {
    "path": "picasso/ContentStreamRequestHandler.java",
    "chars": 2105,
    "preview": "/*\n * Copyright (C) 2013 Square, Inc.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may n"
  },
  {
    "path": "picasso/DeferredRequestCreator.java",
    "chars": 2073,
    "preview": "/*\n * Copyright (C) 2013 Square, Inc.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may n"
  },
  {
    "path": "picasso/Dispatcher.java",
    "chars": 14667,
    "preview": "/*\n * Copyright (C) 2013 Square, Inc.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may n"
  },
  {
    "path": "picasso/Downloader.java",
    "chars": 4726,
    "preview": "/*\n * Copyright (C) 2013 Square, Inc.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may n"
  },
  {
    "path": "picasso/FetchAction.java",
    "chars": 966,
    "preview": "/*\n * Copyright (C) 2013 Square, Inc.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may n"
  },
  {
    "path": "picasso/FileRequestHandler.java",
    "chars": 2052,
    "preview": "/*\n * Copyright (C) 2013 Square, Inc.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may n"
  },
  {
    "path": "picasso/GetAction.java",
    "chars": 962,
    "preview": "/*\n * Copyright (C) 2013 Square, Inc.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may n"
  },
  {
    "path": "picasso/ImageViewAction.java",
    "chars": 2182,
    "preview": "/*\n * Copyright (C) 2013 Square, Inc.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may n"
  },
  {
    "path": "picasso/LruCache.java",
    "chars": 4055,
    "preview": "/*\n * Copyright (C) 2011 The Android Open Source Project\n *\n * Licensed under the Apache License, Version 2.0 (the \"Lice"
  },
  {
    "path": "picasso/MarkableInputStream.java",
    "chars": 4620,
    "preview": "/*\n * Copyright (C) 2013 Square, Inc.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may n"
  },
  {
    "path": "picasso/MediaStoreRequestHandler.java",
    "chars": 4825,
    "preview": "/*\n * Copyright (C) 2014 Square, Inc.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may n"
  },
  {
    "path": "picasso/NetworkRequestHandler.java",
    "chars": 4315,
    "preview": "/*\n * Copyright (C) 2013 Square, Inc.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may n"
  },
  {
    "path": "picasso/Picasso.java",
    "chars": 22799,
    "preview": "/*\n * Copyright (C) 2013 Square, Inc.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may n"
  },
  {
    "path": "picasso/PicassoDrawable.java",
    "chars": 5204,
    "preview": "/*\n * Copyright (C) 2013 Square, Inc.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may n"
  },
  {
    "path": "picasso/PicassoExecutorService.java",
    "chars": 2867,
    "preview": "/*\n * Copyright (C) 2013 Square, Inc.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may n"
  },
  {
    "path": "picasso/RemoteViewsAction.java",
    "chars": 3708,
    "preview": "/*\n * Copyright (C) 2014 Square, Inc.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may n"
  },
  {
    "path": "picasso/Request.java",
    "chars": 11772,
    "preview": "/*\n * Copyright (C) 2013 Square, Inc.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may n"
  },
  {
    "path": "picasso/RequestCreator.java",
    "chars": 19697,
    "preview": "/*\n * Copyright (C) 2013 Square, Inc.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may n"
  },
  {
    "path": "picasso/RequestHandler.java",
    "chars": 5398,
    "preview": "/*\n * Copyright (C) 2014 Square, Inc.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may n"
  },
  {
    "path": "picasso/ResourceRequestHandler.java",
    "chars": 1924,
    "preview": "/*\n * Copyright (C) 2013 Square, Inc.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may n"
  },
  {
    "path": "picasso/Stats.java",
    "chars": 4786,
    "preview": "/*\n * Copyright (C) 2013 Square, Inc.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may n"
  },
  {
    "path": "picasso/StatsSnapshot.java",
    "chars": 5308,
    "preview": "/*\n * Copyright (C) 2013 Square, Inc.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may n"
  },
  {
    "path": "picasso/Target.java",
    "chars": 2350,
    "preview": "/*\n * Copyright (C) 2013 Square, Inc.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may n"
  },
  {
    "path": "picasso/TargetAction.java",
    "chars": 1728,
    "preview": "/*\n * Copyright (C) 2013 Square, Inc.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may n"
  },
  {
    "path": "picasso/Transformation.java",
    "chars": 1230,
    "preview": "/*\n * Copyright (C) 2013 Square, Inc.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may n"
  },
  {
    "path": "picasso/UrlConnectionDownloader.java",
    "chars": 3573,
    "preview": "/*\n * Copyright (C) 2013 Square, Inc.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may n"
  },
  {
    "path": "picasso/Utils.java",
    "chars": 14262,
    "preview": "/*\n * Copyright (C) 2013 Square, Inc.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may n"
  },
  {
    "path": "settings.gradle",
    "chars": 15,
    "preview": "include ':app'\n"
  }
]

// ... and 2 more files (download for full content)

About this extraction

This page contains the full source code of the panyiho/NineGridView GitHub repository, extracted and formatted as plain text for AI agents and large language models (LLMs). The extraction includes 73 files (223.6 KB), approximately 54.5k tokens, and a symbol index with 501 extracted functions, classes, methods, constants, and types. Use this with OpenClaw, Claude, ChatGPT, Cursor, Windsurf, or any other AI tool that accepts text input. You can copy the full output to your clipboard or download it as a .txt file.

Extracted by GitExtract — free GitHub repo to text converter for AI. Built by Nikandr Surkov.

Copied to clipboard!