](https://play.google.com/store/apps/details?id=com.nostra13.universalimageloader.sample) [](http://chart.apis.google.com/chart?chs=300x300&cht=qr&chld=|1&chl=https%3A%2F%2Fplay.google.com%2Fstore%2Fapps%2Fdetails%3Fid%3Dcom.nostra13.universalimageloader.sample) [
](https://github.com/nostra13/Android-Universal-Image-Loader/raw/master/downloads/universal-image-loader-sample-1.9.5.apk)
## [Documentation](https://github.com/nostra13/Android-Universal-Image-Loader/wiki)
* **[Quick Setup](https://github.com/nostra13/Android-Universal-Image-Loader/wiki/Quick-Setup)**
* **[Configuration](https://github.com/nostra13/Android-Universal-Image-Loader/wiki/Configuration)**
* **[Display Options](https://github.com/nostra13/Android-Universal-Image-Loader/wiki/Display-Options)**
* [Useful Info](https://github.com/nostra13/Android-Universal-Image-Loader/wiki/Useful-Info) - Read it before asking a question
* [User Support](https://github.com/nostra13/Android-Universal-Image-Loader/wiki/User-Support) - Read it before creating new issue
* [Sample project](https://github.com/nostra13/Android-Universal-Image-Loader/tree/master/sample) - Learn it to understand the right way of library usage
* [ChangeLog](https://github.com/nostra13/Android-Universal-Image-Loader/blob/master/CHANGELOG.md) - Info about API changes is here
## Usage
### Dependency
```
implementation 'com.nostra13.universalimageloader:universal-image-loader:1.9.5'
```
### Acceptable URIs examples
``` java
"http://site.com/image.png" // from Web
"file:///mnt/sdcard/image.png" // from SD card
"file:///mnt/sdcard/video.mp4" // from SD card (video thumbnail)
"content://media/external/images/media/13" // from content provider
"content://media/external/video/media/13" // from content provider (video thumbnail)
"assets://image.png" // from assets
"drawable://" + R.drawable.img // from drawables (non-9patch images)
```
**NOTE:** Use `drawable://` only if you really need it! Always **consider the native way** to load drawables - `ImageView.setImageResource(...)` instead of using of `ImageLoader`.
### Simple
``` java
ImageLoader imageLoader = ImageLoader.getInstance(); // Get singleton instance
```
``` java
// Load image, decode it to Bitmap and display Bitmap in ImageView (or any other view
// which implements ImageAware interface)
imageLoader.displayImage(imageUri, imageView);
```
``` java
// Load image, decode it to Bitmap and return Bitmap to callback
imageLoader.loadImage(imageUri, new SimpleImageLoadingListener() {
@Override
public void onLoadingComplete(String imageUri, View view, Bitmap loadedImage) {
// Do whatever you want with Bitmap
}
});
```
``` java
// Load image, decode it to Bitmap and return Bitmap synchronously
Bitmap bmp = imageLoader.loadImageSync(imageUri);
```
### Complete
``` java
// Load image, decode it to Bitmap and display Bitmap in ImageView (or any other view
// which implements ImageAware interface)
imageLoader.displayImage(imageUri, imageView, options, new ImageLoadingListener() {
@Override
public void onLoadingStarted(String imageUri, View view) {
...
}
@Override
public void onLoadingFailed(String imageUri, View view, FailReason failReason) {
...
}
@Override
public void onLoadingComplete(String imageUri, View view, Bitmap loadedImage) {
...
}
@Override
public void onLoadingCancelled(String imageUri, View view) {
...
}
}, new ImageLoadingProgressListener() {
@Override
public void onProgressUpdate(String imageUri, View view, int current, int total) {
...
}
});
```
``` java
// Load image, decode it to Bitmap and return Bitmap to callback
ImageSize targetSize = new ImageSize(80, 50); // result Bitmap will be fit to this size
imageLoader.loadImage(imageUri, targetSize, options, new SimpleImageLoadingListener() {
@Override
public void onLoadingComplete(String imageUri, View view, Bitmap loadedImage) {
// Do whatever you want with Bitmap
}
});
```
``` java
// Load image, decode it to Bitmap and return Bitmap synchronously
ImageSize targetSize = new ImageSize(80, 50); // result Bitmap will be fit to this size
Bitmap bmp = imageLoader.loadImageSync(imageUri, targetSize, options);
```
## Load & Display Task Flow

## Applications using Universal Image Loader
**[MediaHouse, UPnP/DLNA Browser](https://play.google.com/store/apps/details?id=com.dbapp.android.mediahouse)** | **[Prezzi Benzina (AndroidFuel)](https://play.google.com/store/apps/details?id=org.vernazza.androidfuel)** | **[ROM Toolbox Lite](https://play.google.com/store/apps/details?id=com.jrummy.liberty.toolbox)**, [Pro](https://play.google.com/store/apps/details?id=com.jrummy.liberty.toolboxpro) | [Stadium Astro](https://play.google.com/store/apps/details?id=com.astro.stadium.activities) | [Chef Astro](https://play.google.com/store/apps/details?id=com.sencha.test) | [Sporee - Live Soccer Scores](https://play.google.com/store/apps/details?id=com.sporee.android) | **[EyeEm - Photo Filter Camera](https://play.google.com/store/apps/details?id=com.baseapp.eyeem)** | **[Topface - meeting is easy](https://play.google.com/store/apps/details?id=com.topface.topface)** | **[reddit is fun](https://play.google.com/store/apps/details?id=com.andrewshu.android.reddit)** | **[Diaro - personal diary](https://play.google.com/store/apps/details?id=com.pixelcrater.Diaro)** | **[Meetup](https://play.google.com/store/apps/details?id=com.meetup)** | [Vingle - Magazines by Fans](https://play.google.com/store/apps/details?id=com.vingle.android) | [Anime Music Radio](https://play.google.com/store/apps/details?id=com.maxxt.animeradio) | [WidgetLocker Theme Viewer](https://play.google.com/store/apps/details?id=com.companionfree.WLThemeViewer) | [ShortBlogger for Tumblr](https://play.google.com/store/apps/details?id=com.luckydroid.tumblelog) | [SnapDish Food Camera](https://play.google.com/store/apps/details?id=com.vuzz.snapdish) | **[Twitch](https://play.google.com/store/apps/details?id=tv.twitch.android.viewer)** | [TVShow Time, TV show guide](https://play.google.com/store/apps/details?id=com.tozelabs.tvshowtime) | [Planning Center Services](https://play.google.com/store/apps/details?id=com.ministrycentered.PlanningCenter) | **[Lapse It](https://play.google.com/store/apps/details?id=com.ui.LapseIt)** | [My Cloud Player for SoundCloud](https://play.google.com/store/apps/details?id=com.mycloudplayers.mycloudplayer) | **[SoundTracking](https://play.google.com/store/apps/details?id=com.schematiclabs.soundtracking)** | [LoopLR Social Video](https://play.google.com/store/apps/details?id=com.looplr) | [Hír24](https://play.google.com/store/apps/details?id=hu.sanomamedia.hir24) | **[Immobilien Scout24](https://play.google.com/store/apps/details?id=de.is24.android)** | **[Lieferheld - Pizza Pasta Sushi](https://play.google.com/store/apps/details?id=de.lieferheld.android)** | [Loocator: free sex datings](https://play.google.com/store/apps/details?id=com.ivicode.loocator) | [벨팡-개편 이벤트,컬러링,벨소리,무료,최신가요,링투유](https://play.google.com/store/apps/details?id=com.mediahubs.www) | [Streambels AirPlay/DLNA Player](https://play.google.com/store/apps/details?id=com.tuxera.streambels) | [Ship Mate - All Cruise Lines](https://play.google.com/store/apps/details?id=shipmate.carnival) | [Disk & Storage Analyzer](https://play.google.com/store/apps/details?id=com.mobile_infographics_tools.mydrive) | [糗事百科](https://play.google.com/store/apps/details?id=qsbk.app) | [Balance BY](https://play.google.com/store/apps/details?id=com.vladyud.balance) | **[Anti Theft Alarm - Security](https://play.google.com/store/apps/details?id=br.com.verde.alarme)** | **[XiiaLive™ - Internet Radio](https://play.google.com/store/apps/details?id=com.android.DroidLiveLite)** | **[Bandsintown Concerts](https://play.google.com/store/apps/details?id=com.bandsintown)** | **[Save As Web Archive](https://play.google.com/store/apps/details?id=jp.fuukiemonster.webmemo)** | [MCPE STORE -Download MCPE file](https://play.google.com/store/apps/details?id=com.newidea.mcpestore) | **[All-In-One Toolbox (29 Tools)](http://aiotoolbox.com/)** | [Zaim](https://play.google.com/store/apps/details?id=net.zaim.android) | **[Calculator Plus Free](https://play.google.com/store/apps/details?id=com.digitalchemy.calculator.freedecimal)** | [Truedialer by Truecaller](https://play.google.com/store/apps/details?id=com.truecaller.phoneapp) | [DoggCatcher Podcast Player](https://play.google.com/store/apps/details?id=com.snoggdoggler.android.applications.doggcatcher.v1_0) | [PingTools Network Utilities](https://play.google.com/store/apps/details?id=ua.com.streamsoft.pingtools) | [The Traveler](https://play.google.com/store/apps/details?id=edu.bsu.android.apps.traveler) | [minube: travel photo album](https://play.google.com/store/apps/details?id=com.minube.app) | [Wear Store for Wear Apps](https://play.google.com/store/apps/details?id=goko.ws2) | [Cast Store for Chromecast Apps](https://play.google.com/store/apps/details?id=goko.gcs) | [WebMoney Keeper](https://play.google.com/store/apps/details?id=com.webmoney.my)
## Donation
You can support the project and thank the author for his hard work :)
* **PayPal** - nostra.uil[at]gmail[dot]com
## Alternative libraries
* [Fresco](https://github.com/facebook/fresco)
* [Glide](https://github.com/bumptech/glide)
* [Picasso](https://github.com/square/picasso)
* [Volley : ImageLoader](https://android.googlesource.com/platform/frameworks/volley/)
================================================
FILE: build.gradle
================================================
// Top-level build file where you can add configuration options common to all sub-projects/modules.
buildscript {
repositories {
google()
jcenter()
}
dependencies {
classpath 'com.android.tools.build:gradle:3.6.2'
}
}
allprojects {
repositories {
google()
jcenter()
}
}
if (JavaVersion.current().isJava8Compatible()) {
allprojects {
tasks.withType(Javadoc) {
options.addStringOption('Xdoclint:none', '-quiet')
}
}
}
================================================
FILE: gradle/maven_push.gradle
================================================
/*
* Copyright 2013 Chris Banes
*
* 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.
*/
apply plugin: 'maven'
apply plugin: 'signing'
def isReleaseBuild() {
return VERSION_NAME.contains("SNAPSHOT") == false
}
def getReleaseRepositoryUrl() {
return hasProperty('RELEASE_REPOSITORY_URL') ? RELEASE_REPOSITORY_URL
: "https://oss.sonatype.org/service/local/staging/deploy/maven2/"
}
def getSnapshotRepositoryUrl() {
return hasProperty('SNAPSHOT_REPOSITORY_URL') ? SNAPSHOT_REPOSITORY_URL
: "https://oss.sonatype.org/content/repositories/snapshots/"
}
def getRepositoryUsername() {
return hasProperty('NEXUS_USERNAME') ? NEXUS_USERNAME : ""
}
def getRepositoryPassword() {
return hasProperty('NEXUS_PASSWORD') ? NEXUS_PASSWORD : ""
}
afterEvaluate { project ->
uploadArchives {
repositories {
mavenDeployer {
beforeDeployment { MavenDeployment deployment -> signing.signPom(deployment) }
pom.groupId = GROUP
pom.artifactId = POM_ARTIFACT_ID
pom.version = VERSION_NAME
repository(url: getReleaseRepositoryUrl()) {
authentication(userName: getRepositoryUsername(), password: getRepositoryPassword())
}
snapshotRepository(url: getSnapshotRepositoryUrl()) {
authentication(userName: getRepositoryUsername(), password: getRepositoryPassword())
}
pom.project {
name POM_NAME
packaging POM_PACKAGING
description POM_DESCRIPTION
url POM_URL
scm {
url POM_SCM_URL
connection POM_SCM_CONNECTION
developerConnection POM_SCM_DEV_CONNECTION
}
licenses {
license {
name POM_LICENCE_NAME
url POM_LICENCE_URL
distribution POM_LICENCE_DIST
}
}
developers {
developer {
id POM_DEVELOPER_ID
name POM_DEVELOPER_NAME
}
}
}
}
}
}
task installArchives(type: Upload) {
description "Installs the artifacts to the local Maven repository."
configuration = configurations['archives']
repositories {
mavenDeployer {
pom.groupId = GROUP
pom.artifactId = POM_ARTIFACT_ID
pom.version = VERSION_NAME
repository url: "file://${System.properties['user.home']}/.m2/repository"
}
}
}
signing {
required { isReleaseBuild() && gradle.taskGraph.hasTask("uploadArchives") }
sign configurations.archives
}
task androidJavadocs(type: Javadoc) {
source = android.sourceSets.main.java.srcDirs
options {
encoding = "UTF-8"
}
classpath += project.files(android.getBootClasspath().join(File.pathSeparator))
}
task androidJavadocsJar(type: Jar, dependsOn: androidJavadocs) {
classifier = 'javadoc'
from androidJavadocs.destinationDir
}
task androidSourcesJar(type: Jar) {
classifier = 'sources'
from android.sourceSets.main.java.srcDirs
}
artifacts {
archives androidSourcesJar
archives androidJavadocsJar
}
}
================================================
FILE: gradle/wrapper/gradle-wrapper.properties
================================================
#Tue Apr 23 15:38:19 MSK 2019
distributionBase=GRADLE_USER_HOME
distributionPath=wrapper/dists
zipStoreBase=GRADLE_USER_HOME
zipStorePath=wrapper/dists
distributionUrl=https\://services.gradle.org/distributions/gradle-5.6.4-all.zip
================================================
FILE: gradle.properties
================================================
org.gradle.daemon=true
VERSION_NAME=1.9.5
VERSION_COE=40
GROUP=com.nostra13.universalimageloader
POM_DESCRIPTION=Powerful and flexible instrument for asynchronous image loading, caching and displaying on Android
POM_URL=https://github.com/nostra13/Android-Universal-Image-Loader
POM_SCM_URL=https://github.com/nostra13/Android-Universal-Image-Loader
POM_SCM_CONNECTION=scm:git@github.com:nostra13/Android-Universal-Image-Loader.git
POM_SCM_DEV_CONNECTION=scm:git@github.com:nostra13/Android-Universal-Image-Loader.git
POM_LICENCE_NAME=The Apache Software License, Version 2.0
POM_LICENCE_URL=http://www.apache.org/licenses/LICENSE-2.0.txt
POM_LICENCE_DIST=repo
POM_DEVELOPER_ID=nostra13
POM_DEVELOPER_NAME=Sergey Tarasevich
================================================
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: library/build.gradle
================================================
apply plugin: 'com.android.library'
android {
compileSdkVersion 28
buildToolsVersion "28.0.3"
defaultConfig {
minSdkVersion 16
targetSdkVersion 28
versionCode 40
versionName "1.9.5"
}
compileOptions {
sourceCompatibility JavaVersion.VERSION_1_8
targetCompatibility JavaVersion.VERSION_1_8
}
}
dependencies {
testImplementation 'junit:junit:4.12'
testImplementation 'org.robolectric:robolectric:3.0-rc3'
testImplementation 'com.squareup.assertj:assertj-android:1.0.0'
}
// build a jar with source files
task sourcesJar(type: Jar) {
from android.sourceSets.main.java.srcDirs
classifier = 'sources'
}
artifacts {
archives sourcesJar
}
// Build a jar file in addition to the default aar file
android.libraryVariants.all { variant ->
def name = variant.buildType.name
def task = project.tasks.create "jar${name.capitalize()}", Jar
task.dependsOn variant.javaCompile
task.from variant.javaCompile.destinationDir
artifacts.add('archives', task);
}
apply from: '../gradle/maven_push.gradle'
================================================
FILE: library/gradle.properties
================================================
POM_NAME=Universal Image Loader Library
POM_ARTIFACT_ID=universal-image-loader
POM_PACKAGING=jar
================================================
FILE: library/project.properties
================================================
# This file is automatically generated by Android Tools.
# Do not modify this file -- YOUR CHANGES WILL BE ERASED!
#
# This file must be checked in Version Control Systems.
#
# To customize properties used by the Ant build system use,
# "ant.properties", and override values to adapt the script to your
# project structure.
# Project target.
target=android-21
android.library=true
================================================
FILE: library/src/main/AndroidManifest.xml
================================================
The cache stores its data in a directory on the filesystem. This * directory must be exclusive to the cache; the cache may delete or overwrite * files from its directory. It is an error for multiple processes to use the * same cache directory at the same time. * *
This cache limits the number of bytes that it will store on the * filesystem. When the number of stored bytes exceeds the limit, the cache will * remove entries in the background until the limit is satisfied. The limit is * not strict: the cache may temporarily exceed it while waiting for files to be * deleted. The limit does not include filesystem overhead or the cache * journal so space-sensitive applications should set a conservative limit. * *
Clients call {@link #edit} to create or update the values of an entry. An * entry may have only one editor at one time; if a value is not available to be * edited then {@link #edit} will return null. *
Clients call {@link #get} to read a snapshot of an entry. The read will * observe the value at the time that {@link #get} was called. Updates and * removals after the call do not impact ongoing reads. * *
This class is tolerant of some I/O errors. If files are missing from the
* filesystem, the corresponding entries will be dropped from the cache. If
* an error occurs while writing a cache value, the edit will fail silently.
* Callers should handle other problems by catching {@code IOException} and
* responding appropriately.
*/
final class DiskLruCache implements Closeable {
static final String JOURNAL_FILE = "journal";
static final String JOURNAL_FILE_TEMP = "journal.tmp";
static final String JOURNAL_FILE_BACKUP = "journal.bkp";
static final String MAGIC = "libcore.io.DiskLruCache";
static final String VERSION_1 = "1";
static final long ANY_SEQUENCE_NUMBER = -1;
static final Pattern LEGAL_KEY_PATTERN = Pattern.compile("[a-z0-9_-]{1,64}");
private static final String CLEAN = "CLEAN";
private static final String DIRTY = "DIRTY";
private static final String REMOVE = "REMOVE";
private static final String READ = "READ";
/*
* This cache uses a journal file named "journal". A typical journal file
* looks like this:
* libcore.io.DiskLruCache
* 1
* 100
* 2
*
* CLEAN 3400330d1dfc7f3f7f4b8d4d803dfcf6 832 21054
* DIRTY 335c4c6028171cfddfbaae1a9c313c52
* CLEAN 335c4c6028171cfddfbaae1a9c313c52 3934 2342
* REMOVE 335c4c6028171cfddfbaae1a9c313c52
* DIRTY 1ab96a171faeeee38496d8b330771a7a
* CLEAN 1ab96a171faeeee38496d8b330771a7a 1600 234
* READ 335c4c6028171cfddfbaae1a9c313c52
* READ 3400330d1dfc7f3f7f4b8d4d803dfcf6
*
* The first five lines of the journal form its header. They are the
* constant string "libcore.io.DiskLruCache", the disk cache's version,
* the application's version, the value count, and a blank line.
*
* Each of the subsequent lines in the file is a record of the state of a
* cache entry. Each line contains space-separated values: a state, a key,
* and optional state-specific values.
* o DIRTY lines track that an entry is actively being created or updated.
* Every successful DIRTY action should be followed by a CLEAN or REMOVE
* action. DIRTY lines without a matching CLEAN or REMOVE indicate that
* temporary files may need to be deleted.
* o CLEAN lines track a cache entry that has been successfully published
* and may be read. A publish line is followed by the lengths of each of
* its values.
* o READ lines track accesses for LRU.
* o REMOVE lines track entries that have been deleted.
*
* The journal file is appended to as cache operations occur. The journal may
* occasionally be compacted by dropping redundant lines. A temporary file named
* "journal.tmp" will be used during compaction; that file should be deleted if
* it exists when the cache is opened.
*/
private final File directory;
private final File journalFile;
private final File journalFileTmp;
private final File journalFileBackup;
private final int appVersion;
private long maxSize;
private int maxFileCount;
private final int valueCount;
private long size = 0;
private int fileCount = 0;
private Writer journalWriter;
private final LinkedHashMap This class is used for buffered reading of lines. For purposes of this class, a line ends
* with "\n" or "\r\n". End of input is reported by throwing {@code EOFException}. Unterminated
* line at end of input is invalid and will be ignored, the caller may use {@code
* hasUnterminatedLine()} to detect it after catching the {@code EOFException}.
*
* This class is intended for reading input that strictly consists of lines, such as line-based
* cache entries or cache journal. Unlike the {@link java.io.BufferedReader} which in conjunction
* with {@link java.io.InputStreamReader} provides similar functionality, this class uses different
* end-of-input reporting and a more restrictive definition of a line.
*
* This class supports only charsets that encode '\r' and '\n' as a single byte with value 13
* and 10, respectively, and the representation of no other character contains these values.
* We currently check in constructor that the charset is one of US-ASCII, UTF-8 and ISO-8859-1.
* The default charset is US_ASCII.
*/
class StrictLineReader implements Closeable {
private static final byte CR = (byte) '\r';
private static final byte LF = (byte) '\n';
private final InputStream in;
private final Charset charset;
/*
* Buffered data is stored in {@code buf}. As long as no exception occurs, 0 <= pos <= end
* and the data in the range [pos, end) is buffered for reading. At end of input, if there is
* an unterminated line, we set end == -1, otherwise end == pos. If the underlying
* {@code InputStream} throws an {@code IOException}, end may remain as either pos or -1.
*/
private byte[] buf;
private int pos;
private int end;
/**
* Constructs a new {@code LineReader} with the specified charset and the default capacity.
*
* @param in the {@code InputStream} to read data from.
* @param charset the charset used to decode data. Only US-ASCII, UTF-8 and ISO-8859-1 are
* supported.
* @throws NullPointerException if {@code in} or {@code charset} is null.
* @throws IllegalArgumentException if the specified charset is not supported.
*/
public StrictLineReader(InputStream in, Charset charset) {
this(in, 8192, charset);
}
/**
* Constructs a new {@code LineReader} with the specified capacity and charset.
*
* @param in the {@code InputStream} to read data from.
* @param capacity the capacity of the buffer.
* @param charset the charset used to decode data. Only US-ASCII, UTF-8 and ISO-8859-1 are
* supported.
* @throws NullPointerException if {@code in} or {@code charset} is null.
* @throws IllegalArgumentException if {@code capacity} is negative or zero
* or the specified charset is not supported.
*/
public StrictLineReader(InputStream in, int capacity, Charset charset) {
if (in == null || charset == null) {
throw new NullPointerException();
}
if (capacity < 0) {
throw new IllegalArgumentException("capacity <= 0");
}
if (!(charset.equals(Util.US_ASCII))) {
throw new IllegalArgumentException("Unsupported encoding");
}
this.in = in;
this.charset = charset;
buf = new byte[capacity];
}
/**
* Closes the reader by closing the underlying {@code InputStream} and
* marking this reader as closed.
*
* @throws IOException for errors when closing the underlying {@code InputStream}.
*/
public void close() throws IOException {
synchronized (in) {
if (buf != null) {
buf = null;
in.close();
}
}
}
/**
* Reads the next line. A line ends with {@code "\n"} or {@code "\r\n"},
* this end of line marker is not included in the result.
*
* @return the next line from the input.
* @throws IOException for underlying {@code InputStream} errors.
* @throws EOFException for the end of source stream.
*/
public String readLine() throws IOException {
synchronized (in) {
if (buf == null) {
throw new IOException("LineReader is closed");
}
// Read more data if we are at the end of the buffered data.
// Though it's an error to read after an exception, we will let {@code fillBuf()}
// throw again if that happens; thus we need to handle end == -1 as well as end == pos.
if (pos >= end) {
fillBuf();
}
// Try to find LF in the buffered data and return the line if successful.
for (int i = pos; i != end; ++i) {
if (buf[i] == LF) {
int lineEnd = (i != pos && buf[i - 1] == CR) ? i - 1 : i;
String res = new String(buf, pos, lineEnd - pos, charset.name());
pos = i + 1;
return res;
}
}
// Let's anticipate up to 80 characters on top of those already read.
ByteArrayOutputStream out = new ByteArrayOutputStream(end - pos + 80) {
@Override
public String toString() {
int length = (count > 0 && buf[count - 1] == CR) ? count - 1 : count;
try {
return new String(buf, 0, length, charset.name());
} catch (UnsupportedEncodingException e) {
throw new AssertionError(e); // Since we control the charset this will never happen.
}
}
};
while (true) {
out.write(buf, pos, end - pos);
// Mark unterminated line in case fillBuf throws EOFException or IOException.
end = -1;
fillBuf();
// Try to find LF in the buffered data and return the line if successful.
for (int i = pos; i != end; ++i) {
if (buf[i] == LF) {
if (i != pos) {
out.write(buf, pos, i - pos);
}
pos = i + 1;
return out.toString();
}
}
}
}
}
/**
* Reads new input data into the buffer. Call only with pos == end or end == -1,
* depending on the desired outcome if the function throws.
*/
private void fillBuf() throws IOException {
int result = in.read(buf, 0, buf.length);
if (result == -1) {
throw new EOFException();
}
pos = 0;
end = result;
}
}
================================================
FILE: library/src/main/java/com/nostra13/universalimageloader/cache/disc/impl/ext/Util.java
================================================
/*
* Copyright (C) 2010 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.nostra13.universalimageloader.cache.disc.impl.ext;
import java.io.Closeable;
import java.io.File;
import java.io.IOException;
import java.io.Reader;
import java.io.StringWriter;
import java.nio.charset.Charset;
/** Junk drawer of utility methods. */
final class Util {
static final Charset US_ASCII = Charset.forName("US-ASCII");
static final Charset UTF_8 = Charset.forName("UTF-8");
private Util() {
}
static String readFully(Reader reader) throws IOException {
try {
StringWriter writer = new StringWriter();
char[] buffer = new char[1024];
int count;
while ((count = reader.read(buffer)) != -1) {
writer.write(buffer, 0, count);
}
return writer.toString();
} finally {
reader.close();
}
}
/**
* Deletes the contents of {@code dir}. Throws an IOException if any file
* could not be deleted, or if {@code dir} is not a readable directory.
*/
static void deleteContents(File dir) throws IOException {
File[] files = dir.listFiles();
if (files == null) {
throw new IOException("not a readable directory: " + dir);
}
for (File file : files) {
if (file.isDirectory()) {
deleteContents(file);
}
if (!file.delete()) {
throw new IOException("failed to delete file: " + file);
}
}
}
static void closeQuietly(/*Auto*/Closeable closeable) {
if (closeable != null) {
try {
closeable.close();
} catch (RuntimeException rethrown) {
throw rethrown;
} catch (Exception ignored) {
}
}
}
}
================================================
FILE: library/src/main/java/com/nostra13/universalimageloader/cache/disc/naming/FileNameGenerator.java
================================================
/*******************************************************************************
* Copyright 2011-2013 Sergey Tarasevich
*
* 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.nostra13.universalimageloader.cache.disc.naming;
/**
* Generates names for files at disk cache
*
* @author Sergey Tarasevich (nostra13[at]gmail[dot]com)
* @since 1.3.1
*/
public interface FileNameGenerator {
/** Generates unique file name for image defined by URI */
String generate(String imageUri);
}
================================================
FILE: library/src/main/java/com/nostra13/universalimageloader/cache/disc/naming/HashCodeFileNameGenerator.java
================================================
/*******************************************************************************
* Copyright 2011-2013 Sergey Tarasevich
*
* 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.nostra13.universalimageloader.cache.disc.naming;
/**
* Names image file as image URI {@linkplain String#hashCode() hashcode}
*
* @author Sergey Tarasevich (nostra13[at]gmail[dot]com)
* @since 1.3.1
*/
public class HashCodeFileNameGenerator implements FileNameGenerator {
@Override
public String generate(String imageUri) {
return String.valueOf(imageUri.hashCode());
}
}
================================================
FILE: library/src/main/java/com/nostra13/universalimageloader/cache/disc/naming/Md5FileNameGenerator.java
================================================
/*******************************************************************************
* Copyright 2011-2013 Sergey Tarasevich
*
* 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.nostra13.universalimageloader.cache.disc.naming;
import com.nostra13.universalimageloader.utils.L;
import java.math.BigInteger;
import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;
/**
* Names image file as MD5 hash of image URI
*
* @author Sergey Tarasevich (nostra13[at]gmail[dot]com)
* @since 1.4.0
*/
public class Md5FileNameGenerator implements FileNameGenerator {
private static final String HASH_ALGORITHM = "MD5";
private static final int RADIX = 10 + 26; // 10 digits + 26 letters
@Override
public String generate(String imageUri) {
byte[] md5 = getMD5(imageUri.getBytes());
BigInteger bi = new BigInteger(md5).abs();
return bi.toString(RADIX);
}
private byte[] getMD5(byte[] data) {
byte[] hash = null;
try {
MessageDigest digest = MessageDigest.getInstance(HASH_ALGORITHM);
digest.update(data);
hash = digest.digest();
} catch (NoSuchAlgorithmException e) {
L.e(e);
}
return hash;
}
}
================================================
FILE: library/src/main/java/com/nostra13/universalimageloader/cache/memory/BaseMemoryCache.java
================================================
/*******************************************************************************
* Copyright 2011-2014 Sergey Tarasevich
*
* 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.nostra13.universalimageloader.cache.memory;
import android.graphics.Bitmap;
import java.lang.ref.Reference;
import java.util.*;
/**
* Base memory cache. Implements common functionality for memory cache. Provides object references (
* {@linkplain Reference not strong}) storing.
*
* @author Sergey Tarasevich (nostra13[at]gmail[dot]com)
* @since 1.0.0
*/
public abstract class BaseMemoryCache implements MemoryCache {
/** Stores not strong references to objects */
private final Map BlockingDeque methods come in four forms, with different ways
* of handling operations that cannot be satisfied immediately, but may be
* satisfied at some point in the future:
* one throws an exception, the second returns a special value (either
* null or false, depending on the operation), the third
* blocks the current thread indefinitely until the operation can succeed,
* and the fourth blocks for only a given maximum time limit before giving
* up. These methods are summarized in the following table:
* Like any {@link BlockingQueue}, a BlockingDeque is thread safe,
* does not permit null elements, and may (or may not) be
* capacity-constrained.
* A BlockingDeque implementation may be used directly as a FIFO
* BlockingQueue. The methods inherited from the
* BlockingQueue interface are precisely equivalent to
* BlockingDeque methods as indicated in the following table:
* Memory consistency effects: As with other concurrent
* collections, actions in a thread prior to placing an object into a
* {@code BlockingDeque}
* happen-before
* actions subsequent to the access or removal of that element from
* the {@code BlockingDeque} in another thread.
* This interface is a member of the
*
* Java Collections Framework.
*
* @param This method is equivalent to {@link #addLast addLast}.
*
* @param e the element to add
* @throws IllegalStateException {@inheritDoc}
* @throws ClassCastException if the class of the specified element
* prevents it from being added to this deque
* @throws NullPointerException if the specified element is null
* @throws IllegalArgumentException if some property of the specified
* element prevents it from being added to this deque
*/
boolean add(E e);
/**
* Inserts the specified element into the queue represented by this deque
* (in other words, at the tail of this deque) if it is possible to do so
* immediately without violating capacity restrictions, returning
* true upon success and false if no space is currently
* available. When using a capacity-restricted deque, this method is
* generally preferable to the {@link #add} method, which can fail to
* insert an element only by throwing an exception.
* This method is equivalent to {@link #offerLast offerLast}.
*
* @param e the element to add
* @throws ClassCastException if the class of the specified element
* prevents it from being added to this deque
* @throws NullPointerException if the specified element is null
* @throws IllegalArgumentException if some property of the specified
* element prevents it from being added to this deque
*/
boolean offer(E e);
/**
* Inserts the specified element into the queue represented by this deque
* (in other words, at the tail of this deque), waiting if necessary for
* space to become available.
* This method is equivalent to {@link #putLast putLast}.
*
* @param e the element to add
* @throws InterruptedException {@inheritDoc}
* @throws ClassCastException if the class of the specified element
* prevents it from being added to this deque
* @throws NullPointerException if the specified element is null
* @throws IllegalArgumentException if some property of the specified
* element prevents it from being added to this deque
*/
void put(E e) throws InterruptedException;
/**
* Inserts the specified element into the queue represented by this deque
* (in other words, at the tail of this deque), waiting up to the
* specified wait time if necessary for space to become available.
* This method is equivalent to
* {@link #offerLast offerLast}.
*
* @param e the element to add
* @return true if the element was added to this deque, else
* false
* @throws InterruptedException {@inheritDoc}
* @throws ClassCastException if the class of the specified element
* prevents it from being added to this deque
* @throws NullPointerException if the specified element is null
* @throws IllegalArgumentException if some property of the specified
* element prevents it from being added to this deque
*/
boolean offer(E e, long timeout, TimeUnit unit)
throws InterruptedException;
/**
* Retrieves and removes the head of the queue represented by this deque
* (in other words, the first element of this deque).
* This method differs from {@link #poll poll} only in that it
* throws an exception if this deque is empty.
* This method is equivalent to {@link #removeFirst() removeFirst}.
*
* @return the head of the queue represented by this deque
* @throws NoSuchElementException if this deque is empty
*/
E remove();
/**
* Retrieves and removes the head of the queue represented by this deque
* (in other words, the first element of this deque), or returns
* null if this deque is empty.
* This method is equivalent to {@link #pollFirst()}.
*
* @return the head of this deque, or null if this deque is empty
*/
E poll();
/**
* Retrieves and removes the head of the queue represented by this deque
* (in other words, the first element of this deque), waiting if
* necessary until an element becomes available.
* This method is equivalent to {@link #takeFirst() takeFirst}.
*
* @return the head of this deque
* @throws InterruptedException if interrupted while waiting
*/
E take() throws InterruptedException;
/**
* Retrieves and removes the head of the queue represented by this deque
* (in other words, the first element of this deque), waiting up to the
* specified wait time if necessary for an element to become available.
* This method is equivalent to
* {@link #pollFirst(long, TimeUnit) pollFirst}.
*
* @return the head of this deque, or null if the
* specified waiting time elapses before an element is available
* @throws InterruptedException if interrupted while waiting
*/
E poll(long timeout, TimeUnit unit)
throws InterruptedException;
/**
* Retrieves, but does not remove, the head of the queue represented by
* this deque (in other words, the first element of this deque).
* This method differs from {@link #peek peek} only in that it throws an
* exception if this deque is empty.
* This method is equivalent to {@link #getFirst() getFirst}.
*
* @return the head of this deque
* @throws NoSuchElementException if this deque is empty
*/
E element();
/**
* Retrieves, but does not remove, the head of the queue represented by
* this deque (in other words, the first element of this deque), or
* returns null if this deque is empty.
* This method is equivalent to {@link #peekFirst() peekFirst}.
*
* @return the head of this deque, or null if this deque is empty
*/
E peek();
/**
* Removes the first occurrence of the specified element from this deque.
* If the deque does not contain the element, it is unchanged.
* More formally, removes the first element e such that
* o.equals(e) (if such an element exists).
* Returns true if this deque contained the specified element
* (or equivalently, if this deque changed as a result of the call).
* This method is equivalent to
* {@link #removeFirstOccurrence removeFirstOccurrence}.
*
* @param o element to be removed from this deque, if present
* @return true if this deque changed as a result of the call
* @throws ClassCastException if the class of the specified element
* is incompatible with this deque (optional)
* @throws NullPointerException if the specified element is null (optional)
*/
boolean remove(Object o);
/**
* Returns true if this deque contains the specified element.
* More formally, returns true if and only if this deque contains
* at least one element e such that o.equals(e).
*
* @param o object to be checked for containment in this deque
* @return true if this deque contains the specified element
* @throws ClassCastException if the class of the specified element
* is incompatible with this deque (optional)
* @throws NullPointerException if the specified element is null (optional)
*/
public boolean contains(Object o);
/**
* Returns the number of elements in this deque.
*
* @return the number of elements in this deque
*/
public int size();
/**
* Returns an iterator over the elements in this deque in proper sequence.
* The elements will be returned in order from first (head) to last (tail).
*
* @return an iterator over the elements in this deque in proper sequence
*/
Iterator This method is equivalent to {@link #addFirst addFirst}.
*
* @throws IllegalStateException {@inheritDoc}
* @throws ClassCastException {@inheritDoc}
* @throws NullPointerException if the specified element is null
* @throws IllegalArgumentException {@inheritDoc}
*/
void push(E e);
}
================================================
FILE: library/src/main/java/com/nostra13/universalimageloader/core/assist/deque/Deque.java
================================================
/*
* Written by Doug Lea and Josh Bloch with assistance from members of
* JCP JSR-166 Expert Group and released to the public domain, as explained
* at http://creativecommons.org/licenses/publicdomain
*/
package com.nostra13.universalimageloader.core.assist.deque;
import java.util.Collection;
import java.util.Iterator;
import java.util.List;
import java.util.NoSuchElementException;
import java.util.Queue;
import java.util.Stack;
/**
* A linear collection that supports element insertion and removal at
* both ends. The name deque is short for "double ended queue"
* and is usually pronounced "deck". Most Deque
* implementations place no fixed limits on the number of elements
* they may contain, but this interface supports capacity-restricted
* deques as well as those with no fixed size limit.
* This interface defines methods to access the elements at both
* ends of the deque. Methods are provided to insert, remove, and
* examine the element. Each of these methods exists in two forms:
* one throws an exception if the operation fails, the other returns a
* special value (either null or false, depending on
* the operation). The latter form of the insert operation is
* designed specifically for use with capacity-restricted
* Deque implementations; in most implementations, insert
* operations cannot fail.
* The twelve methods described above are summarized in the
* following table:
* This interface extends the {@link Queue} interface. When a deque is
* used as a queue, FIFO (First-In-First-Out) behavior results. Elements are
* added at the end of the deque and removed from the beginning. The methods
* inherited from the Queue interface are precisely equivalent to
* Deque methods as indicated in the following table:
* Deques can also be used as LIFO (Last-In-First-Out) stacks. This
* interface should be used in preference to the legacy {@link Stack} class.
* When a deque is used as a stack, elements are pushed and popped from the
* beginning of the deque. Stack methods are precisely equivalent to
* Deque methods as indicated in the table below:
* Note that the {@link #peek peek} method works equally well when
* a deque is used as a queue or a stack; in either case, elements are
* drawn from the beginning of the deque.
* This interface provides two methods to remove interior
* elements, {@link #removeFirstOccurrence removeFirstOccurrence} and
* {@link #removeLastOccurrence removeLastOccurrence}.
* Unlike the {@link List} interface, this interface does not
* provide support for indexed access to elements.
* While Deque implementations are not strictly required
* to prohibit the insertion of null elements, they are strongly
* encouraged to do so. Users of any Deque implementations
* that do allow null elements are strongly encouraged not to
* take advantage of the ability to insert nulls. This is so because
* null is used as a special return value by various methods
* to indicated that the deque is empty.
* Deque implementations generally do not define
* element-based versions of the equals and hashCode
* methods, but instead inherit the identity-based versions from class
* Object.
*
* @param This method is equivalent to {@link #add}.
*
* @param e the element to add
* @throws IllegalStateException if the element cannot be added at this
* time due to capacity restrictions
* @throws ClassCastException if the class of the specified element
* prevents it from being added to this deque
* @throws NullPointerException if the specified element is null and this
* deque does not permit null elements
* @throws IllegalArgumentException if some property of the specified
* element prevents it from being added to this deque
*/
void addLast(E e);
/**
* Inserts the specified element at the front of this deque unless it would
* violate capacity restrictions. When using a capacity-restricted deque,
* this method is generally preferable to the {@link #addFirst} method,
* which can fail to insert an element only by throwing an exception.
*
* @param e the element to add
* @return true if the element was added to this deque, else
* false
* @throws ClassCastException if the class of the specified element
* prevents it from being added to this deque
* @throws NullPointerException if the specified element is null and this
* deque does not permit null elements
* @throws IllegalArgumentException if some property of the specified
* element prevents it from being added to this deque
*/
boolean offerFirst(E e);
/**
* Inserts the specified element at the end of this deque unless it would
* violate capacity restrictions. When using a capacity-restricted deque,
* this method is generally preferable to the {@link #addLast} method,
* which can fail to insert an element only by throwing an exception.
*
* @param e the element to add
* @return true if the element was added to this deque, else
* false
* @throws ClassCastException if the class of the specified element
* prevents it from being added to this deque
* @throws NullPointerException if the specified element is null and this
* deque does not permit null elements
* @throws IllegalArgumentException if some property of the specified
* element prevents it from being added to this deque
*/
boolean offerLast(E e);
/**
* Retrieves and removes the first element of this deque. This method
* differs from {@link #pollFirst pollFirst} only in that it throws an
* exception if this deque is empty.
*
* @return the head of this deque
* @throws NoSuchElementException if this deque is empty
*/
E removeFirst();
/**
* Retrieves and removes the last element of this deque. This method
* differs from {@link #pollLast pollLast} only in that it throws an
* exception if this deque is empty.
*
* @return the tail of this deque
* @throws NoSuchElementException if this deque is empty
*/
E removeLast();
/**
* Retrieves and removes the first element of this deque,
* or returns null if this deque is empty.
*
* @return the head of this deque, or null if this deque is empty
*/
E pollFirst();
/**
* Retrieves and removes the last element of this deque,
* or returns null if this deque is empty.
*
* @return the tail of this deque, or null if this deque is empty
*/
E pollLast();
/**
* Retrieves, but does not remove, the first element of this deque.
* This method is equivalent to {@link #addLast}.
*
* @param e the element to add
* @return true (as specified by {@link Collection#add})
* @throws IllegalStateException if the element cannot be added at this
* time due to capacity restrictions
* @throws ClassCastException if the class of the specified element
* prevents it from being added to this deque
* @throws NullPointerException if the specified element is null and this
* deque does not permit null elements
* @throws IllegalArgumentException if some property of the specified
* element prevents it from being added to this deque
*/
boolean add(E e);
/**
* Inserts the specified element into the queue represented by this deque
* (in other words, at the tail of this deque) if it is possible to do so
* immediately without violating capacity restrictions, returning
* true upon success and false if no space is currently
* available. When using a capacity-restricted deque, this method is
* generally preferable to the {@link #add} method, which can fail to
* insert an element only by throwing an exception.
* This method is equivalent to {@link #offerLast}.
*
* @param e the element to add
* @return true if the element was added to this deque, else
* false
* @throws ClassCastException if the class of the specified element
* prevents it from being added to this deque
* @throws NullPointerException if the specified element is null and this
* deque does not permit null elements
* @throws IllegalArgumentException if some property of the specified
* element prevents it from being added to this deque
*/
boolean offer(E e);
/**
* Retrieves and removes the head of the queue represented by this deque
* (in other words, the first element of this deque).
* This method differs from {@link #poll poll} only in that it throws an
* exception if this deque is empty.
* This method is equivalent to {@link #removeFirst()}.
*
* @return the head of the queue represented by this deque
* @throws NoSuchElementException if this deque is empty
*/
E remove();
/**
* Retrieves and removes the head of the queue represented by this deque
* (in other words, the first element of this deque), or returns
* null if this deque is empty.
* This method is equivalent to {@link #pollFirst()}.
*
* @return the first element of this deque, or null if
* this deque is empty
*/
E poll();
/**
* Retrieves, but does not remove, the head of the queue represented by
* this deque (in other words, the first element of this deque).
* This method differs from {@link #peek peek} only in that it throws an
* exception if this deque is empty.
* This method is equivalent to {@link #getFirst()}.
*
* @return the head of the queue represented by this deque
* @throws NoSuchElementException if this deque is empty
*/
E element();
/**
* Retrieves, but does not remove, the head of the queue represented by
* this deque (in other words, the first element of this deque), or
* returns null if this deque is empty.
* This method is equivalent to {@link #peekFirst()}.
*
* @return the head of the queue represented by this deque, or
* null if this deque is empty
*/
E peek();
// *** Stack methods ***
/**
* Pushes an element onto the stack represented by this deque (in other
* words, at the head of this deque) if it is possible to do so
* immediately without violating capacity restrictions, returning
* true upon success and throwing an
* IllegalStateException if no space is currently available.
* This method is equivalent to {@link #addFirst}.
*
* @param e the element to push
* @throws IllegalStateException if the element cannot be added at this
* time due to capacity restrictions
* @throws ClassCastException if the class of the specified element
* prevents it from being added to this deque
* @throws NullPointerException if the specified element is null and this
* deque does not permit null elements
* @throws IllegalArgumentException if some property of the specified
* element prevents it from being added to this deque
*/
void push(E e);
/**
* Pops an element from the stack represented by this deque. In other
* words, removes and returns the first element of this deque.
* This method is equivalent to {@link #removeFirst()}.
*
* @return the element at the front of this deque (which is the top
* of the stack represented by this deque)
* @throws NoSuchElementException if this deque is empty
*/
E pop();
// *** Collection methods ***
/**
* Removes the first occurrence of the specified element from this deque.
* If the deque does not contain the element, it is unchanged.
* More formally, removes the first element e such that
* (o==null ? e==null : o.equals(e))
* (if such an element exists).
* Returns true if this deque contained the specified element
* (or equivalently, if this deque changed as a result of the call).
* This method is equivalent to {@link #removeFirstOccurrence}.
*
* @param o element to be removed from this deque, if present
* @return true if an element was removed as a result of this call
* @throws ClassCastException if the class of the specified element
* is incompatible with this deque (optional)
* @throws NullPointerException if the specified element is null and this
* deque does not permit null elements (optional)
*/
boolean remove(Object o);
/**
* Returns true if this deque contains the specified element.
* More formally, returns true if and only if this deque contains
* at least one element e such that
* (o==null ? e==null : o.equals(e)).
*
* @param o element whose presence in this deque is to be tested
* @return true if this deque contains the specified element
* @throws ClassCastException if the type of the specified element
* is incompatible with this deque (optional)
* @throws NullPointerException if the specified element is null and this
* deque does not permit null elements (optional)
*/
boolean contains(Object o);
/**
* Returns the number of elements in this deque.
*
* @return the number of elements in this deque
*/
public int size();
/**
* Returns an iterator over the elements in this deque in proper sequence.
* The elements will be returned in order from first (head) to last (tail).
*
* @return an iterator over the elements in this deque in proper sequence
*/
Iterator The optional capacity bound constructor argument serves as a
* way to prevent excessive expansion. The capacity, if unspecified,
* is equal to {@link Integer#MAX_VALUE}. Linked nodes are
* dynamically created upon each insertion unless this would bring the
* deque above capacity.
* Most operations run in constant time (ignoring time spent
* blocking). Exceptions include {@link #remove(Object) remove},
* {@link #removeFirstOccurrence removeFirstOccurrence}, {@link
* #removeLastOccurrence removeLastOccurrence}, {@link #contains
* contains}, {@link #iterator iterator.remove()}, and the bulk
* operations, all of which run in linear time.
* This class and its iterator implement all of the
* optional methods of the {@link Collection} and {@link
* Iterator} interfaces.
* This class is a member of the
*
* Java Collections Framework.
*
* @param This method is equivalent to {@link #addLast}.
*
* @throws IllegalStateException if the element cannot be added at this
* time due to capacity restrictions
* @throws NullPointerException if the specified element is null
*/
public boolean add(E e) {
addLast(e);
return true;
}
/**
* @throws NullPointerException if the specified element is null
*/
public boolean offer(E e) {
return offerLast(e);
}
/**
* @throws NullPointerException {@inheritDoc}
* @throws InterruptedException {@inheritDoc}
*/
public void put(E e) throws InterruptedException {
putLast(e);
}
/**
* @throws NullPointerException {@inheritDoc}
* @throws InterruptedException {@inheritDoc}
*/
public boolean offer(E e, long timeout, TimeUnit unit)
throws InterruptedException {
return offerLast(e, timeout, unit);
}
/**
* Retrieves and removes the head of the queue represented by this deque.
* This method differs from {@link #poll poll} only in that it throws an
* exception if this deque is empty.
* This method is equivalent to {@link #removeFirst() removeFirst}.
*
* @return the head of the queue represented by this deque
* @throws NoSuchElementException if this deque is empty
*/
public E remove() {
return removeFirst();
}
public E poll() {
return pollFirst();
}
public E take() throws InterruptedException {
return takeFirst();
}
public E poll(long timeout, TimeUnit unit) throws InterruptedException {
return pollFirst(timeout, unit);
}
/**
* Retrieves, but does not remove, the head of the queue represented by
* this deque. This method differs from {@link #peek peek} only in that
* it throws an exception if this deque is empty.
* This method is equivalent to {@link #getFirst() getFirst}.
*
* @return the head of the queue represented by this deque
* @throws NoSuchElementException if this deque is empty
*/
public E element() {
return getFirst();
}
public E peek() {
return peekFirst();
}
/**
* Returns the number of additional elements that this deque can ideally
* (in the absence of memory or resource constraints) accept without
* blocking. This is always equal to the initial capacity of this deque
* less the current {@code size} of this deque.
* Note that you cannot always tell if an attempt to insert
* an element will succeed by inspecting {@code remainingCapacity}
* because it may be the case that another thread is about to
* insert or remove an element.
*/
public int remainingCapacity() {
final ReentrantLock lock = this.lock;
lock.lock();
try {
return capacity - count;
} finally {
lock.unlock();
}
}
/**
* @throws UnsupportedOperationException {@inheritDoc}
* @throws ClassCastException {@inheritDoc}
* @throws NullPointerException {@inheritDoc}
* @throws IllegalArgumentException {@inheritDoc}
*/
public int drainTo(Collection super E> c) {
return drainTo(c, Integer.MAX_VALUE);
}
/**
* @throws UnsupportedOperationException {@inheritDoc}
* @throws ClassCastException {@inheritDoc}
* @throws NullPointerException {@inheritDoc}
* @throws IllegalArgumentException {@inheritDoc}
*/
public int drainTo(Collection super E> c, int maxElements) {
if (c == null)
throw new NullPointerException();
if (c == this)
throw new IllegalArgumentException();
final ReentrantLock lock = this.lock;
lock.lock();
try {
int n = Math.min(maxElements, count);
for (int i = 0; i < n; i++) {
c.add(first.item); // In this order, in case add() throws.
unlinkFirst();
}
return n;
} finally {
lock.unlock();
}
}
// Stack methods
/**
* @throws IllegalStateException {@inheritDoc}
* @throws NullPointerException {@inheritDoc}
*/
public void push(E e) {
addFirst(e);
}
/**
* @throws NoSuchElementException {@inheritDoc}
*/
public E pop() {
return removeFirst();
}
// Collection methods
/**
* Removes the first occurrence of the specified element from this deque.
* If the deque does not contain the element, it is unchanged.
* More formally, removes the first element {@code e} such that
* {@code o.equals(e)} (if such an element exists).
* Returns {@code true} if this deque contained the specified element
* (or equivalently, if this deque changed as a result of the call).
* This method is equivalent to
* {@link #removeFirstOccurrence(Object) removeFirstOccurrence}.
*
* @param o element to be removed from this deque, if present
* @return {@code true} if this deque changed as a result of the call
*/
public boolean remove(Object o) {
return removeFirstOccurrence(o);
}
/**
* Returns the number of elements in this deque.
*
* @return the number of elements in this deque
*/
public int size() {
final ReentrantLock lock = this.lock;
lock.lock();
try {
return count;
} finally {
lock.unlock();
}
}
/**
* Returns {@code true} if this deque contains the specified element.
* More formally, returns {@code true} if and only if this deque contains
* at least one element {@code e} such that {@code o.equals(e)}.
*
* @param o object to be checked for containment in this deque
* @return {@code true} if this deque contains the specified element
*/
public boolean contains(Object o) {
if (o == null) return false;
final ReentrantLock lock = this.lock;
lock.lock();
try {
for (Node The returned array will be "safe" in that no references to it are
* maintained by this deque. (In other words, this method must allocate
* a new array). The caller is thus free to modify the returned array.
* This method acts as bridge between array-based and collection-based
* APIs.
*
* @return an array containing all of the elements in this deque
*/
public Object[] toArray() {
final ReentrantLock lock = this.lock;
lock.lock();
try {
Object[] a = new Object[count];
int k = 0;
for (Node If this deque fits in the specified array with room to spare
* (i.e., the array has more elements than this deque), the element in
* the array immediately following the end of the deque is set to
* {@code null}.
* Like the {@link #toArray()} method, this method acts as bridge between
* array-based and collection-based APIs. Further, this method allows
* precise control over the runtime type of the output array, and may,
* under certain circumstances, be used to save allocation costs.
* Suppose {@code x} is a deque known to contain only strings.
* The following code can be used to dump the deque into a newly
* allocated array of {@code String}:
* The returned iterator is a "weakly consistent" iterator that
* will never throw {@link java.util.ConcurrentModificationException
* ConcurrentModificationException}, and guarantees to traverse
* elements as they existed upon construction of the iterator, and
* may (but is not guaranteed to) reflect any modifications
* subsequent to construction.
*
* @return an iterator over the elements in this deque in proper sequence
*/
public Iterator The returned iterator is a "weakly consistent" iterator that
* will never throw {@link java.util.ConcurrentModificationException
* ConcurrentModificationException}, and guarantees to traverse
* elements as they existed upon construction of the iterator, and
* may (but is not guaranteed to) reflect any modifications
* subsequent to construction.
*
* @return an iterator over the elements in this deque in reverse order
*/
public Iterator
*
* NOTE: This cache uses strong and weak references for stored Bitmaps. Strong references - for limited count of
* Bitmaps (depends on cache size), weak references - for all other cached Bitmaps.
*
* @author Sergey Tarasevich (nostra13[at]gmail[dot]com)
* @see BaseMemoryCache
* @since 1.0.0
*/
public abstract class LimitedMemoryCache extends BaseMemoryCache {
private static final int MAX_NORMAL_CACHE_SIZE_IN_MB = 16;
private static final int MAX_NORMAL_CACHE_SIZE = MAX_NORMAL_CACHE_SIZE_IN_MB * 1024 * 1024;
private final int sizeLimit;
private final AtomicInteger cacheSize;
/**
* Contains strong references to stored objects. Each next object is added last. If hard cache size will exceed
* limit then first object is deleted (but it continue exist at {@link #softMap} and can be collected by GC at any
* time)
*/
private final List
*
* NOTE: This cache uses strong and weak references for stored Bitmaps. Strong references - for limited count of
* Bitmaps (depends on cache size), weak references - for all other cached Bitmaps.
*
* @author Sergey Tarasevich (nostra13[at]gmail[dot]com)
* @since 1.0.0
*/
public class FIFOLimitedMemoryCache extends LimitedMemoryCache {
private final List
* NOTE: Used for internal needs. Normally you don't need to use this class.
*
* @author Sergey Tarasevich (nostra13[at]gmail[dot]com)
* @since 1.0.0
*/
public class FuzzyKeyMemoryCache implements MemoryCache {
private final MemoryCache cache;
private final Comparator
*
* NOTE: This cache uses strong and weak references for stored Bitmaps. Strong references - for limited count of
* Bitmaps (depends on cache size), weak references - for all other cached Bitmaps.
*
* @author Sergey Tarasevich (nostra13[at]gmail[dot]com)
* @since 1.3.0
*/
public class LRULimitedMemoryCache extends LimitedMemoryCache {
private static final int INITIAL_CAPACITY = 10;
private static final float LOAD_FACTOR = 1.1f;
/** Cache providing Least-Recently-Used logic */
private final Map
*
* NOTE: This cache uses strong and weak references for stored Bitmaps. Strong references - for limited count of
* Bitmaps (depends on cache size), weak references - for all other cached Bitmaps.
*
* @author Sergey Tarasevich (nostra13[at]gmail[dot]com)
* @since 1.0.0
*/
public class LargestLimitedMemoryCache extends LimitedMemoryCache {
/**
* Contains strong references to stored objects (keys) and sizes of the objects. If hard cache
* size will exceed limit then object with the largest size is deleted (but it continue exist at
* {@link #softMap} and can be collected by GC at any time)
*/
private final Map
*
* NOTE: This cache uses only strong references for stored Bitmaps.
*
* @author Sergey Tarasevich (nostra13[at]gmail[dot]com)
* @since 1.8.1
*/
public class LruMemoryCache implements MemoryCache {
private final LinkedHashMap
*
* NOTE: This cache uses strong and weak references for stored Bitmaps. Strong references - for limited count of
* Bitmaps (depends on cache size), weak references - for all other cached Bitmaps.
*
* @author Sergey Tarasevich (nostra13[at]gmail[dot]com)
* @since 1.0.0
*/
public class UsingFreqLimitedMemoryCache extends LimitedMemoryCache {
/**
* Contains strong references to stored objects (keys) and last object usage date (in milliseconds). If hard cache
* size will exceed limit then object with the least frequently usage is deleted (but it continue exist at
* {@link #softMap} and can be collected by GC at any time)
*/
private final Map
*
* NOTE: This cache uses only weak references for stored Bitmaps.
*
* @author Sergey Tarasevich (nostra13[at]gmail[dot]com)
* @since 1.5.3
*/
public class WeakMemoryCache extends BaseMemoryCache {
@Override
protected Reference
* Default cache size = 1/8 of available app memory.
*/
public static MemoryCache createMemoryCache(Context context, int memoryCacheSize) {
if (memoryCacheSize == 0) {
ActivityManager am = (ActivityManager) context.getSystemService(Context.ACTIVITY_SERVICE);
int memoryClass = am.getMemoryClass();
if (hasHoneycomb() && isLargeHeap(context)) {
memoryClass = getLargeMemoryClass(am);
}
memoryCacheSize = 1024 * 1024 * memoryClass / 8;
}
return new LruMemoryCache(memoryCacheSize);
}
private static boolean hasHoneycomb() {
return Build.VERSION.SDK_INT >= Build.VERSION_CODES.HONEYCOMB;
}
@TargetApi(Build.VERSION_CODES.HONEYCOMB)
private static boolean isLargeHeap(Context context) {
return (context.getApplicationInfo().flags & ApplicationInfo.FLAG_LARGE_HEAP) != 0;
}
@TargetApi(Build.VERSION_CODES.HONEYCOMB)
private static int getLargeMemoryClass(ActivityManager am) {
return am.getLargeMemoryClass();
}
/** Creates default implementation of {@link ImageDownloader} - {@link BaseImageDownloader} */
public static ImageDownloader createImageDownloader(Context context) {
return new BaseImageDownloader(context);
}
/** Creates default implementation of {@link ImageDecoder} - {@link BaseImageDecoder} */
public static ImageDecoder createImageDecoder(boolean loggingEnabled) {
return new BaseImageDecoder(loggingEnabled);
}
/** Creates default implementation of {@link BitmapDisplayer} - {@link SimpleBitmapDisplayer} */
public static BitmapDisplayer createBitmapDisplayer() {
return new SimpleBitmapDisplayer();
}
/** Creates default implementation of {@linkplain ThreadFactory thread factory} for task executor */
private static ThreadFactory createThreadFactory(int threadPriority, String threadNamePrefix) {
return new DefaultThreadFactory(threadPriority, threadNamePrefix);
}
private static class DefaultThreadFactory implements ThreadFactory {
private static final AtomicInteger poolNumber = new AtomicInteger(1);
private final ThreadGroup group;
private final AtomicInteger threadNumber = new AtomicInteger(1);
private final String namePrefix;
private final int threadPriority;
DefaultThreadFactory(int threadPriority, String threadNamePrefix) {
this.threadPriority = threadPriority;
group = Thread.currentThread().getThreadGroup();
namePrefix = threadNamePrefix + poolNumber.getAndIncrement() + "-thread-";
}
@Override
public Thread newThread(Runnable r) {
Thread t = new Thread(group, r, namePrefix + threadNumber.getAndIncrement(), 0);
if (t.isDaemon()) t.setDaemon(false);
t.setPriority(threadPriority);
return t;
}
}
}
================================================
FILE: library/src/main/java/com/nostra13/universalimageloader/core/DisplayBitmapTask.java
================================================
/*******************************************************************************
* Copyright 2011-2014 Sergey Tarasevich
*
* 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.nostra13.universalimageloader.core;
import android.graphics.Bitmap;
import com.nostra13.universalimageloader.core.assist.LoadedFrom;
import com.nostra13.universalimageloader.core.display.BitmapDisplayer;
import com.nostra13.universalimageloader.core.imageaware.ImageAware;
import com.nostra13.universalimageloader.core.listener.ImageLoadingListener;
import com.nostra13.universalimageloader.utils.L;
/**
* Displays bitmap in {@link com.nostra13.universalimageloader.core.imageaware.ImageAware}. Must be called on UI thread.
*
* @author Sergey Tarasevich (nostra13[at]gmail[dot]com)
* @see ImageLoadingListener
* @see BitmapDisplayer
* @since 1.3.1
*/
final class DisplayBitmapTask implements Runnable {
private static final String LOG_DISPLAY_IMAGE_IN_IMAGEAWARE = "Display image in ImageAware (loaded from %1$s) [%2$s]";
private static final String LOG_TASK_CANCELLED_IMAGEAWARE_REUSED = "ImageAware is reused for another image. Task is cancelled. [%s]";
private static final String LOG_TASK_CANCELLED_IMAGEAWARE_COLLECTED = "ImageAware was collected by GC. Task is cancelled. [%s]";
private final Bitmap bitmap;
private final String imageUri;
private final ImageAware imageAware;
private final String memoryCacheKey;
private final BitmapDisplayer displayer;
private final ImageLoadingListener listener;
private final ImageLoaderEngine engine;
private final LoadedFrom loadedFrom;
public DisplayBitmapTask(Bitmap bitmap, ImageLoadingInfo imageLoadingInfo, ImageLoaderEngine engine,
LoadedFrom loadedFrom) {
this.bitmap = bitmap;
imageUri = imageLoadingInfo.uri;
imageAware = imageLoadingInfo.imageAware;
memoryCacheKey = imageLoadingInfo.memoryCacheKey;
displayer = imageLoadingInfo.options.getDisplayer();
listener = imageLoadingInfo.listener;
this.engine = engine;
this.loadedFrom = loadedFrom;
}
@Override
public void run() {
if (imageAware.isCollected()) {
L.d(LOG_TASK_CANCELLED_IMAGEAWARE_COLLECTED, memoryCacheKey);
listener.onLoadingCancelled(imageUri, imageAware.getWrappedView());
} else if (isViewWasReused()) {
L.d(LOG_TASK_CANCELLED_IMAGEAWARE_REUSED, memoryCacheKey);
listener.onLoadingCancelled(imageUri, imageAware.getWrappedView());
} else {
L.d(LOG_DISPLAY_IMAGE_IN_IMAGEAWARE, loadedFrom, memoryCacheKey);
displayer.display(bitmap, imageAware, loadedFrom);
engine.cancelDisplayTaskFor(imageAware);
listener.onLoadingComplete(imageUri, imageAware.getWrappedView(), bitmap);
}
}
/** Checks whether memory cache key (image URI) for current ImageAware is actual */
private boolean isViewWasReused() {
String currentCacheKey = engine.getLoadingUriForView(imageAware);
return !memoryCacheKey.equals(currentCacheKey);
}
}
================================================
FILE: library/src/main/java/com/nostra13/universalimageloader/core/DisplayImageOptions.java
================================================
/*******************************************************************************
* Copyright 2011-2014 Sergey Tarasevich
*
* 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.nostra13.universalimageloader.core;
import android.content.res.Resources;
import android.graphics.Bitmap;
import android.graphics.BitmapFactory.Options;
import android.graphics.drawable.Drawable;
import android.os.Handler;
import com.nostra13.universalimageloader.core.listener.ImageLoadingListener;
import com.nostra13.universalimageloader.core.assist.ImageScaleType;
import com.nostra13.universalimageloader.core.display.BitmapDisplayer;
import com.nostra13.universalimageloader.core.display.SimpleBitmapDisplayer;
import com.nostra13.universalimageloader.core.download.ImageDownloader;
import com.nostra13.universalimageloader.core.process.BitmapProcessor;
/**
* Contains options for image display. Defines:
*
*
*
* You can create instance:
*
*
* i.e. :
* new {@link DisplayImageOptions}.Builder().{@link Builder#cacheInMemory() cacheInMemory()}.
* {@link Builder#showImageOnLoading(int) showImageOnLoading()}.{@link Builder#build() build()}
*
*
* @author Sergey Tarasevich (nostra13[at]gmail[dot]com)
* @since 1.0.0
*/
public final class DisplayImageOptions {
private final int imageResOnLoading;
private final int imageResForEmptyUri;
private final int imageResOnFail;
private final Drawable imageOnLoading;
private final Drawable imageForEmptyUri;
private final Drawable imageOnFail;
private final boolean resetViewBeforeLoading;
private final boolean cacheInMemory;
private final boolean cacheOnDisk;
private final ImageScaleType imageScaleType;
private final Options decodingOptions;
private final int delayBeforeLoading;
private final boolean considerExifParams;
private final Object extraForDownloader;
private final BitmapProcessor preProcessor;
private final BitmapProcessor postProcessor;
private final BitmapDisplayer displayer;
private final Handler handler;
private final boolean isSyncLoading;
private DisplayImageOptions(Builder builder) {
imageResOnLoading = builder.imageResOnLoading;
imageResForEmptyUri = builder.imageResForEmptyUri;
imageResOnFail = builder.imageResOnFail;
imageOnLoading = builder.imageOnLoading;
imageForEmptyUri = builder.imageForEmptyUri;
imageOnFail = builder.imageOnFail;
resetViewBeforeLoading = builder.resetViewBeforeLoading;
cacheInMemory = builder.cacheInMemory;
cacheOnDisk = builder.cacheOnDisk;
imageScaleType = builder.imageScaleType;
decodingOptions = builder.decodingOptions;
delayBeforeLoading = builder.delayBeforeLoading;
considerExifParams = builder.considerExifParams;
extraForDownloader = builder.extraForDownloader;
preProcessor = builder.preProcessor;
postProcessor = builder.postProcessor;
displayer = builder.displayer;
handler = builder.handler;
isSyncLoading = builder.isSyncLoading;
}
public boolean shouldShowImageOnLoading() {
return imageOnLoading != null || imageResOnLoading != 0;
}
public boolean shouldShowImageForEmptyUri() {
return imageForEmptyUri != null || imageResForEmptyUri != 0;
}
public boolean shouldShowImageOnFail() {
return imageOnFail != null || imageResOnFail != 0;
}
public boolean shouldPreProcess() {
return preProcessor != null;
}
public boolean shouldPostProcess() {
return postProcessor != null;
}
public boolean shouldDelayBeforeLoading() {
return delayBeforeLoading > 0;
}
public Drawable getImageOnLoading(Resources res) {
return imageResOnLoading != 0 ? res.getDrawable(imageResOnLoading) : imageOnLoading;
}
public Drawable getImageForEmptyUri(Resources res) {
return imageResForEmptyUri != 0 ? res.getDrawable(imageResForEmptyUri) : imageForEmptyUri;
}
public Drawable getImageOnFail(Resources res) {
return imageResOnFail != 0 ? res.getDrawable(imageResOnFail) : imageOnFail;
}
public boolean isResetViewBeforeLoading() {
return resetViewBeforeLoading;
}
public boolean isCacheInMemory() {
return cacheInMemory;
}
public boolean isCacheOnDisk() {
return cacheOnDisk;
}
public ImageScaleType getImageScaleType() {
return imageScaleType;
}
public Options getDecodingOptions() {
return decodingOptions;
}
public int getDelayBeforeLoading() {
return delayBeforeLoading;
}
public boolean isConsiderExifParams() {
return considerExifParams;
}
public Object getExtraForDownloader() {
return extraForDownloader;
}
public BitmapProcessor getPreProcessor() {
return preProcessor;
}
public BitmapProcessor getPostProcessor() {
return postProcessor;
}
public BitmapDisplayer getDisplayer() {
return displayer;
}
public Handler getHandler() {
return handler;
}
boolean isSyncLoading() {
return isSyncLoading;
}
/**
* Builder for {@link DisplayImageOptions}
*
* @author Sergey Tarasevich (nostra13[at]gmail[dot]com)
*/
public static class Builder {
private int imageResOnLoading = 0;
private int imageResForEmptyUri = 0;
private int imageResOnFail = 0;
private Drawable imageOnLoading = null;
private Drawable imageForEmptyUri = null;
private Drawable imageOnFail = null;
private boolean resetViewBeforeLoading = false;
private boolean cacheInMemory = false;
private boolean cacheOnDisk = false;
private ImageScaleType imageScaleType = ImageScaleType.IN_SAMPLE_POWER_OF_2;
private Options decodingOptions = new Options();
private int delayBeforeLoading = 0;
private boolean considerExifParams = false;
private Object extraForDownloader = null;
private BitmapProcessor preProcessor = null;
private BitmapProcessor postProcessor = null;
private BitmapDisplayer displayer = DefaultConfigurationFactory.createBitmapDisplayer();
private Handler handler = null;
private boolean isSyncLoading = false;
/**
* Stub image will be displayed in {@link com.nostra13.universalimageloader.core.imageaware.ImageAware
* image aware view} during image loading
*
* @param imageRes Stub image resource
* @deprecated Use {@link #showImageOnLoading(int)} instead
*/
@Deprecated
public Builder showStubImage(int imageRes) {
imageResOnLoading = imageRes;
return this;
}
/**
* Incoming image will be displayed in {@link com.nostra13.universalimageloader.core.imageaware.ImageAware
* image aware view} during image loading
*
* @param imageRes Image resource
*/
public Builder showImageOnLoading(int imageRes) {
imageResOnLoading = imageRes;
return this;
}
/**
* Incoming drawable will be displayed in {@link com.nostra13.universalimageloader.core.imageaware.ImageAware
* image aware view} during image loading.
* This option will be ignored if {@link DisplayImageOptions.Builder#showImageOnLoading(int)} is set.
*/
public Builder showImageOnLoading(Drawable drawable) {
imageOnLoading = drawable;
return this;
}
/**
* Incoming image will be displayed in {@link com.nostra13.universalimageloader.core.imageaware.ImageAware
* image aware view} if empty URI (null or empty
* string) will be passed to ImageLoader.displayImage(...) method.
*
* @param imageRes Image resource
*/
public Builder showImageForEmptyUri(int imageRes) {
imageResForEmptyUri = imageRes;
return this;
}
/**
* Incoming drawable will be displayed in {@link com.nostra13.universalimageloader.core.imageaware.ImageAware
* image aware view} if empty URI (null or empty
* string) will be passed to ImageLoader.displayImage(...) method.
* This option will be ignored if {@link DisplayImageOptions.Builder#showImageForEmptyUri(int)} is set.
*/
public Builder showImageForEmptyUri(Drawable drawable) {
imageForEmptyUri = drawable;
return this;
}
/**
* Incoming image will be displayed in {@link com.nostra13.universalimageloader.core.imageaware.ImageAware
* image aware view} if some error occurs during
* requested image loading/decoding.
*
* @param imageRes Image resource
*/
public Builder showImageOnFail(int imageRes) {
imageResOnFail = imageRes;
return this;
}
/**
* Incoming drawable will be displayed in {@link com.nostra13.universalimageloader.core.imageaware.ImageAware
* image aware view} if some error occurs during
* requested image loading/decoding.
* This option will be ignored if {@link DisplayImageOptions.Builder#showImageOnFail(int)} is set.
*/
public Builder showImageOnFail(Drawable drawable) {
imageOnFail = drawable;
return this;
}
/**
* {@link com.nostra13.universalimageloader.core.imageaware.ImageAware
* image aware view} will be reset (set null) before image loading start
*
* @deprecated Use {@link #resetViewBeforeLoading(boolean) resetViewBeforeLoading(true)} instead
*/
public Builder resetViewBeforeLoading() {
resetViewBeforeLoading = true;
return this;
}
/**
* Sets whether {@link com.nostra13.universalimageloader.core.imageaware.ImageAware
* image aware view} will be reset (set null) before image loading start
*/
public Builder resetViewBeforeLoading(boolean resetViewBeforeLoading) {
this.resetViewBeforeLoading = resetViewBeforeLoading;
return this;
}
/**
* Loaded image will be cached in memory
*
* @deprecated Use {@link #cacheInMemory(boolean) cacheInMemory(true)} instead
*/
@Deprecated
public Builder cacheInMemory() {
cacheInMemory = true;
return this;
}
/** Sets whether loaded image will be cached in memory */
public Builder cacheInMemory(boolean cacheInMemory) {
this.cacheInMemory = cacheInMemory;
return this;
}
/**
* Loaded image will be cached on disk
*
* @deprecated Use {@link #cacheOnDisk(boolean) cacheOnDisk(true)} instead
*/
@Deprecated
public Builder cacheOnDisc() {
return cacheOnDisk(true);
}
/**
* Sets whether loaded image will be cached on disk
*
* @deprecated Use {@link #cacheOnDisk(boolean)} instead
*/
@Deprecated
public Builder cacheOnDisc(boolean cacheOnDisk) {
return cacheOnDisk(cacheOnDisk);
}
/** Sets whether loaded image will be cached on disk */
public Builder cacheOnDisk(boolean cacheOnDisk) {
this.cacheOnDisk = cacheOnDisk;
return this;
}
/**
* Sets {@linkplain ImageScaleType scale type} for decoding image. This parameter is used while define scale
* size for decoding image to Bitmap. Default value - {@link ImageScaleType#IN_SAMPLE_POWER_OF_2}
*/
public Builder imageScaleType(ImageScaleType imageScaleType) {
this.imageScaleType = imageScaleType;
return this;
}
/** Sets {@link Bitmap.Config bitmap config} for image decoding. Default value - {@link Bitmap.Config#ARGB_8888} */
public Builder bitmapConfig(Bitmap.Config bitmapConfig) {
if (bitmapConfig == null) throw new IllegalArgumentException("bitmapConfig can't be null");
decodingOptions.inPreferredConfig = bitmapConfig;
return this;
}
/**
* Sets options for image decoding.
* NOTE: {@link Options#inSampleSize} of incoming options will NOT be considered. Library
* calculate the most appropriate sample size itself according yo {@link #imageScaleType(ImageScaleType)}
* options.
* NOTE: This option overlaps {@link #bitmapConfig(android.graphics.Bitmap.Config) bitmapConfig()}
* option.
*/
public Builder decodingOptions(Options decodingOptions) {
if (decodingOptions == null) throw new IllegalArgumentException("decodingOptions can't be null");
this.decodingOptions = decodingOptions;
return this;
}
/** Sets delay time before starting loading task. Default - no delay. */
public Builder delayBeforeLoading(int delayInMillis) {
this.delayBeforeLoading = delayInMillis;
return this;
}
/** Sets auxiliary object which will be passed to {@link ImageDownloader#getStream(String, Object)} */
public Builder extraForDownloader(Object extra) {
this.extraForDownloader = extra;
return this;
}
/** Sets whether ImageLoader will consider EXIF parameters of JPEG image (rotate, flip) */
public Builder considerExifParams(boolean considerExifParams) {
this.considerExifParams = considerExifParams;
return this;
}
/**
* Sets bitmap processor which will be process bitmaps before they will be cached in memory. So memory cache
* will contain bitmap processed by incoming preProcessor.
* Image will be pre-processed even if caching in memory is disabled.
*/
public Builder preProcessor(BitmapProcessor preProcessor) {
this.preProcessor = preProcessor;
return this;
}
/**
* Sets bitmap processor which will be process bitmaps before they will be displayed in
* {@link com.nostra13.universalimageloader.core.imageaware.ImageAware image aware view} but
* after they'll have been saved in memory cache.
*/
public Builder postProcessor(BitmapProcessor postProcessor) {
this.postProcessor = postProcessor;
return this;
}
/**
* Sets custom {@link BitmapDisplayer displayer} for image loading task. Default value -
* {@link DefaultConfigurationFactory#createBitmapDisplayer()}
*/
public Builder displayer(BitmapDisplayer displayer) {
if (displayer == null) throw new IllegalArgumentException("displayer can't be null");
this.displayer = displayer;
return this;
}
Builder syncLoading(boolean isSyncLoading) {
this.isSyncLoading = isSyncLoading;
return this;
}
/**
* Sets custom {@linkplain Handler handler} for displaying images and firing {@linkplain ImageLoadingListener
* listener} events.
*/
public Builder handler(Handler handler) {
this.handler = handler;
return this;
}
/** Sets all options equal to incoming options */
public Builder cloneFrom(DisplayImageOptions options) {
imageResOnLoading = options.imageResOnLoading;
imageResForEmptyUri = options.imageResForEmptyUri;
imageResOnFail = options.imageResOnFail;
imageOnLoading = options.imageOnLoading;
imageForEmptyUri = options.imageForEmptyUri;
imageOnFail = options.imageOnFail;
resetViewBeforeLoading = options.resetViewBeforeLoading;
cacheInMemory = options.cacheInMemory;
cacheOnDisk = options.cacheOnDisk;
imageScaleType = options.imageScaleType;
decodingOptions = options.decodingOptions;
delayBeforeLoading = options.delayBeforeLoading;
considerExifParams = options.considerExifParams;
extraForDownloader = options.extraForDownloader;
preProcessor = options.preProcessor;
postProcessor = options.postProcessor;
displayer = options.displayer;
handler = options.handler;
isSyncLoading = options.isSyncLoading;
return this;
}
/** Builds configured {@link DisplayImageOptions} object */
public DisplayImageOptions build() {
return new DisplayImageOptions(this);
}
}
/**
* Creates options appropriate for single displaying:
*
*
*
* These option are appropriate for simple single-use image (from drawables or from Internet) displaying.
*/
public static DisplayImageOptions createSimple() {
return new Builder().build();
}
}
================================================
FILE: library/src/main/java/com/nostra13/universalimageloader/core/ImageLoader.java
================================================
/*******************************************************************************
* Copyright 2011-2014 Sergey Tarasevich
*
* 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.nostra13.universalimageloader.core;
import android.graphics.Bitmap;
import android.os.Handler;
import android.os.Looper;
import android.text.TextUtils;
import android.view.View;
import android.widget.ImageView;
import com.nostra13.universalimageloader.cache.disc.DiskCache;
import com.nostra13.universalimageloader.cache.memory.MemoryCache;
import com.nostra13.universalimageloader.core.assist.FailReason;
import com.nostra13.universalimageloader.core.assist.FlushedInputStream;
import com.nostra13.universalimageloader.core.assist.ImageSize;
import com.nostra13.universalimageloader.core.assist.LoadedFrom;
import com.nostra13.universalimageloader.core.assist.ViewScaleType;
import com.nostra13.universalimageloader.core.imageaware.ImageAware;
import com.nostra13.universalimageloader.core.imageaware.ImageViewAware;
import com.nostra13.universalimageloader.core.imageaware.NonViewAware;
import com.nostra13.universalimageloader.core.listener.ImageLoadingListener;
import com.nostra13.universalimageloader.core.listener.ImageLoadingProgressListener;
import com.nostra13.universalimageloader.core.listener.SimpleImageLoadingListener;
import com.nostra13.universalimageloader.utils.ImageSizeUtils;
import com.nostra13.universalimageloader.utils.L;
import com.nostra13.universalimageloader.utils.MemoryCacheUtils;
/**
* Singletone for image loading and displaying at {@link ImageView ImageViews}
* NOTE: {@link #init(ImageLoaderConfiguration)} method must be called before any other method.
*
* @author Sergey Tarasevich (nostra13[at]gmail[dot]com)
* @since 1.0.0
*/
public class ImageLoader {
public static final String TAG = ImageLoader.class.getSimpleName();
static final String LOG_INIT_CONFIG = "Initialize ImageLoader with configuration";
static final String LOG_DESTROY = "Destroy ImageLoader";
static final String LOG_LOAD_IMAGE_FROM_MEMORY_CACHE = "Load image from memory cache [%s]";
private static final String WARNING_RE_INIT_CONFIG = "Try to initialize ImageLoader which had already been initialized before. " + "To re-init ImageLoader with new configuration call ImageLoader.destroy() at first.";
private static final String ERROR_WRONG_ARGUMENTS = "Wrong arguments were passed to displayImage() method (ImageView reference must not be null)";
private static final String ERROR_NOT_INIT = "ImageLoader must be init with configuration before using";
private static final String ERROR_INIT_CONFIG_WITH_NULL = "ImageLoader configuration can not be initialized with null";
private ImageLoaderConfiguration configuration;
private ImageLoaderEngine engine;
private ImageLoadingListener defaultListener = new SimpleImageLoadingListener();
private volatile static ImageLoader instance;
/** Returns singleton class instance */
public static ImageLoader getInstance() {
if (instance == null) {
synchronized (ImageLoader.class) {
if (instance == null) {
instance = new ImageLoader();
}
}
}
return instance;
}
protected ImageLoader() {
}
/**
* Initializes ImageLoader instance with configuration.
* If configurations was set before ( {@link #isInited()} == true) then this method does nothing.
* To force initialization with new configuration you should {@linkplain #destroy() destroy ImageLoader} at first.
*
* @param configuration {@linkplain ImageLoaderConfiguration ImageLoader configuration}
* @throws IllegalArgumentException if configuration parameter is null
*/
public synchronized void init(ImageLoaderConfiguration configuration) {
if (configuration == null) {
throw new IllegalArgumentException(ERROR_INIT_CONFIG_WITH_NULL);
}
if (this.configuration == null) {
L.d(LOG_INIT_CONFIG);
engine = new ImageLoaderEngine(configuration);
this.configuration = configuration;
} else {
L.w(WARNING_RE_INIT_CONFIG);
}
}
/**
* Returns true - if ImageLoader {@linkplain #init(ImageLoaderConfiguration) is initialized with
* configuration}; false - otherwise
*/
public boolean isInited() {
return configuration != null;
}
/**
* Adds display image task to execution pool. Image will be set to ImageAware when it's turn.
* Default {@linkplain DisplayImageOptions display image options} from {@linkplain ImageLoaderConfiguration
* configuration} will be used.
* NOTE: {@link #init(ImageLoaderConfiguration)} method must be called before this method call
*
* @param uri Image URI (i.e. "http://site.com/image.png", "file:///mnt/sdcard/image.png")
* @param imageAware {@linkplain com.nostra13.universalimageloader.core.imageaware.ImageAware Image aware view}
* which should display image
* @throws IllegalStateException if {@link #init(ImageLoaderConfiguration)} method wasn't called before
* @throws IllegalArgumentException if passed imageAware is null
*/
public void displayImage(String uri, ImageAware imageAware) {
displayImage(uri, imageAware, null, null, null);
}
/**
* Adds display image task to execution pool. Image will be set to ImageAware when it's turn.
* Default {@linkplain DisplayImageOptions display image options} from {@linkplain ImageLoaderConfiguration
* configuration} will be used.
* NOTE: {@link #init(ImageLoaderConfiguration)} method must be called before this method call
*
* @param uri Image URI (i.e. "http://site.com/image.png", "file:///mnt/sdcard/image.png")
* @param imageAware {@linkplain com.nostra13.universalimageloader.core.imageaware.ImageAware Image aware view}
* which should display image
* @param listener {@linkplain ImageLoadingListener Listener} for image loading process. Listener fires events on
* UI thread if this method is called on UI thread.
* @throws IllegalStateException if {@link #init(ImageLoaderConfiguration)} method wasn't called before
* @throws IllegalArgumentException if passed imageAware is null
*/
public void displayImage(String uri, ImageAware imageAware, ImageLoadingListener listener) {
displayImage(uri, imageAware, null, listener, null);
}
/**
* Adds display image task to execution pool. Image will be set to ImageAware when it's turn.
* NOTE: {@link #init(ImageLoaderConfiguration)} method must be called before this method call
*
* @param uri Image URI (i.e. "http://site.com/image.png", "file:///mnt/sdcard/image.png")
* @param imageAware {@linkplain com.nostra13.universalimageloader.core.imageaware.ImageAware Image aware view}
* which should display image
* @param options {@linkplain com.nostra13.universalimageloader.core.DisplayImageOptions Options} for image
* decoding and displaying. If null - default display image options
* {@linkplain ImageLoaderConfiguration.Builder#defaultDisplayImageOptions(DisplayImageOptions)
* from configuration} will be used.
* @throws IllegalStateException if {@link #init(ImageLoaderConfiguration)} method wasn't called before
* @throws IllegalArgumentException if passed imageAware is null
*/
public void displayImage(String uri, ImageAware imageAware, DisplayImageOptions options) {
displayImage(uri, imageAware, options, null, null);
}
/**
* Adds display image task to execution pool. Image will be set to ImageAware when it's turn.
* NOTE: {@link #init(ImageLoaderConfiguration)} method must be called before this method call
*
* @param uri Image URI (i.e. "http://site.com/image.png", "file:///mnt/sdcard/image.png")
* @param imageAware {@linkplain com.nostra13.universalimageloader.core.imageaware.ImageAware Image aware view}
* which should display image
* @param options {@linkplain com.nostra13.universalimageloader.core.DisplayImageOptions Options} for image
* decoding and displaying. If null - default display image options
* {@linkplain ImageLoaderConfiguration.Builder#defaultDisplayImageOptions(DisplayImageOptions)
* from configuration} will be used.
* @param listener {@linkplain ImageLoadingListener Listener} for image loading process. Listener fires events on
* UI thread if this method is called on UI thread.
* @throws IllegalStateException if {@link #init(ImageLoaderConfiguration)} method wasn't called before
* @throws IllegalArgumentException if passed imageAware is null
*/
public void displayImage(String uri, ImageAware imageAware, DisplayImageOptions options,
ImageLoadingListener listener) {
displayImage(uri, imageAware, options, listener, null);
}
/**
* Adds display image task to execution pool. Image will be set to ImageAware when it's turn.
* NOTE: {@link #init(ImageLoaderConfiguration)} method must be called before this method call
*
* @param uri Image URI (i.e. "http://site.com/image.png", "file:///mnt/sdcard/image.png")
* @param imageAware {@linkplain com.nostra13.universalimageloader.core.imageaware.ImageAware Image aware view}
* which should display image
* @param options {@linkplain com.nostra13.universalimageloader.core.DisplayImageOptions Options} for image
* decoding and displaying. If null - default display image options
* {@linkplain ImageLoaderConfiguration.Builder#defaultDisplayImageOptions(DisplayImageOptions)
* from configuration} will be used.
* @param listener {@linkplain ImageLoadingListener Listener} for image loading process. Listener fires
* events on UI thread if this method is called on UI thread.
* @param progressListener {@linkplain com.nostra13.universalimageloader.core.listener.ImageLoadingProgressListener
* Listener} for image loading progress. Listener fires events on UI thread if this method
* is called on UI thread. Caching on disk should be enabled in
* {@linkplain com.nostra13.universalimageloader.core.DisplayImageOptions options} to make
* this listener work.
* @throws IllegalStateException if {@link #init(ImageLoaderConfiguration)} method wasn't called before
* @throws IllegalArgumentException if passed imageAware is null
*/
public void displayImage(String uri, ImageAware imageAware, DisplayImageOptions options,
ImageLoadingListener listener, ImageLoadingProgressListener progressListener) {
displayImage(uri, imageAware, options, null, listener, progressListener);
}
/**
* Adds display image task to execution pool. Image will be set to ImageAware when it's turn.
* NOTE: {@link #init(ImageLoaderConfiguration)} method must be called before this method call
*
* @param uri Image URI (i.e. "http://site.com/image.png", "file:///mnt/sdcard/image.png")
* @param imageAware {@linkplain com.nostra13.universalimageloader.core.imageaware.ImageAware Image aware view}
* which should display image
* @param options {@linkplain com.nostra13.universalimageloader.core.DisplayImageOptions Options} for image
* decoding and displaying. If null - default display image options
* {@linkplain ImageLoaderConfiguration.Builder#defaultDisplayImageOptions(DisplayImageOptions)
* from configuration} will be used.
* @param targetSize {@linkplain ImageSize} Image target size. If null - size will depend on the view
* @param listener {@linkplain ImageLoadingListener Listener} for image loading process. Listener fires
* events on UI thread if this method is called on UI thread.
* @param progressListener {@linkplain com.nostra13.universalimageloader.core.listener.ImageLoadingProgressListener
* Listener} for image loading progress. Listener fires events on UI thread if this method
* is called on UI thread. Caching on disk should be enabled in
* {@linkplain com.nostra13.universalimageloader.core.DisplayImageOptions options} to make
* this listener work.
* @throws IllegalStateException if {@link #init(ImageLoaderConfiguration)} method wasn't called before
* @throws IllegalArgumentException if passed imageAware is null
*/
public void displayImage(String uri, ImageAware imageAware, DisplayImageOptions options,
ImageSize targetSize, ImageLoadingListener listener, ImageLoadingProgressListener progressListener) {
checkConfiguration();
if (imageAware == null) {
throw new IllegalArgumentException(ERROR_WRONG_ARGUMENTS);
}
if (listener == null) {
listener = defaultListener;
}
if (options == null) {
options = configuration.defaultDisplayImageOptions;
}
if (TextUtils.isEmpty(uri)) {
engine.cancelDisplayTaskFor(imageAware);
listener.onLoadingStarted(uri, imageAware.getWrappedView());
if (options.shouldShowImageForEmptyUri()) {
imageAware.setImageDrawable(options.getImageForEmptyUri(configuration.resources));
} else {
imageAware.setImageDrawable(null);
}
listener.onLoadingComplete(uri, imageAware.getWrappedView(), null);
return;
}
if (targetSize == null) {
targetSize = ImageSizeUtils.defineTargetSizeForView(imageAware, configuration.getMaxImageSize());
}
String memoryCacheKey = MemoryCacheUtils.generateKey(uri, targetSize);
engine.prepareDisplayTaskFor(imageAware, memoryCacheKey);
listener.onLoadingStarted(uri, imageAware.getWrappedView());
Bitmap bmp = configuration.memoryCache.get(memoryCacheKey);
if (bmp != null && !bmp.isRecycled()) {
L.d(LOG_LOAD_IMAGE_FROM_MEMORY_CACHE, memoryCacheKey);
if (options.shouldPostProcess()) {
ImageLoadingInfo imageLoadingInfo = new ImageLoadingInfo(uri, imageAware, targetSize, memoryCacheKey,
options, listener, progressListener, engine.getLockForUri(uri));
ProcessAndDisplayImageTask displayTask = new ProcessAndDisplayImageTask(engine, bmp, imageLoadingInfo,
defineHandler(options));
if (options.isSyncLoading()) {
displayTask.run();
} else {
engine.submit(displayTask);
}
} else {
options.getDisplayer().display(bmp, imageAware, LoadedFrom.MEMORY_CACHE);
listener.onLoadingComplete(uri, imageAware.getWrappedView(), bmp);
}
} else {
if (options.shouldShowImageOnLoading()) {
imageAware.setImageDrawable(options.getImageOnLoading(configuration.resources));
} else if (options.isResetViewBeforeLoading()) {
imageAware.setImageDrawable(null);
}
ImageLoadingInfo imageLoadingInfo = new ImageLoadingInfo(uri, imageAware, targetSize, memoryCacheKey,
options, listener, progressListener, engine.getLockForUri(uri));
LoadAndDisplayImageTask displayTask = new LoadAndDisplayImageTask(engine, imageLoadingInfo,
defineHandler(options));
if (options.isSyncLoading()) {
displayTask.run();
} else {
engine.submit(displayTask);
}
}
}
/**
* Adds display image task to execution pool. Image will be set to ImageView when it's turn.
* Default {@linkplain DisplayImageOptions display image options} from {@linkplain ImageLoaderConfiguration
* configuration} will be used.
* NOTE: {@link #init(ImageLoaderConfiguration)} method must be called before this method call
*
* @param uri Image URI (i.e. "http://site.com/image.png", "file:///mnt/sdcard/image.png")
* @param imageView {@link ImageView} which should display image
* @throws IllegalStateException if {@link #init(ImageLoaderConfiguration)} method wasn't called before
* @throws IllegalArgumentException if passed imageView is null
*/
public void displayImage(String uri, ImageView imageView) {
displayImage(uri, new ImageViewAware(imageView), null, null, null);
}
/**
* Adds display image task to execution pool. Image will be set to ImageView when it's turn.
* Default {@linkplain DisplayImageOptions display image options} from {@linkplain ImageLoaderConfiguration
* configuration} will be used.
* NOTE: {@link #init(ImageLoaderConfiguration)} method must be called before this method call
*
* @param uri Image URI (i.e. "http://site.com/image.png", "file:///mnt/sdcard/image.png")
* @param imageView {@link ImageView} which should display image
* @throws IllegalStateException if {@link #init(ImageLoaderConfiguration)} method wasn't called before
* @throws IllegalArgumentException if passed imageView is null
*/
public void displayImage(String uri, ImageView imageView, ImageSize targetImageSize) {
displayImage(uri, new ImageViewAware(imageView), null, targetImageSize, null, null);
}
/**
* Adds display image task to execution pool. Image will be set to ImageView when it's turn.
* NOTE: {@link #init(ImageLoaderConfiguration)} method must be called before this method call
*
* @param uri Image URI (i.e. "http://site.com/image.png", "file:///mnt/sdcard/image.png")
* @param imageView {@link ImageView} which should display image
* @param options {@linkplain com.nostra13.universalimageloader.core.DisplayImageOptions Options} for image
* decoding and displaying. If null - default display image options
* {@linkplain ImageLoaderConfiguration.Builder#defaultDisplayImageOptions(DisplayImageOptions)
* from configuration} will be used.
* @throws IllegalStateException if {@link #init(ImageLoaderConfiguration)} method wasn't called before
* @throws IllegalArgumentException if passed imageView is null
*/
public void displayImage(String uri, ImageView imageView, DisplayImageOptions options) {
displayImage(uri, new ImageViewAware(imageView), options, null, null);
}
/**
* Adds display image task to execution pool. Image will be set to ImageView when it's turn.
* Default {@linkplain DisplayImageOptions display image options} from {@linkplain ImageLoaderConfiguration
* configuration} will be used.
* NOTE: {@link #init(ImageLoaderConfiguration)} method must be called before this method call
*
* @param uri Image URI (i.e. "http://site.com/image.png", "file:///mnt/sdcard/image.png")
* @param imageView {@link ImageView} which should display image
* @param listener {@linkplain ImageLoadingListener Listener} for image loading process. Listener fires events on
* UI thread if this method is called on UI thread.
* @throws IllegalStateException if {@link #init(ImageLoaderConfiguration)} method wasn't called before
* @throws IllegalArgumentException if passed imageView is null
*/
public void displayImage(String uri, ImageView imageView, ImageLoadingListener listener) {
displayImage(uri, new ImageViewAware(imageView), null, listener, null);
}
/**
* Adds display image task to execution pool. Image will be set to ImageView when it's turn.
* NOTE: {@link #init(ImageLoaderConfiguration)} method must be called before this method call
*
* @param uri Image URI (i.e. "http://site.com/image.png", "file:///mnt/sdcard/image.png")
* @param imageView {@link ImageView} which should display image
* @param options {@linkplain com.nostra13.universalimageloader.core.DisplayImageOptions Options} for image
* decoding and displaying. If null - default display image options
* {@linkplain ImageLoaderConfiguration.Builder#defaultDisplayImageOptions(DisplayImageOptions)
* from configuration} will be used.
* @param listener {@linkplain ImageLoadingListener Listener} for image loading process. Listener fires events on
* UI thread if this method is called on UI thread.
* @throws IllegalStateException if {@link #init(ImageLoaderConfiguration)} method wasn't called before
* @throws IllegalArgumentException if passed imageView is null
*/
public void displayImage(String uri, ImageView imageView, DisplayImageOptions options,
ImageLoadingListener listener) {
displayImage(uri, imageView, options, listener, null);
}
/**
* Adds display image task to execution pool. Image will be set to ImageView when it's turn.
* NOTE: {@link #init(ImageLoaderConfiguration)} method must be called before this method call
*
* @param uri Image URI (i.e. "http://site.com/image.png", "file:///mnt/sdcard/image.png")
* @param imageView {@link ImageView} which should display image
* @param options {@linkplain com.nostra13.universalimageloader.core.DisplayImageOptions Options} for image
* decoding and displaying. If null - default display image options
* {@linkplain ImageLoaderConfiguration.Builder#defaultDisplayImageOptions(DisplayImageOptions)
* from configuration} will be used.
* @param listener {@linkplain ImageLoadingListener Listener} for image loading process. Listener fires
* events on UI thread if this method is called on UI thread.
* @param progressListener {@linkplain com.nostra13.universalimageloader.core.listener.ImageLoadingProgressListener
* Listener} for image loading progress. Listener fires events on UI thread if this method
* is called on UI thread. Caching on disk should be enabled in
* {@linkplain com.nostra13.universalimageloader.core.DisplayImageOptions options} to make
* this listener work.
* @throws IllegalStateException if {@link #init(ImageLoaderConfiguration)} method wasn't called before
* @throws IllegalArgumentException if passed imageView is null
*/
public void displayImage(String uri, ImageView imageView, DisplayImageOptions options,
ImageLoadingListener listener, ImageLoadingProgressListener progressListener) {
displayImage(uri, new ImageViewAware(imageView), options, listener, progressListener);
}
/**
* Adds load image task to execution pool. Image will be returned with
* {@link ImageLoadingListener#onLoadingComplete(String, android.view.View, android.graphics.Bitmap)} callback}.
*
* NOTE: {@link #init(ImageLoaderConfiguration)} method must be called before this method call
*
* @param uri Image URI (i.e. "http://site.com/image.png", "file:///mnt/sdcard/image.png")
* @param listener {@linkplain ImageLoadingListener Listener} for image loading process. Listener fires events on UI
* thread if this method is called on UI thread.
* @throws IllegalStateException if {@link #init(ImageLoaderConfiguration)} method wasn't called before
*/
public void loadImage(String uri, ImageLoadingListener listener) {
loadImage(uri, null, null, listener, null);
}
/**
* Adds load image task to execution pool. Image will be returned with
* {@link ImageLoadingListener#onLoadingComplete(String, android.view.View, android.graphics.Bitmap)} callback}.
*
* NOTE: {@link #init(ImageLoaderConfiguration)} method must be called before this method call
*
* @param uri Image URI (i.e. "http://site.com/image.png", "file:///mnt/sdcard/image.png")
* @param targetImageSize Minimal size for {@link Bitmap} which will be returned in
* {@linkplain ImageLoadingListener#onLoadingComplete(String, android.view.View,
* android.graphics.Bitmap)} callback}. Downloaded image will be decoded
* and scaled to {@link Bitmap} of the size which is equal or larger (usually a bit
* larger) than incoming targetImageSize.
* @param listener {@linkplain ImageLoadingListener Listener} for image loading process. Listener fires
* events on UI thread if this method is called on UI thread.
* @throws IllegalStateException if {@link #init(ImageLoaderConfiguration)} method wasn't called before
*/
public void loadImage(String uri, ImageSize targetImageSize, ImageLoadingListener listener) {
loadImage(uri, targetImageSize, null, listener, null);
}
/**
* Adds load image task to execution pool. Image will be returned with
* {@link ImageLoadingListener#onLoadingComplete(String, android.view.View, android.graphics.Bitmap)} callback}.
*
* NOTE: {@link #init(ImageLoaderConfiguration)} method must be called before this method call
*
* @param uri Image URI (i.e. "http://site.com/image.png", "file:///mnt/sdcard/image.png")
* @param options {@linkplain com.nostra13.universalimageloader.core.DisplayImageOptions Options} for image
* decoding and displaying. If null - default display image options
* {@linkplain ImageLoaderConfiguration.Builder#defaultDisplayImageOptions(DisplayImageOptions) from
* configuration} will be used.
* @param listener {@linkplain ImageLoadingListener Listener} for image loading process. Listener fires events on UI
* thread if this method is called on UI thread.
* @throws IllegalStateException if {@link #init(ImageLoaderConfiguration)} method wasn't called before
*/
public void loadImage(String uri, DisplayImageOptions options, ImageLoadingListener listener) {
loadImage(uri, null, options, listener, null);
}
/**
* Adds load image task to execution pool. Image will be returned with
* {@link ImageLoadingListener#onLoadingComplete(String, android.view.View, android.graphics.Bitmap)} callback}.
*
* NOTE: {@link #init(ImageLoaderConfiguration)} method must be called before this method call
*
* @param uri Image URI (i.e. "http://site.com/image.png", "file:///mnt/sdcard/image.png")
* @param targetImageSize Minimal size for {@link Bitmap} which will be returned in
* {@linkplain ImageLoadingListener#onLoadingComplete(String, android.view.View,
* android.graphics.Bitmap)} callback}. Downloaded image will be decoded
* and scaled to {@link Bitmap} of the size which is equal or larger (usually a bit
* larger) than incoming targetImageSize.
* @param options {@linkplain com.nostra13.universalimageloader.core.DisplayImageOptions Options} for image
* decoding and displaying. If null - default display image options
* {@linkplain ImageLoaderConfiguration.Builder#defaultDisplayImageOptions(DisplayImageOptions)
* from configuration} will be used.
* @param listener {@linkplain ImageLoadingListener Listener} for image loading process. Listener fires
* events on UI thread if this method is called on UI thread.
* @throws IllegalStateException if {@link #init(ImageLoaderConfiguration)} method wasn't called before
*/
public void loadImage(String uri, ImageSize targetImageSize, DisplayImageOptions options,
ImageLoadingListener listener) {
loadImage(uri, targetImageSize, options, listener, null);
}
/**
* Adds load image task to execution pool. Image will be returned with
* {@link ImageLoadingListener#onLoadingComplete(String, android.view.View, android.graphics.Bitmap)} callback}.
*
* NOTE: {@link #init(ImageLoaderConfiguration)} method must be called before this method call
*
* @param uri Image URI (i.e. "http://site.com/image.png", "file:///mnt/sdcard/image.png")
* @param targetImageSize Minimal size for {@link Bitmap} which will be returned in
* {@linkplain ImageLoadingListener#onLoadingComplete(String, android.view.View,
* android.graphics.Bitmap)} callback}. Downloaded image will be decoded
* and scaled to {@link Bitmap} of the size which is equal or larger (usually a bit
* larger) than incoming targetImageSize.
* @param options {@linkplain com.nostra13.universalimageloader.core.DisplayImageOptions Options} for image
* decoding and displaying. If null - default display image options
* {@linkplain ImageLoaderConfiguration.Builder#defaultDisplayImageOptions(DisplayImageOptions)
* from configuration} will be used.
* @param listener {@linkplain ImageLoadingListener Listener} for image loading process. Listener fires
* events on UI thread if this method is called on UI thread.
* @param progressListener {@linkplain com.nostra13.universalimageloader.core.listener.ImageLoadingProgressListener
* Listener} for image loading progress. Listener fires events on UI thread if this method
* is called on UI thread. Caching on disk should be enabled in
* {@linkplain com.nostra13.universalimageloader.core.DisplayImageOptions options} to make
* this listener work.
* @throws IllegalStateException if {@link #init(ImageLoaderConfiguration)} method wasn't called before
*/
public void loadImage(String uri, ImageSize targetImageSize, DisplayImageOptions options,
ImageLoadingListener listener, ImageLoadingProgressListener progressListener) {
checkConfiguration();
if (targetImageSize == null) {
targetImageSize = configuration.getMaxImageSize();
}
if (options == null) {
options = configuration.defaultDisplayImageOptions;
}
NonViewAware imageAware = new NonViewAware(uri, targetImageSize, ViewScaleType.CROP);
displayImage(uri, imageAware, options, listener, progressListener);
}
/**
* Loads and decodes image synchronously.
* Default display image options
* {@linkplain ImageLoaderConfiguration.Builder#defaultDisplayImageOptions(DisplayImageOptions) from
* configuration} will be used.
* NOTE: {@link #init(ImageLoaderConfiguration)} method must be called before this method call
*
* @param uri Image URI (i.e. "http://site.com/image.png", "file:///mnt/sdcard/image.png")
* @return Result image Bitmap. Can be null if image loading/decoding was failed or cancelled.
* @throws IllegalStateException if {@link #init(ImageLoaderConfiguration)} method wasn't called before
*/
public Bitmap loadImageSync(String uri) {
return loadImageSync(uri, null, null);
}
/**
* Loads and decodes image synchronously.
* NOTE: {@link #init(ImageLoaderConfiguration)} method must be called before this method call
*
* @param uri Image URI (i.e. "http://site.com/image.png", "file:///mnt/sdcard/image.png")
* @param options {@linkplain com.nostra13.universalimageloader.core.DisplayImageOptions Options} for image
* decoding and scaling. If null - default display image options
* {@linkplain ImageLoaderConfiguration.Builder#defaultDisplayImageOptions(DisplayImageOptions) from
* configuration} will be used.
* @return Result image Bitmap. Can be null if image loading/decoding was failed or cancelled.
* @throws IllegalStateException if {@link #init(ImageLoaderConfiguration)} method wasn't called before
*/
public Bitmap loadImageSync(String uri, DisplayImageOptions options) {
return loadImageSync(uri, null, options);
}
/**
* Loads and decodes image synchronously.
* Default display image options
* {@linkplain ImageLoaderConfiguration.Builder#defaultDisplayImageOptions(DisplayImageOptions) from
* configuration} will be used.
* NOTE: {@link #init(ImageLoaderConfiguration)} method must be called before this method call
*
* @param uri Image URI (i.e. "http://site.com/image.png", "file:///mnt/sdcard/image.png")
* @param targetImageSize Minimal size for {@link Bitmap} which will be returned. Downloaded image will be decoded
* and scaled to {@link Bitmap} of the size which is equal or larger (usually a bit
* larger) than incoming targetImageSize.
* @return Result image Bitmap. Can be null if image loading/decoding was failed or cancelled.
* @throws IllegalStateException if {@link #init(ImageLoaderConfiguration)} method wasn't called before
*/
public Bitmap loadImageSync(String uri, ImageSize targetImageSize) {
return loadImageSync(uri, targetImageSize, null);
}
/**
* Loads and decodes image synchronously.
* NOTE: {@link #init(ImageLoaderConfiguration)} method must be called before this method call
*
* @param uri Image URI (i.e. "http://site.com/image.png", "file:///mnt/sdcard/image.png")
* @param targetImageSize Minimal size for {@link Bitmap} which will be returned. Downloaded image will be decoded
* and scaled to {@link Bitmap} of the size which is equal or larger (usually a bit
* larger) than incoming targetImageSize.
* @param options {@linkplain com.nostra13.universalimageloader.core.DisplayImageOptions Options} for image
* decoding and scaling. If null - default display image options
* {@linkplain ImageLoaderConfiguration.Builder#defaultDisplayImageOptions(DisplayImageOptions)
* from configuration} will be used.
* @return Result image Bitmap. Can be null if image loading/decoding was failed or cancelled.
* @throws IllegalStateException if {@link #init(ImageLoaderConfiguration)} method wasn't called before
*/
public Bitmap loadImageSync(String uri, ImageSize targetImageSize, DisplayImageOptions options) {
if (options == null) {
options = configuration.defaultDisplayImageOptions;
}
options = new DisplayImageOptions.Builder().cloneFrom(options).syncLoading(true).build();
SyncImageLoadingListener listener = new SyncImageLoadingListener();
loadImage(uri, targetImageSize, options, listener);
return listener.getLoadedBitmap();
}
/**
* Checks if ImageLoader's configuration was initialized
*
* @throws IllegalStateException if configuration wasn't initialized
*/
private void checkConfiguration() {
if (configuration == null) {
throw new IllegalStateException(ERROR_NOT_INIT);
}
}
/** Sets a default loading listener for all display and loading tasks. */
public void setDefaultLoadingListener(ImageLoadingListener listener) {
defaultListener = listener == null ? new SimpleImageLoadingListener() : listener;
}
/**
* Returns memory cache
*
* @throws IllegalStateException if {@link #init(ImageLoaderConfiguration)} method wasn't called before
*/
public MemoryCache getMemoryCache() {
checkConfiguration();
return configuration.memoryCache;
}
/**
* Clears memory cache
*
* @throws IllegalStateException if {@link #init(ImageLoaderConfiguration)} method wasn't called before
*/
public void clearMemoryCache() {
checkConfiguration();
configuration.memoryCache.clear();
}
/**
* Returns disk cache
*
* @throws IllegalStateException if {@link #init(ImageLoaderConfiguration)} method wasn't called before
* @deprecated Use {@link #getDiskCache()} instead
*/
@Deprecated
public DiskCache getDiscCache() {
return getDiskCache();
}
/**
* Returns disk cache
*
* @throws IllegalStateException if {@link #init(ImageLoaderConfiguration)} method wasn't called before
*/
public DiskCache getDiskCache() {
checkConfiguration();
return configuration.diskCache;
}
/**
* Clears disk cache.
*
* @throws IllegalStateException if {@link #init(ImageLoaderConfiguration)} method wasn't called before
* @deprecated Use {@link #clearDiskCache()} instead
*/
@Deprecated
public void clearDiscCache() {
clearDiskCache();
}
/**
* Clears disk cache.
*
* @throws IllegalStateException if {@link #init(ImageLoaderConfiguration)} method wasn't called before
*/
public void clearDiskCache() {
checkConfiguration();
configuration.diskCache.clear();
}
/**
* Returns URI of image which is loading at this moment into passed
* {@link com.nostra13.universalimageloader.core.imageaware.ImageAware ImageAware}
*/
public String getLoadingUriForView(ImageAware imageAware) {
return engine.getLoadingUriForView(imageAware);
}
/**
* Returns URI of image which is loading at this moment into passed
* {@link android.widget.ImageView ImageView}
*/
public String getLoadingUriForView(ImageView imageView) {
return engine.getLoadingUriForView(new ImageViewAware(imageView));
}
/**
* Cancel the task of loading and displaying image for passed
* {@link com.nostra13.universalimageloader.core.imageaware.ImageAware ImageAware}.
*
* @param imageAware {@link com.nostra13.universalimageloader.core.imageaware.ImageAware ImageAware} for
* which display task will be cancelled
*/
public void cancelDisplayTask(ImageAware imageAware) {
engine.cancelDisplayTaskFor(imageAware);
}
/**
* Cancel the task of loading and displaying image for passed
* {@link android.widget.ImageView ImageView}.
*
* @param imageView {@link android.widget.ImageView ImageView} for which display task will be cancelled
*/
public void cancelDisplayTask(ImageView imageView) {
engine.cancelDisplayTaskFor(new ImageViewAware(imageView));
}
/**
* Denies or allows ImageLoader to download images from the network.
*
* If downloads are denied and if image isn't cached then
* {@link ImageLoadingListener#onLoadingFailed(String, View, FailReason)} callback will be fired with
* {@link FailReason.FailType#NETWORK_DENIED}
*
* @param denyNetworkDownloads pass true - to deny engine to download images from the network; false -
* to allow engine to download images from network.
*/
public void denyNetworkDownloads(boolean denyNetworkDownloads) {
engine.denyNetworkDownloads(denyNetworkDownloads);
}
/**
* Sets option whether ImageLoader will use {@link FlushedInputStream} for network downloads to handle this known problem or not.
*
* @param handleSlowNetwork pass true - to use {@link FlushedInputStream} for network downloads; false
* - otherwise.
*/
public void handleSlowNetwork(boolean handleSlowNetwork) {
engine.handleSlowNetwork(handleSlowNetwork);
}
/**
* Pause ImageLoader. All new "load&display" tasks won't be executed until ImageLoader is {@link #resume() resumed}.
*
* Already running tasks are not paused.
*/
public void pause() {
if (isInited()) {
engine.pause();
} else {
L.w("Trying to pause not-initialized ImageLoader");
}
}
/** Resumes waiting "load&display" tasks */
public void resume() {
if (isInited()) {
engine.resume();
} else {
L.w("Trying to resume not-initialized ImageLoader");
}
}
/**
* Cancels all running and scheduled display image tasks.
* NOTE: This method doesn't shutdown
* {@linkplain com.nostra13.universalimageloader.core.ImageLoaderConfiguration.Builder#taskExecutor(java.util.concurrent.Executor)
* custom task executors} if you set them.
* ImageLoader still can be used after calling this method.
*/
public void stop() {
if (isInited()) {
engine.stop();
} else {
L.w("Trying to stop not-initialized ImageLoader");
}
}
/**
* {@linkplain #stop() Stops ImageLoader} and clears current configuration.
* You can {@linkplain #init(ImageLoaderConfiguration) init} ImageLoader with new configuration after calling this
* method.
*/
public void destroy() {
if (isInited()) {
L.d(LOG_DESTROY);
stop();
configuration.diskCache.close();
engine = null;
configuration = null;
} else {
L.w("Trying to destroy not-initialized ImageLoader");
}
}
private static Handler defineHandler(DisplayImageOptions options) {
Handler handler = options.getHandler();
if (options.isSyncLoading()) {
handler = null;
} else if (handler == null && Looper.myLooper() == Looper.getMainLooper()) {
handler = new Handler();
}
return handler;
}
/**
* Listener which is designed for synchronous image loading.
*
* @author Sergey Tarasevich (nostra13[at]gmail[dot]com)
* @since 1.9.0
*/
private static class SyncImageLoadingListener extends SimpleImageLoadingListener {
private Bitmap loadedImage;
@Override
public void onLoadingComplete(String imageUri, View view, Bitmap loadedImage) {
this.loadedImage = loadedImage;
}
public Bitmap getLoadedBitmap() {
return loadedImage;
}
}
}
================================================
FILE: library/src/main/java/com/nostra13/universalimageloader/core/ImageLoaderConfiguration.java
================================================
/*******************************************************************************
* Copyright 2011-2014 Sergey Tarasevich
*
* 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.nostra13.universalimageloader.core;
import android.content.Context;
import android.content.res.Resources;
import android.util.DisplayMetrics;
import com.nostra13.universalimageloader.cache.disc.DiskCache;
import com.nostra13.universalimageloader.cache.disc.naming.FileNameGenerator;
import com.nostra13.universalimageloader.cache.memory.MemoryCache;
import com.nostra13.universalimageloader.cache.memory.impl.FuzzyKeyMemoryCache;
import com.nostra13.universalimageloader.core.assist.FlushedInputStream;
import com.nostra13.universalimageloader.core.assist.ImageSize;
import com.nostra13.universalimageloader.core.assist.QueueProcessingType;
import com.nostra13.universalimageloader.core.decode.ImageDecoder;
import com.nostra13.universalimageloader.core.download.ImageDownloader;
import com.nostra13.universalimageloader.core.process.BitmapProcessor;
import com.nostra13.universalimageloader.utils.L;
import com.nostra13.universalimageloader.utils.MemoryCacheUtils;
import java.io.IOException;
import java.io.InputStream;
import java.util.concurrent.Executor;
/**
* Presents configuration for {@link ImageLoader}
*
* @author Sergey Tarasevich (nostra13[at]gmail[dot]com)
* @see ImageLoader
* @see MemoryCache
* @see DiskCache
* @see DisplayImageOptions
* @see ImageDownloader
* @see FileNameGenerator
* @since 1.0.0
*/
public final class ImageLoaderConfiguration {
final Resources resources;
final int maxImageWidthForMemoryCache;
final int maxImageHeightForMemoryCache;
final int maxImageWidthForDiskCache;
final int maxImageHeightForDiskCache;
final BitmapProcessor processorForDiskCache;
final Executor taskExecutor;
final Executor taskExecutorForCachedImages;
final boolean customExecutor;
final boolean customExecutorForCachedImages;
final int threadPoolSize;
final int threadPriority;
final QueueProcessingType tasksProcessingType;
final MemoryCache memoryCache;
final DiskCache diskCache;
final ImageDownloader downloader;
final ImageDecoder decoder;
final DisplayImageOptions defaultDisplayImageOptions;
final ImageDownloader networkDeniedDownloader;
final ImageDownloader slowNetworkDownloader;
private ImageLoaderConfiguration(final Builder builder) {
resources = builder.context.getResources();
maxImageWidthForMemoryCache = builder.maxImageWidthForMemoryCache;
maxImageHeightForMemoryCache = builder.maxImageHeightForMemoryCache;
maxImageWidthForDiskCache = builder.maxImageWidthForDiskCache;
maxImageHeightForDiskCache = builder.maxImageHeightForDiskCache;
processorForDiskCache = builder.processorForDiskCache;
taskExecutor = builder.taskExecutor;
taskExecutorForCachedImages = builder.taskExecutorForCachedImages;
threadPoolSize = builder.threadPoolSize;
threadPriority = builder.threadPriority;
tasksProcessingType = builder.tasksProcessingType;
diskCache = builder.diskCache;
memoryCache = builder.memoryCache;
defaultDisplayImageOptions = builder.defaultDisplayImageOptions;
downloader = builder.downloader;
decoder = builder.decoder;
customExecutor = builder.customExecutor;
customExecutorForCachedImages = builder.customExecutorForCachedImages;
networkDeniedDownloader = new NetworkDeniedImageDownloader(downloader);
slowNetworkDownloader = new SlowNetworkImageDownloader(downloader);
L.writeDebugLogs(builder.writeLogs);
}
/**
* Creates default configuration for {@link ImageLoader}
* Default values:
*
*
*/
public static ImageLoaderConfiguration createDefault(Context context) {
return new Builder(context).build();
}
ImageSize getMaxImageSize() {
DisplayMetrics displayMetrics = resources.getDisplayMetrics();
int width = maxImageWidthForMemoryCache;
if (width <= 0) {
width = displayMetrics.widthPixels;
}
int height = maxImageHeightForMemoryCache;
if (height <= 0) {
height = displayMetrics.heightPixels;
}
return new ImageSize(width, height);
}
/**
* Builder for {@link ImageLoaderConfiguration}
*
* @author Sergey Tarasevich (nostra13[at]gmail[dot]com)
*/
public static class Builder {
private static final String WARNING_OVERLAP_DISK_CACHE_PARAMS = "diskCache(), diskCacheSize() and diskCacheFileCount calls overlap each other";
private static final String WARNING_OVERLAP_DISK_CACHE_NAME_GENERATOR = "diskCache() and diskCacheFileNameGenerator() calls overlap each other";
private static final String WARNING_OVERLAP_MEMORY_CACHE = "memoryCache() and memoryCacheSize() calls overlap each other";
private static final String WARNING_OVERLAP_EXECUTOR = "threadPoolSize(), threadPriority() and tasksProcessingOrder() calls "
+ "can overlap taskExecutor() and taskExecutorForCachedImages() calls.";
/** {@value} */
public static final int DEFAULT_THREAD_POOL_SIZE = 3;
/** {@value} */
public static final int DEFAULT_THREAD_PRIORITY = Thread.NORM_PRIORITY - 2;
/** {@value} */
public static final QueueProcessingType DEFAULT_TASK_PROCESSING_TYPE = QueueProcessingType.FIFO;
private Context context;
private int maxImageWidthForMemoryCache = 0;
private int maxImageHeightForMemoryCache = 0;
private int maxImageWidthForDiskCache = 0;
private int maxImageHeightForDiskCache = 0;
private BitmapProcessor processorForDiskCache = null;
private Executor taskExecutor = null;
private Executor taskExecutorForCachedImages = null;
private boolean customExecutor = false;
private boolean customExecutorForCachedImages = false;
private int threadPoolSize = DEFAULT_THREAD_POOL_SIZE;
private int threadPriority = DEFAULT_THREAD_PRIORITY;
private boolean denyCacheImageMultipleSizesInMemory = false;
private QueueProcessingType tasksProcessingType = DEFAULT_TASK_PROCESSING_TYPE;
private int memoryCacheSize = 0;
private long diskCacheSize = 0;
private int diskCacheFileCount = 0;
private MemoryCache memoryCache = null;
private DiskCache diskCache = null;
private FileNameGenerator diskCacheFileNameGenerator = null;
private ImageDownloader downloader = null;
private ImageDecoder decoder;
private DisplayImageOptions defaultDisplayImageOptions = null;
private boolean writeLogs = false;
public Builder(Context context) {
this.context = context.getApplicationContext();
}
/**
* Sets options for memory cache
*
* @param maxImageWidthForMemoryCache Maximum image width which will be used for memory saving during decoding
* an image to {@link android.graphics.Bitmap Bitmap}. Default value - device's screen width
* @param maxImageHeightForMemoryCache Maximum image height which will be used for memory saving during decoding
* an image to {@link android.graphics.Bitmap Bitmap}. Default value - device's screen height
*/
public Builder memoryCacheExtraOptions(int maxImageWidthForMemoryCache, int maxImageHeightForMemoryCache) {
this.maxImageWidthForMemoryCache = maxImageWidthForMemoryCache;
this.maxImageHeightForMemoryCache = maxImageHeightForMemoryCache;
return this;
}
/**
* @deprecated Use
* {@link #diskCacheExtraOptions(int, int, com.nostra13.universalimageloader.core.process.BitmapProcessor)}
* instead
*/
@Deprecated
public Builder discCacheExtraOptions(int maxImageWidthForDiskCache, int maxImageHeightForDiskCache,
BitmapProcessor processorForDiskCache) {
return diskCacheExtraOptions(maxImageWidthForDiskCache, maxImageHeightForDiskCache, processorForDiskCache);
}
/**
* Sets options for resizing/compressing of downloaded images before saving to disk cache.
* NOTE: Use this option only when you have appropriate needs. It can make ImageLoader slower.
*
* @param maxImageWidthForDiskCache Maximum width of downloaded images for saving at disk cache
* @param maxImageHeightForDiskCache Maximum height of downloaded images for saving at disk cache
* @param processorForDiskCache null-ok; {@linkplain BitmapProcessor Bitmap processor} which process images before saving them in disc cache
*/
public Builder diskCacheExtraOptions(int maxImageWidthForDiskCache, int maxImageHeightForDiskCache,
BitmapProcessor processorForDiskCache) {
this.maxImageWidthForDiskCache = maxImageWidthForDiskCache;
this.maxImageHeightForDiskCache = maxImageHeightForDiskCache;
this.processorForDiskCache = processorForDiskCache;
return this;
}
/**
* Sets custom {@linkplain Executor executor} for tasks of loading and displaying images.
*
* NOTE: If you set custom executor then following configuration options will not be considered for this
* executor:
*
*
*
* @see #taskExecutorForCachedImages(Executor)
*/
public Builder taskExecutor(Executor executor) {
if (threadPoolSize != DEFAULT_THREAD_POOL_SIZE || threadPriority != DEFAULT_THREAD_PRIORITY || tasksProcessingType != DEFAULT_TASK_PROCESSING_TYPE) {
L.w(WARNING_OVERLAP_EXECUTOR);
}
this.taskExecutor = executor;
return this;
}
/**
* Sets custom {@linkplain Executor executor} for tasks of displaying cached on disk images (these tasks
* are executed quickly so UIL prefer to use separate executor for them).
*
* If you set the same executor for {@linkplain #taskExecutor(Executor) general tasks} and
* tasks about cached images (this method) then these tasks will be in the
* same thread pool. So short-lived tasks can wait a long time for their turn.
*
* NOTE: If you set custom executor then following configuration options will not be considered for this
* executor:
*
*
*
* @see #taskExecutor(Executor)
*/
public Builder taskExecutorForCachedImages(Executor executorForCachedImages) {
if (threadPoolSize != DEFAULT_THREAD_POOL_SIZE || threadPriority != DEFAULT_THREAD_PRIORITY || tasksProcessingType != DEFAULT_TASK_PROCESSING_TYPE) {
L.w(WARNING_OVERLAP_EXECUTOR);
}
this.taskExecutorForCachedImages = executorForCachedImages;
return this;
}
/**
* Sets thread pool size for image display tasks.
* Default value - {@link #DEFAULT_THREAD_POOL_SIZE this}
*/
public Builder threadPoolSize(int threadPoolSize) {
if (taskExecutor != null || taskExecutorForCachedImages != null) {
L.w(WARNING_OVERLAP_EXECUTOR);
}
this.threadPoolSize = threadPoolSize;
return this;
}
/**
* Sets the priority for image loading threads. Should be NOT greater than {@link Thread#MAX_PRIORITY} or
* less than {@link Thread#MIN_PRIORITY}
* Default value - {@link #DEFAULT_THREAD_PRIORITY this}
*/
public Builder threadPriority(int threadPriority) {
if (taskExecutor != null || taskExecutorForCachedImages != null) {
L.w(WARNING_OVERLAP_EXECUTOR);
}
if (threadPriority < Thread.MIN_PRIORITY) {
this.threadPriority = Thread.MIN_PRIORITY;
} else {
if (threadPriority > Thread.MAX_PRIORITY) {
this.threadPriority = Thread.MAX_PRIORITY;
} else {
this.threadPriority = threadPriority;
}
}
return this;
}
/**
* When you display an image in a small {@link android.widget.ImageView ImageView} and later you try to display
* this image (from identical URI) in a larger {@link android.widget.ImageView ImageView} so decoded image of
* bigger size will be cached in memory as a previous decoded image of smaller size.
* So the default behavior is to allow to cache multiple sizes of one image in memory. You can
* deny it by calling this method: so when some image will be cached in memory then previous
* cached size of this image (if it exists) will be removed from memory cache before.
*/
public Builder denyCacheImageMultipleSizesInMemory() {
this.denyCacheImageMultipleSizesInMemory = true;
return this;
}
/**
* Sets type of queue processing for tasks for loading and displaying images.
* Default value - {@link QueueProcessingType#FIFO}
*/
public Builder tasksProcessingOrder(QueueProcessingType tasksProcessingType) {
if (taskExecutor != null || taskExecutorForCachedImages != null) {
L.w(WARNING_OVERLAP_EXECUTOR);
}
this.tasksProcessingType = tasksProcessingType;
return this;
}
/**
* Sets maximum memory cache size for {@link android.graphics.Bitmap bitmaps} (in bytes).
* Default value - 1/8 of available app memory.
* NOTE: If you use this method then
* {@link com.nostra13.universalimageloader.cache.memory.impl.LruMemoryCache LruMemoryCache} will be used as
* memory cache. You can use {@link #memoryCache(MemoryCache)} method to set your own implementation of
* {@link MemoryCache}.
*/
public Builder memoryCacheSize(int memoryCacheSize) {
if (memoryCacheSize <= 0) throw new IllegalArgumentException("memoryCacheSize must be a positive number");
if (memoryCache != null) {
L.w(WARNING_OVERLAP_MEMORY_CACHE);
}
this.memoryCacheSize = memoryCacheSize;
return this;
}
/**
* Sets maximum memory cache size (in percent of available app memory) for {@link android.graphics.Bitmap
* bitmaps}.
* Default value - 1/8 of available app memory.
* NOTE: If you use this method then
* {@link com.nostra13.universalimageloader.cache.memory.impl.LruMemoryCache LruMemoryCache} will be used as
* memory cache. You can use {@link #memoryCache(MemoryCache)} method to set your own implementation of
* {@link MemoryCache}.
*/
public Builder memoryCacheSizePercentage(int availableMemoryPercent) {
if (availableMemoryPercent <= 0 || availableMemoryPercent >= 100) {
throw new IllegalArgumentException("availableMemoryPercent must be in range (0 < % < 100)");
}
if (memoryCache != null) {
L.w(WARNING_OVERLAP_MEMORY_CACHE);
}
long availableMemory = Runtime.getRuntime().maxMemory();
memoryCacheSize = (int) (availableMemory * (availableMemoryPercent / 100f));
return this;
}
/**
* Sets memory cache for {@link android.graphics.Bitmap bitmaps}.
* Default value - {@link com.nostra13.universalimageloader.cache.memory.impl.LruMemoryCache LruMemoryCache}
* with limited memory cache size (size = 1/8 of available app memory)
*
* NOTE: If you set custom memory cache then following configuration option will not be considered:
*
*
*/
public Builder memoryCache(MemoryCache memoryCache) {
if (memoryCacheSize != 0) {
L.w(WARNING_OVERLAP_MEMORY_CACHE);
}
this.memoryCache = memoryCache;
return this;
}
/** @deprecated Use {@link #diskCacheSize(int)} instead */
@Deprecated
public Builder discCacheSize(int maxCacheSize) {
return diskCacheSize(maxCacheSize);
}
/**
* Sets maximum disk cache size for images (in bytes).
* By default: disk cache is unlimited.
* NOTE: If you use this method then
* {@link com.nostra13.universalimageloader.cache.disc.impl.ext.LruDiskCache LruDiskCache}
* will be used as disk cache. You can use {@link #diskCache(DiskCache)} method for introduction your own
* implementation of {@link DiskCache}
*/
public Builder diskCacheSize(int maxCacheSize) {
if (maxCacheSize <= 0) throw new IllegalArgumentException("maxCacheSize must be a positive number");
if (diskCache != null) {
L.w(WARNING_OVERLAP_DISK_CACHE_PARAMS);
}
this.diskCacheSize = maxCacheSize;
return this;
}
/** @deprecated Use {@link #diskCacheFileCount(int)} instead */
@Deprecated
public Builder discCacheFileCount(int maxFileCount) {
return diskCacheFileCount(maxFileCount);
}
/**
* Sets maximum file count in disk cache directory.
* By default: disk cache is unlimited.
* NOTE: If you use this method then
* {@link com.nostra13.universalimageloader.cache.disc.impl.ext.LruDiskCache LruDiskCache}
* will be used as disk cache. You can use {@link #diskCache(DiskCache)} method for introduction your own
* implementation of {@link DiskCache}
*/
public Builder diskCacheFileCount(int maxFileCount) {
if (maxFileCount <= 0) throw new IllegalArgumentException("maxFileCount must be a positive number");
if (diskCache != null) {
L.w(WARNING_OVERLAP_DISK_CACHE_PARAMS);
}
this.diskCacheFileCount = maxFileCount;
return this;
}
/** @deprecated Use {@link #diskCacheFileNameGenerator(com.nostra13.universalimageloader.cache.disc.naming.FileNameGenerator)} */
@Deprecated
public Builder discCacheFileNameGenerator(FileNameGenerator fileNameGenerator) {
return diskCacheFileNameGenerator(fileNameGenerator);
}
/**
* Sets name generator for files cached in disk cache.
* Default value -
* {@link com.nostra13.universalimageloader.core.DefaultConfigurationFactory#createFileNameGenerator()
* DefaultConfigurationFactory.createFileNameGenerator()}
*/
public Builder diskCacheFileNameGenerator(FileNameGenerator fileNameGenerator) {
if (diskCache != null) {
L.w(WARNING_OVERLAP_DISK_CACHE_NAME_GENERATOR);
}
this.diskCacheFileNameGenerator = fileNameGenerator;
return this;
}
/** @deprecated Use {@link #diskCache(com.nostra13.universalimageloader.cache.disc.DiskCache)} */
@Deprecated
public Builder discCache(DiskCache diskCache) {
return diskCache(diskCache);
}
/**
* Sets disk cache for images.
* Default value - {@link com.nostra13.universalimageloader.cache.disc.impl.UnlimitedDiskCache
* UnlimitedDiskCache}. Cache directory is defined by
* {@link com.nostra13.universalimageloader.utils.StorageUtils#getCacheDirectory(Context)
* StorageUtils.getCacheDirectory(Context)}.
*
* NOTE: If you set custom disk cache then following configuration option will not be considered:
*
*
*/
public Builder diskCache(DiskCache diskCache) {
if (diskCacheSize > 0 || diskCacheFileCount > 0) {
L.w(WARNING_OVERLAP_DISK_CACHE_PARAMS);
}
if (diskCacheFileNameGenerator != null) {
L.w(WARNING_OVERLAP_DISK_CACHE_NAME_GENERATOR);
}
this.diskCache = diskCache;
return this;
}
/**
* Sets utility which will be responsible for downloading of image.
* Default value -
* {@link com.nostra13.universalimageloader.core.DefaultConfigurationFactory#createImageDownloader(Context)
* DefaultConfigurationFactory.createImageDownloader()}
*/
public Builder imageDownloader(ImageDownloader imageDownloader) {
this.downloader = imageDownloader;
return this;
}
/**
* Sets utility which will be responsible for decoding of image stream.
* Default value -
* {@link com.nostra13.universalimageloader.core.DefaultConfigurationFactory#createImageDecoder(boolean)
* DefaultConfigurationFactory.createImageDecoder()}
*/
public Builder imageDecoder(ImageDecoder imageDecoder) {
this.decoder = imageDecoder;
return this;
}
/**
* Sets default {@linkplain DisplayImageOptions display image options} for image displaying. These options will
* be used for every {@linkplain ImageLoader#displayImage(String, android.widget.ImageView) image display call}
* without passing custom {@linkplain DisplayImageOptions options}
* Default value - {@link DisplayImageOptions#createSimple() Simple options}
*/
public Builder defaultDisplayImageOptions(DisplayImageOptions defaultDisplayImageOptions) {
this.defaultDisplayImageOptions = defaultDisplayImageOptions;
return this;
}
/**
* Enables detail logging of {@link ImageLoader} work. To prevent detail logs don't call this method.
* Consider {@link com.nostra13.universalimageloader.utils.L#disableLogging()} to disable
* ImageLoader logging completely (even error logs)
*/
public Builder writeDebugLogs() {
this.writeLogs = true;
return this;
}
/** Builds configured {@link ImageLoaderConfiguration} object */
public ImageLoaderConfiguration build() {
initEmptyFieldsWithDefaultValues();
return new ImageLoaderConfiguration(this);
}
private void initEmptyFieldsWithDefaultValues() {
if (taskExecutor == null) {
taskExecutor = DefaultConfigurationFactory
.createExecutor(threadPoolSize, threadPriority, tasksProcessingType);
} else {
customExecutor = true;
}
if (taskExecutorForCachedImages == null) {
taskExecutorForCachedImages = DefaultConfigurationFactory
.createExecutor(threadPoolSize, threadPriority, tasksProcessingType);
} else {
customExecutorForCachedImages = true;
}
if (diskCache == null) {
if (diskCacheFileNameGenerator == null) {
diskCacheFileNameGenerator = DefaultConfigurationFactory.createFileNameGenerator();
}
diskCache = DefaultConfigurationFactory
.createDiskCache(context, diskCacheFileNameGenerator, diskCacheSize, diskCacheFileCount);
}
if (memoryCache == null) {
memoryCache = DefaultConfigurationFactory.createMemoryCache(context, memoryCacheSize);
}
if (denyCacheImageMultipleSizesInMemory) {
memoryCache = new FuzzyKeyMemoryCache(memoryCache, MemoryCacheUtils.createFuzzyKeyComparator());
}
if (downloader == null) {
downloader = DefaultConfigurationFactory.createImageDownloader(context);
}
if (decoder == null) {
decoder = DefaultConfigurationFactory.createImageDecoder(writeLogs);
}
if (defaultDisplayImageOptions == null) {
defaultDisplayImageOptions = DisplayImageOptions.createSimple();
}
}
}
/**
* Decorator. Prevents downloads from network (throws {@link IllegalStateException exception}).
* In most cases this downloader shouldn't be used directly.
*
* @author Sergey Tarasevich (nostra13[at]gmail[dot]com)
* @since 1.8.0
*/
private static class NetworkDeniedImageDownloader implements ImageDownloader {
private final ImageDownloader wrappedDownloader;
public NetworkDeniedImageDownloader(ImageDownloader wrappedDownloader) {
this.wrappedDownloader = wrappedDownloader;
}
@Override
public InputStream getStream(String imageUri, Object extra) throws IOException {
switch (Scheme.ofUri(imageUri)) {
case HTTP:
case HTTPS:
throw new IllegalStateException();
default:
return wrappedDownloader.getStream(imageUri, extra);
}
}
}
/**
* Decorator. Handles this problem on slow networks
* using {@link com.nostra13.universalimageloader.core.assist.FlushedInputStream}.
*
* @author Sergey Tarasevich (nostra13[at]gmail[dot]com)
* @since 1.8.1
*/
private static class SlowNetworkImageDownloader implements ImageDownloader {
private final ImageDownloader wrappedDownloader;
public SlowNetworkImageDownloader(ImageDownloader wrappedDownloader) {
this.wrappedDownloader = wrappedDownloader;
}
@Override
public InputStream getStream(String imageUri, Object extra) throws IOException {
InputStream imageStream = wrappedDownloader.getStream(imageUri, extra);
switch (Scheme.ofUri(imageUri)) {
case HTTP:
case HTTPS:
return new FlushedInputStream(imageStream);
default:
return imageStream;
}
}
}
}
================================================
FILE: library/src/main/java/com/nostra13/universalimageloader/core/ImageLoaderEngine.java
================================================
/*******************************************************************************
* Copyright 2011-2014 Sergey Tarasevich
*
* 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.nostra13.universalimageloader.core;
import android.view.View;
import com.nostra13.universalimageloader.core.assist.FailReason;
import com.nostra13.universalimageloader.core.assist.FlushedInputStream;
import com.nostra13.universalimageloader.core.imageaware.ImageAware;
import com.nostra13.universalimageloader.core.listener.ImageLoadingListener;
import java.io.File;
import java.util.Collections;
import java.util.HashMap;
import java.util.Map;
import java.util.WeakHashMap;
import java.util.concurrent.Executor;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.atomic.AtomicBoolean;
import java.util.concurrent.locks.ReentrantLock;
import static com.nostra13.universalimageloader.core.download.ImageDownloader.*;
/**
* {@link ImageLoader} engine which responsible for {@linkplain LoadAndDisplayImageTask display task} execution.
*
* @author Sergey Tarasevich (nostra13[at]gmail[dot]com)
* @since 1.7.1
*/
class ImageLoaderEngine {
final ImageLoaderConfiguration configuration;
private Executor taskExecutor;
private Executor taskExecutorForCachedImages;
private Executor taskDistributor;
private final Map
If downloads are denied and if image
* isn't cached then {@link ImageLoadingListener#onLoadingFailed(String, View, FailReason)} callback will be fired
* with {@link FailReason.FailType#NETWORK_DENIED}
*
* @param denyNetworkDownloads pass true - to deny engine to download images from the network; false -
* to allow engine to download images from network.
*/
void denyNetworkDownloads(boolean denyNetworkDownloads) {
networkDenied.set(denyNetworkDownloads);
}
/**
* Sets option whether ImageLoader will use {@link FlushedInputStream} for network downloads to handle this known problem or not.
*
* @param handleSlowNetwork pass true - to use {@link FlushedInputStream} for network downloads; false
* - otherwise.
*/
void handleSlowNetwork(boolean handleSlowNetwork) {
slowNetwork.set(handleSlowNetwork);
}
/**
* Pauses engine. All new "load&display" tasks won't be executed until ImageLoader is {@link #resume() resumed}.
Already running tasks are not paused.
*/
void pause() {
paused.set(true);
}
/** Resumes engine work. Paused "load&display" tasks will continue its work. */
void resume() {
paused.set(false);
synchronized (pauseLock) {
pauseLock.notifyAll();
}
}
/**
* Stops engine, cancels all running and scheduled display image tasks. Clears internal data.
*
* NOTE: This method doesn't shutdown
* {@linkplain com.nostra13.universalimageloader.core.ImageLoaderConfiguration.Builder#taskExecutor(java.util.concurrent.Executor)
* custom task executors} if you set them.
*/
void stop() {
if (!configuration.customExecutor) {
((ExecutorService) taskExecutor).shutdownNow();
}
if (!configuration.customExecutorForCachedImages) {
((ExecutorService) taskExecutorForCachedImages).shutdownNow();
}
cacheKeysForImageAwares.clear();
uriLocks.clear();
}
void fireCallback(Runnable r) {
taskDistributor.execute(r);
}
ReentrantLock getLockForUri(String uri) {
ReentrantLock lock = uriLocks.get(uri);
if (lock == null) {
lock = new ReentrantLock();
uriLocks.put(uri, lock);
}
return lock;
}
AtomicBoolean getPause() {
return paused;
}
Object getPauseLock() {
return pauseLock;
}
boolean isNetworkDenied() {
return networkDenied.get();
}
boolean isSlowNetwork() {
return slowNetwork.get();
}
}
================================================
FILE: library/src/main/java/com/nostra13/universalimageloader/core/ImageLoadingInfo.java
================================================
/*******************************************************************************
* Copyright 2011-2013 Sergey Tarasevich
*
* 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.nostra13.universalimageloader.core;
import com.nostra13.universalimageloader.core.listener.ImageLoadingListener;
import com.nostra13.universalimageloader.core.listener.ImageLoadingProgressListener;
import com.nostra13.universalimageloader.core.assist.ImageSize;
import com.nostra13.universalimageloader.core.imageaware.ImageAware;
import java.util.concurrent.locks.ReentrantLock;
/**
* Information for load'n'display image task
*
* @author Sergey Tarasevich (nostra13[at]gmail[dot]com)
* @see com.nostra13.universalimageloader.utils.MemoryCacheUtils
* @see DisplayImageOptions
* @see ImageLoadingListener
* @see com.nostra13.universalimageloader.core.listener.ImageLoadingProgressListener
* @since 1.3.1
*/
final class ImageLoadingInfo {
final String uri;
final String memoryCacheKey;
final ImageAware imageAware;
final ImageSize targetSize;
final DisplayImageOptions options;
final ImageLoadingListener listener;
final ImageLoadingProgressListener progressListener;
final ReentrantLock loadFromUriLock;
public ImageLoadingInfo(String uri, ImageAware imageAware, ImageSize targetSize, String memoryCacheKey,
DisplayImageOptions options, ImageLoadingListener listener,
ImageLoadingProgressListener progressListener, ReentrantLock loadFromUriLock) {
this.uri = uri;
this.imageAware = imageAware;
this.targetSize = targetSize;
this.options = options;
this.listener = listener;
this.progressListener = progressListener;
this.loadFromUriLock = loadFromUriLock;
this.memoryCacheKey = memoryCacheKey;
}
}
================================================
FILE: library/src/main/java/com/nostra13/universalimageloader/core/LoadAndDisplayImageTask.java
================================================
/*******************************************************************************
* Copyright 2011-2014 Sergey Tarasevich
*
* 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.nostra13.universalimageloader.core;
import android.graphics.Bitmap;
import android.os.Handler;
import com.nostra13.universalimageloader.core.assist.FailReason;
import com.nostra13.universalimageloader.core.assist.FailReason.FailType;
import com.nostra13.universalimageloader.core.assist.ImageScaleType;
import com.nostra13.universalimageloader.core.assist.ImageSize;
import com.nostra13.universalimageloader.core.assist.LoadedFrom;
import com.nostra13.universalimageloader.core.assist.ViewScaleType;
import com.nostra13.universalimageloader.core.decode.ImageDecoder;
import com.nostra13.universalimageloader.core.decode.ImageDecodingInfo;
import com.nostra13.universalimageloader.core.download.ImageDownloader;
import com.nostra13.universalimageloader.core.download.ImageDownloader.Scheme;
import com.nostra13.universalimageloader.core.imageaware.ImageAware;
import com.nostra13.universalimageloader.core.listener.ImageLoadingListener;
import com.nostra13.universalimageloader.core.listener.ImageLoadingProgressListener;
import com.nostra13.universalimageloader.utils.IoUtils;
import com.nostra13.universalimageloader.utils.L;
import java.io.File;
import java.io.IOException;
import java.io.InputStream;
import java.util.concurrent.atomic.AtomicBoolean;
import java.util.concurrent.locks.ReentrantLock;
/**
* Presents load'n'display image task. Used to load image from Internet or file system, decode it to {@link Bitmap}, and
* display it in {@link com.nostra13.universalimageloader.core.imageaware.ImageAware} using {@link DisplayBitmapTask}.
*
* @author Sergey Tarasevich (nostra13[at]gmail[dot]com)
* @see ImageLoaderConfiguration
* @see ImageLoadingInfo
* @since 1.3.1
*/
final class LoadAndDisplayImageTask implements Runnable, IoUtils.CopyListener {
private static final String LOG_WAITING_FOR_RESUME = "ImageLoader is paused. Waiting... [%s]";
private static final String LOG_RESUME_AFTER_PAUSE = ".. Resume loading [%s]";
private static final String LOG_DELAY_BEFORE_LOADING = "Delay %d ms before loading... [%s]";
private static final String LOG_START_DISPLAY_IMAGE_TASK = "Start display image task [%s]";
private static final String LOG_WAITING_FOR_IMAGE_LOADED = "Image already is loading. Waiting... [%s]";
private static final String LOG_GET_IMAGE_FROM_MEMORY_CACHE_AFTER_WAITING = "...Get cached bitmap from memory after waiting. [%s]";
private static final String LOG_LOAD_IMAGE_FROM_NETWORK = "Load image from network [%s]";
private static final String LOG_LOAD_IMAGE_FROM_DISK_CACHE = "Load image from disk cache [%s]";
private static final String LOG_RESIZE_CACHED_IMAGE_FILE = "Resize image in disk cache [%s]";
private static final String LOG_PREPROCESS_IMAGE = "PreProcess image before caching in memory [%s]";
private static final String LOG_POSTPROCESS_IMAGE = "PostProcess image before displaying [%s]";
private static final String LOG_CACHE_IMAGE_IN_MEMORY = "Cache image in memory [%s]";
private static final String LOG_CACHE_IMAGE_ON_DISK = "Cache image on disk [%s]";
private static final String LOG_PROCESS_IMAGE_BEFORE_CACHE_ON_DISK = "Process image before cache on disk [%s]";
private static final String LOG_TASK_CANCELLED_IMAGEAWARE_REUSED = "ImageAware is reused for another image. Task is cancelled. [%s]";
private static final String LOG_TASK_CANCELLED_IMAGEAWARE_COLLECTED = "ImageAware was collected by GC. Task is cancelled. [%s]";
private static final String LOG_TASK_INTERRUPTED = "Task was interrupted [%s]";
private static final String ERROR_NO_IMAGE_STREAM = "No stream for image [%s]";
private static final String ERROR_PRE_PROCESSOR_NULL = "Pre-processor returned null [%s]";
private static final String ERROR_POST_PROCESSOR_NULL = "Post-processor returned null [%s]";
private static final String ERROR_PROCESSOR_FOR_DISK_CACHE_NULL = "Bitmap processor for disk cache returned null [%s]";
private final ImageLoaderEngine engine;
private final ImageLoadingInfo imageLoadingInfo;
private final Handler handler;
// Helper references
private final ImageLoaderConfiguration configuration;
private final ImageDownloader downloader;
private final ImageDownloader networkDeniedDownloader;
private final ImageDownloader slowNetworkDownloader;
private final ImageDecoder decoder;
final String uri;
private final String memoryCacheKey;
final ImageAware imageAware;
private final ImageSize targetSize;
final DisplayImageOptions options;
final ImageLoadingListener listener;
final ImageLoadingProgressListener progressListener;
private final boolean syncLoading;
// State vars
private LoadedFrom loadedFrom = LoadedFrom.NETWORK;
public LoadAndDisplayImageTask(ImageLoaderEngine engine, ImageLoadingInfo imageLoadingInfo, Handler handler) {
this.engine = engine;
this.imageLoadingInfo = imageLoadingInfo;
this.handler = handler;
configuration = engine.configuration;
downloader = configuration.downloader;
networkDeniedDownloader = configuration.networkDeniedDownloader;
slowNetworkDownloader = configuration.slowNetworkDownloader;
decoder = configuration.decoder;
uri = imageLoadingInfo.uri;
memoryCacheKey = imageLoadingInfo.memoryCacheKey;
imageAware = imageLoadingInfo.imageAware;
targetSize = imageLoadingInfo.targetSize;
options = imageLoadingInfo.options;
listener = imageLoadingInfo.listener;
progressListener = imageLoadingInfo.progressListener;
syncLoading = options.isSyncLoading();
}
@Override
public void run() {
if (waitIfPaused()) return;
if (delayIfNeed()) return;
ReentrantLock loadFromUriLock = imageLoadingInfo.loadFromUriLock;
L.d(LOG_START_DISPLAY_IMAGE_TASK, memoryCacheKey);
if (loadFromUriLock.isLocked()) {
L.d(LOG_WAITING_FOR_IMAGE_LOADED, memoryCacheKey);
}
loadFromUriLock.lock();
Bitmap bmp;
try {
checkTaskNotActual();
bmp = configuration.memoryCache.get(memoryCacheKey);
if (bmp == null || bmp.isRecycled()) {
bmp = tryLoadBitmap();
if (bmp == null) return; // listener callback already was fired
checkTaskNotActual();
checkTaskInterrupted();
if (options.shouldPreProcess()) {
L.d(LOG_PREPROCESS_IMAGE, memoryCacheKey);
bmp = options.getPreProcessor().process(bmp);
if (bmp == null) {
L.e(ERROR_PRE_PROCESSOR_NULL, memoryCacheKey);
}
}
if (bmp != null && options.isCacheInMemory()) {
L.d(LOG_CACHE_IMAGE_IN_MEMORY, memoryCacheKey);
configuration.memoryCache.put(memoryCacheKey, bmp);
}
} else {
loadedFrom = LoadedFrom.MEMORY_CACHE;
L.d(LOG_GET_IMAGE_FROM_MEMORY_CACHE_AFTER_WAITING, memoryCacheKey);
}
if (bmp != null && options.shouldPostProcess()) {
L.d(LOG_POSTPROCESS_IMAGE, memoryCacheKey);
bmp = options.getPostProcessor().process(bmp);
if (bmp == null) {
L.e(ERROR_POST_PROCESSOR_NULL, memoryCacheKey);
}
}
checkTaskNotActual();
checkTaskInterrupted();
} catch (TaskCancelledException e) {
fireCancelEvent();
return;
} finally {
loadFromUriLock.unlock();
}
DisplayBitmapTask displayBitmapTask = new DisplayBitmapTask(bmp, imageLoadingInfo, engine, loadedFrom);
runTask(displayBitmapTask, syncLoading, handler, engine);
}
/** @return true - if task should be interrupted; false - otherwise */
private boolean waitIfPaused() {
AtomicBoolean pause = engine.getPause();
if (pause.get()) {
synchronized (engine.getPauseLock()) {
if (pause.get()) {
L.d(LOG_WAITING_FOR_RESUME, memoryCacheKey);
try {
engine.getPauseLock().wait();
} catch (InterruptedException e) {
L.e(LOG_TASK_INTERRUPTED, memoryCacheKey);
return true;
}
L.d(LOG_RESUME_AFTER_PAUSE, memoryCacheKey);
}
}
}
return isTaskNotActual();
}
/** @return true - if task should be interrupted; false - otherwise */
private boolean delayIfNeed() {
if (options.shouldDelayBeforeLoading()) {
L.d(LOG_DELAY_BEFORE_LOADING, options.getDelayBeforeLoading(), memoryCacheKey);
try {
Thread.sleep(options.getDelayBeforeLoading());
} catch (InterruptedException e) {
L.e(LOG_TASK_INTERRUPTED, memoryCacheKey);
return true;
}
return isTaskNotActual();
}
return false;
}
private Bitmap tryLoadBitmap() throws TaskCancelledException {
Bitmap bitmap = null;
try {
File imageFile = configuration.diskCache.get(uri);
if (imageFile != null && imageFile.exists() && imageFile.length() > 0) {
L.d(LOG_LOAD_IMAGE_FROM_DISK_CACHE, memoryCacheKey);
loadedFrom = LoadedFrom.DISC_CACHE;
checkTaskNotActual();
bitmap = decodeImage(Scheme.FILE.wrap(imageFile.getAbsolutePath()));
}
if (bitmap == null || bitmap.getWidth() <= 0 || bitmap.getHeight() <= 0) {
L.d(LOG_LOAD_IMAGE_FROM_NETWORK, memoryCacheKey);
loadedFrom = LoadedFrom.NETWORK;
String imageUriForDecoding = uri;
if (options.isCacheOnDisk() && tryCacheImageOnDisk()) {
imageFile = configuration.diskCache.get(uri);
if (imageFile != null) {
imageUriForDecoding = Scheme.FILE.wrap(imageFile.getAbsolutePath());
}
}
checkTaskNotActual();
bitmap = decodeImage(imageUriForDecoding);
if (bitmap == null || bitmap.getWidth() <= 0 || bitmap.getHeight() <= 0) {
fireFailEvent(FailType.DECODING_ERROR, null);
}
}
} catch (IllegalStateException e) {
fireFailEvent(FailType.NETWORK_DENIED, null);
} catch (TaskCancelledException e) {
throw e;
} catch (IOException e) {
L.e(e);
fireFailEvent(FailType.IO_ERROR, e);
} catch (OutOfMemoryError e) {
L.e(e);
fireFailEvent(FailType.OUT_OF_MEMORY, e);
} catch (Throwable e) {
L.e(e);
fireFailEvent(FailType.UNKNOWN, e);
}
return bitmap;
}
private Bitmap decodeImage(String imageUri) throws IOException {
ViewScaleType viewScaleType = imageAware.getScaleType();
ImageDecodingInfo decodingInfo = new ImageDecodingInfo(memoryCacheKey, imageUri, uri, targetSize, viewScaleType,
getDownloader(), options);
return decoder.decode(decodingInfo);
}
/** @return true - if image was downloaded successfully; false - otherwise */
private boolean tryCacheImageOnDisk() throws TaskCancelledException {
L.d(LOG_CACHE_IMAGE_ON_DISK, memoryCacheKey);
boolean loaded;
try {
loaded = downloadImage();
if (loaded) {
int width = configuration.maxImageWidthForDiskCache;
int height = configuration.maxImageHeightForDiskCache;
if (width > 0 || height > 0) {
L.d(LOG_RESIZE_CACHED_IMAGE_FILE, memoryCacheKey);
resizeAndSaveImage(width, height); // TODO : process boolean result
}
}
} catch (IOException e) {
L.e(e);
loaded = false;
}
return loaded;
}
private boolean downloadImage() throws IOException {
InputStream is = getDownloader().getStream(uri, options.getExtraForDownloader());
if (is == null) {
L.e(ERROR_NO_IMAGE_STREAM, memoryCacheKey);
return false;
} else {
try {
return configuration.diskCache.save(uri, is, this);
} finally {
IoUtils.closeSilently(is);
}
}
}
/** Decodes image file into Bitmap, resize it and save it back */
private boolean resizeAndSaveImage(int maxWidth, int maxHeight) throws IOException {
// Decode image file, compress and re-save it
boolean saved = false;
File targetFile = configuration.diskCache.get(uri);
if (targetFile != null && targetFile.exists()) {
ImageSize targetImageSize = new ImageSize(maxWidth, maxHeight);
DisplayImageOptions specialOptions = new DisplayImageOptions.Builder().cloneFrom(options)
.imageScaleType(ImageScaleType.IN_SAMPLE_INT).build();
ImageDecodingInfo decodingInfo = new ImageDecodingInfo(memoryCacheKey,
Scheme.FILE.wrap(targetFile.getAbsolutePath()), uri, targetImageSize, ViewScaleType.FIT_INSIDE,
getDownloader(), specialOptions);
Bitmap bmp = decoder.decode(decodingInfo);
if (bmp != null && configuration.processorForDiskCache != null) {
L.d(LOG_PROCESS_IMAGE_BEFORE_CACHE_ON_DISK, memoryCacheKey);
bmp = configuration.processorForDiskCache.process(bmp);
if (bmp == null) {
L.e(ERROR_PROCESSOR_FOR_DISK_CACHE_NULL, memoryCacheKey);
}
}
if (bmp != null) {
saved = configuration.diskCache.save(uri, bmp);
bmp.recycle();
}
}
return saved;
}
@Override
public boolean onBytesCopied(int current, int total) {
return syncLoading || fireProgressEvent(current, total);
}
/** @return true - if loading should be continued; false - if loading should be interrupted */
private boolean fireProgressEvent(final int current, final int total) {
if (isTaskInterrupted() || isTaskNotActual()) return false;
if (progressListener != null) {
Runnable r = new Runnable() {
@Override
public void run() {
progressListener.onProgressUpdate(uri, imageAware.getWrappedView(), current, total);
}
};
runTask(r, false, handler, engine);
}
return true;
}
private void fireFailEvent(final FailType failType, final Throwable failCause) {
if (syncLoading || isTaskInterrupted() || isTaskNotActual()) return;
Runnable r = new Runnable() {
@Override
public void run() {
if (options.shouldShowImageOnFail()) {
imageAware.setImageDrawable(options.getImageOnFail(configuration.resources));
}
listener.onLoadingFailed(uri, imageAware.getWrappedView(), new FailReason(failType, failCause));
}
};
runTask(r, false, handler, engine);
}
private void fireCancelEvent() {
if (syncLoading || isTaskInterrupted()) return;
Runnable r = new Runnable() {
@Override
public void run() {
listener.onLoadingCancelled(uri, imageAware.getWrappedView());
}
};
runTask(r, false, handler, engine);
}
private ImageDownloader getDownloader() {
ImageDownloader d;
if (engine.isNetworkDenied()) {
d = networkDeniedDownloader;
} else if (engine.isSlowNetwork()) {
d = slowNetworkDownloader;
} else {
d = downloader;
}
return d;
}
/**
* @throws TaskCancelledException if task is not actual (target ImageAware is collected by GC or the image URI of
* this task doesn't match to image URI which is actual for current ImageAware at
* this moment)
*/
private void checkTaskNotActual() throws TaskCancelledException {
checkViewCollected();
checkViewReused();
}
/**
* @return true - if task is not actual (target ImageAware is collected by GC or the image URI of this task
* doesn't match to image URI which is actual for current ImageAware at this moment)); false - otherwise
*/
private boolean isTaskNotActual() {
return isViewCollected() || isViewReused();
}
/** @throws TaskCancelledException if target ImageAware is collected */
private void checkViewCollected() throws TaskCancelledException {
if (isViewCollected()) {
throw new TaskCancelledException();
}
}
/** @return true - if target ImageAware is collected by GC; false - otherwise */
private boolean isViewCollected() {
if (imageAware.isCollected()) {
L.d(LOG_TASK_CANCELLED_IMAGEAWARE_COLLECTED, memoryCacheKey);
return true;
}
return false;
}
/** @throws TaskCancelledException if target ImageAware is collected by GC */
private void checkViewReused() throws TaskCancelledException {
if (isViewReused()) {
throw new TaskCancelledException();
}
}
/** @return true - if current ImageAware is reused for displaying another image; false - otherwise */
private boolean isViewReused() {
String currentCacheKey = engine.getLoadingUriForView(imageAware);
// Check whether memory cache key (image URI) for current ImageAware is actual.
// If ImageAware is reused for another task then current task should be cancelled.
boolean imageAwareWasReused = !memoryCacheKey.equals(currentCacheKey);
if (imageAwareWasReused) {
L.d(LOG_TASK_CANCELLED_IMAGEAWARE_REUSED, memoryCacheKey);
return true;
}
return false;
}
/** @throws TaskCancelledException if current task was interrupted */
private void checkTaskInterrupted() throws TaskCancelledException {
if (isTaskInterrupted()) {
throw new TaskCancelledException();
}
}
/** @return true - if current task was interrupted; false - otherwise */
private boolean isTaskInterrupted() {
if (Thread.interrupted()) {
L.d(LOG_TASK_INTERRUPTED, memoryCacheKey);
return true;
}
return false;
}
String getLoadingUri() {
return uri;
}
static void runTask(Runnable r, boolean sync, Handler handler, ImageLoaderEngine engine) {
if (sync) {
r.run();
} else if (handler == null) {
engine.fireCallback(r);
} else {
handler.post(r);
}
}
/**
* Exceptions for case when task is cancelled (thread is interrupted, image view is reused for another task, view is
* collected by GC).
*
* @author Sergey Tarasevich (nostra13[at]gmail[dot]com)
* @since 1.9.1
*/
class TaskCancelledException extends Exception {
}
}
================================================
FILE: library/src/main/java/com/nostra13/universalimageloader/core/ProcessAndDisplayImageTask.java
================================================
/*******************************************************************************
* Copyright 2011-2014 Sergey Tarasevich
*
* 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.nostra13.universalimageloader.core;
import android.graphics.Bitmap;
import android.os.Handler;
import android.widget.ImageView;
import com.nostra13.universalimageloader.core.assist.LoadedFrom;
import com.nostra13.universalimageloader.core.process.BitmapProcessor;
import com.nostra13.universalimageloader.utils.L;
/**
* Presents process'n'display image task. Processes image {@linkplain Bitmap} and display it in {@link ImageView} using
* {@link DisplayBitmapTask}.
*
* @author Sergey Tarasevich (nostra13[at]gmail[dot]com)
* @since 1.8.0
*/
final class ProcessAndDisplayImageTask implements Runnable {
private static final String LOG_POSTPROCESS_IMAGE = "PostProcess image before displaying [%s]";
private final ImageLoaderEngine engine;
private final Bitmap bitmap;
private final ImageLoadingInfo imageLoadingInfo;
private final Handler handler;
public ProcessAndDisplayImageTask(ImageLoaderEngine engine, Bitmap bitmap, ImageLoadingInfo imageLoadingInfo,
Handler handler) {
this.engine = engine;
this.bitmap = bitmap;
this.imageLoadingInfo = imageLoadingInfo;
this.handler = handler;
}
@Override
public void run() {
L.d(LOG_POSTPROCESS_IMAGE, imageLoadingInfo.memoryCacheKey);
BitmapProcessor processor = imageLoadingInfo.options.getPostProcessor();
Bitmap processedBitmap = processor.process(bitmap);
DisplayBitmapTask displayBitmapTask = new DisplayBitmapTask(processedBitmap, imageLoadingInfo, engine,
LoadedFrom.MEMORY_CACHE);
LoadAndDisplayImageTask.runTask(displayBitmapTask, imageLoadingInfo.options.isSyncLoading(), handler, engine);
}
}
================================================
FILE: library/src/main/java/com/nostra13/universalimageloader/core/assist/ContentLengthInputStream.java
================================================
/*******************************************************************************
* Copyright 2013-2014 Sergey Tarasevich
*
* 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.nostra13.universalimageloader.core.assist;
import java.io.IOException;
import java.io.InputStream;
/**
* Decorator for {@link java.io.InputStream InputStream}. Provides possibility to return defined stream length by
* {@link #available()} method.
*
* @author Sergey Tarasevich (nostra13[at]gmail[dot]com), Mariotaku
* @since 1.9.1
*/
public class ContentLengthInputStream extends InputStream {
private final InputStream stream;
private final int length;
public ContentLengthInputStream(InputStream stream, int length) {
this.stream = stream;
this.length = length;
}
@Override
public int available() {
return length;
}
@Override
public void close() throws IOException {
stream.close();
}
@Override
public void mark(int readLimit) {
stream.mark(readLimit);
}
@Override
public int read() throws IOException {
return stream.read();
}
@Override
public int read(byte[] buffer) throws IOException {
return stream.read(buffer);
}
@Override
public int read(byte[] buffer, int byteOffset, int byteCount) throws IOException {
return stream.read(buffer, byteOffset, byteCount);
}
@Override
public void reset() throws IOException {
stream.reset();
}
@Override
public long skip(long byteCount) throws IOException {
return stream.skip(byteCount);
}
@Override
public boolean markSupported() {
return stream.markSupported();
}
}
================================================
FILE: library/src/main/java/com/nostra13/universalimageloader/core/assist/FailReason.java
================================================
/*******************************************************************************
* Copyright 2011-2014 Sergey Tarasevich
*
* 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.nostra13.universalimageloader.core.assist;
/**
* Presents the reason why image loading and displaying was failed
*
* @author Sergey Tarasevich (nostra13[at]gmail[dot]com)
* @since 1.0.0
*/
public class FailReason {
private final FailType type;
private final Throwable cause;
public FailReason(FailType type, Throwable cause) {
this.type = type;
this.cause = cause;
}
/** @return {@linkplain FailType Fail type} */
public FailType getType() {
return type;
}
/** @return Thrown exception/error, can be null */
public Throwable getCause() {
return cause;
}
/** Presents type of fail while image loading */
public static enum FailType {
/** Input/output error. Can be caused by network communication fail or error while caching image on file system. */
IO_ERROR,
/**
* Error while
* {@linkplain android.graphics.BitmapFactory#decodeStream(java.io.InputStream, android.graphics.Rect, android.graphics.BitmapFactory.Options)
* decode image to Bitmap}
*/
DECODING_ERROR,
/**
* {@linkplain com.nostra13.universalimageloader.core.ImageLoader#denyNetworkDownloads(boolean) Network
* downloads are denied} and requested image wasn't cached in disk cache before.
*/
NETWORK_DENIED,
/** Not enough memory to create needed Bitmap for image */
OUT_OF_MEMORY,
/** Unknown error was occurred while loading image */
UNKNOWN
}
}
================================================
FILE: library/src/main/java/com/nostra13/universalimageloader/core/assist/FlushedInputStream.java
================================================
package com.nostra13.universalimageloader.core.assist;
import java.io.FilterInputStream;
import java.io.IOException;
import java.io.InputStream;
/**
* Many streams obtained over slow connection show this
* problem.
*/
public class FlushedInputStream extends FilterInputStream {
public FlushedInputStream(InputStream inputStream) {
super(inputStream);
}
@Override
public long skip(long n) throws IOException {
long totalBytesSkipped = 0L;
while (totalBytesSkipped < n) {
long bytesSkipped = in.skip(n - totalBytesSkipped);
if (bytesSkipped == 0L) {
int by_te = read();
if (by_te < 0) {
break; // we reached EOF
} else {
bytesSkipped = 1; // we read one byte
}
}
totalBytesSkipped += bytesSkipped;
}
return totalBytesSkipped;
}
}
================================================
FILE: library/src/main/java/com/nostra13/universalimageloader/core/assist/ImageScaleType.java
================================================
/*******************************************************************************
* Copyright 2011-2014 Sergey Tarasevich
*
* 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.nostra13.universalimageloader.core.assist;
/**
* Type of image scaling during decoding.
*
* @author Sergey Tarasevich (nostra13[at]gmail[dot]com)
* @since 1.5.0
*/
public enum ImageScaleType {
/** Image won't be scaled */
NONE,
/**
* Image will be scaled down only if image size is greater than
* {@linkplain javax.microedition.khronos.opengles.GL10#GL_MAX_TEXTURE_SIZE maximum acceptable texture size}.
* Usually it's 2048x2048.
* If Bitmap is expected to display than it must not exceed this size (otherwise you'll get the exception
* "OpenGLRenderer: Bitmap too large to be uploaded into a texture".
* Image will be subsampled in an integer number of times (1, 2, 3, ...) to maximum texture size of device.
*/
NONE_SAFE,
/**
* Image will be reduces 2-fold until next reduce step make image smaller target size.
* It's fast type and it's preferable for usage in lists/grids/galleries (and other
* {@linkplain android.widget.AdapterView adapter-views}) .
* Relates to {@link android.graphics.BitmapFactory.Options#inSampleSize}
* Note: If original image size is smaller than target size then original image won't be scaled.
*/
IN_SAMPLE_POWER_OF_2,
/**
* Image will be subsampled in an integer number of times (1, 2, 3, ...). Use it if memory economy is quite
* important.
* Relates to {@link android.graphics.BitmapFactory.Options#inSampleSize}
* Note: If original image size is smaller than target size then original image won't be scaled.
*/
IN_SAMPLE_INT,
/**
* Image will scaled-down exactly to target size (scaled width or height or both will be equal to target size;
* depends on {@linkplain android.widget.ImageView.ScaleType ImageView's scale type}). Use it if memory economy is
* critically important.
* Note: If original image size is smaller than target size then original image won't be scaled.
*
* NOTE: For creating result Bitmap (of exact size) additional Bitmap will be created with
* {@link android.graphics.Bitmap#createBitmap(android.graphics.Bitmap, int, int, int, int, android.graphics.Matrix, boolean)
* Bitmap.createBitmap(...)}.
* Cons: Saves memory by keeping smaller Bitmap in memory cache (comparing with IN_SAMPLE... scale types)
* Pros: Requires more memory in one time for creation of result Bitmap.
*/
EXACTLY,
/**
* Image will scaled exactly to target size (scaled width or height or both will be equal to target size; depends on
* {@linkplain android.widget.ImageView.ScaleType ImageView's scale type}). Use it if memory economy is critically
* important.
* Note: If original image size is smaller than target size then original image will be stretched to
* target size.
*
* NOTE: For creating result Bitmap (of exact size) additional Bitmap will be created with
* {@link android.graphics.Bitmap#createBitmap(android.graphics.Bitmap, int, int, int, int, android.graphics.Matrix, boolean)
* Bitmap.createBitmap(...)}.
* Cons: Saves memory by keeping smaller Bitmap in memory cache (comparing with IN_SAMPLE... scale types)
* Pros: Requires more memory in one time for creation of result Bitmap.
*/
EXACTLY_STRETCHED
}
================================================
FILE: library/src/main/java/com/nostra13/universalimageloader/core/assist/ImageSize.java
================================================
/*******************************************************************************
* Copyright 2011-2013 Sergey Tarasevich
*
* 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.nostra13.universalimageloader.core.assist;
/**
* Present width and height values
*
* @author Sergey Tarasevich (nostra13[at]gmail[dot]com)
* @since 1.0.0
*/
public class ImageSize {
private static final int TO_STRING_MAX_LENGHT = 9; // "9999x9999".length()
private static final String SEPARATOR = "x";
private final int width;
private final int height;
public ImageSize(int width, int height) {
this.width = width;
this.height = height;
}
public ImageSize(int width, int height, int rotation) {
if (rotation % 180 == 0) {
this.width = width;
this.height = height;
} else {
this.width = height;
this.height = width;
}
}
public int getWidth() {
return width;
}
public int getHeight() {
return height;
}
/** Scales down dimensions in sampleSize times. Returns new object. */
public ImageSize scaleDown(int sampleSize) {
return new ImageSize(width / sampleSize, height / sampleSize);
}
/** Scales dimensions according to incoming scale. Returns new object. */
public ImageSize scale(float scale) {
return new ImageSize((int) (width * scale), (int) (height * scale));
}
@Override
public String toString() {
return new StringBuilder(TO_STRING_MAX_LENGHT).append(width).append(SEPARATOR).append(height).toString();
}
}
================================================
FILE: library/src/main/java/com/nostra13/universalimageloader/core/assist/LoadedFrom.java
================================================
package com.nostra13.universalimageloader.core.assist;
/**
* Source image loaded from.
*
* @author Sergey Tarasevich (nostra13[at]gmail[dot]com)
*/
public enum LoadedFrom {
NETWORK, DISC_CACHE, MEMORY_CACHE
}
================================================
FILE: library/src/main/java/com/nostra13/universalimageloader/core/assist/QueueProcessingType.java
================================================
/*******************************************************************************
* Copyright 2011-2013 Sergey Tarasevich
*
* 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.nostra13.universalimageloader.core.assist;
/**
* Queue processing type which will be used for display task processing
*
* @author Sergey Tarasevich (nostra13[at]gmail[dot]com)
* @since 1.6.3
*/
public enum QueueProcessingType {
FIFO, LIFO
}
================================================
FILE: library/src/main/java/com/nostra13/universalimageloader/core/assist/ViewScaleType.java
================================================
/*******************************************************************************
* Copyright 2011-2013 Sergey Tarasevich
*
* 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.nostra13.universalimageloader.core.assist;
import android.widget.ImageView;
import android.widget.ImageView.ScaleType;
/**
* Simplify {@linkplain ScaleType ImageView's scale type} to 2 types: {@link #FIT_INSIDE} and {@link #CROP}
*
* @author Sergey Tarasevich (nostra13[at]gmail[dot]com)
* @since 1.6.1
*/
public enum ViewScaleType {
/**
* Scale the image uniformly (maintain the image's aspect ratio) so that at least one dimension (width or height) of
* the image will be equal to or less the corresponding dimension of the view.
*/
FIT_INSIDE,
/**
* Scale the image uniformly (maintain the image's aspect ratio) so that both dimensions (width and height) of the
* image will be equal to or larger than the corresponding dimension of the view.
*/
CROP;
/**
* Defines scale type of ImageView.
*
* @param imageView {@link ImageView}
* @return {@link #FIT_INSIDE} for
*
*
* {@link #CROP} for
*
*
*/
public static ViewScaleType fromImageView(ImageView imageView) {
switch (imageView.getScaleType()) {
case FIT_CENTER:
case FIT_XY:
case FIT_START:
case FIT_END:
case CENTER_INSIDE:
return FIT_INSIDE;
case MATRIX:
case CENTER:
case CENTER_CROP:
default:
return CROP;
}
}
}
================================================
FILE: library/src/main/java/com/nostra13/universalimageloader/core/assist/deque/BlockingDeque.java
================================================
/*
* Written by Doug Lea with assistance from members of JCP JSR-166
* Expert Group and released to the public domain, as explained at
* http://creativecommons.org/licenses/publicdomain
*/
package com.nostra13.universalimageloader.core.assist.deque;
import java.util.Iterator;
import java.util.NoSuchElementException;
import java.util.concurrent.BlockingQueue;
import java.util.concurrent.TimeUnit;
/**
* A {@link Deque} that additionally supports blocking operations that wait
* for the deque to become non-empty when retrieving an element, and wait for
* space to become available in the deque when storing an element.
*
*
*
*
*
*
* First Element (Head)
*
*
*
* Throws exception
* Special value
* Blocks
* Times out
*
*
* Insert
* {@link #addFirst addFirst(e)}
* {@link #offerFirst offerFirst(e)}
* {@link #putFirst putFirst(e)}
* {@link #offerFirst offerFirst(e, time, unit)}
*
*
* Remove
* {@link #removeFirst removeFirst()}
* {@link #pollFirst pollFirst()}
* {@link #takeFirst takeFirst()}
* {@link #pollFirst(long, TimeUnit) pollFirst(time, unit)}
*
*
* Examine
* {@link #getFirst getFirst()}
* {@link #peekFirst peekFirst()}
* not applicable
* not applicable
*
*
* Last Element (Tail)
*
*
*
* Throws exception
* Special value
* Blocks
* Times out
*
*
* Insert
* {@link #addLast addLast(e)}
* {@link #offerLast offerLast(e)}
* {@link #putLast putLast(e)}
* {@link #offerLast offerLast(e, time, unit)}
*
*
* Remove
* {@link #removeLast() removeLast()}
* {@link #pollLast() pollLast()}
* {@link #takeLast takeLast()}
* {@link #pollLast(long, TimeUnit) pollLast(time, unit)}
*
*
* Examine
* {@link #getLast getLast()}
* {@link #peekLast peekLast()}
* not applicable
* not applicable
*
*
*
*
*
* BlockingQueue Method
* Equivalent BlockingDeque Method
*
*
* Insert
*
*
* {@link #add add(e)}
* {@link #addLast addLast(e)}
*
*
* {@link #offer offer(e)}
* {@link #offerLast offerLast(e)}
*
*
* {@link #put put(e)}
* {@link #putLast putLast(e)}
*
*
* {@link #offer offer(e, time, unit)}
* {@link #offerLast offerLast(e, time, unit)}
*
*
* Remove
*
*
* {@link #remove() remove()}
* {@link #removeFirst() removeFirst()}
*
*
* {@link #poll() poll()}
* {@link #pollFirst() pollFirst()}
*
*
* {@link #take() take()}
* {@link #takeFirst() takeFirst()}
*
*
* {@link #poll(long, TimeUnit) poll(time, unit)}
* {@link #pollFirst(long, TimeUnit) pollFirst(time, unit)}
*
*
* Examine
*
*
* {@link #element() element()}
* {@link #getFirst() getFirst()}
*
*
* {@link #peek() peek()}
* {@link #peekFirst() peekFirst()}
*
*
*
*
*
*
* First Element (Head)
* Last Element (Tail)
*
*
*
* Throws exception
* Special value
* Throws exception
* Special value
*
*
* Insert
* {@link #addFirst addFirst(e)}
* {@link #offerFirst offerFirst(e)}
* {@link #addLast addLast(e)}
* {@link #offerLast offerLast(e)}
*
*
* Remove
* {@link #removeFirst removeFirst()}
* {@link #pollFirst pollFirst()}
* {@link #removeLast removeLast()}
* {@link #pollLast pollLast()}
*
*
* Examine
* {@link #getFirst getFirst()}
* {@link #peekFirst peekFirst()}
* {@link #getLast getLast()}
* {@link #peekLast peekLast()}
*
*
*
*
*
* Queue Method
* Equivalent Deque Method
*
*
* {@link java.util.Queue#add add(e)}
* {@link #addLast addLast(e)}
*
*
* {@link java.util.Queue#offer offer(e)}
* {@link #offerLast offerLast(e)}
*
*
* {@link java.util.Queue#remove remove()}
* {@link #removeFirst removeFirst()}
*
*
* {@link java.util.Queue#poll poll()}
* {@link #pollFirst pollFirst()}
*
*
* {@link java.util.Queue#element element()}
* {@link #getFirst getFirst()}
*
*
* {@link java.util.Queue#peek peek()}
* {@link #peek peekFirst()}
*
*
*
*
*
* Stack Method
* Equivalent Deque Method
*
*
* {@link #push push(e)}
* {@link #addFirst addFirst(e)}
*
*
* {@link #pop pop()}
* {@link #removeFirst removeFirst()}
*
*
* {@link #peek peek()}
* {@link #peekFirst peekFirst()}
*
* String[] y = x.toArray(new String[0]);
*
* Note that {@code toArray(new Object[0])} is identical in function to
* {@code toArray()}.
*
* @param a the array into which the elements of the deque are to
* be stored, if it is big enough; otherwise, a new array of the
* same runtime type is allocated for this purpose
* @return an array containing all of the elements in this deque
* @throws ArrayStoreException if the runtime type of the specified array
* is not a supertype of the runtime type of every element in
* this deque
* @throws NullPointerException if the specified array is null
*/
@SuppressWarnings("unchecked")
public
* Implementations have to be thread-safe.
*
* @author Sergey Tarasevich (nostra13[at]gmail[dot]com)
* @see com.nostra13.universalimageloader.core.imageaware.ImageAware
* @see com.nostra13.universalimageloader.core.assist.LoadedFrom
* @since 1.5.6
*/
public interface BitmapDisplayer {
/**
* Displays bitmap in {@link com.nostra13.universalimageloader.core.imageaware.ImageAware}.
* NOTE: This method is called on UI thread so it's strongly recommended not to do any heavy work in it.
*
* @param bitmap Source bitmap
* @param imageAware {@linkplain com.nostra13.universalimageloader.core.imageaware.ImageAware Image aware view} to
* display Bitmap
* @param loadedFrom Source of loaded image
*/
void display(Bitmap bitmap, ImageAware imageAware, LoadedFrom loadedFrom);
}
================================================
FILE: library/src/main/java/com/nostra13/universalimageloader/core/display/CircleBitmapDisplayer.java
================================================
/*******************************************************************************
* Copyright 2015 Sergey Tarasevich
*
* 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.nostra13.universalimageloader.core.display;
import android.graphics.Bitmap;
import android.graphics.BitmapShader;
import android.graphics.Canvas;
import android.graphics.ColorFilter;
import android.graphics.Matrix;
import android.graphics.Paint;
import android.graphics.PixelFormat;
import android.graphics.Rect;
import android.graphics.RectF;
import android.graphics.Shader;
import android.graphics.drawable.Drawable;
import com.nostra13.universalimageloader.core.assist.LoadedFrom;
import com.nostra13.universalimageloader.core.imageaware.ImageAware;
import com.nostra13.universalimageloader.core.imageaware.ImageViewAware;
/**
* Can display bitmap cropped by a circle. This implementation works only with ImageViews wrapped
* in ImageViewAware.
*
* If this implementation doesn't meet your needs then consider
* RoundedImageView or
* CircularImageView projects for usage.
*
* @author Qualtagh, Sergey Tarasevich (nostra13[at]gmail[dot]com)
* @since 1.9.5
*/
public class CircleBitmapDisplayer implements BitmapDisplayer {
protected final Integer strokeColor;
protected final float strokeWidth;
public CircleBitmapDisplayer() {
this(null);
}
public CircleBitmapDisplayer(Integer strokeColor) {
this(strokeColor, 0);
}
public CircleBitmapDisplayer(Integer strokeColor, float strokeWidth) {
this.strokeColor = strokeColor;
this.strokeWidth = strokeWidth;
}
@Override
public void display(Bitmap bitmap, ImageAware imageAware, LoadedFrom loadedFrom) {
if (!(imageAware instanceof ImageViewAware)) {
throw new IllegalArgumentException("ImageAware should wrap ImageView. ImageViewAware is expected.");
}
imageAware.setImageDrawable(new CircleDrawable(bitmap, strokeColor, strokeWidth));
}
public static class CircleDrawable extends Drawable {
protected float radius;
protected final RectF mRect = new RectF();
protected final RectF mBitmapRect;
protected final BitmapShader bitmapShader;
protected final Paint paint;
protected final Paint strokePaint;
protected final float strokeWidth;
protected float strokeRadius;
public CircleDrawable(Bitmap bitmap, Integer strokeColor, float strokeWidth) {
int diameter = Math.min(bitmap.getWidth(), bitmap.getHeight());
radius = diameter / 2f;
bitmapShader = new BitmapShader(bitmap, Shader.TileMode.CLAMP, Shader.TileMode.CLAMP);
float left = (bitmap.getWidth() - diameter) / 2f;
float top = (bitmap.getHeight() - diameter) / 2f;
mBitmapRect = new RectF(left, top, diameter, diameter);
paint = new Paint();
paint.setAntiAlias(true);
paint.setShader(bitmapShader);
paint.setFilterBitmap(true);
paint.setDither(true);
if (strokeColor == null) {
strokePaint = null;
} else {
strokePaint = new Paint();
strokePaint.setStyle(Paint.Style.STROKE);
strokePaint.setColor(strokeColor);
strokePaint.setStrokeWidth(strokeWidth);
strokePaint.setAntiAlias(true);
}
this.strokeWidth = strokeWidth;
strokeRadius = radius - strokeWidth / 2;
}
@Override
protected void onBoundsChange(Rect bounds) {
super.onBoundsChange(bounds);
mRect.set(0, 0, bounds.width(), bounds.height());
radius = Math.min(bounds.width(), bounds.height()) / 2;
strokeRadius = radius - strokeWidth / 2;
// Resize the original bitmap to fit the new bound
Matrix shaderMatrix = new Matrix();
shaderMatrix.setRectToRect(mBitmapRect, mRect, Matrix.ScaleToFit.FILL);
bitmapShader.setLocalMatrix(shaderMatrix);
}
@Override
public void draw(Canvas canvas) {
canvas.drawCircle(radius, radius, radius, paint);
if (strokePaint != null) {
canvas.drawCircle(radius, radius, strokeRadius, strokePaint);
}
}
@Override
public int getOpacity() {
return PixelFormat.TRANSLUCENT;
}
@Override
public void setAlpha(int alpha) {
paint.setAlpha(alpha);
}
@Override
public void setColorFilter(ColorFilter cf) {
paint.setColorFilter(cf);
}
}
}
================================================
FILE: library/src/main/java/com/nostra13/universalimageloader/core/display/FadeInBitmapDisplayer.java
================================================
/*******************************************************************************
* Copyright 2011-2014 Sergey Tarasevich, Daniel Martí
*
* 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.nostra13.universalimageloader.core.display;
import android.graphics.Bitmap;
import android.view.View;
import android.view.animation.AlphaAnimation;
import android.view.animation.DecelerateInterpolator;
import android.widget.ImageView;
import com.nostra13.universalimageloader.core.assist.LoadedFrom;
import com.nostra13.universalimageloader.core.imageaware.ImageAware;
/**
* Displays image with "fade in" animation
*
* @author Sergey Tarasevich (nostra13[at]gmail[dot]com), Daniel Martí
* @since 1.6.4
*/
public class FadeInBitmapDisplayer implements BitmapDisplayer {
private final int durationMillis;
private final boolean animateFromNetwork;
private final boolean animateFromDisk;
private final boolean animateFromMemory;
/**
* @param durationMillis Duration of "fade-in" animation (in milliseconds)
*/
public FadeInBitmapDisplayer(int durationMillis) {
this(durationMillis, true, true, true);
}
/**
* @param durationMillis Duration of "fade-in" animation (in milliseconds)
* @param animateFromNetwork Whether animation should be played if image is loaded from network
* @param animateFromDisk Whether animation should be played if image is loaded from disk cache
* @param animateFromMemory Whether animation should be played if image is loaded from memory cache
*/
public FadeInBitmapDisplayer(int durationMillis, boolean animateFromNetwork, boolean animateFromDisk,
boolean animateFromMemory) {
this.durationMillis = durationMillis;
this.animateFromNetwork = animateFromNetwork;
this.animateFromDisk = animateFromDisk;
this.animateFromMemory = animateFromMemory;
}
@Override
public void display(Bitmap bitmap, ImageAware imageAware, LoadedFrom loadedFrom) {
imageAware.setImageBitmap(bitmap);
if ((animateFromNetwork && loadedFrom == LoadedFrom.NETWORK) ||
(animateFromDisk && loadedFrom == LoadedFrom.DISC_CACHE) ||
(animateFromMemory && loadedFrom == LoadedFrom.MEMORY_CACHE)) {
animate(imageAware.getWrappedView(), durationMillis);
}
}
/**
* Animates {@link ImageView} with "fade-in" effect
*
* @param imageView {@link ImageView} which display image in
* @param durationMillis The length of the animation in milliseconds
*/
public static void animate(View imageView, int durationMillis) {
if (imageView != null) {
AlphaAnimation fadeImage = new AlphaAnimation(0, 1);
fadeImage.setDuration(durationMillis);
fadeImage.setInterpolator(new DecelerateInterpolator());
imageView.startAnimation(fadeImage);
}
}
}
================================================
FILE: library/src/main/java/com/nostra13/universalimageloader/core/display/RoundedBitmapDisplayer.java
================================================
/*******************************************************************************
* Copyright 2011-2014 Sergey Tarasevich
*
* 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.nostra13.universalimageloader.core.display;
import android.graphics.*;
import android.graphics.drawable.Drawable;
import com.nostra13.universalimageloader.core.assist.LoadedFrom;
import com.nostra13.universalimageloader.core.imageaware.ImageAware;
import com.nostra13.universalimageloader.core.imageaware.ImageViewAware;
/**
* Can display bitmap with rounded corners. This implementation works only with ImageViews wrapped
* in ImageViewAware.
*
* This implementation is inspired by
*
* Romain Guy's article. It rounds images using custom drawable drawing. Original bitmap isn't changed.
*
*
* If this implementation doesn't meet your needs then consider
* RoundedImageView or
* CircularImageView projects for usage.
*
* @author Sergey Tarasevich (nostra13[at]gmail[dot]com)
* @since 1.5.6
*/
public class RoundedBitmapDisplayer implements BitmapDisplayer {
protected final int cornerRadius;
protected final int margin;
public RoundedBitmapDisplayer(int cornerRadiusPixels) {
this(cornerRadiusPixels, 0);
}
public RoundedBitmapDisplayer(int cornerRadiusPixels, int marginPixels) {
this.cornerRadius = cornerRadiusPixels;
this.margin = marginPixels;
}
@Override
public void display(Bitmap bitmap, ImageAware imageAware, LoadedFrom loadedFrom) {
if (!(imageAware instanceof ImageViewAware)) {
throw new IllegalArgumentException("ImageAware should wrap ImageView. ImageViewAware is expected.");
}
imageAware.setImageDrawable(new RoundedDrawable(bitmap, cornerRadius, margin));
}
public static class RoundedDrawable extends Drawable {
protected final float cornerRadius;
protected final int margin;
protected final RectF mRect = new RectF(),
mBitmapRect;
protected final BitmapShader bitmapShader;
protected final Paint paint;
public RoundedDrawable(Bitmap bitmap, int cornerRadius, int margin) {
this.cornerRadius = cornerRadius;
this.margin = margin;
bitmapShader = new BitmapShader(bitmap, Shader.TileMode.CLAMP, Shader.TileMode.CLAMP);
mBitmapRect = new RectF (margin, margin, bitmap.getWidth() - margin, bitmap.getHeight() - margin);
paint = new Paint();
paint.setAntiAlias(true);
paint.setShader(bitmapShader);
paint.setFilterBitmap(true);
paint.setDither(true);
}
@Override
protected void onBoundsChange(Rect bounds) {
super.onBoundsChange(bounds);
mRect.set(margin, margin, bounds.width() - margin, bounds.height() - margin);
// Resize the original bitmap to fit the new bound
Matrix shaderMatrix = new Matrix();
shaderMatrix.setRectToRect(mBitmapRect, mRect, Matrix.ScaleToFit.FILL);
bitmapShader.setLocalMatrix(shaderMatrix);
}
@Override
public void draw(Canvas canvas) {
canvas.drawRoundRect(mRect, cornerRadius, cornerRadius, paint);
}
@Override
public int getOpacity() {
return PixelFormat.TRANSLUCENT;
}
@Override
public void setAlpha(int alpha) {
paint.setAlpha(alpha);
}
@Override
public void setColorFilter(ColorFilter cf) {
paint.setColorFilter(cf);
}
}
}
================================================
FILE: library/src/main/java/com/nostra13/universalimageloader/core/display/RoundedVignetteBitmapDisplayer.java
================================================
/*******************************************************************************
* Copyright 2013 Sergey Tarasevich
*
* 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.nostra13.universalimageloader.core.display;
import android.graphics.*;
import com.nostra13.universalimageloader.core.assist.LoadedFrom;
import com.nostra13.universalimageloader.core.imageaware.ImageAware;
import com.nostra13.universalimageloader.core.imageaware.ImageViewAware;
/**
* Can display bitmap with rounded corners and vignette effect. This implementation works only with ImageViews wrapped
* in ImageViewAware.
*
* This implementation is inspired by
*
* Romain Guy's article. It rounds images using custom drawable drawing. Original bitmap isn't changed.
*
*
* If this implementation doesn't meet your needs then consider
* this project for usage.
*
* @author Sergey Tarasevich (nostra13[at]gmail[dot]com)
* @since 1.9.1
*/
public class RoundedVignetteBitmapDisplayer extends RoundedBitmapDisplayer {
public RoundedVignetteBitmapDisplayer(int cornerRadiusPixels, int marginPixels) {
super(cornerRadiusPixels, marginPixels);
}
@Override
public void display(Bitmap bitmap, ImageAware imageAware, LoadedFrom loadedFrom) {
if (!(imageAware instanceof ImageViewAware)) {
throw new IllegalArgumentException("ImageAware should wrap ImageView. ImageViewAware is expected.");
}
imageAware.setImageDrawable(new RoundedVignetteDrawable(bitmap, cornerRadius, margin));
}
protected static class RoundedVignetteDrawable extends RoundedDrawable {
RoundedVignetteDrawable(Bitmap bitmap, int cornerRadius, int margin) {
super(bitmap, cornerRadius, margin);
}
@Override
protected void onBoundsChange(Rect bounds) {
super.onBoundsChange(bounds);
RadialGradient vignette = new RadialGradient(
mRect.centerX(), mRect.centerY() * 1.0f / 0.7f, mRect.centerX() * 1.3f,
new int[]{0, 0, 0x7f000000}, new float[]{0.0f, 0.7f, 1.0f},
Shader.TileMode.CLAMP);
Matrix oval = new Matrix();
oval.setScale(1.0f, 0.7f);
vignette.setLocalMatrix(oval);
paint.setShader(new ComposeShader(bitmapShader, vignette, PorterDuff.Mode.SRC_OVER));
}
}
}
================================================
FILE: library/src/main/java/com/nostra13/universalimageloader/core/display/SimpleBitmapDisplayer.java
================================================
/*******************************************************************************
* Copyright 2011-2013 Sergey Tarasevich
*
* 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.nostra13.universalimageloader.core.display;
import android.graphics.Bitmap;
import com.nostra13.universalimageloader.core.assist.LoadedFrom;
import com.nostra13.universalimageloader.core.imageaware.ImageAware;
/**
* Just displays {@link Bitmap} in {@link com.nostra13.universalimageloader.core.imageaware.ImageAware}
*
* @author Sergey Tarasevich (nostra13[at]gmail[dot]com)
* @since 1.5.6
*/
public final class SimpleBitmapDisplayer implements BitmapDisplayer {
@Override
public void display(Bitmap bitmap, ImageAware imageAware, LoadedFrom loadedFrom) {
imageAware.setImageBitmap(bitmap);
}
}
================================================
FILE: library/src/main/java/com/nostra13/universalimageloader/core/download/BaseImageDownloader.java
================================================
/*******************************************************************************
* Copyright 2011-2014 Sergey Tarasevich
*
* 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.nostra13.universalimageloader.core.download;
import android.annotation.TargetApi;
import android.content.ContentResolver;
import android.content.Context;
import android.graphics.Bitmap;
import android.graphics.Bitmap.CompressFormat;
import android.media.ThumbnailUtils;
import android.net.Uri;
import android.os.Build;
import android.provider.ContactsContract;
import android.provider.MediaStore;
import android.webkit.MimeTypeMap;
import com.nostra13.universalimageloader.core.DisplayImageOptions;
import com.nostra13.universalimageloader.core.assist.ContentLengthInputStream;
import com.nostra13.universalimageloader.utils.IoUtils;
import java.io.BufferedInputStream;
import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.io.InputStream;
import java.net.HttpURLConnection;
import java.net.URL;
import java.net.URLConnection;
/**
* Provides retrieving of {@link InputStream} of image by URI from network or file system or app resources.
* {@link URLConnection} is used to retrieve image stream from network.
*
* @author Sergey Tarasevich (nostra13[at]gmail[dot]com)
* @since 1.8.0
*/
public class BaseImageDownloader implements ImageDownloader {
/** {@value} */
public static final int DEFAULT_HTTP_CONNECT_TIMEOUT = 5 * 1000; // milliseconds
/** {@value} */
public static final int DEFAULT_HTTP_READ_TIMEOUT = 20 * 1000; // milliseconds
/** {@value} */
protected static final int BUFFER_SIZE = 32 * 1024; // 32 Kb
/** {@value} */
protected static final String ALLOWED_URI_CHARS = "@#&=*+-_.,:!?()/~'%";
protected static final int MAX_REDIRECT_COUNT = 5;
protected static final String CONTENT_CONTACTS_URI_PREFIX = "content://com.android.contacts/";
private static final String ERROR_UNSUPPORTED_SCHEME = "UIL doesn't support scheme(protocol) by default [%s]. " + "You should implement this support yourself (BaseImageDownloader.getStreamFromOtherSource(...))";
protected final Context context;
protected final int connectTimeout;
protected final int readTimeout;
public BaseImageDownloader(Context context) {
this(context, DEFAULT_HTTP_CONNECT_TIMEOUT, DEFAULT_HTTP_READ_TIMEOUT);
}
public BaseImageDownloader(Context context, int connectTimeout, int readTimeout) {
this.context = context.getApplicationContext();
this.connectTimeout = connectTimeout;
this.readTimeout = readTimeout;
}
@Override
public InputStream getStream(String imageUri, Object extra) throws IOException {
switch (Scheme.ofUri(imageUri)) {
case HTTP:
case HTTPS:
return getStreamFromNetwork(imageUri, extra);
case FILE:
return getStreamFromFile(imageUri, extra);
case CONTENT:
return getStreamFromContent(imageUri, extra);
case ASSETS:
return getStreamFromAssets(imageUri, extra);
case DRAWABLE:
return getStreamFromDrawable(imageUri, extra);
case UNKNOWN:
default:
return getStreamFromOtherSource(imageUri, extra);
}
}
/**
* Retrieves {@link InputStream} of image by URI (image is located in the network).
*
* @param imageUri Image URI
* @param extra Auxiliary object which was passed to {@link DisplayImageOptions.Builder#extraForDownloader(Object)
* DisplayImageOptions.extraForDownloader(Object)}; can be null
* @return {@link InputStream} of image
* @throws IOException if some I/O error occurs during network request or if no InputStream could be created for
* URL.
*/
protected InputStream getStreamFromNetwork(String imageUri, Object extra) throws IOException {
HttpURLConnection conn = createConnection(imageUri, extra);
int redirectCount = 0;
while (conn.getResponseCode() / 100 == 3 && redirectCount < MAX_REDIRECT_COUNT) {
conn = createConnection(conn.getHeaderField("Location"), extra);
redirectCount++;
}
InputStream imageStream;
try {
imageStream = conn.getInputStream();
} catch (IOException e) {
// Read all data to allow reuse connection (http://bit.ly/1ad35PY)
IoUtils.readAndCloseStream(conn.getErrorStream());
throw e;
}
if (!shouldBeProcessed(conn)) {
IoUtils.closeSilently(imageStream);
throw new IOException("Image request failed with response code " + conn.getResponseCode());
}
return new ContentLengthInputStream(new BufferedInputStream(imageStream, BUFFER_SIZE), conn.getContentLength());
}
/**
* @param conn Opened request connection (response code is available)
* @return true - if data from connection is correct and should be read and processed;
* false - if response contains irrelevant data and shouldn't be processed
* @throws IOException
*/
protected boolean shouldBeProcessed(HttpURLConnection conn) throws IOException {
return conn.getResponseCode() == 200;
}
/**
* Create {@linkplain HttpURLConnection HTTP connection} for incoming URL
*
* @param url URL to connect to
* @param extra Auxiliary object which was passed to {@link DisplayImageOptions.Builder#extraForDownloader(Object)
* DisplayImageOptions.extraForDownloader(Object)}; can be null
* @return {@linkplain HttpURLConnection Connection} for incoming URL. Connection isn't established so it still configurable.
* @throws IOException if some I/O error occurs during network request or if no InputStream could be created for
* URL.
*/
protected HttpURLConnection createConnection(String url, Object extra) throws IOException {
String encodedUrl = Uri.encode(url, ALLOWED_URI_CHARS);
HttpURLConnection conn = (HttpURLConnection) new URL(encodedUrl).openConnection();
conn.setConnectTimeout(connectTimeout);
conn.setReadTimeout(readTimeout);
return conn;
}
/**
* Retrieves {@link InputStream} of image by URI (image is located on the local file system or SD card).
*
* @param imageUri Image URI
* @param extra Auxiliary object which was passed to {@link DisplayImageOptions.Builder#extraForDownloader(Object)
* DisplayImageOptions.extraForDownloader(Object)}; can be null
* @return {@link InputStream} of image
* @throws IOException if some I/O error occurs reading from file system
*/
protected InputStream getStreamFromFile(String imageUri, Object extra) throws IOException {
String filePath = Scheme.FILE.crop(imageUri);
if (isVideoFileUri(imageUri)) {
return getVideoThumbnailStream(filePath);
} else {
BufferedInputStream imageStream = new BufferedInputStream(new FileInputStream(filePath), BUFFER_SIZE);
return new ContentLengthInputStream(imageStream, (int) new File(filePath).length());
}
}
@TargetApi(Build.VERSION_CODES.FROYO)
private InputStream getVideoThumbnailStream(String filePath) {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.FROYO) {
Bitmap bitmap = ThumbnailUtils
.createVideoThumbnail(filePath, MediaStore.Images.Thumbnails.FULL_SCREEN_KIND);
if (bitmap != null) {
ByteArrayOutputStream bos = new ByteArrayOutputStream();
bitmap.compress(CompressFormat.PNG, 0, bos);
return new ByteArrayInputStream(bos.toByteArray());
}
}
return null;
}
/**
* Retrieves {@link InputStream} of image by URI (image is accessed using {@link ContentResolver}).
*
* @param imageUri Image URI
* @param extra Auxiliary object which was passed to {@link DisplayImageOptions.Builder#extraForDownloader(Object)
* DisplayImageOptions.extraForDownloader(Object)}; can be null
* @return {@link InputStream} of image
* @throws FileNotFoundException if the provided URI could not be opened
*/
protected InputStream getStreamFromContent(String imageUri, Object extra) throws FileNotFoundException {
ContentResolver res = context.getContentResolver();
Uri uri = Uri.parse(imageUri);
if (isVideoContentUri(uri)) { // video thumbnail
Long origId = Long.valueOf(uri.getLastPathSegment());
Bitmap bitmap = MediaStore.Video.Thumbnails
.getThumbnail(res, origId, MediaStore.Images.Thumbnails.MINI_KIND, null);
if (bitmap != null) {
ByteArrayOutputStream bos = new ByteArrayOutputStream();
bitmap.compress(CompressFormat.PNG, 0, bos);
return new ByteArrayInputStream(bos.toByteArray());
}
} else if (imageUri.startsWith(CONTENT_CONTACTS_URI_PREFIX)) { // contacts photo
return getContactPhotoStream(uri);
}
return res.openInputStream(uri);
}
@TargetApi(Build.VERSION_CODES.ICE_CREAM_SANDWICH)
protected InputStream getContactPhotoStream(Uri uri) {
ContentResolver res = context.getContentResolver();
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.ICE_CREAM_SANDWICH) {
return ContactsContract.Contacts.openContactPhotoInputStream(res, uri, true);
} else {
return ContactsContract.Contacts.openContactPhotoInputStream(res, uri);
}
}
/**
* Retrieves {@link InputStream} of image by URI (image is located in assets of application).
*
* @param imageUri Image URI
* @param extra Auxiliary object which was passed to {@link DisplayImageOptions.Builder#extraForDownloader(Object)
* DisplayImageOptions.extraForDownloader(Object)}; can be null
* @return {@link InputStream} of image
* @throws IOException if some I/O error occurs file reading
*/
protected InputStream getStreamFromAssets(String imageUri, Object extra) throws IOException {
String filePath = Scheme.ASSETS.crop(imageUri);
return context.getAssets().open(filePath);
}
/**
* Retrieves {@link InputStream} of image by URI (image is located in drawable resources of application).
*
* @param imageUri Image URI
* @param extra Auxiliary object which was passed to {@link DisplayImageOptions.Builder#extraForDownloader(Object)
* DisplayImageOptions.extraForDownloader(Object)}; can be null
* @return {@link InputStream} of image
*/
protected InputStream getStreamFromDrawable(String imageUri, Object extra) {
String drawableIdString = Scheme.DRAWABLE.crop(imageUri);
int drawableId = Integer.parseInt(drawableIdString);
return context.getResources().openRawResource(drawableId);
}
/**
* Retrieves {@link InputStream} of image by URI from other source with unsupported scheme. Should be overriden by
* successors to implement image downloading from special sources.
* This method is called only if image URI has unsupported scheme. Throws {@link UnsupportedOperationException} by
* default.
*
* @param imageUri Image URI
* @param extra Auxiliary object which was passed to {@link DisplayImageOptions.Builder#extraForDownloader(Object)
* DisplayImageOptions.extraForDownloader(Object)}; can be null
* @return {@link InputStream} of image
* @throws IOException if some I/O error occurs
* @throws UnsupportedOperationException if image URI has unsupported scheme(protocol)
*/
protected InputStream getStreamFromOtherSource(String imageUri, Object extra) throws IOException {
throw new UnsupportedOperationException(String.format(ERROR_UNSUPPORTED_SCHEME, imageUri));
}
private boolean isVideoContentUri(Uri uri) {
String mimeType = context.getContentResolver().getType(uri);
return mimeType != null && mimeType.startsWith("video/");
}
private boolean isVideoFileUri(String uri) {
String extension = MimeTypeMap.getFileExtensionFromUrl(Uri.encode(uri));
String mimeType = MimeTypeMap.getSingleton().getMimeTypeFromExtension(extension);
return mimeType != null && mimeType.startsWith("video/");
}
}
================================================
FILE: library/src/main/java/com/nostra13/universalimageloader/core/download/ImageDownloader.java
================================================
/*******************************************************************************
* Copyright 2011-2013 Sergey Tarasevich
*
* 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.nostra13.universalimageloader.core.download;
import com.nostra13.universalimageloader.core.DisplayImageOptions;
import java.io.IOException;
import java.io.InputStream;
import java.util.Locale;
/**
* Provides retrieving of {@link InputStream} of image by URI.
* Implementations have to be thread-safe.
*
* @author Sergey Tarasevich (nostra13[at]gmail[dot]com)
* @since 1.4.0
*/
public interface ImageDownloader {
/**
* Retrieves {@link InputStream} of image by URI.
*
* @param imageUri Image URI
* @param extra Auxiliary object which was passed to {@link DisplayImageOptions.Builder#extraForDownloader(Object)
* DisplayImageOptions.extraForDownloader(Object)}; can be null
* @return {@link InputStream} of image
* @throws IOException if some I/O error occurs during getting image stream
* @throws UnsupportedOperationException if image URI has unsupported scheme(protocol)
*/
InputStream getStream(String imageUri, Object extra) throws IOException;
/** Represents supported schemes(protocols) of URI. Provides convenient methods for work with schemes and URIs. */
public enum Scheme {
HTTP("http"), HTTPS("https"), FILE("file"), CONTENT("content"), ASSETS("assets"), DRAWABLE("drawable"), UNKNOWN("");
private String scheme;
private String uriPrefix;
Scheme(String scheme) {
this.scheme = scheme;
uriPrefix = scheme + "://";
}
/**
* Defines scheme of incoming URI
*
* @param uri URI for scheme detection
* @return Scheme of incoming URI
*/
public static Scheme ofUri(String uri) {
if (uri != null) {
for (Scheme s : values()) {
if (s.belongsTo(uri)) {
return s;
}
}
}
return UNKNOWN;
}
private boolean belongsTo(String uri) {
return uri.toLowerCase(Locale.US).startsWith(uriPrefix);
}
/** Appends scheme to incoming path */
public String wrap(String path) {
return uriPrefix + path;
}
/** Removed scheme part ("scheme://") from incoming URI */
public String crop(String uri) {
if (!belongsTo(uri)) {
throw new IllegalArgumentException(String.format("URI [%1$s] doesn't have expected scheme [%2$s]", uri, scheme));
}
return uri.substring(uriPrefix.length());
}
}
}
================================================
FILE: library/src/main/java/com/nostra13/universalimageloader/core/imageaware/ImageAware.java
================================================
/*******************************************************************************
* Copyright 2013-2014 Sergey Tarasevich
*
* 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.nostra13.universalimageloader.core.imageaware;
import android.graphics.Bitmap;
import android.graphics.drawable.Drawable;
import android.view.View;
import com.nostra13.universalimageloader.core.assist.ViewScaleType;
/**
* Represents image aware view which provides all needed properties and behavior for image processing and displaying
* through {@link com.nostra13.universalimageloader.core.ImageLoader ImageLoader}.
* It can wrap any Android {@link android.view.View View} which can be accessed by {@link #getWrappedView()}. Wrapped
* view is returned in {@link com.nostra13.universalimageloader.core.listener.ImageLoadingListener ImageLoadingListener}'s
* callbacks.
*
* @author Sergey Tarasevich (nostra13[at]gmail[dot]com)
* @see ViewAware
* @see ImageViewAware
* @see NonViewAware
* @since 1.9.0
*/
public interface ImageAware {
/**
* Returns width of image aware view. This value is used to define scale size for original image.
* Can return 0 if width is undefined.
* Is called on UI thread if ImageLoader was called on UI thread. Otherwise - on background thread.
*/
int getWidth();
/**
* Returns height of image aware view. This value is used to define scale size for original image.
* Can return 0 if height is undefined.
* Is called on UI thread if ImageLoader was called on UI thread. Otherwise - on background thread.
*/
int getHeight();
/**
* Returns {@linkplain com.nostra13.universalimageloader.core.assist.ViewScaleType scale type} which is used for
* scaling image for this image aware view. Must NOT return null.
*/
ViewScaleType getScaleType();
/**
* Returns wrapped Android {@link android.view.View View}. Can return null if no view is wrapped or view was
* collected by GC.
* Is called on UI thread if ImageLoader was called on UI thread. Otherwise - on background thread.
*/
View getWrappedView();
/**
* Returns a flag whether image aware view is collected by GC or whatsoever. If so then ImageLoader stop processing
* of task for this image aware view and fires
* {@link com.nostra13.universalimageloader.core.listener.ImageLoadingListener#onLoadingCancelled(String,
* android.view.View) ImageLoadingListener#onLoadingCancelled(String, View)} callback.
* Mey be called on UI thread if ImageLoader was called on UI thread. Otherwise - on background thread.
*
* @return true - if view is collected by GC and ImageLoader should stop processing this image aware view;
* false - otherwise
*/
boolean isCollected();
/**
* Returns ID of image aware view. Point of ID is similar to Object's hashCode. This ID should be unique for every
* image view instance and should be the same for same instances. This ID identifies processing task in ImageLoader
* so ImageLoader won't process two image aware views with the same ID in one time. When ImageLoader get new task
* it cancels old task with this ID (if any) and starts new task.
*
* It's reasonable to return hash code of wrapped view (if any) to prevent displaying non-actual images in view
* because of view re-using.
*/
int getId();
/**
* Sets image drawable into this image aware view.
* Displays drawable in this image aware view
* {@linkplain com.nostra13.universalimageloader.core.DisplayImageOptions.Builder#showImageForEmptyUri(
*android.graphics.drawable.Drawable) for empty Uri},
* {@linkplain com.nostra13.universalimageloader.core.DisplayImageOptions.Builder#showImageOnLoading(
*android.graphics.drawable.Drawable) on loading} or
* {@linkplain com.nostra13.universalimageloader.core.DisplayImageOptions.Builder#showImageOnFail(
*android.graphics.drawable.Drawable) on loading fail}. These drawables can be specified in
* {@linkplain com.nostra13.universalimageloader.core.DisplayImageOptions display options}.
* Also can be called in {@link com.nostra13.universalimageloader.core.display.BitmapDisplayer BitmapDisplayer}.< br />
* Is called on UI thread if ImageLoader was called on UI thread. Otherwise - on background thread.
*
* @return true if drawable was set successfully; false - otherwise
*/
boolean setImageDrawable(Drawable drawable);
/**
* Sets image bitmap into this image aware view.
* Displays loaded and decoded image {@link android.graphics.Bitmap} in this image view aware.
* Actually it's used only in
* {@link com.nostra13.universalimageloader.core.display.BitmapDisplayer BitmapDisplayer}.< br />
* Is called on UI thread if ImageLoader was called on UI thread. Otherwise - on background thread.
*
* @return true if bitmap was set successfully; false - otherwise
*/
boolean setImageBitmap(Bitmap bitmap);
}
================================================
FILE: library/src/main/java/com/nostra13/universalimageloader/core/imageaware/ImageViewAware.java
================================================
/*******************************************************************************
* Copyright 2013-2014 Sergey Tarasevich
*
* 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.nostra13.universalimageloader.core.imageaware;
import android.graphics.Bitmap;
import android.graphics.drawable.AnimationDrawable;
import android.graphics.drawable.Drawable;
import android.view.View;
import android.widget.ImageView;
import com.nostra13.universalimageloader.core.assist.ViewScaleType;
import com.nostra13.universalimageloader.utils.L;
import java.lang.reflect.Field;
/**
* Wrapper for Android {@link android.widget.ImageView ImageView}. Keeps weak reference of ImageView to prevent memory
* leaks.
*
* @author Sergey Tarasevich (nostra13[at]gmail[dot]com)
* @since 1.9.0
*/
public class ImageViewAware extends ViewAware {
/**
* Constructor.
* References {@link #ImageViewAware(android.widget.ImageView, boolean) ImageViewAware(imageView, true)}.
*
* @param imageView {@link android.widget.ImageView ImageView} to work with
*/
public ImageViewAware(ImageView imageView) {
super(imageView);
}
/**
* Constructor
*
* @param imageView {@link android.widget.ImageView ImageView} to work with
* @param checkActualViewSize true - then {@link #getWidth()} and {@link #getHeight()} will check actual
* size of ImageView. It can cause known issues like
* this.
* But it helps to save memory because memory cache keeps bitmaps of actual (less in
* general) size.
*
* false - then {@link #getWidth()} and {@link #getHeight()} will NOT
* consider actual size of ImageView, just layout parameters.
If you set 'false'
* it's recommended 'android:layout_width' and 'android:layout_height' (or
* 'android:maxWidth' and 'android:maxHeight') are set with concrete values. It helps to
* save memory.
*
*/
public ImageViewAware(ImageView imageView, boolean checkActualViewSize) {
super(imageView, checkActualViewSize);
}
/**
* {@inheritDoc}
*
* 3) Get maxWidth.
*/
@Override
public int getWidth() {
int width = super.getWidth();
if (width <= 0) {
ImageView imageView = (ImageView) viewRef.get();
if (imageView != null) {
width = imageView.getMaxWidth();
}
}
return width;
}
/**
* {@inheritDoc}
*
* 3) Get maxHeight
*/
@Override
public int getHeight() {
int height = super.getHeight();
if (height <= 0) {
ImageView imageView = (ImageView) viewRef.get();
if (imageView != null) {
height = imageView.getMaxHeight();
}
}
return height;
}
@Override
public ViewScaleType getScaleType() {
ImageView imageView = (ImageView) viewRef.get();
if (imageView != null) {
return ViewScaleType.fromImageView(imageView);
}
return super.getScaleType();
}
@Override
public ImageView getWrappedView() {
return (ImageView) super.getWrappedView();
}
@Override
protected void setImageDrawableInto(Drawable drawable, View view) {
((ImageView) view).setImageDrawable(drawable);
if (drawable instanceof AnimationDrawable) {
((AnimationDrawable)drawable).start();
}
}
@Override
protected void setImageBitmapInto(Bitmap bitmap, View view) {
((ImageView) view).setImageBitmap(bitmap);
}
}
================================================
FILE: library/src/main/java/com/nostra13/universalimageloader/core/imageaware/NonViewAware.java
================================================
/*******************************************************************************
* Copyright 2013-2014 Sergey Tarasevich
*
* 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.nostra13.universalimageloader.core.imageaware;
import android.graphics.Bitmap;
import android.graphics.drawable.Drawable;
import android.text.TextUtils;
import android.view.View;
import com.nostra13.universalimageloader.core.assist.ImageSize;
import com.nostra13.universalimageloader.core.assist.ViewScaleType;
/**
* ImageAware which provides needed info for processing of original image but do nothing for displaying image. It's
* used when user need just load and decode image and get it in {@linkplain
* com.nostra13.universalimageloader.core.listener.ImageLoadingListener#onLoadingComplete(String, android.view.View,
* android.graphics.Bitmap) callback}.
*
* @author Sergey Tarasevich (nostra13[at]gmail[dot]com)
* @since 1.9.0
*/
public class NonViewAware implements ImageAware {
protected final String imageUri;
protected final ImageSize imageSize;
protected final ViewScaleType scaleType;
public NonViewAware(ImageSize imageSize, ViewScaleType scaleType) {
this(null, imageSize, scaleType);
}
public NonViewAware(String imageUri, ImageSize imageSize, ViewScaleType scaleType) {
if (imageSize == null) throw new IllegalArgumentException("imageSize must not be null");
if (scaleType == null) throw new IllegalArgumentException("scaleType must not be null");
this.imageUri = imageUri;
this.imageSize = imageSize;
this.scaleType = scaleType;
}
@Override
public int getWidth() {
return imageSize.getWidth();
}
@Override
public int getHeight() {
return imageSize.getHeight();
}
@Override
public ViewScaleType getScaleType() {
return scaleType;
}
@Override
public View getWrappedView() {
return null;
}
@Override
public boolean isCollected() {
return false;
}
@Override
public int getId() {
return TextUtils.isEmpty(imageUri) ? super.hashCode() : imageUri.hashCode();
}
@Override
public boolean setImageDrawable(Drawable drawable) { // Do nothing
return true;
}
@Override
public boolean setImageBitmap(Bitmap bitmap) { // Do nothing
return true;
}
}
================================================
FILE: library/src/main/java/com/nostra13/universalimageloader/core/imageaware/ViewAware.java
================================================
/*******************************************************************************
* Copyright 2014 Sergey Tarasevich
*
* 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.nostra13.universalimageloader.core.imageaware;
import android.graphics.Bitmap;
import android.graphics.drawable.Drawable;
import android.os.Looper;
import android.view.View;
import android.view.ViewGroup;
import com.nostra13.universalimageloader.core.assist.ViewScaleType;
import com.nostra13.universalimageloader.utils.L;
import java.lang.ref.Reference;
import java.lang.ref.WeakReference;
/**
* Wrapper for Android {@link android.view.View View}. Keeps weak reference of View to prevent memory leaks.
*
* @author Sergey Tarasevich (nostra13[at]gmail[dot]com)
* @since 1.9.2
*/
public abstract class ViewAware implements ImageAware {
public static final String WARN_CANT_SET_DRAWABLE = "Can't set a drawable into view. You should call ImageLoader on UI thread for it.";
public static final String WARN_CANT_SET_BITMAP = "Can't set a bitmap into view. You should call ImageLoader on UI thread for it.";
protected Reference
* References {@link #ViewAware(android.view.View, boolean) ImageViewAware(imageView, true)}.
*
* @param view {@link android.view.View View} to work with
*/
public ViewAware(View view) {
this(view, true);
}
/**
* Constructor
*
* @param view {@link android.view.View View} to work with
* @param checkActualViewSize true - then {@link #getWidth()} and {@link #getHeight()} will check actual
* size of View. It can cause known issues like
* this.
* But it helps to save memory because memory cache keeps bitmaps of actual (less in
* general) size.
*
* false - then {@link #getWidth()} and {@link #getHeight()} will NOT
* consider actual size of View, just layout parameters.
If you set 'false'
* it's recommended 'android:layout_width' and 'android:layout_height' (or
* 'android:maxWidth' and 'android:maxHeight') are set with concrete values. It helps to
* save memory.
*/
public ViewAware(View view, boolean checkActualViewSize) {
if (view == null) throw new IllegalArgumentException("view must not be null");
this.viewRef = new WeakReference
* Size computing algorithm (go by steps until get non-zero value):
* 1) Get the actual drawn getWidth() of the View
* 2) Get layout_width
*/
@Override
public int getWidth() {
View view = viewRef.get();
if (view != null) {
final ViewGroup.LayoutParams params = view.getLayoutParams();
int width = 0;
if (checkActualViewSize && params != null && params.width != ViewGroup.LayoutParams.WRAP_CONTENT) {
width = view.getWidth(); // Get actual image width
}
if (width <= 0 && params != null) width = params.width; // Get layout width parameter
return width;
}
return 0;
}
/**
* {@inheritDoc}
*
* Height is defined by target {@link android.view.View view} parameters, configuration
* parameters or device display dimensions.
* Size computing algorithm (go by steps until get non-zero value):
* 1) Get the actual drawn getHeight() of the View
* 2) Get layout_height
*/
@Override
public int getHeight() {
View view = viewRef.get();
if (view != null) {
final ViewGroup.LayoutParams params = view.getLayoutParams();
int height = 0;
if (checkActualViewSize && params != null && params.height != ViewGroup.LayoutParams.WRAP_CONTENT) {
height = view.getHeight(); // Get actual image height
}
if (height <= 0 && params != null) height = params.height; // Get layout height parameter
return height;
}
return 0;
}
@Override
public ViewScaleType getScaleType() {
return ViewScaleType.CROP;
}
@Override
public View getWrappedView() {
return viewRef.get();
}
@Override
public boolean isCollected() {
return viewRef.get() == null;
}
@Override
public int getId() {
View view = viewRef.get();
return view == null ? super.hashCode() : view.hashCode();
}
@Override
public boolean setImageDrawable(Drawable drawable) {
if (Looper.myLooper() == Looper.getMainLooper()) {
View view = viewRef.get();
if (view != null) {
setImageDrawableInto(drawable, view);
return true;
}
} else {
L.w(WARN_CANT_SET_DRAWABLE);
}
return false;
}
@Override
public boolean setImageBitmap(Bitmap bitmap) {
if (Looper.myLooper() == Looper.getMainLooper()) {
View view = viewRef.get();
if (view != null) {
setImageBitmapInto(bitmap, view);
return true;
}
} else {
L.w(WARN_CANT_SET_BITMAP);
}
return false;
}
/**
* Should set drawable into incoming view. Incoming view is guaranteed not null.
* This method is called on UI thread.
*/
protected abstract void setImageDrawableInto(Drawable drawable, View view);
/**
* Should set Bitmap into incoming view. Incoming view is guaranteed not null.< br />
* This method is called on UI thread.
*/
protected abstract void setImageBitmapInto(Bitmap bitmap, View view);
}
================================================
FILE: library/src/main/java/com/nostra13/universalimageloader/core/listener/ImageLoadingListener.java
================================================
/*******************************************************************************
* Copyright 2011-2013 Sergey Tarasevich
*
* 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.nostra13.universalimageloader.core.listener;
import android.graphics.Bitmap;
import android.view.View;
import com.nostra13.universalimageloader.core.assist.FailReason;
/**
* Listener for image loading process.
* You can use {@link SimpleImageLoadingListener} for implementing only needed methods.
*
* @author Sergey Tarasevich (nostra13[at]gmail[dot]com)
* @see SimpleImageLoadingListener
* @see com.nostra13.universalimageloader.core.assist.FailReason
* @since 1.0.0
*/
public interface ImageLoadingListener {
/**
* Is called when image loading task was started
*
* @param imageUri Loading image URI
* @param view View for image
*/
void onLoadingStarted(String imageUri, View view);
/**
* Is called when an error was occurred during image loading
*
* @param imageUri Loading image URI
* @param view View for image. Can be null.
* @param failReason {@linkplain com.nostra13.universalimageloader.core.assist.FailReason The reason} why image
* loading was failed
*/
void onLoadingFailed(String imageUri, View view, FailReason failReason);
/**
* Is called when image is loaded successfully (and displayed in View if one was specified)
*
* @param imageUri Loaded image URI
* @param view View for image. Can be null.
* @param loadedImage Bitmap of loaded and decoded image
*/
void onLoadingComplete(String imageUri, View view, Bitmap loadedImage);
/**
* Is called when image loading task was cancelled because View for image was reused in newer task
*
* @param imageUri Loading image URI
* @param view View for image. Can be null.
*/
void onLoadingCancelled(String imageUri, View view);
}
================================================
FILE: library/src/main/java/com/nostra13/universalimageloader/core/listener/ImageLoadingProgressListener.java
================================================
/*******************************************************************************
* Copyright 2013 Sergey Tarasevich
*
* 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.nostra13.universalimageloader.core.listener;
import android.view.View;
/**
* Listener for image loading progress.
*
* @author Sergey Tarasevich (nostra13[at]gmail[dot]com)
* @since 1.9.1
*/
public interface ImageLoadingProgressListener {
/**
* Is called when image loading progress changed.
*
* @param imageUri Image URI
* @param view View for image. Can be null.
* @param current Downloaded size in bytes
* @param total Total size in bytes
*/
void onProgressUpdate(String imageUri, View view, int current, int total);
}
================================================
FILE: library/src/main/java/com/nostra13/universalimageloader/core/listener/PauseOnScrollListener.java
================================================
/*******************************************************************************
* Copyright 2011-2013 Sergey Tarasevich
*
* 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.nostra13.universalimageloader.core.listener;
import android.widget.AbsListView;
import android.widget.AbsListView.OnScrollListener;
import android.widget.GridView;
import android.widget.ListView;
import com.nostra13.universalimageloader.core.ImageLoader;
/**
* Listener-helper for {@linkplain AbsListView list views} ({@link ListView}, {@link GridView}) which can
* {@linkplain ImageLoader#pause() pause ImageLoader's tasks} while list view is scrolling (touch scrolling and/or
* fling). It prevents redundant loadings.
* Set it to your list view's {@link AbsListView#setOnScrollListener(OnScrollListener) setOnScrollListener(...)}.
* This listener can wrap your custom {@linkplain OnScrollListener listener}.
*
* @author Sergey Tarasevich (nostra13[at]gmail[dot]com)
* @since 1.7.0
*/
public class PauseOnScrollListener implements OnScrollListener {
private ImageLoader imageLoader;
private final boolean pauseOnScroll;
private final boolean pauseOnFling;
private final OnScrollListener externalListener;
/**
* Constructor
*
* @param imageLoader {@linkplain ImageLoader} instance for controlling
* @param pauseOnScroll Whether {@linkplain ImageLoader#pause() pause ImageLoader} during touch scrolling
* @param pauseOnFling Whether {@linkplain ImageLoader#pause() pause ImageLoader} during fling
*/
public PauseOnScrollListener(ImageLoader imageLoader, boolean pauseOnScroll, boolean pauseOnFling) {
this(imageLoader, pauseOnScroll, pauseOnFling, null);
}
/**
* Constructor
*
* @param imageLoader {@linkplain ImageLoader} instance for controlling
* @param pauseOnScroll Whether {@linkplain ImageLoader#pause() pause ImageLoader} during touch scrolling
* @param pauseOnFling Whether {@linkplain ImageLoader#pause() pause ImageLoader} during fling
* @param customListener Your custom {@link OnScrollListener} for {@linkplain AbsListView list view} which also
* will be get scroll events
*/
public PauseOnScrollListener(ImageLoader imageLoader, boolean pauseOnScroll, boolean pauseOnFling,
OnScrollListener customListener) {
this.imageLoader = imageLoader;
this.pauseOnScroll = pauseOnScroll;
this.pauseOnFling = pauseOnFling;
externalListener = customListener;
}
@Override
public void onScrollStateChanged(AbsListView view, int scrollState) {
switch (scrollState) {
case OnScrollListener.SCROLL_STATE_IDLE:
imageLoader.resume();
break;
case OnScrollListener.SCROLL_STATE_TOUCH_SCROLL:
if (pauseOnScroll) {
imageLoader.pause();
}
break;
case OnScrollListener.SCROLL_STATE_FLING:
if (pauseOnFling) {
imageLoader.pause();
}
break;
}
if (externalListener != null) {
externalListener.onScrollStateChanged(view, scrollState);
}
}
@Override
public void onScroll(AbsListView view, int firstVisibleItem, int visibleItemCount, int totalItemCount) {
if (externalListener != null) {
externalListener.onScroll(view, firstVisibleItem, visibleItemCount, totalItemCount);
}
}
}
================================================
FILE: library/src/main/java/com/nostra13/universalimageloader/core/listener/SimpleImageLoadingListener.java
================================================
/*******************************************************************************
* Copyright 2011-2013 Sergey Tarasevich
*
* 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.nostra13.universalimageloader.core.listener;
import android.graphics.Bitmap;
import android.view.View;
import com.nostra13.universalimageloader.core.assist.FailReason;
/**
* A convenient class to extend when you only want to listen for a subset of all the image loading events. This
* implements all methods in the {@link com.nostra13.universalimageloader.core.listener.ImageLoadingListener} but does
* nothing.
*
* @author Sergey Tarasevich (nostra13[at]gmail[dot]com)
* @since 1.4.0
*/
public class SimpleImageLoadingListener implements ImageLoadingListener {
@Override
public void onLoadingStarted(String imageUri, View view) {
// Empty implementation
}
@Override
public void onLoadingFailed(String imageUri, View view, FailReason failReason) {
// Empty implementation
}
@Override
public void onLoadingComplete(String imageUri, View view, Bitmap loadedImage) {
// Empty implementation
}
@Override
public void onLoadingCancelled(String imageUri, View view) {
// Empty implementation
}
}
================================================
FILE: library/src/main/java/com/nostra13/universalimageloader/core/process/BitmapProcessor.java
================================================
/*******************************************************************************
* Copyright 2011-2013 Sergey Tarasevich
*
* 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.nostra13.universalimageloader.core.process;
import android.graphics.Bitmap;
import com.nostra13.universalimageloader.core.DisplayImageOptions;
/**
* Makes some processing on {@link Bitmap}. Implementations can apply any changes to original {@link Bitmap}.
* Implementations have to be thread-safe.
*
* @author Sergey Tarasevich (nostra13[at]gmail[dot]com)
* @since 1.8.0
*/
public interface BitmapProcessor {
/**
* Makes some processing of incoming bitmap.
* This method is executing on additional thread (not on UI thread).
* Note: If this processor is used as {@linkplain DisplayImageOptions.Builder#preProcessor(BitmapProcessor)
* pre-processor} then don't forget {@linkplain Bitmap#recycle() to recycle} incoming bitmap if you return a new
* created one.
*
* @param bitmap Original {@linkplain Bitmap bitmap}
* @return Processed {@linkplain Bitmap bitmap}
*/
Bitmap process(Bitmap bitmap);
}
================================================
FILE: library/src/main/java/com/nostra13/universalimageloader/utils/DiskCacheUtils.java
================================================
/*******************************************************************************
* Copyright 2011-2014 Sergey Tarasevich
*
* 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.nostra13.universalimageloader.utils;
import com.nostra13.universalimageloader.cache.disc.DiskCache;
import java.io.File;
/**
* Utility for convenient work with disk cache.
* NOTE: This utility works with file system so avoid using it on application main thread.
*
* @author Sergey Tarasevich (nostra13[at]gmail[dot]com)
* @since 1.8.0
*/
public final class DiskCacheUtils {
private DiskCacheUtils() {
}
/** Returns {@link File} of cached image or null if image was not cached in disk cache */
public static File findInCache(String imageUri, DiskCache diskCache) {
File image = diskCache.get(imageUri);
return image != null && image.exists() ? image : null;
}
/**
* Removed cached image file from disk cache (if image was cached in disk cache before)
*
* @return true - if cached image file existed and was deleted; false - otherwise.
*/
public static boolean removeFromCache(String imageUri, DiskCache diskCache) {
File image = diskCache.get(imageUri);
return image != null && image.exists() && image.delete();
}
}
================================================
FILE: library/src/main/java/com/nostra13/universalimageloader/utils/ImageSizeUtils.java
================================================
/*******************************************************************************
* Copyright 2013-2014 Sergey Tarasevich
*
* 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.nostra13.universalimageloader.utils;
import android.graphics.BitmapFactory;
import android.opengl.GLES10;
import com.nostra13.universalimageloader.core.assist.ImageSize;
import com.nostra13.universalimageloader.core.assist.ViewScaleType;
import com.nostra13.universalimageloader.core.imageaware.ImageAware;
import javax.microedition.khronos.opengles.GL10;
/**
* Provides calculations with image sizes, scales
*
* @author Sergey Tarasevich (nostra13[at]gmail[dot]com)
* @since 1.8.3
*/
public final class ImageSizeUtils {
private static final int DEFAULT_MAX_BITMAP_DIMENSION = 2048;
private static ImageSize maxBitmapSize;
static {
int[] maxTextureSize = new int[1];
GLES10.glGetIntegerv(GL10.GL_MAX_TEXTURE_SIZE, maxTextureSize, 0);
int maxBitmapDimension = Math.max(maxTextureSize[0], DEFAULT_MAX_BITMAP_DIMENSION);
maxBitmapSize = new ImageSize(maxBitmapDimension, maxBitmapDimension);
}
private ImageSizeUtils() {
}
/**
* Defines target size for image aware view. Size is defined by target
* {@link com.nostra13.universalimageloader.core.imageaware.ImageAware view} parameters, configuration
* parameters or device display dimensions.
*/
public static ImageSize defineTargetSizeForView(ImageAware imageAware, ImageSize maxImageSize) {
int width = imageAware.getWidth();
if (width <= 0) {
width = maxImageSize.getWidth();
} else {
width = Math.min(width, maxImageSize.getWidth());
}
int height = imageAware.getHeight();
if (height <= 0) {
height = maxImageSize.getHeight();
} else {
height = Math.min(height, maxImageSize.getHeight());
}
return new ImageSize(width, height);
}
/**
* Computes sample size for downscaling image size (srcSize) to view size (targetSize). This sample
* size is used during
* {@linkplain BitmapFactory#decodeStream(java.io.InputStream, android.graphics.Rect, android.graphics.BitmapFactory.Options)
* decoding image} to bitmap.
*
* Examples:
*
*
* srcSize(100x100), targetSize(10x10), powerOf2Scale = true -> sampleSize = 8
* srcSize(100x100), targetSize(10x10), powerOf2Scale = false -> sampleSize = 10
*
* srcSize(100x100), targetSize(20x40), viewScaleType = FIT_INSIDE -> sampleSize = 5
* srcSize(100x100), targetSize(20x40), viewScaleType = CROP -> sampleSize = 2
*
*
*
* The sample size is the number of pixels in either dimension that correspond to a single pixel in the decoded
* bitmap. For example, inSampleSize == 4 returns an image that is 1/4 the width/height of the original, and 1/16
* the number of pixels. Any value <= 1 is treated the same as 1.
*
* @param srcSize Original (image) size
* @param targetSize Target (view) size
* @param viewScaleType {@linkplain ViewScaleType Scale type} for placing image in view
* @param powerOf2Scale true - if sample size be a power of 2 (1, 2, 4, 8, ...)
* @return Computed sample size
*/
public static int computeImageSampleSize(ImageSize srcSize, ImageSize targetSize, ViewScaleType viewScaleType,
boolean powerOf2Scale) {
final int srcWidth = srcSize.getWidth();
final int srcHeight = srcSize.getHeight();
final int targetWidth = targetSize.getWidth();
final int targetHeight = targetSize.getHeight();
int scale = 1;
switch (viewScaleType) {
case FIT_INSIDE:
if (powerOf2Scale) {
final int halfWidth = srcWidth / 2;
final int halfHeight = srcHeight / 2;
while ((halfWidth / scale) > targetWidth || (halfHeight / scale) > targetHeight) { // ||
scale *= 2;
}
} else {
scale = Math.max(srcWidth / targetWidth, srcHeight / targetHeight); // max
}
break;
case CROP:
if (powerOf2Scale) {
final int halfWidth = srcWidth / 2;
final int halfHeight = srcHeight / 2;
while ((halfWidth / scale) > targetWidth && (halfHeight / scale) > targetHeight) { // &&
scale *= 2;
}
} else {
scale = Math.min(srcWidth / targetWidth, srcHeight / targetHeight); // min
}
break;
}
if (scale < 1) {
scale = 1;
}
scale = considerMaxTextureSize(srcWidth, srcHeight, scale, powerOf2Scale);
return scale;
}
private static int considerMaxTextureSize(int srcWidth, int srcHeight, int scale, boolean powerOf2) {
final int maxWidth = maxBitmapSize.getWidth();
final int maxHeight = maxBitmapSize.getHeight();
while ((srcWidth / scale) > maxWidth || (srcHeight / scale) > maxHeight) {
if (powerOf2) {
scale *= 2;
} else {
scale++;
}
}
return scale;
}
/**
* Computes minimal sample size for downscaling image so result image size won't exceed max acceptable OpenGL
* texture size.
* We can't create Bitmap in memory with size exceed max texture size (usually this is 2048x2048) so this method
* calculate minimal sample size which should be applied to image to fit into these limits.
*
* @param srcSize Original image size
* @return Minimal sample size
*/
public static int computeMinImageSampleSize(ImageSize srcSize) {
final int srcWidth = srcSize.getWidth();
final int srcHeight = srcSize.getHeight();
final int targetWidth = maxBitmapSize.getWidth();
final int targetHeight = maxBitmapSize.getHeight();
final int widthScale = (int) Math.ceil((float) srcWidth / targetWidth);
final int heightScale = (int) Math.ceil((float) srcHeight / targetHeight);
return Math.max(widthScale, heightScale); // max
}
/**
* Computes scale of target size (targetSize) to source size (srcSize).
*
* Examples:
*
*
* srcSize(40x40), targetSize(10x10) -> scale = 0.25
*
* srcSize(10x10), targetSize(20x20), stretch = false -> scale = 1
* srcSize(10x10), targetSize(20x20), stretch = true -> scale = 2
*
* srcSize(100x100), targetSize(20x40), viewScaleType = FIT_INSIDE -> scale = 0.2
* srcSize(100x100), targetSize(20x40), viewScaleType = CROP -> scale = 0.4
*
*
* @param srcSize Source (image) size
* @param targetSize Target (view) size
* @param viewScaleType {@linkplain ViewScaleType Scale type} for placing image in view
* @param stretch Whether source size should be stretched if target size is larger than source size. If false
* then result scale value can't be greater than 1.
* @return Computed scale
*/
public static float computeImageScale(ImageSize srcSize, ImageSize targetSize, ViewScaleType viewScaleType,
boolean stretch) {
final int srcWidth = srcSize.getWidth();
final int srcHeight = srcSize.getHeight();
final int targetWidth = targetSize.getWidth();
final int targetHeight = targetSize.getHeight();
final float widthScale = (float) srcWidth / targetWidth;
final float heightScale = (float) srcHeight / targetHeight;
final int destWidth;
final int destHeight;
if ((viewScaleType == ViewScaleType.FIT_INSIDE && widthScale >= heightScale) || (viewScaleType == ViewScaleType.CROP && widthScale < heightScale)) {
destWidth = targetWidth;
destHeight = (int) (srcHeight / widthScale);
} else {
destWidth = (int) (srcWidth / heightScale);
destHeight = targetHeight;
}
float scale = 1;
if ((!stretch && destWidth < srcWidth && destHeight < srcHeight) || (stretch && destWidth != srcWidth && destHeight != srcHeight)) {
scale = (float) destWidth / srcWidth;
}
return scale;
}
}
================================================
FILE: library/src/main/java/com/nostra13/universalimageloader/utils/IoUtils.java
================================================
/*******************************************************************************
* Copyright 2011-2014 Sergey Tarasevich
*
* 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.nostra13.universalimageloader.utils;
import java.io.Closeable;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
/**
* Provides I/O operations
*
* @author Sergey Tarasevich (nostra13[at]gmail[dot]com)
* @since 1.0.0
*/
public final class IoUtils {
/** {@value} */
public static final int DEFAULT_BUFFER_SIZE = 32 * 1024; // 32 KB
/** {@value} */
public static final int DEFAULT_IMAGE_TOTAL_SIZE = 500 * 1024; // 500 Kb
/** {@value} */
public static final int CONTINUE_LOADING_PERCENTAGE = 75;
private IoUtils() {
}
/**
* Copies stream, fires progress events by listener, can be interrupted by listener. Uses buffer size =
* {@value #DEFAULT_BUFFER_SIZE} bytes.
*
* @param is Input stream
* @param os Output stream
* @param listener null-ok; Listener of copying progress and controller of copying interrupting
* @return true - if stream copied successfully; false - if copying was interrupted by listener
* @throws IOException
*/
public static boolean copyStream(InputStream is, OutputStream os, CopyListener listener) throws IOException {
return copyStream(is, os, listener, DEFAULT_BUFFER_SIZE);
}
/**
* Copies stream, fires progress events by listener, can be interrupted by listener.
*
* @param is Input stream
* @param os Output stream
* @param listener null-ok; Listener of copying progress and controller of copying interrupting
* @param bufferSize Buffer size for copying, also represents a step for firing progress listener callback, i.e.
* progress event will be fired after every copied bufferSize bytes
* @return true - if stream copied successfully; false - if copying was interrupted by listener
* @throws IOException
*/
public static boolean copyStream(InputStream is, OutputStream os, CopyListener listener, int bufferSize)
throws IOException {
int current = 0;
int total = is.available();
if (total <= 0) {
total = DEFAULT_IMAGE_TOTAL_SIZE;
}
final byte[] bytes = new byte[bufferSize];
int count;
if (shouldStopLoading(listener, current, total)) return false;
while ((count = is.read(bytes, 0, bufferSize)) != -1) {
os.write(bytes, 0, count);
current += count;
if (shouldStopLoading(listener, current, total)) return false;
}
os.flush();
return true;
}
private static boolean shouldStopLoading(CopyListener listener, int current, int total) {
if (listener != null) {
boolean shouldContinue = listener.onBytesCopied(current, total);
if (!shouldContinue) {
if (100 * current / total < CONTINUE_LOADING_PERCENTAGE) {
return true; // if loaded more than 75% then continue loading anyway
}
}
}
return false;
}
/**
* Reads all data from stream and close it silently
*
* @param is Input stream
*/
public static void readAndCloseStream(InputStream is) {
final byte[] bytes = new byte[DEFAULT_BUFFER_SIZE];
try {
while (is.read(bytes, 0, DEFAULT_BUFFER_SIZE) != -1);
} catch (IOException ignored) {
} finally {
closeSilently(is);
}
}
public static void closeSilently(Closeable closeable) {
if (closeable != null) {
try {
closeable.close();
} catch (Exception ignored) {
}
}
}
/** Listener and controller for copy process */
public static interface CopyListener {
/**
* @param current Loaded bytes
* @param total Total bytes for loading
* @return true - if copying should be continued; false - if copying should be interrupted
*/
boolean onBytesCopied(int current, int total);
}
}
================================================
FILE: library/src/main/java/com/nostra13/universalimageloader/utils/L.java
================================================
/*******************************************************************************
* Copyright 2011-2014 Sergey Tarasevich
*
* 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.nostra13.universalimageloader.utils;
import android.util.Log;
import com.nostra13.universalimageloader.core.ImageLoader;
/**
* "Less-word" analog of Android {@link android.util.Log logger}
*
* @author Sergey Tarasevich (nostra13[at]gmail[dot]com)
* @since 1.6.4
*/
public final class L {
private static final String LOG_FORMAT = "%1$s\n%2$s";
private static volatile boolean writeDebugLogs = false;
private static volatile boolean writeLogs = true;
private L() {
}
/**
* Enables logger (if {@link #disableLogging()} was called before)
*
* @deprecated Use {@link #writeLogs(boolean) writeLogs(true)} instead
*/
@Deprecated
public static void enableLogging() {
writeLogs(true);
}
/**
* Disables logger, no logs will be passed to LogCat, all log methods will do nothing
*
* @deprecated Use {@link #writeLogs(boolean) writeLogs(false)} instead
*/
@Deprecated
public static void disableLogging() {
writeLogs(false);
}
/**
* Enables/disables detail logging of {@link ImageLoader} work.
* Consider {@link com.nostra13.universalimageloader.utils.L#disableLogging()} to disable
* ImageLoader logging completely (even error logs)
* Debug logs are disabled by default.
*/
public static void writeDebugLogs(boolean writeDebugLogs) {
L.writeDebugLogs = writeDebugLogs;
}
/** Enables/disables logging of {@link ImageLoader} completely (even error logs). */
public static void writeLogs(boolean writeLogs) {
L.writeLogs = writeLogs;
}
public static void d(String message, Object... args) {
if (writeDebugLogs) {
log(Log.DEBUG, null, message, args);
}
}
public static void i(String message, Object... args) {
log(Log.INFO, null, message, args);
}
public static void w(String message, Object... args) {
log(Log.WARN, null, message, args);
}
public static void e(Throwable ex) {
log(Log.ERROR, ex, null);
}
public static void e(String message, Object... args) {
log(Log.ERROR, null, message, args);
}
public static void e(Throwable ex, String message, Object... args) {
log(Log.ERROR, ex, message, args);
}
private static void log(int priority, Throwable ex, String message, Object... args) {
if (!writeLogs) return;
if (args.length > 0) {
message = String.format(message, args);
}
String log;
if (ex == null) {
log = message;
} else {
String logMessage = message == null ? ex.getMessage() : message;
String logBody = Log.getStackTraceString(ex);
log = String.format(LOG_FORMAT, logMessage, logBody);
}
Log.println(priority, ImageLoader.TAG, log);
}
}
================================================
FILE: library/src/main/java/com/nostra13/universalimageloader/utils/MemoryCacheUtils.java
================================================
/*******************************************************************************
* Copyright 2011-2014 Sergey Tarasevich
*
* 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.nostra13.universalimageloader.utils;
import android.graphics.Bitmap;
import com.nostra13.universalimageloader.cache.memory.MemoryCache;
import com.nostra13.universalimageloader.core.ImageLoaderConfiguration;
import com.nostra13.universalimageloader.core.assist.ImageSize;
import java.util.ArrayList;
import java.util.Comparator;
import java.util.List;
/**
* Utility for generating of keys for memory cache, key comparing and other work with memory cache
*
* @author Sergey Tarasevich (nostra13[at]gmail[dot]com)
* @since 1.6.3
*/
public final class MemoryCacheUtils {
private static final String URI_AND_SIZE_SEPARATOR = "_";
private static final String WIDTH_AND_HEIGHT_SEPARATOR = "x";
private MemoryCacheUtils() {
}
/**
* Generates key for memory cache for incoming image (URI + size).
* Pattern for cache key - [imageUri]_[width]x[height].
*/
public static String generateKey(String imageUri, ImageSize targetSize) {
return new StringBuilder(imageUri).append(URI_AND_SIZE_SEPARATOR).append(targetSize.getWidth()).append(WIDTH_AND_HEIGHT_SEPARATOR).append(targetSize.getHeight()).toString();
}
public static Comparator
* Note: Memory cache can contain multiple sizes of the same image if only you didn't set
* {@link ImageLoaderConfiguration.Builder#denyCacheImageMultipleSizesInMemory()
* denyCacheImageMultipleSizesInMemory()} option in {@linkplain ImageLoaderConfiguration configuration}
*/
public static List
* Note: Memory cache can contain multiple sizes of the same image if only you didn't set
* {@link ImageLoaderConfiguration.Builder#denyCacheImageMultipleSizesInMemory()
* denyCacheImageMultipleSizesInMemory()} option in {@linkplain ImageLoaderConfiguration configuration}
*/
public static List
* Note: Memory cache can contain multiple sizes of the same image if only you didn't set
* {@link ImageLoaderConfiguration.Builder#denyCacheImageMultipleSizesInMemory()
* denyCacheImageMultipleSizesInMemory()} option in {@linkplain ImageLoaderConfiguration configuration}
*/
public static void removeFromCache(String imageUri, MemoryCache memoryCache) {
List
* NOTE: Can be null in some unpredictable cases (if SD card is unmounted and
* {@link android.content.Context#getCacheDir() Context.getCacheDir()} returns null).
*/
public static File getCacheDirectory(Context context) {
return getCacheDirectory(context, true);
}
/**
* Returns application cache directory. Cache directory will be created on SD card
* ("/Android/data/[app_package_name]/cache") (if card is mounted and app has appropriate permission) or
* on device's file system depending incoming parameters.
*
* @param context Application context
* @param preferExternal Whether prefer external location for cache
* @return Cache {@link File directory}.
* NOTE: Can be null in some unpredictable cases (if SD card is unmounted and
* {@link android.content.Context#getCacheDir() Context.getCacheDir()} returns null).
*/
public static File getCacheDirectory(Context context, boolean preferExternal) {
File appCacheDir = null;
String externalStorageState;
try {
externalStorageState = Environment.getExternalStorageState();
} catch (NullPointerException e) { // (sh)it happens (Issue #660)
externalStorageState = "";
} catch (IncompatibleClassChangeError e) { // (sh)it happens too (Issue #989)
externalStorageState = "";
}
if (preferExternal && MEDIA_MOUNTED.equals(externalStorageState) && hasExternalStoragePermission(context)) {
appCacheDir = getExternalCacheDir(context);
}
if (appCacheDir == null) {
appCacheDir = context.getCacheDir();
}
if (appCacheDir == null) {
String cacheDirPath = "/data/data/" + context.getPackageName() + "/cache/";
L.w("Can't define system cache directory! '%s' will be used.", cacheDirPath);
appCacheDir = new File(cacheDirPath);
}
return appCacheDir;
}
/**
* Returns individual application cache directory (for only image caching from ImageLoader). Cache directory will be
* created on SD card ("/Android/data/[app_package_name]/cache/uil-images") if card is mounted and app has
* appropriate permission. Else - Android defines cache directory on device's file system.
*
* @param context Application context
* @return Cache {@link File directory}
*/
public static File getIndividualCacheDirectory(Context context) {
return getIndividualCacheDirectory(context, INDIVIDUAL_DIR_NAME);
}
/**
* Returns individual application cache directory (for only image caching from ImageLoader). Cache directory will be
* created on SD card ("/Android/data/[app_package_name]/cache/uil-images") if card is mounted and app has
* appropriate permission. Else - Android defines cache directory on device's file system.
*
* @param context Application context
* @param cacheDir Cache directory path (e.g.: "AppCacheDir", "AppDir/cache/images")
* @return Cache {@link File directory}
*/
public static File getIndividualCacheDirectory(Context context, String cacheDir) {
File appCacheDir = getCacheDirectory(context);
File individualCacheDir = new File(appCacheDir, cacheDir);
if (!individualCacheDir.exists()) {
if (!individualCacheDir.mkdir()) {
individualCacheDir = appCacheDir;
}
}
return individualCacheDir;
}
/**
* Returns specified application cache directory. Cache directory will be created on SD card by defined path if card
* is mounted and app has appropriate permission. Else - Android defines cache directory on device's file system.
*
* @param context Application context
* @param cacheDir Cache directory path (e.g.: "AppCacheDir", "AppDir/cache/images")
* @return Cache {@link File directory}
*/
public static File getOwnCacheDirectory(Context context, String cacheDir) {
File appCacheDir = null;
if (MEDIA_MOUNTED.equals(Environment.getExternalStorageState()) && hasExternalStoragePermission(context)) {
appCacheDir = new File(Environment.getExternalStorageDirectory(), cacheDir);
}
if (appCacheDir == null || (!appCacheDir.exists() && !appCacheDir.mkdirs())) {
appCacheDir = context.getCacheDir();
}
return appCacheDir;
}
/**
* Returns specified application cache directory. Cache directory will be created on SD card by defined path if card
* is mounted and app has appropriate permission. Else - Android defines cache directory on device's file system.
*
* @param context Application context
* @param cacheDir Cache directory path (e.g.: "AppCacheDir", "AppDir/cache/images")
* @return Cache {@link File directory}
*/
public static File getOwnCacheDirectory(Context context, String cacheDir, boolean preferExternal) {
File appCacheDir = null;
if (preferExternal && MEDIA_MOUNTED.equals(Environment.getExternalStorageState()) && hasExternalStoragePermission(context)) {
appCacheDir = new File(Environment.getExternalStorageDirectory(), cacheDir);
}
if (appCacheDir == null || (!appCacheDir.exists() && !appCacheDir.mkdirs())) {
appCacheDir = context.getCacheDir();
}
return appCacheDir;
}
private static File getExternalCacheDir(Context context) {
File dataDir = new File(new File(Environment.getExternalStorageDirectory(), "Android"), "data");
File appCacheDir = new File(new File(dataDir, context.getPackageName()), "cache");
if (!appCacheDir.exists()) {
if (!appCacheDir.mkdirs()) {
L.w("Unable to create external cache directory");
return null;
}
try {
new File(appCacheDir, ".nomedia").createNewFile();
} catch (IOException e) {
L.i("Can't create \".nomedia\" file in application external cache directory");
}
}
return appCacheDir;
}
private static boolean hasExternalStoragePermission(Context context) {
int perm = context.checkCallingOrSelfPermission(EXTERNAL_STORAGE_PERMISSION);
return perm == PackageManager.PERMISSION_GRANTED;
}
}
================================================
FILE: library/src/test/java/com/nostra13/universalimageloader/core/assist/ImageSizeTest.java
================================================
package com.nostra13.universalimageloader.core.assist;
import android.app.Activity;
import android.content.Context;
import android.view.View;
import android.view.ViewGroup;
import android.widget.FrameLayout;
import android.widget.ImageView;
import com.nostra13.universalimageloader.core.imageaware.ImageAware;
import com.nostra13.universalimageloader.core.imageaware.ImageViewAware;
import com.nostra13.universalimageloader.utils.ImageSizeUtils;
import org.assertj.core.api.Assertions;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.robolectric.RobolectricTestRunner;
@RunWith(RobolectricTestRunner.class)
public class ImageSizeTest {
private Activity mActivity;
private ImageView mView;
private ImageAware mImageAware;
@Before
public void setUp() throws Exception {
mActivity = new Activity();
// Make and set view with some prelim values to test
mView = new TestImageView(mActivity);
mView.setLayoutParams(new FrameLayout.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.MATCH_PARENT));
mView.measure(View.MeasureSpec.makeMeasureSpec(250, View.MeasureSpec.EXACTLY), View.MeasureSpec.makeMeasureSpec(250, View.MeasureSpec.EXACTLY));
mImageAware = new ImageViewAware(mView);
}
@Test
public void testGetImageSizeScaleTo_useImageActualSize() throws Exception {
// We layout the view to give it a width and height
mView.measure(View.MeasureSpec.makeMeasureSpec(200, View.MeasureSpec.EXACTLY), View.MeasureSpec.makeMeasureSpec(200, View.MeasureSpec.EXACTLY));
mView.layout(0, 0, 200, 200);
ImageSize expected = new ImageSize(200, 200);
ImageSize result = ImageSizeUtils.defineTargetSizeForView(mImageAware, new ImageSize(590, 590));
Assertions.assertThat(result).isNotNull();
Assertions.assertThat(result.getWidth()).isEqualTo(expected.getWidth());
Assertions.assertThat(result.getHeight()).isEqualTo(expected.getHeight());
}
/**
* This will make sure the view falls back to the ViewParams/Max/Or Config if wrap content so that it is never
* shrunk to the first image size. In this case it falls back to the config size
*
* @throws Exception
*/
@Test
public void testGetImageSizeScaleTo_dontUseImageActualSizeWithWrapContent() throws Exception {
//Set it to wrap content so that it will fall back to
mView.setLayoutParams(new FrameLayout.LayoutParams(ViewGroup.LayoutParams.WRAP_CONTENT, ViewGroup.LayoutParams.WRAP_CONTENT));
mView.measure(View.MeasureSpec.makeMeasureSpec(250, View.MeasureSpec.EXACTLY), View.MeasureSpec.makeMeasureSpec(250, View.MeasureSpec.EXACTLY));
// We layout the view to give it a width and height
mView.layout(0, 0, 200, 200);
ImageSize expected = new ImageSize(500, 500);
ImageSize result = ImageSizeUtils.defineTargetSizeForView(mImageAware, new ImageSize(500, 500));
Assertions.assertThat(result).isNotNull().isEqualsToByComparingFields(expected);
}
@Test
public void testGetImageSizeScaleTo_useImageLayoutParams() throws Exception {
// Set a defined width
mView.setLayoutParams(new FrameLayout.LayoutParams(300, 300));
ImageSize expected = new ImageSize(300, 300);
ImageSize result = ImageSizeUtils.defineTargetSizeForView(mImageAware, new ImageSize(500, 500));
Assertions.assertThat(result).isNotNull().isEqualsToByComparingFields(expected);
}
@Test
public void testGetImageSizeScaleTo_useImageConfigMaxSize() throws Exception {
ImageSize expected = new ImageSize(500, 500);
ImageSize result = ImageSizeUtils.defineTargetSizeForView(mImageAware, new ImageSize(500, 500));
Assertions.assertThat(result).isNotNull().isEqualsToByComparingFields(expected);
}
@Test
public void testComputeImageSampleSize_fitInside() throws Exception {
final ViewScaleType scaleType = ViewScaleType.FIT_INSIDE;
int result;
ImageSize srcSize = new ImageSize(300, 100);
ImageSize targetSize = new ImageSize(30, 10);
result = ImageSizeUtils.computeImageSampleSize(srcSize, targetSize, scaleType, false);
Assertions.assertThat(result).isEqualTo(10);
result = ImageSizeUtils.computeImageSampleSize(srcSize, targetSize, scaleType, true);
Assertions.assertThat(result).isEqualTo(8);
srcSize = new ImageSize(300, 100);
targetSize = new ImageSize(200, 200);
result = ImageSizeUtils.computeImageSampleSize(srcSize, targetSize, scaleType, false);
Assertions.assertThat(result).isEqualTo(1);
result = ImageSizeUtils.computeImageSampleSize(srcSize, targetSize, scaleType, true);
Assertions.assertThat(result).isEqualTo(1);
srcSize = new ImageSize(300, 100);
targetSize = new ImageSize(55, 40);
result = ImageSizeUtils.computeImageSampleSize(srcSize, targetSize, scaleType, false);
Assertions.assertThat(result).isEqualTo(5);
result = ImageSizeUtils.computeImageSampleSize(srcSize, targetSize, scaleType, true);
Assertions.assertThat(result).isEqualTo(4);
srcSize = new ImageSize(300, 100);
targetSize = new ImageSize(30, 40);
result = ImageSizeUtils.computeImageSampleSize(srcSize, targetSize, scaleType, false);
Assertions.assertThat(result).isEqualTo(10);
result = ImageSizeUtils.computeImageSampleSize(srcSize, targetSize, scaleType, true);
Assertions.assertThat(result).isEqualTo(8);
srcSize = new ImageSize(5000, 70);
targetSize = new ImageSize(2000, 30);
result = ImageSizeUtils.computeImageSampleSize(srcSize, targetSize, scaleType, false);
Assertions.assertThat(result).isEqualTo(3);
result = ImageSizeUtils.computeImageSampleSize(srcSize, targetSize, scaleType, true);
Assertions.assertThat(result).isEqualTo(4);
}
@Test
public void testComputeImageSampleSize_centerCrop() throws Exception {
final ViewScaleType scaleType = ViewScaleType.CROP;
int result;
ImageSize srcSize = new ImageSize(300, 100);
ImageSize targetSize = new ImageSize(30, 10);
result = ImageSizeUtils.computeImageSampleSize(srcSize, targetSize, scaleType, false);
Assertions.assertThat(result).isEqualTo(10);
result = ImageSizeUtils.computeImageSampleSize(srcSize, targetSize, scaleType, true);
Assertions.assertThat(result).isEqualTo(8);
srcSize = new ImageSize(300, 100);
targetSize = new ImageSize(200, 200);
result = ImageSizeUtils.computeImageSampleSize(srcSize, targetSize, scaleType, false);
Assertions.assertThat(result).isEqualTo(1);
result = ImageSizeUtils.computeImageSampleSize(srcSize, targetSize, scaleType, true);
Assertions.assertThat(result).isEqualTo(1);
srcSize = new ImageSize(300, 100);
targetSize = new ImageSize(55, 40);
result = ImageSizeUtils.computeImageSampleSize(srcSize, targetSize, scaleType, false);
Assertions.assertThat(result).isEqualTo(2);
result = ImageSizeUtils.computeImageSampleSize(srcSize, targetSize, scaleType, true);
Assertions.assertThat(result).isEqualTo(2);
srcSize = new ImageSize(300, 100);
targetSize = new ImageSize(30, 30);
result = ImageSizeUtils.computeImageSampleSize(srcSize, targetSize, scaleType, false);
Assertions.assertThat(result).isEqualTo(3);
result = ImageSizeUtils.computeImageSampleSize(srcSize, targetSize, scaleType, true);
Assertions.assertThat(result).isEqualTo(2);
srcSize = new ImageSize(5000, 70);
targetSize = new ImageSize(300, 30);
result = ImageSizeUtils.computeImageSampleSize(srcSize, targetSize, scaleType, false);
Assertions.assertThat(result).isEqualTo(3);
result = ImageSizeUtils.computeImageSampleSize(srcSize, targetSize, scaleType, true);
Assertions.assertThat(result).isEqualTo(4);
}
/** Fixes {@link NoSuchMethodError} for ImageView#onLayout(...) */
private class TestImageView extends ImageView {
TestImageView(Context activity) {
super(activity);
}
@Override
public void onLayout(boolean changed, int left, int top, int right, int bottom) {
super.onLayout(changed, left, top, right, bottom);
}
}
}
================================================
FILE: library/src/test/java/com/nostra13/universalimageloader/core/download/BaseImageDownloaderTest.java
================================================
package com.nostra13.universalimageloader.core.download;
import org.assertj.core.api.Assertions;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.robolectric.RobolectricTestRunner;
import com.nostra13.universalimageloader.core.download.ImageDownloader.Scheme;
@RunWith(RobolectricTestRunner.class)
public class BaseImageDownloaderTest {
@Test
public void testSchemeHttp() throws Exception {
String uri = "http://image.com/1.png";
Scheme result = Scheme.ofUri(uri);
Scheme expected = Scheme.HTTP;
Assertions.assertThat(result).isEqualTo(expected);
}
@Test
public void testSchemeHttps() throws Exception {
String uri = "https://image.com/1.png";
Scheme result = Scheme.ofUri(uri);
Scheme expected = Scheme.HTTPS;
Assertions.assertThat(result).isEqualTo(expected);
}
@Test
public void testSchemeContent() throws Exception {
String uri = "content://path/to/content";
Scheme result = Scheme.ofUri(uri);
Scheme expected = Scheme.CONTENT;
Assertions.assertThat(result).isEqualTo(expected);
}
@Test
public void testSchemeAssets() throws Exception {
String uri = "assets://folder/1.png";
Scheme result = Scheme.ofUri(uri);
Scheme expected = Scheme.ASSETS;
Assertions.assertThat(result).isEqualTo(expected);
}
@Test
public void testSchemeDrawables() throws Exception {
String uri = "drawable://123456890";
Scheme result = Scheme.ofUri(uri);
Scheme expected = Scheme.DRAWABLE;
Assertions.assertThat(result).isEqualTo(expected);
}
@Test
public void testSchemeFile() throws Exception {
String uri = "file://path/on/the/device/1.png";
Scheme result = Scheme.ofUri(uri);
Scheme expected = Scheme.FILE;
Assertions.assertThat(result).isEqualTo(expected);
}
@Test
public void testSchemeUnknown() throws Exception {
String uri = "other://image.com/1.png";
Scheme result = Scheme.ofUri(uri);
Scheme expected = Scheme.UNKNOWN;
Assertions.assertThat(result).isEqualTo(expected);
}
}
================================================
FILE: sample/build.gradle
================================================
apply plugin: 'com.android.application'
android {
compileSdkVersion 28
buildToolsVersion "28.0.3"
defaultConfig {
applicationId "com.nostra13.universalimageloader"
minSdkVersion 16
targetSdkVersion 28
versionCode 40
versionName "1.9.5"
}
useLibrary 'org.apache.http.legacy'
lintOptions {
abortOnError false
}
}
dependencies {
implementation project(':library')
implementation 'com.android.support:appcompat-v7:28.0.0'
implementation 'com.squareup.okhttp:okhttp:2.4.0'
}
File propFile = file('signing.properties');
if (propFile.exists()) {
def Properties props = new Properties()
props.load(new FileInputStream(propFile))
if (props.containsKey('STORE_FILE') && props.containsKey('STORE_PASSWORD') &&
props.containsKey('KEY_ALIAS') && props.containsKey('KEY_PASSWORD')) {
android.signingConfigs.release.storeFile = file(props['STORE_FILE'])
android.signingConfigs.release.storePassword = props['STORE_PASSWORD']
android.signingConfigs.release.keyAlias = props['KEY_ALIAS']
android.signingConfigs.release.keyPassword = props['KEY_PASSWORD']
} else {
android.buildTypes.release.signingConfig = null
}
} else {
android.buildTypes.release.signingConfig = null
}
================================================
FILE: sample/gradle.properties
================================================
POM_NAME=Universal Image Loader Sample
POM_ARTIFACT_ID=universal-image-loader-sample
POM_PACKAGING=apk
================================================
FILE: sample/project.properties
================================================
# This file is automatically generated by Android Tools.
# Do not modify this file -- YOUR CHANGES WILL BE ERASED!
#
# This file must be checked in Version Control Systems.
#
# To customize properties used by the Ant build system use,
# "ant.properties", and override values to adapt the script to your
# project structure.
# Project target.
target=android-21
================================================
FILE: sample/src/main/AndroidManifest.xml
================================================
* NOTE: It's strongly recommended your {@link ImageView} has defined width (layout_width) and height
* (layout_height) .
* NOTE: New {@link Bitmap} object is created for displaying. So this class needs more memory and can cause
* {@link OutOfMemoryError}.
*
* @author Sergey Tarasevich (nostra13[at]gmail[dot]com)
*/
public class OldRoundedBitmapDisplayer implements BitmapDisplayer {
private final int roundPixels;
public OldRoundedBitmapDisplayer(int roundPixels) {
this.roundPixels = roundPixels;
}
@Override
public void display(Bitmap bitmap, ImageAware imageAware, LoadedFrom loadedFrom) {
if (!(imageAware instanceof ImageViewAware)) {
throw new IllegalArgumentException("ImageAware should wrap ImageView. ImageViewAware is expected.");
}
Bitmap roundedBitmap = roundCorners(bitmap, (ImageViewAware) imageAware, roundPixels);
imageAware.setImageBitmap(roundedBitmap);
}
/**
* Process incoming {@linkplain Bitmap} to make rounded corners according to target
* {@link com.nostra13.universalimageloader.core.imageaware.ImageViewAware}.
* This method doesn't display result bitmap in {@link ImageView}
*
* @param bitmap Incoming Bitmap to process
* @param imageAware Target {@link com.nostra13.universalimageloader.core.imageaware.ImageAware ImageAware} to
* display bitmap in
* @param roundPixels Rounded pixels of corner
* @return Result bitmap with rounded corners
*/
public static Bitmap roundCorners(Bitmap bitmap, ImageViewAware imageAware, int roundPixels) {
ImageView imageView = imageAware.getWrappedView();
if (imageView == null) {
L.w("View is collected probably. Can't round bitmap corners without view properties.");
return bitmap;
}
Bitmap roundBitmap;
int bw = bitmap.getWidth();
int bh = bitmap.getHeight();
int vw = imageAware.getWidth();
int vh = imageAware.getHeight();
if (vw <= 0) vw = bw;
if (vh <= 0) vh = bh;
final ImageView.ScaleType scaleType = imageView.getScaleType();
if (scaleType == null) {
return bitmap;
}
int width, height;
Rect srcRect;
Rect destRect;
switch (scaleType) {
case CENTER_INSIDE:
float vRation = (float) vw / vh;
float bRation = (float) bw / bh;
int destWidth;
int destHeight;
if (vRation > bRation) {
destHeight = Math.min(vh, bh);
destWidth = (int) (bw / ((float) bh / destHeight));
} else {
destWidth = Math.min(vw, bw);
destHeight = (int) (bh / ((float) bw / destWidth));
}
int x = (vw - destWidth) / 2;
int y = (vh - destHeight) / 2;
srcRect = new Rect(0, 0, bw, bh);
destRect = new Rect(x, y, x + destWidth, y + destHeight);
width = vw;
height = vh;
break;
case FIT_CENTER:
case FIT_START:
case FIT_END:
default:
vRation = (float) vw / vh;
bRation = (float) bw / bh;
if (vRation > bRation) {
width = (int) (bw / ((float) bh / vh));
height = vh;
} else {
width = vw;
height = (int) (bh / ((float) bw / vw));
}
srcRect = new Rect(0, 0, bw, bh);
destRect = new Rect(0, 0, width, height);
break;
case CENTER_CROP:
vRation = (float) vw / vh;
bRation = (float) bw / bh;
int srcWidth;
int srcHeight;
if (vRation > bRation) {
srcWidth = bw;
srcHeight = (int) (vh * ((float) bw / vw));
x = 0;
y = (bh - srcHeight) / 2;
} else {
srcWidth = (int) (vw * ((float) bh / vh));
srcHeight = bh;
x = (bw - srcWidth) / 2;
y = 0;
}
width = srcWidth;// Math.min(vw, bw);
height = srcHeight;//Math.min(vh, bh);
srcRect = new Rect(x, y, x + srcWidth, y + srcHeight);
destRect = new Rect(0, 0, width, height);
break;
case FIT_XY:
width = vw;
height = vh;
srcRect = new Rect(0, 0, bw, bh);
destRect = new Rect(0, 0, width, height);
break;
case CENTER:
case MATRIX:
width = Math.min(vw, bw);
height = Math.min(vh, bh);
x = (bw - width) / 2;
y = (bh - height) / 2;
srcRect = new Rect(x, y, x + width, y + height);
destRect = new Rect(0, 0, width, height);
break;
}
try {
roundBitmap = getRoundedCornerBitmap(bitmap, roundPixels, srcRect, destRect, width, height);
} catch (OutOfMemoryError e) {
L.e(e, "Can't create bitmap with rounded corners. Not enough memory.");
roundBitmap = bitmap;
}
return roundBitmap;
}
private static Bitmap getRoundedCornerBitmap(Bitmap bitmap, int roundPixels, Rect srcRect, Rect destRect, int width,
int height) {
Bitmap output = Bitmap.createBitmap(width, height, Config.ARGB_8888);
Canvas canvas = new Canvas(output);
final Paint paint = new Paint();
final RectF destRectF = new RectF(destRect);
paint.setAntiAlias(true);
canvas.drawARGB(0, 0, 0, 0);
paint.setColor(0xFF000000);
canvas.drawRoundRect(destRectF, roundPixels, roundPixels, paint);
paint.setXfermode(new PorterDuffXfermode(PorterDuff.Mode.SRC_IN));
canvas.drawBitmap(bitmap, srcRect, destRectF, paint);
return output;
}
}
================================================
FILE: sample/src/main/java/com/nostra13/universalimageloader/sample/fragment/AbsListViewBaseFragment.java
================================================
/*******************************************************************************
* Copyright 2011-2014 Sergey Tarasevich
*
* 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.nostra13.universalimageloader.sample.fragment;
import android.content.Intent;
import android.view.Menu;
import android.view.MenuItem;
import android.widget.AbsListView;
import com.nostra13.universalimageloader.core.ImageLoader;
import com.nostra13.universalimageloader.core.listener.PauseOnScrollListener;
import com.nostra13.universalimageloader.sample.Constants;
import com.nostra13.universalimageloader.sample.R;
import com.nostra13.universalimageloader.sample.activity.SimpleImageActivity;
/**
* @author Sergey Tarasevich (nostra13[at]gmail[dot]com)
*/
public abstract class AbsListViewBaseFragment extends BaseFragment {
protected static final String STATE_PAUSE_ON_SCROLL = "STATE_PAUSE_ON_SCROLL";
protected static final String STATE_PAUSE_ON_FLING = "STATE_PAUSE_ON_FLING";
protected AbsListView listView;
protected boolean pauseOnScroll = false;
protected boolean pauseOnFling = true;
@Override
public void onResume() {
super.onResume();
applyScrollListener();
}
@Override
public void onPrepareOptionsMenu(Menu menu) {
MenuItem pauseOnScrollItem = menu.findItem(R.id.item_pause_on_scroll);
pauseOnScrollItem.setVisible(true);
pauseOnScrollItem.setChecked(pauseOnScroll);
MenuItem pauseOnFlingItem = menu.findItem(R.id.item_pause_on_fling);
pauseOnFlingItem.setVisible(true);
pauseOnFlingItem.setChecked(pauseOnFling);
}
@Override
public boolean onOptionsItemSelected(MenuItem item) {
switch (item.getItemId()) {
case R.id.item_pause_on_scroll:
pauseOnScroll = !pauseOnScroll;
item.setChecked(pauseOnScroll);
applyScrollListener();
return true;
case R.id.item_pause_on_fling:
pauseOnFling = !pauseOnFling;
item.setChecked(pauseOnFling);
applyScrollListener();
return true;
default:
return super.onOptionsItemSelected(item);
}
}
protected void startImagePagerActivity(int position) {
Intent intent = new Intent(getActivity(), SimpleImageActivity.class);
intent.putExtra(Constants.Extra.FRAGMENT_INDEX, ImagePagerFragment.INDEX);
intent.putExtra(Constants.Extra.IMAGE_POSITION, position);
startActivity(intent);
}
private void applyScrollListener() {
listView.setOnScrollListener(new PauseOnScrollListener(ImageLoader.getInstance(), pauseOnScroll, pauseOnFling));
}
}
================================================
FILE: sample/src/main/java/com/nostra13/universalimageloader/sample/fragment/BaseFragment.java
================================================
/*******************************************************************************
* Copyright 2011-2014 Sergey Tarasevich
*
* 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.nostra13.universalimageloader.sample.fragment;
import android.os.Bundle;
import android.support.v4.app.Fragment;
import android.view.Menu;
import android.view.MenuInflater;
import android.view.MenuItem;
import com.nostra13.universalimageloader.core.ImageLoader;
import com.nostra13.universalimageloader.sample.R;
/**
* @author Sergey Tarasevich (nostra13[at]gmail[dot]com)
*/
public abstract class BaseFragment extends Fragment {
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setHasOptionsMenu(true);
}
public void onCreateOptionsMenu(Menu menu, MenuInflater inflater) {
inflater.inflate(R.menu.main_menu, menu);
}
@Override
public boolean onOptionsItemSelected(MenuItem item) {
switch (item.getItemId()) {
case R.id.item_clear_memory_cache:
ImageLoader.getInstance().clearMemoryCache();
return true;
case R.id.item_clear_disc_cache:
ImageLoader.getInstance().clearDiskCache();
return true;
default:
return false;
}
}
}
================================================
FILE: sample/src/main/java/com/nostra13/universalimageloader/sample/fragment/ImageGalleryFragment.java
================================================
/*******************************************************************************
* Copyright 2011-2014 Sergey Tarasevich
*
* 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.nostra13.universalimageloader.sample.fragment;
import android.content.Context;
import android.content.Intent;
import android.graphics.Bitmap;
import android.os.Bundle;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.AdapterView;
import android.widget.AdapterView.OnItemClickListener;
import android.widget.BaseAdapter;
import android.widget.Gallery;
import android.widget.ImageView;
import com.nostra13.universalimageloader.core.DisplayImageOptions;
import com.nostra13.universalimageloader.core.ImageLoader;
import com.nostra13.universalimageloader.core.display.RoundedBitmapDisplayer;
import com.nostra13.universalimageloader.sample.Constants;
import com.nostra13.universalimageloader.sample.R;
import com.nostra13.universalimageloader.sample.activity.SimpleImageActivity;
/**
* @author Sergey Tarasevich (nostra13[at]gmail[dot]com)
*/
public class ImageGalleryFragment extends BaseFragment {
public static final int INDEX = 3;
@SuppressWarnings("deprecation")
@Override
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
View rootView = inflater.inflate(R.layout.fr_image_gallery, container, false);
Gallery gallery = (Gallery) rootView.findViewById(R.id.gallery);
gallery.setAdapter(new ImageAdapter(getActivity()));
gallery.setOnItemClickListener(new OnItemClickListener() {
@Override
public void onItemClick(AdapterView> parent, View view, int position, long id) {
startImagePagerActivity(position);
}
});
return rootView;
}
private void startImagePagerActivity(int position) {
Intent intent = new Intent(getActivity(), SimpleImageActivity.class);
intent.putExtra(Constants.Extra.FRAGMENT_INDEX, ImagePagerFragment.INDEX);
intent.putExtra(Constants.Extra.IMAGE_POSITION, position);
startActivity(intent);
}
private static class ImageAdapter extends BaseAdapter {
private static final String[] IMAGE_URLS = Constants.IMAGES;
private LayoutInflater inflater;
private DisplayImageOptions options;
ImageAdapter(Context context) {
inflater = LayoutInflater.from(context);
options = new DisplayImageOptions.Builder()
.showImageOnLoading(R.drawable.ic_stub)
.showImageForEmptyUri(R.drawable.ic_empty)
.showImageOnFail(R.drawable.ic_error)
.cacheInMemory(true)
.cacheOnDisk(true)
.considerExifParams(true)
.bitmapConfig(Bitmap.Config.RGB_565)
.displayer(new RoundedBitmapDisplayer(20))
.build();
}
@Override
public int getCount() {
return IMAGE_URLS.length;
}
@Override
public Object getItem(int position) {
return position;
}
@Override
public long getItemId(int position) {
return position;
}
@Override
public View getView(int position, View convertView, ViewGroup parent) {
ImageView imageView = (ImageView) convertView;
if (imageView == null) {
imageView = (ImageView) inflater.inflate(R.layout.item_gallery_image, parent, false);
}
ImageLoader.getInstance().displayImage(IMAGE_URLS[position], imageView, options);
return imageView;
}
}
}
================================================
FILE: sample/src/main/java/com/nostra13/universalimageloader/sample/fragment/ImageGridFragment.java
================================================
/*******************************************************************************
* Copyright 2011-2014 Sergey Tarasevich
*
* 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.nostra13.universalimageloader.sample.fragment;
import android.content.Context;
import android.graphics.Bitmap;
import android.os.Bundle;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.AdapterView;
import android.widget.AdapterView.OnItemClickListener;
import android.widget.BaseAdapter;
import android.widget.GridView;
import android.widget.ImageView;
import android.widget.ProgressBar;
import com.nostra13.universalimageloader.core.DisplayImageOptions;
import com.nostra13.universalimageloader.core.ImageLoader;
import com.nostra13.universalimageloader.core.assist.FailReason;
import com.nostra13.universalimageloader.core.listener.ImageLoadingProgressListener;
import com.nostra13.universalimageloader.core.listener.SimpleImageLoadingListener;
import com.nostra13.universalimageloader.sample.Constants;
import com.nostra13.universalimageloader.sample.R;
/**
* @author Sergey Tarasevich (nostra13[at]gmail[dot]com)
*/
public class ImageGridFragment extends AbsListViewBaseFragment {
public static final int INDEX = 1;
@Override
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
View rootView = inflater.inflate(R.layout.fr_image_grid, container, false);
listView = (GridView) rootView.findViewById(R.id.grid);
((GridView) listView).setAdapter(new ImageAdapter(getActivity()));
listView.setOnItemClickListener(new OnItemClickListener() {
@Override
public void onItemClick(AdapterView> parent, View view, int position, long id) {
startImagePagerActivity(position);
}
});
return rootView;
}
private static class ImageAdapter extends BaseAdapter {
private static final String[] IMAGE_URLS = Constants.IMAGES;
private LayoutInflater inflater;
private DisplayImageOptions options;
ImageAdapter(Context context) {
inflater = LayoutInflater.from(context);
options = new DisplayImageOptions.Builder()
.showImageOnLoading(R.drawable.ic_stub)
.showImageForEmptyUri(R.drawable.ic_empty)
.showImageOnFail(R.drawable.ic_error)
.cacheInMemory(true)
.cacheOnDisk(true)
.considerExifParams(true)
.bitmapConfig(Bitmap.Config.RGB_565)
.build();
}
@Override
public int getCount() {
return IMAGE_URLS.length;
}
@Override
public Object getItem(int position) {
return null;
}
@Override
public long getItemId(int position) {
return position;
}
@Override
public View getView(int position, View convertView, ViewGroup parent) {
final ViewHolder holder;
View view = convertView;
if (view == null) {
view = inflater.inflate(R.layout.item_grid_image, parent, false);
holder = new ViewHolder();
assert view != null;
holder.imageView = (ImageView) view.findViewById(R.id.image);
holder.progressBar = (ProgressBar) view.findViewById(R.id.progress);
view.setTag(holder);
} else {
holder = (ViewHolder) view.getTag();
}
ImageLoader.getInstance()
.displayImage(IMAGE_URLS[position], holder.imageView, options, new SimpleImageLoadingListener() {
@Override
public void onLoadingStarted(String imageUri, View view) {
holder.progressBar.setProgress(0);
holder.progressBar.setVisibility(View.VISIBLE);
}
@Override
public void onLoadingFailed(String imageUri, View view, FailReason failReason) {
holder.progressBar.setVisibility(View.GONE);
}
@Override
public void onLoadingComplete(String imageUri, View view, Bitmap loadedImage) {
holder.progressBar.setVisibility(View.GONE);
}
}, new ImageLoadingProgressListener() {
@Override
public void onProgressUpdate(String imageUri, View view, int current, int total) {
holder.progressBar.setProgress(Math.round(100.0f * current / total));
}
});
return view;
}
}
static class ViewHolder {
ImageView imageView;
ProgressBar progressBar;
}
}
================================================
FILE: sample/src/main/java/com/nostra13/universalimageloader/sample/fragment/ImageListFragment.java
================================================
/*******************************************************************************
* Copyright 2011-2014 Sergey Tarasevich
*
* 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.nostra13.universalimageloader.sample.fragment;
import android.content.Context;
import android.graphics.Bitmap;
import android.graphics.Color;
import android.os.Bundle;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.AdapterView;
import android.widget.AdapterView.OnItemClickListener;
import android.widget.BaseAdapter;
import android.widget.ImageView;
import android.widget.ListView;
import android.widget.TextView;
import com.nostra13.universalimageloader.core.DisplayImageOptions;
import com.nostra13.universalimageloader.core.ImageLoader;
import com.nostra13.universalimageloader.core.display.CircleBitmapDisplayer;
import com.nostra13.universalimageloader.core.display.FadeInBitmapDisplayer;
import com.nostra13.universalimageloader.core.listener.ImageLoadingListener;
import com.nostra13.universalimageloader.core.listener.SimpleImageLoadingListener;
import com.nostra13.universalimageloader.sample.Constants;
import com.nostra13.universalimageloader.sample.R;
import java.util.Collections;
import java.util.LinkedList;
import java.util.List;
/**
* @author Sergey Tarasevich (nostra13[at]gmail[dot]com)
*/
public class ImageListFragment extends AbsListViewBaseFragment {
public static final int INDEX = 0;
@Override
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
View rootView = inflater.inflate(R.layout.fr_image_list, container, false);
listView = (ListView) rootView.findViewById(android.R.id.list);
((ListView) listView).setAdapter(new ImageAdapter(getActivity()));
listView.setOnItemClickListener(new OnItemClickListener() {
@Override
public void onItemClick(AdapterView> parent, View view, int position, long id) {
startImagePagerActivity(position);
}
});
return rootView;
}
@Override
public void onDestroy() {
super.onDestroy();
AnimateFirstDisplayListener.displayedImages.clear();
}
private static class ImageAdapter extends BaseAdapter {
private static final String[] IMAGE_URLS = Constants.IMAGES;
private LayoutInflater inflater;
private ImageLoadingListener animateFirstListener = new AnimateFirstDisplayListener();
private DisplayImageOptions options;
ImageAdapter(Context context) {
inflater = LayoutInflater.from(context);
options = new DisplayImageOptions.Builder()
.showImageOnLoading(R.drawable.ic_stub)
.showImageForEmptyUri(R.drawable.ic_empty)
.showImageOnFail(R.drawable.ic_error)
.cacheInMemory(true)
.cacheOnDisk(true)
.considerExifParams(true)
.displayer(new CircleBitmapDisplayer(Color.WHITE, 5))
.build();
}
@Override
public int getCount() {
return IMAGE_URLS.length;
}
@Override
public Object getItem(int position) {
return position;
}
@Override
public long getItemId(int position) {
return position;
}
@Override
public View getView(final int position, View convertView, ViewGroup parent) {
View view = convertView;
final ViewHolder holder;
if (convertView == null) {
view = inflater.inflate(R.layout.item_list_image, parent, false);
holder = new ViewHolder();
holder.text = (TextView) view.findViewById(R.id.text);
holder.image = (ImageView) view.findViewById(R.id.image);
view.setTag(holder);
} else {
holder = (ViewHolder) view.getTag();
}
holder.text.setText("Item " + (position + 1));
ImageLoader.getInstance().displayImage(IMAGE_URLS[position], holder.image, options, animateFirstListener);
return view;
}
}
static class ViewHolder {
TextView text;
ImageView image;
}
private static class AnimateFirstDisplayListener extends SimpleImageLoadingListener {
static final List