@ -0,0 +1,74 @@ |
|||||||
|
gps/ |
||||||
|
|
||||||
|
|
||||||
|
# Built application files |
||||||
|
*.apk |
||||||
|
*.ap_ |
||||||
|
|
||||||
|
# Files for the Dalvik VM |
||||||
|
*.dex |
||||||
|
|
||||||
|
# Java class files |
||||||
|
*.class |
||||||
|
|
||||||
|
# Generated files |
||||||
|
bin/ |
||||||
|
gen/ |
||||||
|
|
||||||
|
# Gradle files |
||||||
|
.gradle/ |
||||||
|
build/ |
||||||
|
|
||||||
|
# Local configuration file (sdk path, etc) |
||||||
|
local.properties |
||||||
|
|
||||||
|
# Proguard folder generated by Eclipse |
||||||
|
proguard/ |
||||||
|
|
||||||
|
################# |
||||||
|
## Eclipse |
||||||
|
################# |
||||||
|
|
||||||
|
*.pydevproject |
||||||
|
.project |
||||||
|
.metadata |
||||||
|
bin/ |
||||||
|
tmp/ |
||||||
|
*.tmp |
||||||
|
*.bak |
||||||
|
*.swp |
||||||
|
*~.nib |
||||||
|
local.properties |
||||||
|
.classpath |
||||||
|
.settings/ |
||||||
|
.loadpath |
||||||
|
|
||||||
|
# External tool builders |
||||||
|
.externalToolBuilders/ |
||||||
|
|
||||||
|
# Locally stored "Eclipse launch configurations" |
||||||
|
*.launch |
||||||
|
|
||||||
|
# CDT-specific |
||||||
|
.cproject |
||||||
|
|
||||||
|
# PDT-specific |
||||||
|
.buildpath |
||||||
|
|
||||||
|
|
||||||
|
## Windows detritus |
||||||
|
############# |
||||||
|
|
||||||
|
# Windows image file caches |
||||||
|
Thumbs.db |
||||||
|
ehthumbs.db |
||||||
|
|
||||||
|
# Folder config file |
||||||
|
Desktop.ini |
||||||
|
|
||||||
|
# Recycle Bin used on file shares |
||||||
|
$RECYCLE.BIN/ |
||||||
|
|
||||||
|
# Mac crap |
||||||
|
.DS_Store |
||||||
|
|
@ -0,0 +1,98 @@ |
|||||||
|
<?xml version="1.0" encoding="utf-8"?> |
||||||
|
<manifest xmlns:android="http://schemas.android.com/apk/res/android" |
||||||
|
package="org.floens.chan" |
||||||
|
android:versionCode="10" |
||||||
|
android:versionName="Chan v0.8" |
||||||
|
android:installLocation="auto" > |
||||||
|
|
||||||
|
<uses-sdk |
||||||
|
android:minSdkVersion="14" |
||||||
|
android:targetSdkVersion="19" /> |
||||||
|
<uses-permission android:name="android.permission.INTERNET" /> |
||||||
|
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" /> |
||||||
|
<uses-permission android:name="android.permission.NFC" /> |
||||||
|
|
||||||
|
<application |
||||||
|
android:name="org.floens.chan.ChanApplication" |
||||||
|
android:allowBackup="true" |
||||||
|
android:icon="@drawable/ic_launcher" |
||||||
|
android:label="@string/app_name" |
||||||
|
android:theme="@style/AppTheme" > |
||||||
|
<activity |
||||||
|
android:name="org.floens.chan.activity.BoardActivity" |
||||||
|
android:label="@string/app_name" |
||||||
|
android:configChanges="keyboardHidden|orientation|screenSize" > |
||||||
|
<intent-filter> |
||||||
|
<action android:name="android.intent.action.MAIN" /> |
||||||
|
<category android:name="android.intent.category.LAUNCHER" /> |
||||||
|
</intent-filter> |
||||||
|
<intent-filter> |
||||||
|
<action android:name="android.intent.action.VIEW" /> |
||||||
|
<category android:name="android.intent.category.DEFAULT" /> |
||||||
|
<category android:name="android.intent.category.BROWSABLE" /> |
||||||
|
|
||||||
|
<data android:host="4chan.org" android:scheme="http" android:pathPrefix="/" /> |
||||||
|
<data android:host="4chan.org" android:scheme="https" android:pathPrefix="/" /> |
||||||
|
<data android:host="www.4chan.org" android:scheme="http" android:pathPrefix="/" /> |
||||||
|
<data android:host="www.4chan.org" android:scheme="https" android:pathPrefix="/" /> |
||||||
|
<data android:host="boards.4chan.org" android:scheme="http" android:pathPrefix="/" /> |
||||||
|
<data android:host="boards.4chan.org" android:scheme="https" android:pathPrefix="/" /> |
||||||
|
</intent-filter> |
||||||
|
</activity> |
||||||
|
<activity |
||||||
|
android:name="org.floens.chan.activity.ThreadActivity" |
||||||
|
android:configChanges="keyboardHidden|orientation|screenSize" |
||||||
|
android:parentActivityName="org.floens.chan.activity.BoardActivity" > |
||||||
|
<meta-data |
||||||
|
android:name="android.support.PARENT_ACTIVITY" |
||||||
|
android:value="org.floens.chan.activity.BoardActivity" /> |
||||||
|
</activity> |
||||||
|
<activity |
||||||
|
android:name="org.floens.chan.activity.CatalogActivity" |
||||||
|
android:configChanges="keyboardHidden|orientation|screenSize" > |
||||||
|
</activity> |
||||||
|
<activity |
||||||
|
android:name="org.floens.chan.activity.SettingsActivity" |
||||||
|
android:label="@string/action_settings" |
||||||
|
android:parentActivityName="org.floens.chan.activity.BoardActivity" > |
||||||
|
<meta-data |
||||||
|
android:name="android.support.PARENT_ACTIVITY" |
||||||
|
android:value="org.floens.chan.activity.BoardActivity" /> |
||||||
|
</activity> |
||||||
|
<activity |
||||||
|
android:name="org.floens.chan.activity.AboutActivity" |
||||||
|
android:label="@string/about" |
||||||
|
android:parentActivityName="org.floens.chan.activity.BoardActivity" > |
||||||
|
<meta-data |
||||||
|
android:name="android.support.PARENT_ACTIVITY" |
||||||
|
android:value="org.floens.chan.activity.BoardActivity" /> |
||||||
|
</activity> |
||||||
|
<activity |
||||||
|
android:name="org.floens.chan.activity.BoardEditor" |
||||||
|
android:label="@string/board_edit" |
||||||
|
android:parentActivityName="org.floens.chan.activity.BoardActivity" > |
||||||
|
<meta-data |
||||||
|
android:name="android.support.PARENT_ACTIVITY" |
||||||
|
android:value="org.floens.chan.activity.BoardActivity" /> |
||||||
|
</activity> |
||||||
|
<activity |
||||||
|
android:name="org.floens.chan.imageview.activity.ImageViewActivity" |
||||||
|
android:configChanges="keyboardHidden|orientation|screenSize" |
||||||
|
android:theme="@style/Theme.ImageList" > |
||||||
|
</activity> |
||||||
|
<activity |
||||||
|
android:name="org.floens.chan.activity.ImagePickActivity" > |
||||||
|
</activity> |
||||||
|
|
||||||
|
<service |
||||||
|
android:name="org.floens.chan.service.PinnedService" |
||||||
|
android:exported="false" |
||||||
|
></service> |
||||||
|
</application> |
||||||
|
|
||||||
|
</manifest> |
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
@ -0,0 +1,63 @@ |
|||||||
|
<!DOCTYPE html> |
||||||
|
<html> |
||||||
|
<head> |
||||||
|
<title>Open Source Licences</title> |
||||||
|
<style type="text/css"> |
||||||
|
body { |
||||||
|
margin: 0; |
||||||
|
padding: 10px; |
||||||
|
font-family: sans-serif; |
||||||
|
} |
||||||
|
|
||||||
|
pre { |
||||||
|
background-color: #F0F0F0; |
||||||
|
padding: 12px; |
||||||
|
white-space: pre-wrap; |
||||||
|
} |
||||||
|
</style> |
||||||
|
</head> |
||||||
|
<body> |
||||||
|
<h3>PhotoView</h3> |
||||||
|
<a href="https://github.com/chrisbanes/PhotoView">https://github.com/chrisbanes/PhotoView</a> |
||||||
|
<pre> |
||||||
|
<code> |
||||||
|
Copyright 2011, 2012 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. |
||||||
|
</code> |
||||||
|
</pre> |
||||||
|
<br> |
||||||
|
|
||||||
|
<h3>This software includes several Android classes from the Android Open Source Project.</h3> |
||||||
|
<pre> |
||||||
|
<code> |
||||||
|
Copyright (C) 2012 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. |
||||||
|
</code> |
||||||
|
</pre> |
||||||
|
<br> |
||||||
|
|
||||||
|
</body> |
||||||
|
</html> |
||||||
|
|
@ -0,0 +1,42 @@ |
|||||||
|
buildscript { |
||||||
|
repositories { |
||||||
|
mavenCentral() |
||||||
|
} |
||||||
|
dependencies { |
||||||
|
classpath 'com.android.tools.build:gradle:0.5.+' |
||||||
|
} |
||||||
|
} |
||||||
|
apply plugin: 'android' |
||||||
|
|
||||||
|
dependencies { |
||||||
|
compile fileTree(dir: 'libs', include: '*.jar') |
||||||
|
} |
||||||
|
|
||||||
|
android { |
||||||
|
compileSdkVersion 19 |
||||||
|
buildToolsVersion "19.0.0" |
||||||
|
|
||||||
|
sourceSets { |
||||||
|
main { |
||||||
|
manifest.srcFile 'AndroidManifest.xml' |
||||||
|
java.srcDirs = ['src'] |
||||||
|
resources.srcDirs = ['src'] |
||||||
|
aidl.srcDirs = ['src'] |
||||||
|
renderscript.srcDirs = ['src'] |
||||||
|
res.srcDirs = ['res'] |
||||||
|
assets.srcDirs = ['assets'] |
||||||
|
} |
||||||
|
|
||||||
|
// Move the tests to tests/java, tests/res, etc... |
||||||
|
instrumentTest.setRoot('tests') |
||||||
|
|
||||||
|
// Move the build types to build-types/<type> |
||||||
|
// For instance, build-types/debug/java, build-types/debug/AndroidManifest.xml, ... |
||||||
|
// This moves them out of them default location under src/<type>/... which would |
||||||
|
// conflict with src/ being used by the main source set. |
||||||
|
// Adding new build types or product flavors should be accompanied |
||||||
|
// by a similar customization. |
||||||
|
debug.setRoot('build-types/debug') |
||||||
|
release.setRoot('build-types/release') |
||||||
|
} |
||||||
|
} |
@ -0,0 +1,6 @@ |
|||||||
|
#Wed Apr 10 15:27:10 PDT 2013 |
||||||
|
distributionBase=GRADLE_USER_HOME |
||||||
|
distributionPath=wrapper/dists |
||||||
|
zipStoreBase=GRADLE_USER_HOME |
||||||
|
zipStorePath=wrapper/dists |
||||||
|
distributionUrl=http\://services.gradle.org/distributions/gradle-1.6-bin.zip |
@ -0,0 +1,164 @@ |
|||||||
|
#!/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 "$@" |
@ -0,0 +1,90 @@ |
|||||||
|
@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 |
@ -0,0 +1,6 @@ |
|||||||
|
<?xml version="1.0" encoding="UTF-8"?> |
||||||
|
<lint> |
||||||
|
<issue id="Registered"> |
||||||
|
<ignore path="src/org/floens/chan/activity/BaseActivity.java" /> |
||||||
|
</issue> |
||||||
|
</lint> |
@ -0,0 +1,20 @@ |
|||||||
|
# To enable ProGuard in your project, edit project.properties |
||||||
|
# to define the proguard.config property as described in that file. |
||||||
|
# |
||||||
|
# Add project specific ProGuard rules here. |
||||||
|
# By default, the flags in this file are appended to flags specified |
||||||
|
# in ${sdk.dir}/tools/proguard/proguard-android.txt |
||||||
|
# You can edit the include path and order by changing the ProGuard |
||||||
|
# include property in project.properties. |
||||||
|
# |
||||||
|
# For more details, see |
||||||
|
# http://developer.android.com/guide/developing/tools/proguard.html |
||||||
|
|
||||||
|
# Add any project specific keep options here: |
||||||
|
|
||||||
|
# If your project uses WebView with JS, uncomment the following |
||||||
|
# and specify the fully qualified class name to the JavaScript interface |
||||||
|
# class: |
||||||
|
#-keepclassmembers class fqcn.of.javascript.interface.for.webview { |
||||||
|
# public *; |
||||||
|
#} |
@ -0,0 +1,14 @@ |
|||||||
|
# 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 edit |
||||||
|
# "ant.properties", and override values to adapt the script to your |
||||||
|
# project structure. |
||||||
|
# |
||||||
|
# To enable ProGuard to shrink and obfuscate your code, uncomment this (available properties: sdk.dir, user.home): |
||||||
|
#proguard.config=${sdk.dir}/tools/proguard/proguard-android.txt:proguard-project.txt |
||||||
|
|
||||||
|
# Project target. |
||||||
|
target=android-19 |
@ -0,0 +1,8 @@ |
|||||||
|
<?xml version="1.0" encoding="utf-8"?> |
||||||
|
<set xmlns:android="http://schemas.android.com/apk/res/android" > |
||||||
|
<alpha |
||||||
|
android:fromAlpha="0.0" |
||||||
|
android:toAlpha="1.0" |
||||||
|
android:duration="200" /> |
||||||
|
|
||||||
|
</set> |
@ -0,0 +1,8 @@ |
|||||||
|
<?xml version="1.0" encoding="utf-8"?> |
||||||
|
<set xmlns:android="http://schemas.android.com/apk/res/android" > |
||||||
|
<alpha |
||||||
|
android:fromAlpha="1.0" |
||||||
|
android:toAlpha="0.0" |
||||||
|
android:duration="200" /> |
||||||
|
|
||||||
|
</set> |
After Width: | Height: | Size: 161 B |
After Width: | Height: | Size: 294 B |
After Width: | Height: | Size: 454 B |
After Width: | Height: | Size: 567 B |
After Width: | Height: | Size: 262 B |
After Width: | Height: | Size: 389 B |
After Width: | Height: | Size: 667 B |
After Width: | Height: | Size: 2.8 KiB |
After Width: | Height: | Size: 5.0 KiB |
After Width: | Height: | Size: 3.7 KiB |
After Width: | Height: | Size: 142 B |
After Width: | Height: | Size: 263 B |
After Width: | Height: | Size: 333 B |
After Width: | Height: | Size: 411 B |
After Width: | Height: | Size: 185 B |
After Width: | Height: | Size: 329 B |
After Width: | Height: | Size: 484 B |
After Width: | Height: | Size: 2.8 KiB |
After Width: | Height: | Size: 4.2 KiB |
After Width: | Height: | Size: 3.3 KiB |
After Width: | Height: | Size: 587 B |
After Width: | Height: | Size: 174 B |
After Width: | Height: | Size: 307 B |
After Width: | Height: | Size: 552 B |
After Width: | Height: | Size: 701 B |
After Width: | Height: | Size: 234 B |
After Width: | Height: | Size: 463 B |
After Width: | Height: | Size: 876 B |
After Width: | Height: | Size: 1.0 KiB |
After Width: | Height: | Size: 5.9 KiB |
After Width: | Height: | Size: 4.1 KiB |
After Width: | Height: | Size: 208 B |
After Width: | Height: | Size: 422 B |
After Width: | Height: | Size: 781 B |
After Width: | Height: | Size: 1.0 KiB |
After Width: | Height: | Size: 288 B |
After Width: | Height: | Size: 625 B |
After Width: | Height: | Size: 1.2 KiB |
After Width: | Height: | Size: 202 B |
After Width: | Height: | Size: 4.9 KiB |
@ -0,0 +1,20 @@ |
|||||||
|
<android.support.v4.widget.DrawerLayout |
||||||
|
xmlns:android="http://schemas.android.com/apk/res/android" |
||||||
|
android:id="@+id/drawer_layout" |
||||||
|
android:layout_width="match_parent" |
||||||
|
android:layout_height="match_parent"> |
||||||
|
<FrameLayout |
||||||
|
android:id="@+id/container" |
||||||
|
android:layout_width="match_parent" |
||||||
|
android:layout_height="match_parent" > |
||||||
|
</FrameLayout> |
||||||
|
<ListView |
||||||
|
android:id="@+id/left_drawer" |
||||||
|
android:layout_width="280dp" |
||||||
|
android:divider="#333" |
||||||
|
android:dividerHeight="1dp" |
||||||
|
android:background="#444" |
||||||
|
android:layout_height="match_parent" |
||||||
|
android:layout_gravity="start" |
||||||
|
android:choiceMode="singleChoice" /> |
||||||
|
</android.support.v4.widget.DrawerLayout> |
@ -0,0 +1,6 @@ |
|||||||
|
<?xml version="1.0" encoding="utf-8"?> |
||||||
|
<org.floens.chan.view.DynamicListView xmlns:android="http://schemas.android.com/apk/res/android" |
||||||
|
android:layout_width="match_parent" |
||||||
|
android:layout_height="match_parent" |
||||||
|
android:id="@+id/board_edit_list" > |
||||||
|
</org.floens.chan.view.DynamicListView> |
@ -0,0 +1,28 @@ |
|||||||
|
<?xml version="1.0" encoding="utf-8"?> |
||||||
|
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android" |
||||||
|
android:layout_width="match_parent" |
||||||
|
android:layout_height="match_parent" |
||||||
|
android:orientation="horizontal" > |
||||||
|
<TextView |
||||||
|
android:id="@+id/board_view_text" |
||||||
|
android:layout_width="wrap_content" |
||||||
|
android:layout_height="wrap_content" |
||||||
|
android:textSize="24sp" |
||||||
|
android:gravity="center_vertical" |
||||||
|
android:paddingLeft="24dp" |
||||||
|
android:paddingRight="24dp" |
||||||
|
android:minHeight="60dp" |
||||||
|
/> |
||||||
|
<Button |
||||||
|
android:id="@+id/board_view_delete" |
||||||
|
android:layout_alignParentRight="true" |
||||||
|
android:layout_marginTop="10dp" |
||||||
|
android:layout_marginRight="10dp" |
||||||
|
android:layout_height="40dp" |
||||||
|
android:layout_width="40dp" |
||||||
|
android:focusable="false" |
||||||
|
android:focusableInTouchMode="false" |
||||||
|
android:background="@drawable/ic_action_discard" /> |
||||||
|
|
||||||
|
</RelativeLayout> |
||||||
|
|
@ -0,0 +1,23 @@ |
|||||||
|
<?xml version="1.0" encoding="utf-8"?> |
||||||
|
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" |
||||||
|
android:layout_width="match_parent" |
||||||
|
android:layout_height="wrap_content" > |
||||||
|
<TextView |
||||||
|
android:id="@+id/drawer_item_text" |
||||||
|
android:layout_width="match_parent" |
||||||
|
android:layout_height="wrap_content" |
||||||
|
android:minHeight="48dp" |
||||||
|
android:paddingTop="8dp" |
||||||
|
android:paddingBottom="8dp" |
||||||
|
android:paddingLeft="16dp" |
||||||
|
android:paddingRight="56dp" |
||||||
|
android:textSize="19sp" |
||||||
|
android:textColor="#fff" |
||||||
|
android:gravity="center_vertical" |
||||||
|
android:ellipsize="end" |
||||||
|
android:lines="1" |
||||||
|
android:singleLine="true" |
||||||
|
android:orientation="vertical" > |
||||||
|
</TextView> |
||||||
|
</LinearLayout> |
||||||
|
|
@ -0,0 +1,15 @@ |
|||||||
|
<?xml version="1.0" encoding="utf-8"?> |
||||||
|
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" |
||||||
|
android:layout_width="match_parent" |
||||||
|
android:layout_height="wrap_content" > |
||||||
|
<TextView |
||||||
|
android:id="@+id/drawer_item_header" |
||||||
|
android:layout_width="match_parent" |
||||||
|
android:layout_height="wrap_content" |
||||||
|
android:paddingTop="24dp" |
||||||
|
android:paddingLeft="16dp" |
||||||
|
android:paddingRight="56dp" |
||||||
|
android:paddingBottom="8dp" |
||||||
|
style="?android:attr/listSeparatorTextViewStyle" |
||||||
|
android:orientation="vertical" /> |
||||||
|
</LinearLayout> |
@ -0,0 +1,14 @@ |
|||||||
|
<?xml version="1.0" encoding="utf-8"?> |
||||||
|
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android" |
||||||
|
android:layout_width="match_parent" |
||||||
|
android:layout_height="match_parent" |
||||||
|
android:background="@color/image_list_background" |
||||||
|
android:orientation="vertical" > |
||||||
|
|
||||||
|
<org.floens.chan.view.HackyViewPager xmlns:android="http://schemas.android.com/apk/res/android" |
||||||
|
android:id="@+id/image_pager" |
||||||
|
android:layout_width="match_parent" |
||||||
|
android:layout_height="wrap_content"> |
||||||
|
</org.floens.chan.view.HackyViewPager> |
||||||
|
</RelativeLayout> |
||||||
|
|
@ -0,0 +1,140 @@ |
|||||||
|
<?xml version="1.0" encoding="utf-8"?> |
||||||
|
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" |
||||||
|
android:layout_width="match_parent" |
||||||
|
android:layout_height="match_parent" |
||||||
|
android:minWidth="500dp" |
||||||
|
android:minHeight="400dp" |
||||||
|
android:orientation="vertical" |
||||||
|
android:divider="?android:attr/dividerHorizontal" |
||||||
|
android:showDividers="middle" > |
||||||
|
|
||||||
|
<ViewFlipper |
||||||
|
android:id="@+id/reply_flipper" |
||||||
|
android:orientation="horizontal" |
||||||
|
android:layout_weight="1" |
||||||
|
android:layout_width="match_parent" |
||||||
|
android:layout_height="0dp"> |
||||||
|
<ScrollView |
||||||
|
android:layout_width="match_parent" |
||||||
|
android:layout_height="match_parent" > |
||||||
|
<LinearLayout |
||||||
|
android:id="@+id/reply_data" |
||||||
|
style="?android:attr/buttonBarStyle" |
||||||
|
android:layout_width="match_parent" |
||||||
|
android:layout_height="wrap_content" |
||||||
|
android:orientation="vertical" > |
||||||
|
|
||||||
|
<EditText |
||||||
|
android:id="@+id/reply_name" |
||||||
|
android:layout_width="match_parent" |
||||||
|
android:layout_height="wrap_content" |
||||||
|
android:hint="@string/reply_name" /> |
||||||
|
|
||||||
|
<EditText |
||||||
|
android:id="@+id/reply_email" |
||||||
|
android:layout_width="match_parent" |
||||||
|
android:layout_height="wrap_content" |
||||||
|
android:hint="@string/reply_email" |
||||||
|
android:inputType="textEmailAddress" /> |
||||||
|
|
||||||
|
<EditText |
||||||
|
android:id="@+id/reply_subject" |
||||||
|
android:layout_width="match_parent" |
||||||
|
android:layout_height="wrap_content" |
||||||
|
android:hint="@string/reply_subject" /> |
||||||
|
|
||||||
|
<EditText |
||||||
|
android:id="@+id/reply_comment" |
||||||
|
android:layout_width="match_parent" |
||||||
|
android:layout_height="wrap_content" |
||||||
|
android:hint="@string/reply_comment" |
||||||
|
android:inputType="textMultiLine|textCapSentences|textAutoCorrect" |
||||||
|
android:imeActionLabel="@string/reply_submit" |
||||||
|
android:minLines="2" /> |
||||||
|
|
||||||
|
<LinearLayout |
||||||
|
android:orientation="vertical" |
||||||
|
android:layout_width="match_parent" |
||||||
|
android:layout_height="wrap_content" > |
||||||
|
<LinearLayout |
||||||
|
android:orientation="horizontal" |
||||||
|
android:layout_width="match_parent" |
||||||
|
android:layout_height="wrap_content" > |
||||||
|
<Button |
||||||
|
android:layout_weight="1" |
||||||
|
android:layout_width="0dp" |
||||||
|
android:layout_height="wrap_content" |
||||||
|
android:id="@+id/reply_file" |
||||||
|
android:text="@string/reply_file" /> |
||||||
|
<Button |
||||||
|
android:layout_weight="1" |
||||||
|
android:layout_width="0dp" |
||||||
|
android:layout_height="wrap_content" |
||||||
|
android:id="@+id/reply_file_delete" |
||||||
|
android:text="@string/reply_file_delete" /> |
||||||
|
</LinearLayout> |
||||||
|
|
||||||
|
<org.floens.chan.utils.LoadView |
||||||
|
android:id="@+id/reply_image" |
||||||
|
android:layout_width="match_parent" |
||||||
|
android:layout_height="wrap_content" |
||||||
|
android:minHeight="100dp" |
||||||
|
android:adjustViewBounds="true" |
||||||
|
android:scaleType="centerCrop" /> |
||||||
|
</LinearLayout> |
||||||
|
</LinearLayout> |
||||||
|
</ScrollView> |
||||||
|
<LinearLayout |
||||||
|
android:layout_width="match_parent" |
||||||
|
android:layout_height="match_parent" |
||||||
|
android:padding="8dp" |
||||||
|
android:orientation="vertical" > |
||||||
|
|
||||||
|
<org.floens.chan.utils.LoadView |
||||||
|
android:id="@+id/reply_captcha_container" |
||||||
|
android:layout_width="300dp" |
||||||
|
android:layout_height="60dp" |
||||||
|
android:layout_gravity="center" /> |
||||||
|
|
||||||
|
<EditText |
||||||
|
android:id="@+id/reply_captcha" |
||||||
|
android:hint="@string/reply_captcha" |
||||||
|
android:inputType="textNoSuggestions" |
||||||
|
android:layout_width="match_parent" |
||||||
|
android:layout_height="wrap_content" /> |
||||||
|
|
||||||
|
</LinearLayout> |
||||||
|
<org.floens.chan.utils.LoadView |
||||||
|
android:id="@+id/reply_response" |
||||||
|
android:layout_width="match_parent" |
||||||
|
android:layout_height="match_parent" |
||||||
|
android:orientation="vertical" > |
||||||
|
|
||||||
|
</org.floens.chan.utils.LoadView> |
||||||
|
</ViewFlipper> |
||||||
|
<LinearLayout |
||||||
|
android:orientation="horizontal" |
||||||
|
android:layout_width="match_parent" |
||||||
|
android:layout_height="wrap_content" |
||||||
|
style="?android:attr/buttonBarStyle" > |
||||||
|
|
||||||
|
<Button |
||||||
|
android:id="@+id/reply_cancel" |
||||||
|
style="?android:attr/buttonBarButtonStyle" |
||||||
|
android:layout_width="0dp" |
||||||
|
android:layout_height="wrap_content" |
||||||
|
android:layout_weight="1" |
||||||
|
android:text="@string/reply_cancel" /> |
||||||
|
|
||||||
|
<Button |
||||||
|
android:id="@+id/reply_submit" |
||||||
|
style="?android:attr/buttonBarButtonStyle" |
||||||
|
android:layout_width="0dp" |
||||||
|
android:layout_height="wrap_content" |
||||||
|
android:layout_weight="1" |
||||||
|
android:text="@string/reply_submit" /> |
||||||
|
</LinearLayout> |
||||||
|
</LinearLayout> |
||||||
|
|
||||||
|
|
||||||
|
|
@ -0,0 +1,22 @@ |
|||||||
|
<?xml version="1.0" encoding="utf-8"?> |
||||||
|
<menu xmlns:android="http://schemas.android.com/apk/res/android" > |
||||||
|
<item |
||||||
|
android:id="@+id/action_reload" |
||||||
|
android:icon="@drawable/ic_action_refresh" |
||||||
|
android:orderInCategory="2" |
||||||
|
android:showAsAction="always" |
||||||
|
android:title="@string/action_reload" /> |
||||||
|
|
||||||
|
<item |
||||||
|
android:id="@+id/action_open_browser" |
||||||
|
android:orderInCategory="5" |
||||||
|
android:showAsAction="never" |
||||||
|
android:title="@string/action_open_browser" /> |
||||||
|
|
||||||
|
<item |
||||||
|
android:id="@+id/action_settings" |
||||||
|
android:orderInCategory="100" |
||||||
|
android:showAsAction="never" |
||||||
|
android:title="@string/action_settings" /> |
||||||
|
|
||||||
|
</menu> |
@ -0,0 +1,10 @@ |
|||||||
|
<?xml version="1.0" encoding="utf-8"?> |
||||||
|
<menu xmlns:android="http://schemas.android.com/apk/res/android" > |
||||||
|
|
||||||
|
<item |
||||||
|
android:id="@+id/action_add_board" |
||||||
|
android:icon="@drawable/ic_action_new" |
||||||
|
android:showAsAction="always" |
||||||
|
android:title="@string/board_add" /> |
||||||
|
|
||||||
|
</menu> |
@ -0,0 +1,9 @@ |
|||||||
|
<?xml version="1.0" encoding="utf-8"?> |
||||||
|
<menu xmlns:android="http://schemas.android.com/apk/res/android" > |
||||||
|
<item |
||||||
|
android:id="@+id/action_image_save" |
||||||
|
android:orderInCategory="1" |
||||||
|
android:showAsAction="never" |
||||||
|
android:title="@string/image_save" /> |
||||||
|
|
||||||
|
</menu> |
@ -0,0 +1,16 @@ |
|||||||
|
<menu xmlns:android="http://schemas.android.com/apk/res/android" > |
||||||
|
<item |
||||||
|
android:id="@+id/action_catalog" |
||||||
|
android:icon="@drawable/ic_action_picture" |
||||||
|
android:orderInCategory="3" |
||||||
|
android:showAsAction="ifRoom" |
||||||
|
android:title="@string/action_catalog" /> |
||||||
|
|
||||||
|
<item |
||||||
|
android:id="@+id/action_reply" |
||||||
|
android:icon="@drawable/ic_action_chat" |
||||||
|
android:orderInCategory="4" |
||||||
|
android:showAsAction="ifRoom" |
||||||
|
android:title="@string/action_reply" /> |
||||||
|
|
||||||
|
</menu> |
@ -0,0 +1,15 @@ |
|||||||
|
<menu xmlns:android="http://schemas.android.com/apk/res/android" > |
||||||
|
<item |
||||||
|
android:id="@+id/action_pin" |
||||||
|
android:icon="@drawable/ic_action_make_available_offline" |
||||||
|
android:orderInCategory="1" |
||||||
|
android:showAsAction="always" |
||||||
|
android:title="@string/action_pin" /> |
||||||
|
|
||||||
|
<item |
||||||
|
android:id="@+id/action_reply" |
||||||
|
android:icon="@drawable/ic_action_chat" |
||||||
|
android:orderInCategory="4" |
||||||
|
android:showAsAction="ifRoom" |
||||||
|
android:title="@string/action_reply" /> |
||||||
|
</menu> |
@ -0,0 +1,8 @@ |
|||||||
|
<resources> |
||||||
|
|
||||||
|
<!-- |
||||||
|
Customize dimensions originally defined in res/values/dimens.xml (such as |
||||||
|
screen margins) for sw600dp devices (e.g. 7" tablets) here. |
||||||
|
--> |
||||||
|
|
||||||
|
</resources> |
@ -0,0 +1,8 @@ |
|||||||
|
<resources> |
||||||
|
|
||||||
|
<!-- |
||||||
|
Customize dimensions originally defined in res/values/dimens.xml (such as |
||||||
|
screen margins) for sw720dp devices (e.g. 10" tablets) in landscape here. |
||||||
|
--> |
||||||
|
|
||||||
|
</resources> |
@ -0,0 +1,4 @@ |
|||||||
|
<?xml version="1.0" encoding="utf-8"?> |
||||||
|
<resources> |
||||||
|
<color name="image_list_background">#88000000</color> |
||||||
|
</resources> |
@ -0,0 +1,17 @@ |
|||||||
|
<resources> |
||||||
|
<dimen name="general_padding">12dp</dimen> |
||||||
|
|
||||||
|
<dimen name="post_padding">11dp</dimen> |
||||||
|
<dimen name="post_text_padding">6dp</dimen> |
||||||
|
<dimen name="thumbnail_size">70dp</dimen> |
||||||
|
<dimen name="post_icon_width">24dp</dimen> |
||||||
|
<dimen name="post_icon_height">14dp</dimen> |
||||||
|
<dimen name="post_max_height">200dp</dimen> |
||||||
|
|
||||||
|
<dimen name="post_popup_min_width">200dp</dimen> |
||||||
|
<dimen name="post_popup_min_height">100dp</dimen> |
||||||
|
|
||||||
|
<dimen name="about_text_padding">11dp</dimen> |
||||||
|
|
||||||
|
<dimen name="image_popup_padding">8dp</dimen> |
||||||
|
</resources> |
@ -0,0 +1,80 @@ |
|||||||
|
<?xml version="1.0" encoding="utf-8"?> |
||||||
|
<resources> |
||||||
|
|
||||||
|
<string name="app_name">Chan</string> |
||||||
|
<string name="action_settings">Settings</string> |
||||||
|
<string name="action_reload">Reload</string> |
||||||
|
<string name="action_pin">Pin</string> |
||||||
|
<string name="action_reply">Reply</string> |
||||||
|
<string name="action_open_browser">Open in browser</string> |
||||||
|
<string name="action_catalog">Open catalog</string> |
||||||
|
|
||||||
|
<string name="preference_general">General</string> |
||||||
|
|
||||||
|
<string name="about">About</string> |
||||||
|
<string name="about_licenses">Open Source Licenses</string> |
||||||
|
<string name="about_licences_summary">Legal information about licenses</string> |
||||||
|
|
||||||
|
<string name="one_reply">reply</string> |
||||||
|
<string name="multiple_replies">replies</string> |
||||||
|
|
||||||
|
<string name="image_save">Save image</string> |
||||||
|
<string name="image_save_folder_name">Chan</string> |
||||||
|
<string name="image_save_succeeded">Saved image to</string> |
||||||
|
<string name="image_save_failed">Saving image failed</string> |
||||||
|
<string name="image_save_directory_error">Cannot make save directory</string> |
||||||
|
<string name="image_save_not_mounted">Cannot write to storage</string> |
||||||
|
|
||||||
|
<string name="image_preview_failed">Failed to show image</string> |
||||||
|
<string name="image_open_failed">Failed to open image</string> |
||||||
|
|
||||||
|
<string name="thread_load_failed_network">No network</string> |
||||||
|
<string name="thread_load_failed_parsing">Server inaccessible</string> |
||||||
|
<string name="thread_load_failed_server">404 not found</string> |
||||||
|
|
||||||
|
<string name="end_of_line">No more posts</string> |
||||||
|
|
||||||
|
<string name="board_edit">Edit my boards</string> |
||||||
|
<string name="board_add">Add board</string> |
||||||
|
<string name="board_add_confirm">Add</string> |
||||||
|
<string name="board_add_cancel">Cancel</string> |
||||||
|
<string name="board_add_fail">Unknown board code</string> |
||||||
|
<string name="board_add_success">Added</string> |
||||||
|
<string name="board_add_duplicate">You already have that board</string> |
||||||
|
|
||||||
|
<string name="drawer_open">Open drawer</string> |
||||||
|
<string name="drawer_close">Close drawer</string> |
||||||
|
|
||||||
|
<string name="drawer_pinned">Pinned threads</string> |
||||||
|
|
||||||
|
<string name="reply">Reply</string> |
||||||
|
<string name="reply_name">Name</string> |
||||||
|
<string name="reply_email">Email</string> |
||||||
|
<string name="reply_subject">Subject</string> |
||||||
|
<string name="reply_comment">Comment</string> |
||||||
|
<string name="reply_file">Pick file</string> |
||||||
|
<string name="reply_file_delete">Remove file</string> |
||||||
|
<string name="reply_submit">Submit</string> |
||||||
|
<string name="reply_back">Back</string> |
||||||
|
<string name="reply_cancel">Cancel</string> |
||||||
|
<string name="reply_captcha">Enter captcha</string> |
||||||
|
<string name="reply_close">Close</string> |
||||||
|
<string name="reply_error">Error sending reply</string> |
||||||
|
<string name="reply_error_captcha">Wrong captcha</string> |
||||||
|
<string name="reply_error_file">No file selected</string> |
||||||
|
<string name="reply_success">Post Successful</string> |
||||||
|
<string name="reply_captcha_load_error">Failed to load captcha</string> |
||||||
|
|
||||||
|
<string name="open_link_confirmation_preference">Ask before opening links</string> |
||||||
|
<string name="open_link_confirmation">Open link?</string> |
||||||
|
|
||||||
|
<string name="open_unknown_title">Unsupported link</string> |
||||||
|
<string name="open_unknown">Chan can\'t open this link. Opening it in your browser instead.</string> |
||||||
|
|
||||||
|
<string name="post_info">Info</string> |
||||||
|
<string-array name="post_options"> |
||||||
|
<item>Info</item> |
||||||
|
<item>Quote</item> |
||||||
|
<item>Copy text</item> |
||||||
|
</string-array> |
||||||
|
</resources> |
@ -0,0 +1,21 @@ |
|||||||
|
<resources xmlns:android="http://schemas.android.com/apk/res/android"> |
||||||
|
|
||||||
|
|
||||||
|
<style name="AppTheme" parent="android:Theme.Holo.Light.DarkActionBar"> |
||||||
|
|
||||||
|
</style> |
||||||
|
|
||||||
|
<style name="Theme.ImageList" parent="AppTheme"> |
||||||
|
<item name="android:windowIsTranslucent">true</item> |
||||||
|
<item name="android:windowBackground">@android:color/transparent</item> |
||||||
|
<item name="android:backgroundDimEnabled">true</item> |
||||||
|
</style> |
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
</resources> |
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
@ -0,0 +1,39 @@ |
|||||||
|
<?xml version="1.0" encoding="utf-8"?> |
||||||
|
<PreferenceScreen xmlns:android="http://schemas.android.com/apk/res/android" > |
||||||
|
<PreferenceCategory |
||||||
|
android:title="@string/preference_general" > |
||||||
|
|
||||||
|
<Preference |
||||||
|
android:title="@string/board_edit" > |
||||||
|
<intent |
||||||
|
android:action="android.intent.action.VIEW" |
||||||
|
android:targetPackage="org.floens.chan" |
||||||
|
android:targetClass="org.floens.chan.activity.BoardEditor" /> |
||||||
|
</Preference> |
||||||
|
|
||||||
|
<CheckBoxPreference |
||||||
|
android:title="@string/open_link_confirmation_preference" |
||||||
|
android:key="preference_open_link_confirmation" |
||||||
|
android:defaultValue="true" /> |
||||||
|
|
||||||
|
</PreferenceCategory> |
||||||
|
|
||||||
|
<PreferenceCategory |
||||||
|
android:title="@string/about" > |
||||||
|
|
||||||
|
<Preference |
||||||
|
android:title="@string/about_licenses" |
||||||
|
android:summary="@string/about_licences_summary" |
||||||
|
android:key="about_licences" /> |
||||||
|
|
||||||
|
<Preference |
||||||
|
android:title="Chan" |
||||||
|
android:key="about_version" /> |
||||||
|
|
||||||
|
</PreferenceCategory> |
||||||
|
</PreferenceScreen> |
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
@ -0,0 +1,58 @@ |
|||||||
|
/* |
||||||
|
* Copyright (C) 2011 The Android Open Source Project |
||||||
|
* |
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License"); |
||||||
|
* you may not use this file except in compliance with the License. |
||||||
|
* You may obtain a copy of the License at |
||||||
|
* |
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
* |
||||||
|
* Unless required by applicable law or agreed to in writing, software |
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS, |
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
||||||
|
* See the License for the specific language governing permissions and |
||||||
|
* limitations under the License. |
||||||
|
*/ |
||||||
|
|
||||||
|
package com.android.volley; |
||||||
|
|
||||||
|
import android.content.Intent; |
||||||
|
|
||||||
|
/** |
||||||
|
* Error indicating that there was an authentication failure when performing a Request. |
||||||
|
*/ |
||||||
|
@SuppressWarnings("serial") |
||||||
|
public class AuthFailureError extends VolleyError { |
||||||
|
/** An intent that can be used to resolve this exception. (Brings up the password dialog.) */ |
||||||
|
private Intent mResolutionIntent; |
||||||
|
|
||||||
|
public AuthFailureError() { } |
||||||
|
|
||||||
|
public AuthFailureError(Intent intent) { |
||||||
|
mResolutionIntent = intent; |
||||||
|
} |
||||||
|
|
||||||
|
public AuthFailureError(NetworkResponse response) { |
||||||
|
super(response); |
||||||
|
} |
||||||
|
|
||||||
|
public AuthFailureError(String message) { |
||||||
|
super(message); |
||||||
|
} |
||||||
|
|
||||||
|
public AuthFailureError(String message, Exception reason) { |
||||||
|
super(message, reason); |
||||||
|
} |
||||||
|
|
||||||
|
public Intent getResolutionIntent() { |
||||||
|
return mResolutionIntent; |
||||||
|
} |
||||||
|
|
||||||
|
@Override |
||||||
|
public String getMessage() { |
||||||
|
if (mResolutionIntent != null) { |
||||||
|
return "User needs to (re)enter credentials."; |
||||||
|
} |
||||||
|
return super.getMessage(); |
||||||
|
} |
||||||
|
} |
@ -0,0 +1,97 @@ |
|||||||
|
/* |
||||||
|
* Copyright (C) 2011 The Android Open Source Project |
||||||
|
* |
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License"); |
||||||
|
* you may not use this file except in compliance with the License. |
||||||
|
* You may obtain a copy of the License at |
||||||
|
* |
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
* |
||||||
|
* Unless required by applicable law or agreed to in writing, software |
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS, |
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
||||||
|
* See the License for the specific language governing permissions and |
||||||
|
* limitations under the License. |
||||||
|
*/ |
||||||
|
|
||||||
|
package com.android.volley; |
||||||
|
|
||||||
|
import java.util.Collections; |
||||||
|
import java.util.Map; |
||||||
|
|
||||||
|
/** |
||||||
|
* An interface for a cache keyed by a String with a byte array as data. |
||||||
|
*/ |
||||||
|
public interface Cache { |
||||||
|
/** |
||||||
|
* Retrieves an entry from the cache. |
||||||
|
* @param key Cache key |
||||||
|
* @return An {@link Entry} or null in the event of a cache miss |
||||||
|
*/ |
||||||
|
public Entry get(String key); |
||||||
|
|
||||||
|
/** |
||||||
|
* Adds or replaces an entry to the cache. |
||||||
|
* @param key Cache key |
||||||
|
* @param entry Data to store and metadata for cache coherency, TTL, etc. |
||||||
|
*/ |
||||||
|
public void put(String key, Entry entry); |
||||||
|
|
||||||
|
/** |
||||||
|
* Performs any potentially long-running actions needed to initialize the cache; |
||||||
|
* will be called from a worker thread. |
||||||
|
*/ |
||||||
|
public void initialize(); |
||||||
|
|
||||||
|
/** |
||||||
|
* Invalidates an entry in the cache. |
||||||
|
* @param key Cache key |
||||||
|
* @param fullExpire True to fully expire the entry, false to soft expire |
||||||
|
*/ |
||||||
|
public void invalidate(String key, boolean fullExpire); |
||||||
|
|
||||||
|
/** |
||||||
|
* Removes an entry from the cache. |
||||||
|
* @param key Cache key |
||||||
|
*/ |
||||||
|
public void remove(String key); |
||||||
|
|
||||||
|
/** |
||||||
|
* Empties the cache. |
||||||
|
*/ |
||||||
|
public void clear(); |
||||||
|
|
||||||
|
/** |
||||||
|
* Data and metadata for an entry returned by the cache. |
||||||
|
*/ |
||||||
|
public static class Entry { |
||||||
|
/** The data returned from cache. */ |
||||||
|
public byte[] data; |
||||||
|
|
||||||
|
/** ETag for cache coherency. */ |
||||||
|
public String etag; |
||||||
|
|
||||||
|
/** Date of this response as reported by the server. */ |
||||||
|
public long serverDate; |
||||||
|
|
||||||
|
/** TTL for this record. */ |
||||||
|
public long ttl; |
||||||
|
|
||||||
|
/** Soft TTL for this record. */ |
||||||
|
public long softTtl; |
||||||
|
|
||||||
|
/** Immutable response headers as received from server; must be non-null. */ |
||||||
|
public Map<String, String> responseHeaders = Collections.emptyMap(); |
||||||
|
|
||||||
|
/** True if the entry is expired. */ |
||||||
|
public boolean isExpired() { |
||||||
|
return this.ttl < System.currentTimeMillis(); |
||||||
|
} |
||||||
|
|
||||||
|
/** True if a refresh is needed from the original data source. */ |
||||||
|
public boolean refreshNeeded() { |
||||||
|
return this.softTtl < System.currentTimeMillis(); |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
} |
@ -0,0 +1,159 @@ |
|||||||
|
/* |
||||||
|
* Copyright (C) 2011 The Android Open Source Project |
||||||
|
* |
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License"); |
||||||
|
* you may not use this file except in compliance with the License. |
||||||
|
* You may obtain a copy of the License at |
||||||
|
* |
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
* |
||||||
|
* Unless required by applicable law or agreed to in writing, software |
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS, |
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
||||||
|
* See the License for the specific language governing permissions and |
||||||
|
* limitations under the License. |
||||||
|
*/ |
||||||
|
|
||||||
|
package com.android.volley; |
||||||
|
|
||||||
|
import java.util.concurrent.BlockingQueue; |
||||||
|
|
||||||
|
import android.os.Process; |
||||||
|
|
||||||
|
/** |
||||||
|
* Provides a thread for performing cache triage on a queue of requests. |
||||||
|
* |
||||||
|
* Requests added to the specified cache queue are resolved from cache. |
||||||
|
* Any deliverable response is posted back to the caller via a |
||||||
|
* {@link ResponseDelivery}. Cache misses and responses that require |
||||||
|
* refresh are enqueued on the specified network queue for processing |
||||||
|
* by a {@link NetworkDispatcher}. |
||||||
|
*/ |
||||||
|
@SuppressWarnings("rawtypes") |
||||||
|
public class CacheDispatcher extends Thread { |
||||||
|
|
||||||
|
private static final boolean DEBUG = VolleyLog.DEBUG; |
||||||
|
|
||||||
|
/** The queue of requests coming in for triage. */ |
||||||
|
private final BlockingQueue<Request> mCacheQueue; |
||||||
|
|
||||||
|
/** The queue of requests going out to the network. */ |
||||||
|
private final BlockingQueue<Request> mNetworkQueue; |
||||||
|
|
||||||
|
/** The cache to read from. */ |
||||||
|
private final Cache mCache; |
||||||
|
|
||||||
|
/** For posting responses. */ |
||||||
|
private final ResponseDelivery mDelivery; |
||||||
|
|
||||||
|
/** Used for telling us to die. */ |
||||||
|
private volatile boolean mQuit = false; |
||||||
|
|
||||||
|
/** |
||||||
|
* Creates a new cache triage dispatcher thread. You must call {@link #start()} |
||||||
|
* in order to begin processing. |
||||||
|
* |
||||||
|
* @param cacheQueue Queue of incoming requests for triage |
||||||
|
* @param networkQueue Queue to post requests that require network to |
||||||
|
* @param cache Cache interface to use for resolution |
||||||
|
* @param delivery Delivery interface to use for posting responses |
||||||
|
*/ |
||||||
|
public CacheDispatcher( |
||||||
|
BlockingQueue<Request> cacheQueue, BlockingQueue<Request> networkQueue, |
||||||
|
Cache cache, ResponseDelivery delivery) { |
||||||
|
mCacheQueue = cacheQueue; |
||||||
|
mNetworkQueue = networkQueue; |
||||||
|
mCache = cache; |
||||||
|
mDelivery = delivery; |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* Forces this dispatcher to quit immediately. If any requests are still in |
||||||
|
* the queue, they are not guaranteed to be processed. |
||||||
|
*/ |
||||||
|
public void quit() { |
||||||
|
mQuit = true; |
||||||
|
interrupt(); |
||||||
|
} |
||||||
|
|
||||||
|
@Override |
||||||
|
public void run() { |
||||||
|
if (DEBUG) VolleyLog.v("start new dispatcher"); |
||||||
|
Process.setThreadPriority(Process.THREAD_PRIORITY_BACKGROUND); |
||||||
|
|
||||||
|
// Make a blocking call to initialize the cache.
|
||||||
|
mCache.initialize(); |
||||||
|
|
||||||
|
while (true) { |
||||||
|
try { |
||||||
|
// Get a request from the cache triage queue, blocking until
|
||||||
|
// at least one is available.
|
||||||
|
final Request request = mCacheQueue.take(); |
||||||
|
request.addMarker("cache-queue-take"); |
||||||
|
|
||||||
|
// If the request has been canceled, don't bother dispatching it.
|
||||||
|
if (request.isCanceled()) { |
||||||
|
request.finish("cache-discard-canceled"); |
||||||
|
continue; |
||||||
|
} |
||||||
|
|
||||||
|
// Attempt to retrieve this item from cache.
|
||||||
|
Cache.Entry entry = mCache.get(request.getCacheKey()); |
||||||
|
if (entry == null) { |
||||||
|
request.addMarker("cache-miss"); |
||||||
|
// Cache miss; send off to the network dispatcher.
|
||||||
|
mNetworkQueue.put(request); |
||||||
|
continue; |
||||||
|
} |
||||||
|
|
||||||
|
// If it is completely expired, just send it to the network.
|
||||||
|
if (entry.isExpired()) { |
||||||
|
request.addMarker("cache-hit-expired"); |
||||||
|
request.setCacheEntry(entry); |
||||||
|
mNetworkQueue.put(request); |
||||||
|
continue; |
||||||
|
} |
||||||
|
|
||||||
|
// We have a cache hit; parse its data for delivery back to the request.
|
||||||
|
request.addMarker("cache-hit"); |
||||||
|
Response<?> response = request.parseNetworkResponse( |
||||||
|
new NetworkResponse(entry.data, entry.responseHeaders)); |
||||||
|
request.addMarker("cache-hit-parsed"); |
||||||
|
|
||||||
|
if (!entry.refreshNeeded()) { |
||||||
|
// Completely unexpired cache hit. Just deliver the response.
|
||||||
|
mDelivery.postResponse(request, response); |
||||||
|
} else { |
||||||
|
// Soft-expired cache hit. We can deliver the cached response,
|
||||||
|
// but we need to also send the request to the network for
|
||||||
|
// refreshing.
|
||||||
|
request.addMarker("cache-hit-refresh-needed"); |
||||||
|
request.setCacheEntry(entry); |
||||||
|
|
||||||
|
// Mark the response as intermediate.
|
||||||
|
response.intermediate = true; |
||||||
|
|
||||||
|
// Post the intermediate response back to the user and have
|
||||||
|
// the delivery then forward the request along to the network.
|
||||||
|
mDelivery.postResponse(request, response, new Runnable() { |
||||||
|
@Override |
||||||
|
public void run() { |
||||||
|
try { |
||||||
|
mNetworkQueue.put(request); |
||||||
|
} catch (InterruptedException e) { |
||||||
|
// Not much we can do about this.
|
||||||
|
} |
||||||
|
} |
||||||
|
}); |
||||||
|
} |
||||||
|
|
||||||
|
} catch (InterruptedException e) { |
||||||
|
// We may have been interrupted because it was time to quit.
|
||||||
|
if (mQuit) { |
||||||
|
return; |
||||||
|
} |
||||||
|
continue; |
||||||
|
} |
||||||
|
} |
||||||
|
} |
||||||
|
} |
@ -0,0 +1,98 @@ |
|||||||
|
/* |
||||||
|
* Copyright (C) 2011 The Android Open Source Project |
||||||
|
* |
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License"); |
||||||
|
* you may not use this file except in compliance with the License. |
||||||
|
* You may obtain a copy of the License at |
||||||
|
* |
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
* |
||||||
|
* Unless required by applicable law or agreed to in writing, software |
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS, |
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
||||||
|
* See the License for the specific language governing permissions and |
||||||
|
* limitations under the License. |
||||||
|
*/ |
||||||
|
|
||||||
|
package com.android.volley; |
||||||
|
|
||||||
|
/** |
||||||
|
* Default retry policy for requests. |
||||||
|
*/ |
||||||
|
public class DefaultRetryPolicy implements RetryPolicy { |
||||||
|
/** The current timeout in milliseconds. */ |
||||||
|
private int mCurrentTimeoutMs; |
||||||
|
|
||||||
|
/** The current retry count. */ |
||||||
|
private int mCurrentRetryCount; |
||||||
|
|
||||||
|
/** The maximum number of attempts. */ |
||||||
|
private final int mMaxNumRetries; |
||||||
|
|
||||||
|
/** The backoff multiplier for for the policy. */ |
||||||
|
private final float mBackoffMultiplier; |
||||||
|
|
||||||
|
/** The default socket timeout in milliseconds */ |
||||||
|
public static final int DEFAULT_TIMEOUT_MS = 2500; |
||||||
|
|
||||||
|
/** The default number of retries */ |
||||||
|
public static final int DEFAULT_MAX_RETRIES = 1; |
||||||
|
|
||||||
|
/** The default backoff multiplier */ |
||||||
|
public static final float DEFAULT_BACKOFF_MULT = 1f; |
||||||
|
|
||||||
|
/** |
||||||
|
* Constructs a new retry policy using the default timeouts. |
||||||
|
*/ |
||||||
|
public DefaultRetryPolicy() { |
||||||
|
this(DEFAULT_TIMEOUT_MS, DEFAULT_MAX_RETRIES, DEFAULT_BACKOFF_MULT); |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* Constructs a new retry policy. |
||||||
|
* @param initialTimeoutMs The initial timeout for the policy. |
||||||
|
* @param maxNumRetries The maximum number of retries. |
||||||
|
* @param backoffMultiplier Backoff multiplier for the policy. |
||||||
|
*/ |
||||||
|
public DefaultRetryPolicy(int initialTimeoutMs, int maxNumRetries, float backoffMultiplier) { |
||||||
|
mCurrentTimeoutMs = initialTimeoutMs; |
||||||
|
mMaxNumRetries = maxNumRetries; |
||||||
|
mBackoffMultiplier = backoffMultiplier; |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* Returns the current timeout. |
||||||
|
*/ |
||||||
|
@Override |
||||||
|
public int getCurrentTimeout() { |
||||||
|
return mCurrentTimeoutMs; |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* Returns the current retry count. |
||||||
|
*/ |
||||||
|
@Override |
||||||
|
public int getCurrentRetryCount() { |
||||||
|
return mCurrentRetryCount; |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* Prepares for the next retry by applying a backoff to the timeout. |
||||||
|
* @param error The error code of the last attempt. |
||||||
|
*/ |
||||||
|
@Override |
||||||
|
public void retry(VolleyError error) throws VolleyError { |
||||||
|
mCurrentRetryCount++; |
||||||
|
mCurrentTimeoutMs += (mCurrentTimeoutMs * mBackoffMultiplier); |
||||||
|
if (!hasAttemptRemaining()) { |
||||||
|
throw error; |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* Returns true if this policy has attempts remaining, false otherwise. |
||||||
|
*/ |
||||||
|
protected boolean hasAttemptRemaining() { |
||||||
|
return mCurrentRetryCount <= mMaxNumRetries; |
||||||
|
} |
||||||
|
} |
@ -0,0 +1,118 @@ |
|||||||
|
/* |
||||||
|
* Copyright (C) 2011 The Android Open Source Project |
||||||
|
* |
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License"); |
||||||
|
* you may not use this file except in compliance with the License. |
||||||
|
* You may obtain a copy of the License at |
||||||
|
* |
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
* |
||||||
|
* Unless required by applicable law or agreed to in writing, software |
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS, |
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
||||||
|
* See the License for the specific language governing permissions and |
||||||
|
* limitations under the License. |
||||||
|
*/ |
||||||
|
|
||||||
|
package com.android.volley; |
||||||
|
|
||||||
|
import java.util.concurrent.Executor; |
||||||
|
|
||||||
|
import android.os.Handler; |
||||||
|
|
||||||
|
/** |
||||||
|
* Delivers responses and errors. |
||||||
|
*/ |
||||||
|
public class ExecutorDelivery implements ResponseDelivery { |
||||||
|
/** Used for posting responses, typically to the main thread. */ |
||||||
|
private final Executor mResponsePoster; |
||||||
|
|
||||||
|
/** |
||||||
|
* Creates a new response delivery interface. |
||||||
|
* @param handler {@link Handler} to post responses on |
||||||
|
*/ |
||||||
|
public ExecutorDelivery(final Handler handler) { |
||||||
|
// Make an Executor that just wraps the handler.
|
||||||
|
mResponsePoster = new Executor() { |
||||||
|
@Override |
||||||
|
public void execute(Runnable command) { |
||||||
|
handler.post(command); |
||||||
|
} |
||||||
|
}; |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* Creates a new response delivery interface, mockable version |
||||||
|
* for testing. |
||||||
|
* @param executor For running delivery tasks |
||||||
|
*/ |
||||||
|
public ExecutorDelivery(Executor executor) { |
||||||
|
mResponsePoster = executor; |
||||||
|
} |
||||||
|
|
||||||
|
@Override |
||||||
|
public void postResponse(Request<?> request, Response<?> response) { |
||||||
|
postResponse(request, response, null); |
||||||
|
} |
||||||
|
|
||||||
|
@Override |
||||||
|
public void postResponse(Request<?> request, Response<?> response, Runnable runnable) { |
||||||
|
request.markDelivered(); |
||||||
|
request.addMarker("post-response"); |
||||||
|
mResponsePoster.execute(new ResponseDeliveryRunnable(request, response, runnable)); |
||||||
|
} |
||||||
|
|
||||||
|
@Override |
||||||
|
public void postError(Request<?> request, VolleyError error) { |
||||||
|
request.addMarker("post-error"); |
||||||
|
Response<?> response = Response.error(error); |
||||||
|
mResponsePoster.execute(new ResponseDeliveryRunnable(request, response, null)); |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* A Runnable used for delivering network responses to a listener on the |
||||||
|
* main thread. |
||||||
|
*/ |
||||||
|
@SuppressWarnings("rawtypes") |
||||||
|
private class ResponseDeliveryRunnable implements Runnable { |
||||||
|
private final Request mRequest; |
||||||
|
private final Response mResponse; |
||||||
|
private final Runnable mRunnable; |
||||||
|
|
||||||
|
public ResponseDeliveryRunnable(Request request, Response response, Runnable runnable) { |
||||||
|
mRequest = request; |
||||||
|
mResponse = response; |
||||||
|
mRunnable = runnable; |
||||||
|
} |
||||||
|
|
||||||
|
@SuppressWarnings("unchecked") |
||||||
|
@Override |
||||||
|
public void run() { |
||||||
|
// If this request has canceled, finish it and don't deliver.
|
||||||
|
if (mRequest.isCanceled()) { |
||||||
|
mRequest.finish("canceled-at-delivery"); |
||||||
|
return; |
||||||
|
} |
||||||
|
|
||||||
|
// Deliver a normal response or error, depending.
|
||||||
|
if (mResponse.isSuccess()) { |
||||||
|
mRequest.deliverResponse(mResponse.result); |
||||||
|
} else { |
||||||
|
mRequest.deliverError(mResponse.error); |
||||||
|
} |
||||||
|
|
||||||
|
// If this is an intermediate response, add a marker, otherwise we're done
|
||||||
|
// and the request can be finished.
|
||||||
|
if (mResponse.intermediate) { |
||||||
|
mRequest.addMarker("intermediate-response"); |
||||||
|
} else { |
||||||
|
mRequest.finish("done"); |
||||||
|
} |
||||||
|
|
||||||
|
// If we have been provided a post-delivery runnable, run it.
|
||||||
|
if (mRunnable != null) { |
||||||
|
mRunnable.run(); |
||||||
|
} |
||||||
|
} |
||||||
|
} |
||||||
|
} |
@ -0,0 +1,30 @@ |
|||||||
|
/* |
||||||
|
* Copyright (C) 2011 The Android Open Source Project |
||||||
|
* |
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License"); |
||||||
|
* you may not use this file except in compliance with the License. |
||||||
|
* You may obtain a copy of the License at |
||||||
|
* |
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
* |
||||||
|
* Unless required by applicable law or agreed to in writing, software |
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS, |
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
||||||
|
* See the License for the specific language governing permissions and |
||||||
|
* limitations under the License. |
||||||
|
*/ |
||||||
|
|
||||||
|
package com.android.volley; |
||||||
|
|
||||||
|
/** |
||||||
|
* An interface for performing requests. |
||||||
|
*/ |
||||||
|
public interface Network { |
||||||
|
/** |
||||||
|
* Performs the specified request. |
||||||
|
* @param request Request to process |
||||||
|
* @return A {@link NetworkResponse} with data and caching metadata; will never be null |
||||||
|
* @throws VolleyError on errors |
||||||
|
*/ |
||||||
|
public NetworkResponse performRequest(Request<?> request) throws VolleyError; |
||||||
|
} |
@ -0,0 +1,142 @@ |
|||||||
|
/* |
||||||
|
* Copyright (C) 2011 The Android Open Source Project |
||||||
|
* |
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License"); |
||||||
|
* you may not use this file except in compliance with the License. |
||||||
|
* You may obtain a copy of the License at |
||||||
|
* |
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
* |
||||||
|
* Unless required by applicable law or agreed to in writing, software |
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS, |
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
||||||
|
* See the License for the specific language governing permissions and |
||||||
|
* limitations under the License. |
||||||
|
*/ |
||||||
|
|
||||||
|
package com.android.volley; |
||||||
|
|
||||||
|
import java.util.concurrent.BlockingQueue; |
||||||
|
|
||||||
|
import android.net.TrafficStats; |
||||||
|
import android.os.Build; |
||||||
|
import android.os.Process; |
||||||
|
|
||||||
|
/** |
||||||
|
* Provides a thread for performing network dispatch from a queue of requests. |
||||||
|
* |
||||||
|
* Requests added to the specified queue are processed from the network via a |
||||||
|
* specified {@link Network} interface. Responses are committed to cache, if |
||||||
|
* eligible, using a specified {@link Cache} interface. Valid responses and |
||||||
|
* errors are posted back to the caller via a {@link ResponseDelivery}. |
||||||
|
*/ |
||||||
|
@SuppressWarnings("rawtypes") |
||||||
|
public class NetworkDispatcher extends Thread { |
||||||
|
/** The queue of requests to service. */ |
||||||
|
private final BlockingQueue<Request> mQueue; |
||||||
|
/** The network interface for processing requests. */ |
||||||
|
private final Network mNetwork; |
||||||
|
/** The cache to write to. */ |
||||||
|
private final Cache mCache; |
||||||
|
/** For posting responses and errors. */ |
||||||
|
private final ResponseDelivery mDelivery; |
||||||
|
/** Used for telling us to die. */ |
||||||
|
private volatile boolean mQuit = false; |
||||||
|
|
||||||
|
/** |
||||||
|
* Creates a new network dispatcher thread. You must call {@link #start()} |
||||||
|
* in order to begin processing. |
||||||
|
* |
||||||
|
* @param queue Queue of incoming requests for triage |
||||||
|
* @param network Network interface to use for performing requests |
||||||
|
* @param cache Cache interface to use for writing responses to cache |
||||||
|
* @param delivery Delivery interface to use for posting responses |
||||||
|
*/ |
||||||
|
public NetworkDispatcher(BlockingQueue<Request> queue, |
||||||
|
Network network, Cache cache, |
||||||
|
ResponseDelivery delivery) { |
||||||
|
mQueue = queue; |
||||||
|
mNetwork = network; |
||||||
|
mCache = cache; |
||||||
|
mDelivery = delivery; |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* Forces this dispatcher to quit immediately. If any requests are still in |
||||||
|
* the queue, they are not guaranteed to be processed. |
||||||
|
*/ |
||||||
|
public void quit() { |
||||||
|
mQuit = true; |
||||||
|
interrupt(); |
||||||
|
} |
||||||
|
|
||||||
|
@Override |
||||||
|
public void run() { |
||||||
|
Process.setThreadPriority(Process.THREAD_PRIORITY_BACKGROUND); |
||||||
|
Request request; |
||||||
|
while (true) { |
||||||
|
try { |
||||||
|
// Take a request from the queue.
|
||||||
|
request = mQueue.take(); |
||||||
|
} catch (InterruptedException e) { |
||||||
|
// We may have been interrupted because it was time to quit.
|
||||||
|
if (mQuit) { |
||||||
|
return; |
||||||
|
} |
||||||
|
continue; |
||||||
|
} |
||||||
|
|
||||||
|
try { |
||||||
|
request.addMarker("network-queue-take"); |
||||||
|
|
||||||
|
// If the request was cancelled already, do not perform the
|
||||||
|
// network request.
|
||||||
|
if (request.isCanceled()) { |
||||||
|
request.finish("network-discard-cancelled"); |
||||||
|
continue; |
||||||
|
} |
||||||
|
|
||||||
|
// Tag the request (if API >= 14)
|
||||||
|
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.ICE_CREAM_SANDWICH) { |
||||||
|
TrafficStats.setThreadStatsTag(request.getTrafficStatsTag()); |
||||||
|
} |
||||||
|
|
||||||
|
// Perform the network request.
|
||||||
|
NetworkResponse networkResponse = mNetwork.performRequest(request); |
||||||
|
request.addMarker("network-http-complete"); |
||||||
|
|
||||||
|
// If the server returned 304 AND we delivered a response already,
|
||||||
|
// we're done -- don't deliver a second identical response.
|
||||||
|
if (networkResponse.notModified && request.hasHadResponseDelivered()) { |
||||||
|
request.finish("not-modified"); |
||||||
|
continue; |
||||||
|
} |
||||||
|
|
||||||
|
// Parse the response here on the worker thread.
|
||||||
|
Response<?> response = request.parseNetworkResponse(networkResponse); |
||||||
|
request.addMarker("network-parse-complete"); |
||||||
|
|
||||||
|
// Write to cache if applicable.
|
||||||
|
// TODO: Only update cache metadata instead of entire record for 304s.
|
||||||
|
if (request.shouldCache() && response.cacheEntry != null) { |
||||||
|
mCache.put(request.getCacheKey(), response.cacheEntry); |
||||||
|
request.addMarker("network-cache-written"); |
||||||
|
} |
||||||
|
|
||||||
|
// Post the response back.
|
||||||
|
request.markDelivered(); |
||||||
|
mDelivery.postResponse(request, response); |
||||||
|
} catch (VolleyError volleyError) { |
||||||
|
parseAndDeliverNetworkError(request, volleyError); |
||||||
|
} catch (Exception e) { |
||||||
|
VolleyLog.e(e, "Unhandled exception %s", e.toString()); |
||||||
|
mDelivery.postError(request, new VolleyError(e)); |
||||||
|
} |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
private void parseAndDeliverNetworkError(Request<?> request, VolleyError error) { |
||||||
|
error = request.parseNetworkError(error); |
||||||
|
mDelivery.postError(request, error); |
||||||
|
} |
||||||
|
} |
@ -0,0 +1,36 @@ |
|||||||
|
/* |
||||||
|
* Copyright (C) 2011 The Android Open Source Project |
||||||
|
* |
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License"); |
||||||
|
* you may not use this file except in compliance with the License. |
||||||
|
* You may obtain a copy of the License at |
||||||
|
* |
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
* |
||||||
|
* Unless required by applicable law or agreed to in writing, software |
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS, |
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
||||||
|
* See the License for the specific language governing permissions and |
||||||
|
* limitations under the License. |
||||||
|
*/ |
||||||
|
|
||||||
|
package com.android.volley; |
||||||
|
|
||||||
|
|
||||||
|
/** |
||||||
|
* Indicates that there was a network error when performing a Volley request. |
||||||
|
*/ |
||||||
|
@SuppressWarnings("serial") |
||||||
|
public class NetworkError extends VolleyError { |
||||||
|
public NetworkError() { |
||||||
|
super(); |
||||||
|
} |
||||||
|
|
||||||
|
public NetworkError(Throwable cause) { |
||||||
|
super(cause); |
||||||
|
} |
||||||
|
|
||||||
|
public NetworkError(NetworkResponse networkResponse) { |
||||||
|
super(networkResponse); |
||||||
|
} |
||||||
|
} |
@ -0,0 +1,62 @@ |
|||||||
|
/* |
||||||
|
* Copyright (C) 2011 The Android Open Source Project |
||||||
|
* |
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License"); |
||||||
|
* you may not use this file except in compliance with the License. |
||||||
|
* You may obtain a copy of the License at |
||||||
|
* |
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
* |
||||||
|
* Unless required by applicable law or agreed to in writing, software |
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS, |
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
||||||
|
* See the License for the specific language governing permissions and |
||||||
|
* limitations under the License. |
||||||
|
*/ |
||||||
|
|
||||||
|
package com.android.volley; |
||||||
|
|
||||||
|
import java.util.Collections; |
||||||
|
import java.util.Map; |
||||||
|
|
||||||
|
import org.apache.http.HttpStatus; |
||||||
|
|
||||||
|
/** |
||||||
|
* Data and headers returned from {@link Network#performRequest(Request)}. |
||||||
|
*/ |
||||||
|
public class NetworkResponse { |
||||||
|
/** |
||||||
|
* Creates a new network response. |
||||||
|
* @param statusCode the HTTP status code |
||||||
|
* @param data Response body |
||||||
|
* @param headers Headers returned with this response, or null for none |
||||||
|
* @param notModified True if the server returned a 304 and the data was already in cache |
||||||
|
*/ |
||||||
|
public NetworkResponse(int statusCode, byte[] data, Map<String, String> headers, |
||||||
|
boolean notModified) { |
||||||
|
this.statusCode = statusCode; |
||||||
|
this.data = data; |
||||||
|
this.headers = headers; |
||||||
|
this.notModified = notModified; |
||||||
|
} |
||||||
|
|
||||||
|
public NetworkResponse(byte[] data) { |
||||||
|
this(HttpStatus.SC_OK, data, Collections.<String, String>emptyMap(), false); |
||||||
|
} |
||||||
|
|
||||||
|
public NetworkResponse(byte[] data, Map<String, String> headers) { |
||||||
|
this(HttpStatus.SC_OK, data, headers, false); |
||||||
|
} |
||||||
|
|
||||||
|
/** The HTTP status code. */ |
||||||
|
public final int statusCode; |
||||||
|
|
||||||
|
/** Raw data from this response. */ |
||||||
|
public final byte[] data; |
||||||
|
|
||||||
|
/** Response headers. */ |
||||||
|
public final Map<String, String> headers; |
||||||
|
|
||||||
|
/** True if the server returned a 304 (Not Modified). */ |
||||||
|
public final boolean notModified; |
||||||
|
} |
@ -0,0 +1,31 @@ |
|||||||
|
/* |
||||||
|
* Copyright (C) 2011 The Android Open Source Project |
||||||
|
* |
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License"); |
||||||
|
* you may not use this file except in compliance with the License. |
||||||
|
* You may obtain a copy of the License at |
||||||
|
* |
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
* |
||||||
|
* Unless required by applicable law or agreed to in writing, software |
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS, |
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
||||||
|
* See the License for the specific language governing permissions and |
||||||
|
* limitations under the License. |
||||||
|
*/ |
||||||
|
|
||||||
|
package com.android.volley; |
||||||
|
|
||||||
|
/** |
||||||
|
* Error indicating that no connection could be established when performing a Volley request. |
||||||
|
*/ |
||||||
|
@SuppressWarnings("serial") |
||||||
|
public class NoConnectionError extends NetworkError { |
||||||
|
public NoConnectionError() { |
||||||
|
super(); |
||||||
|
} |
||||||
|
|
||||||
|
public NoConnectionError(Throwable reason) { |
||||||
|
super(reason); |
||||||
|
} |
||||||
|
} |
@ -0,0 +1,34 @@ |
|||||||
|
/* |
||||||
|
* Copyright (C) 2011 The Android Open Source Project |
||||||
|
* |
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License"); |
||||||
|
* you may not use this file except in compliance with the License. |
||||||
|
* You may obtain a copy of the License at |
||||||
|
* |
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
* |
||||||
|
* Unless required by applicable law or agreed to in writing, software |
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS, |
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
||||||
|
* See the License for the specific language governing permissions and |
||||||
|
* limitations under the License. |
||||||
|
*/ |
||||||
|
|
||||||
|
package com.android.volley; |
||||||
|
|
||||||
|
|
||||||
|
/** |
||||||
|
* Indicates that the server's response could not be parsed. |
||||||
|
*/ |
||||||
|
@SuppressWarnings("serial") |
||||||
|
public class ParseError extends VolleyError { |
||||||
|
public ParseError() { } |
||||||
|
|
||||||
|
public ParseError(NetworkResponse networkResponse) { |
||||||
|
super(networkResponse); |
||||||
|
} |
||||||
|
|
||||||
|
public ParseError(Throwable cause) { |
||||||
|
super(cause); |
||||||
|
} |
||||||
|
} |
@ -0,0 +1,548 @@ |
|||||||
|
/* |
||||||
|
* Copyright (C) 2011 The Android Open Source Project |
||||||
|
* |
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License"); |
||||||
|
* you may not use this file except in compliance with the License. |
||||||
|
* You may obtain a copy of the License at |
||||||
|
* |
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
* |
||||||
|
* Unless required by applicable law or agreed to in writing, software |
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS, |
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
||||||
|
* See the License for the specific language governing permissions and |
||||||
|
* limitations under the License. |
||||||
|
*/ |
||||||
|
|
||||||
|
package com.android.volley; |
||||||
|
|
||||||
|
import java.io.UnsupportedEncodingException; |
||||||
|
import java.net.URLEncoder; |
||||||
|
import java.util.Collections; |
||||||
|
import java.util.Map; |
||||||
|
|
||||||
|
import android.net.TrafficStats; |
||||||
|
import android.net.Uri; |
||||||
|
import android.os.Handler; |
||||||
|
import android.os.Looper; |
||||||
|
import android.os.SystemClock; |
||||||
|
import android.text.TextUtils; |
||||||
|
|
||||||
|
import com.android.volley.VolleyLog.MarkerLog; |
||||||
|
|
||||||
|
/** |
||||||
|
* Base class for all network requests. |
||||||
|
* |
||||||
|
* @param <T> The type of parsed response this request expects. |
||||||
|
*/ |
||||||
|
public abstract class Request<T> implements Comparable<Request<T>> { |
||||||
|
|
||||||
|
/** |
||||||
|
* Default encoding for POST or PUT parameters. See {@link #getParamsEncoding()}. |
||||||
|
*/ |
||||||
|
private static final String DEFAULT_PARAMS_ENCODING = "UTF-8"; |
||||||
|
|
||||||
|
/** |
||||||
|
* Supported request methods. |
||||||
|
*/ |
||||||
|
public interface Method { |
||||||
|
int DEPRECATED_GET_OR_POST = -1; |
||||||
|
int GET = 0; |
||||||
|
int POST = 1; |
||||||
|
int PUT = 2; |
||||||
|
int DELETE = 3; |
||||||
|
} |
||||||
|
|
||||||
|
/** An event log tracing the lifetime of this request; for debugging. */ |
||||||
|
private final MarkerLog mEventLog = MarkerLog.ENABLED ? new MarkerLog() : null; |
||||||
|
|
||||||
|
/** Request method of this request. Currently supports GET, POST, PUT, and DELETE. */ |
||||||
|
private final int mMethod; |
||||||
|
|
||||||
|
/** URL of this request. */ |
||||||
|
private final String mUrl; |
||||||
|
|
||||||
|
/** Default tag for {@link TrafficStats}. */ |
||||||
|
private final int mDefaultTrafficStatsTag; |
||||||
|
|
||||||
|
/** Listener interface for errors. */ |
||||||
|
private final Response.ErrorListener mErrorListener; |
||||||
|
|
||||||
|
/** Sequence number of this request, used to enforce FIFO ordering. */ |
||||||
|
private Integer mSequence; |
||||||
|
|
||||||
|
/** The request queue this request is associated with. */ |
||||||
|
private RequestQueue mRequestQueue; |
||||||
|
|
||||||
|
/** Whether or not responses to this request should be cached. */ |
||||||
|
private boolean mShouldCache = true; |
||||||
|
|
||||||
|
/** Whether or not this request has been canceled. */ |
||||||
|
private boolean mCanceled = false; |
||||||
|
|
||||||
|
/** Whether or not a response has been delivered for this request yet. */ |
||||||
|
private boolean mResponseDelivered = false; |
||||||
|
|
||||||
|
// A cheap variant of request tracing used to dump slow requests.
|
||||||
|
private long mRequestBirthTime = 0; |
||||||
|
|
||||||
|
/** Threshold at which we should log the request (even when debug logging is not enabled). */ |
||||||
|
private static final long SLOW_REQUEST_THRESHOLD_MS = 3000; |
||||||
|
|
||||||
|
/** The retry policy for this request. */ |
||||||
|
private RetryPolicy mRetryPolicy; |
||||||
|
|
||||||
|
/** |
||||||
|
* When a request can be retrieved from cache but must be refreshed from |
||||||
|
* the network, the cache entry will be stored here so that in the event of |
||||||
|
* a "Not Modified" response, we can be sure it hasn't been evicted from cache. |
||||||
|
*/ |
||||||
|
private Cache.Entry mCacheEntry = null; |
||||||
|
|
||||||
|
/** An opaque token tagging this request; used for bulk cancellation. */ |
||||||
|
private Object mTag; |
||||||
|
|
||||||
|
/** |
||||||
|
* Creates a new request with the given URL and error listener. Note that |
||||||
|
* the normal response listener is not provided here as delivery of responses |
||||||
|
* is provided by subclasses, who have a better idea of how to deliver an |
||||||
|
* already-parsed response. |
||||||
|
* |
||||||
|
* @deprecated Use {@link #Request(int, String, com.android.volley.Response.ErrorListener)}. |
||||||
|
*/ |
||||||
|
@Deprecated |
||||||
|
public Request(String url, Response.ErrorListener listener) { |
||||||
|
this(Method.DEPRECATED_GET_OR_POST, url, listener); |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* Creates a new request with the given method (one of the values from {@link Method}), |
||||||
|
* URL, and error listener. Note that the normal response listener is not provided here as |
||||||
|
* delivery of responses is provided by subclasses, who have a better idea of how to deliver |
||||||
|
* an already-parsed response. |
||||||
|
*/ |
||||||
|
public Request(int method, String url, Response.ErrorListener listener) { |
||||||
|
mMethod = method; |
||||||
|
mUrl = url; |
||||||
|
mErrorListener = listener; |
||||||
|
setRetryPolicy(new DefaultRetryPolicy()); |
||||||
|
|
||||||
|
mDefaultTrafficStatsTag = TextUtils.isEmpty(url) ? 0: Uri.parse(url).getHost().hashCode(); |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* Return the method for this request. Can be one of the values in {@link Method}. |
||||||
|
*/ |
||||||
|
public int getMethod() { |
||||||
|
return mMethod; |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* Set a tag on this request. Can be used to cancel all requests with this |
||||||
|
* tag by {@link RequestQueue#cancelAll(Object)}. |
||||||
|
*/ |
||||||
|
public void setTag(Object tag) { |
||||||
|
mTag = tag; |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* Returns this request's tag. |
||||||
|
* @see Request#setTag(Object) |
||||||
|
*/ |
||||||
|
public Object getTag() { |
||||||
|
return mTag; |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* @return A tag for use with {@link TrafficStats#setThreadStatsTag(int)} |
||||||
|
*/ |
||||||
|
public int getTrafficStatsTag() { |
||||||
|
return mDefaultTrafficStatsTag; |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* Sets the retry policy for this request. |
||||||
|
*/ |
||||||
|
public void setRetryPolicy(RetryPolicy retryPolicy) { |
||||||
|
mRetryPolicy = retryPolicy; |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* Adds an event to this request's event log; for debugging. |
||||||
|
*/ |
||||||
|
public void addMarker(String tag) { |
||||||
|
if (MarkerLog.ENABLED) { |
||||||
|
mEventLog.add(tag, Thread.currentThread().getId()); |
||||||
|
} else if (mRequestBirthTime == 0) { |
||||||
|
mRequestBirthTime = SystemClock.elapsedRealtime(); |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* Notifies the request queue that this request has finished (successfully or with error). |
||||||
|
* |
||||||
|
* <p>Also dumps all events from this request's event log; for debugging.</p> |
||||||
|
*/ |
||||||
|
void finish(final String tag) { |
||||||
|
if (mRequestQueue != null) { |
||||||
|
mRequestQueue.finish(this); |
||||||
|
} |
||||||
|
if (MarkerLog.ENABLED) { |
||||||
|
final long threadId = Thread.currentThread().getId(); |
||||||
|
if (Looper.myLooper() != Looper.getMainLooper()) { |
||||||
|
// If we finish marking off of the main thread, we need to
|
||||||
|
// actually do it on the main thread to ensure correct ordering.
|
||||||
|
Handler mainThread = new Handler(Looper.getMainLooper()); |
||||||
|
mainThread.post(new Runnable() { |
||||||
|
@Override |
||||||
|
public void run() { |
||||||
|
mEventLog.add(tag, threadId); |
||||||
|
mEventLog.finish(this.toString()); |
||||||
|
} |
||||||
|
}); |
||||||
|
return; |
||||||
|
} |
||||||
|
|
||||||
|
mEventLog.add(tag, threadId); |
||||||
|
mEventLog.finish(this.toString()); |
||||||
|
} else { |
||||||
|
long requestTime = SystemClock.elapsedRealtime() - mRequestBirthTime; |
||||||
|
if (requestTime >= SLOW_REQUEST_THRESHOLD_MS) { |
||||||
|
VolleyLog.d("%d ms: %s", requestTime, this.toString()); |
||||||
|
} |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* Associates this request with the given queue. The request queue will be notified when this |
||||||
|
* request has finished. |
||||||
|
*/ |
||||||
|
public void setRequestQueue(RequestQueue requestQueue) { |
||||||
|
mRequestQueue = requestQueue; |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* Sets the sequence number of this request. Used by {@link RequestQueue}. |
||||||
|
*/ |
||||||
|
public final void setSequence(int sequence) { |
||||||
|
mSequence = sequence; |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* Returns the sequence number of this request. |
||||||
|
*/ |
||||||
|
public final int getSequence() { |
||||||
|
if (mSequence == null) { |
||||||
|
throw new IllegalStateException("getSequence called before setSequence"); |
||||||
|
} |
||||||
|
return mSequence; |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* Returns the URL of this request. |
||||||
|
*/ |
||||||
|
public String getUrl() { |
||||||
|
return mUrl; |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* Returns the cache key for this request. By default, this is the URL. |
||||||
|
*/ |
||||||
|
public String getCacheKey() { |
||||||
|
return getUrl(); |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* Annotates this request with an entry retrieved for it from cache. |
||||||
|
* Used for cache coherency support. |
||||||
|
*/ |
||||||
|
public void setCacheEntry(Cache.Entry entry) { |
||||||
|
mCacheEntry = entry; |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* Returns the annotated cache entry, or null if there isn't one. |
||||||
|
*/ |
||||||
|
public Cache.Entry getCacheEntry() { |
||||||
|
return mCacheEntry; |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* Mark this request as canceled. No callback will be delivered. |
||||||
|
*/ |
||||||
|
public void cancel() { |
||||||
|
mCanceled = true; |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* Returns true if this request has been canceled. |
||||||
|
*/ |
||||||
|
public boolean isCanceled() { |
||||||
|
return mCanceled; |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* Returns a list of extra HTTP headers to go along with this request. Can |
||||||
|
* throw {@link AuthFailureError} as authentication may be required to |
||||||
|
* provide these values. |
||||||
|
* @throws AuthFailureError In the event of auth failure |
||||||
|
*/ |
||||||
|
public Map<String, String> getHeaders() throws AuthFailureError { |
||||||
|
return Collections.emptyMap(); |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* Returns a Map of POST parameters to be used for this request, or null if |
||||||
|
* a simple GET should be used. Can throw {@link AuthFailureError} as |
||||||
|
* authentication may be required to provide these values. |
||||||
|
* |
||||||
|
* <p>Note that only one of getPostParams() and getPostBody() can return a non-null |
||||||
|
* value.</p> |
||||||
|
* @throws AuthFailureError In the event of auth failure |
||||||
|
* |
||||||
|
* @deprecated Use {@link #getParams()} instead. |
||||||
|
*/ |
||||||
|
@Deprecated |
||||||
|
protected Map<String, String> getPostParams() throws AuthFailureError { |
||||||
|
return getParams(); |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* Returns which encoding should be used when converting POST parameters returned by |
||||||
|
* {@link #getPostParams()} into a raw POST body. |
||||||
|
* |
||||||
|
* <p>This controls both encodings: |
||||||
|
* <ol> |
||||||
|
* <li>The string encoding used when converting parameter names and values into bytes prior |
||||||
|
* to URL encoding them.</li> |
||||||
|
* <li>The string encoding used when converting the URL encoded parameters into a raw |
||||||
|
* byte array.</li> |
||||||
|
* </ol> |
||||||
|
* |
||||||
|
* @deprecated Use {@link #getParamsEncoding()} instead. |
||||||
|
*/ |
||||||
|
@Deprecated |
||||||
|
protected String getPostParamsEncoding() { |
||||||
|
return getParamsEncoding(); |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* @deprecated Use {@link #getBodyContentType()} instead. |
||||||
|
*/ |
||||||
|
@Deprecated |
||||||
|
public String getPostBodyContentType() { |
||||||
|
return getBodyContentType(); |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* Returns the raw POST body to be sent. |
||||||
|
* |
||||||
|
* @throws AuthFailureError In the event of auth failure |
||||||
|
* |
||||||
|
* @deprecated Use {@link #getBody()} instead. |
||||||
|
*/ |
||||||
|
@Deprecated |
||||||
|
public byte[] getPostBody() throws AuthFailureError { |
||||||
|
// Note: For compatibility with legacy clients of volley, this implementation must remain
|
||||||
|
// here instead of simply calling the getBody() function because this function must
|
||||||
|
// call getPostParams() and getPostParamsEncoding() since legacy clients would have
|
||||||
|
// overridden these two member functions for POST requests.
|
||||||
|
Map<String, String> postParams = getPostParams(); |
||||||
|
if (postParams != null && postParams.size() > 0) { |
||||||
|
return encodeParameters(postParams, getPostParamsEncoding()); |
||||||
|
} |
||||||
|
return null; |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* Returns a Map of parameters to be used for a POST or PUT request. Can throw |
||||||
|
* {@link AuthFailureError} as authentication may be required to provide these values. |
||||||
|
* |
||||||
|
* <p>Note that you can directly override {@link #getBody()} for custom data.</p> |
||||||
|
* |
||||||
|
* @throws AuthFailureError in the event of auth failure |
||||||
|
*/ |
||||||
|
protected Map<String, String> getParams() throws AuthFailureError { |
||||||
|
return null; |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* Returns which encoding should be used when converting POST or PUT parameters returned by |
||||||
|
* {@link #getParams()} into a raw POST or PUT body. |
||||||
|
* |
||||||
|
* <p>This controls both encodings: |
||||||
|
* <ol> |
||||||
|
* <li>The string encoding used when converting parameter names and values into bytes prior |
||||||
|
* to URL encoding them.</li> |
||||||
|
* <li>The string encoding used when converting the URL encoded parameters into a raw |
||||||
|
* byte array.</li> |
||||||
|
* </ol> |
||||||
|
*/ |
||||||
|
protected String getParamsEncoding() { |
||||||
|
return DEFAULT_PARAMS_ENCODING; |
||||||
|
} |
||||||
|
|
||||||
|
public String getBodyContentType() { |
||||||
|
return "application/x-www-form-urlencoded; charset=" + getParamsEncoding(); |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* Returns the raw POST or PUT body to be sent. |
||||||
|
* |
||||||
|
* @throws AuthFailureError in the event of auth failure |
||||||
|
*/ |
||||||
|
public byte[] getBody() throws AuthFailureError { |
||||||
|
Map<String, String> params = getParams(); |
||||||
|
if (params != null && params.size() > 0) { |
||||||
|
return encodeParameters(params, getParamsEncoding()); |
||||||
|
} |
||||||
|
return null; |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* Converts <code>params</code> into an application/x-www-form-urlencoded encoded string. |
||||||
|
*/ |
||||||
|
private byte[] encodeParameters(Map<String, String> params, String paramsEncoding) { |
||||||
|
StringBuilder encodedParams = new StringBuilder(); |
||||||
|
try { |
||||||
|
for (Map.Entry<String, String> entry : params.entrySet()) { |
||||||
|
encodedParams.append(URLEncoder.encode(entry.getKey(), paramsEncoding)); |
||||||
|
encodedParams.append('='); |
||||||
|
encodedParams.append(URLEncoder.encode(entry.getValue(), paramsEncoding)); |
||||||
|
encodedParams.append('&'); |
||||||
|
} |
||||||
|
return encodedParams.toString().getBytes(paramsEncoding); |
||||||
|
} catch (UnsupportedEncodingException uee) { |
||||||
|
throw new RuntimeException("Encoding not supported: " + paramsEncoding, uee); |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* Set whether or not responses to this request should be cached. |
||||||
|
*/ |
||||||
|
public final void setShouldCache(boolean shouldCache) { |
||||||
|
mShouldCache = shouldCache; |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* Returns true if responses to this request should be cached. |
||||||
|
*/ |
||||||
|
public final boolean shouldCache() { |
||||||
|
return mShouldCache; |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* Priority values. Requests will be processed from higher priorities to |
||||||
|
* lower priorities, in FIFO order. |
||||||
|
*/ |
||||||
|
public enum Priority { |
||||||
|
LOW, |
||||||
|
NORMAL, |
||||||
|
HIGH, |
||||||
|
IMMEDIATE |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* Returns the {@link Priority} of this request; {@link Priority#NORMAL} by default. |
||||||
|
*/ |
||||||
|
public Priority getPriority() { |
||||||
|
return Priority.NORMAL; |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* Returns the socket timeout in milliseconds per retry attempt. (This value can be changed |
||||||
|
* per retry attempt if a backoff is specified via backoffTimeout()). If there are no retry |
||||||
|
* attempts remaining, this will cause delivery of a {@link TimeoutError} error. |
||||||
|
*/ |
||||||
|
public final int getTimeoutMs() { |
||||||
|
return mRetryPolicy.getCurrentTimeout(); |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* Returns the retry policy that should be used for this request. |
||||||
|
*/ |
||||||
|
public RetryPolicy getRetryPolicy() { |
||||||
|
return mRetryPolicy; |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* Mark this request as having a response delivered on it. This can be used |
||||||
|
* later in the request's lifetime for suppressing identical responses. |
||||||
|
*/ |
||||||
|
public void markDelivered() { |
||||||
|
mResponseDelivered = true; |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* Returns true if this request has had a response delivered for it. |
||||||
|
*/ |
||||||
|
public boolean hasHadResponseDelivered() { |
||||||
|
return mResponseDelivered; |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* Subclasses must implement this to parse the raw network response |
||||||
|
* and return an appropriate response type. This method will be |
||||||
|
* called from a worker thread. The response will not be delivered |
||||||
|
* if you return null. |
||||||
|
* @param response Response from the network |
||||||
|
* @return The parsed response, or null in the case of an error |
||||||
|
*/ |
||||||
|
abstract protected Response<T> parseNetworkResponse(NetworkResponse response); |
||||||
|
|
||||||
|
/** |
||||||
|
* Subclasses can override this method to parse 'networkError' and return a more specific error. |
||||||
|
* |
||||||
|
* <p>The default implementation just returns the passed 'networkError'.</p> |
||||||
|
* |
||||||
|
* @param volleyError the error retrieved from the network |
||||||
|
* @return an NetworkError augmented with additional information |
||||||
|
*/ |
||||||
|
protected VolleyError parseNetworkError(VolleyError volleyError) { |
||||||
|
return volleyError; |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* Subclasses must implement this to perform delivery of the parsed |
||||||
|
* response to their listeners. The given response is guaranteed to |
||||||
|
* be non-null; responses that fail to parse are not delivered. |
||||||
|
* @param response The parsed response returned by |
||||||
|
* {@link #parseNetworkResponse(NetworkResponse)} |
||||||
|
*/ |
||||||
|
abstract protected void deliverResponse(T response); |
||||||
|
|
||||||
|
/** |
||||||
|
* Delivers error message to the ErrorListener that the Request was |
||||||
|
* initialized with. |
||||||
|
* |
||||||
|
* @param error Error details |
||||||
|
*/ |
||||||
|
public void deliverError(VolleyError error) { |
||||||
|
if (mErrorListener != null) { |
||||||
|
mErrorListener.onErrorResponse(error); |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* Our comparator sorts from high to low priority, and secondarily by |
||||||
|
* sequence number to provide FIFO ordering. |
||||||
|
*/ |
||||||
|
@Override |
||||||
|
public int compareTo(Request<T> other) { |
||||||
|
Priority left = this.getPriority(); |
||||||
|
Priority right = other.getPriority(); |
||||||
|
|
||||||
|
// High-priority requests are "lesser" so they are sorted to the front.
|
||||||
|
// Equal priorities are sorted by sequence number to provide FIFO ordering.
|
||||||
|
return left == right ? |
||||||
|
this.mSequence - other.mSequence : |
||||||
|
right.ordinal() - left.ordinal(); |
||||||
|
} |
||||||
|
|
||||||
|
@Override |
||||||
|
public String toString() { |
||||||
|
String trafficStatsTag = "0x" + Integer.toHexString(getTrafficStatsTag()); |
||||||
|
return (mCanceled ? "[X] " : "[ ] ") + getUrl() + " " + trafficStatsTag + " " |
||||||
|
+ getPriority() + " " + mSequence; |
||||||
|
} |
||||||
|
} |
@ -0,0 +1,287 @@ |
|||||||
|
/* |
||||||
|
* Copyright (C) 2011 The Android Open Source Project |
||||||
|
* |
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License"); |
||||||
|
* you may not use this file except in compliance with the License. |
||||||
|
* You may obtain a copy of the License at |
||||||
|
* |
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
* |
||||||
|
* Unless required by applicable law or agreed to in writing, software |
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS, |
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
||||||
|
* See the License for the specific language governing permissions and |
||||||
|
* limitations under the License. |
||||||
|
*/ |
||||||
|
|
||||||
|
package com.android.volley; |
||||||
|
|
||||||
|
import java.util.HashMap; |
||||||
|
import java.util.HashSet; |
||||||
|
import java.util.LinkedList; |
||||||
|
import java.util.Map; |
||||||
|
import java.util.Queue; |
||||||
|
import java.util.Set; |
||||||
|
import java.util.concurrent.PriorityBlockingQueue; |
||||||
|
import java.util.concurrent.atomic.AtomicInteger; |
||||||
|
|
||||||
|
import android.os.Handler; |
||||||
|
import android.os.Looper; |
||||||
|
|
||||||
|
/** |
||||||
|
* A request dispatch queue with a thread pool of dispatchers. |
||||||
|
* |
||||||
|
* Calling {@link #add(Request)} will enqueue the given Request for dispatch, |
||||||
|
* resolving from either cache or network on a worker thread, and then delivering |
||||||
|
* a parsed response on the main thread. |
||||||
|
*/ |
||||||
|
@SuppressWarnings("rawtypes") |
||||||
|
public class RequestQueue { |
||||||
|
|
||||||
|
/** Used for generating monotonically-increasing sequence numbers for requests. */ |
||||||
|
private AtomicInteger mSequenceGenerator = new AtomicInteger(); |
||||||
|
|
||||||
|
/** |
||||||
|
* Staging area for requests that already have a duplicate request in flight. |
||||||
|
* |
||||||
|
* <ul> |
||||||
|
* <li>containsKey(cacheKey) indicates that there is a request in flight for the given cache |
||||||
|
* key.</li> |
||||||
|
* <li>get(cacheKey) returns waiting requests for the given cache key. The in flight request |
||||||
|
* is <em>not</em> contained in that list. Is null if no requests are staged.</li> |
||||||
|
* </ul> |
||||||
|
*/ |
||||||
|
private final Map<String, Queue<Request>> mWaitingRequests = |
||||||
|
new HashMap<String, Queue<Request>>(); |
||||||
|
|
||||||
|
/** |
||||||
|
* The set of all requests currently being processed by this RequestQueue. A Request |
||||||
|
* will be in this set if it is waiting in any queue or currently being processed by |
||||||
|
* any dispatcher. |
||||||
|
*/ |
||||||
|
private final Set<Request> mCurrentRequests = new HashSet<Request>(); |
||||||
|
|
||||||
|
/** The cache triage queue. */ |
||||||
|
private final PriorityBlockingQueue<Request> mCacheQueue = |
||||||
|
new PriorityBlockingQueue<Request>(); |
||||||
|
|
||||||
|
/** The queue of requests that are actually going out to the network. */ |
||||||
|
private final PriorityBlockingQueue<Request> mNetworkQueue = |
||||||
|
new PriorityBlockingQueue<Request>(); |
||||||
|
|
||||||
|
/** Number of network request dispatcher threads to start. */ |
||||||
|
private static final int DEFAULT_NETWORK_THREAD_POOL_SIZE = 4; |
||||||
|
|
||||||
|
/** Cache interface for retrieving and storing respones. */ |
||||||
|
private final Cache mCache; |
||||||
|
|
||||||
|
/** Network interface for performing requests. */ |
||||||
|
private final Network mNetwork; |
||||||
|
|
||||||
|
/** Response delivery mechanism. */ |
||||||
|
private final ResponseDelivery mDelivery; |
||||||
|
|
||||||
|
/** The network dispatchers. */ |
||||||
|
private NetworkDispatcher[] mDispatchers; |
||||||
|
|
||||||
|
/** The cache dispatcher. */ |
||||||
|
private CacheDispatcher mCacheDispatcher; |
||||||
|
|
||||||
|
/** |
||||||
|
* Creates the worker pool. Processing will not begin until {@link #start()} is called. |
||||||
|
* |
||||||
|
* @param cache A Cache to use for persisting responses to disk |
||||||
|
* @param network A Network interface for performing HTTP requests |
||||||
|
* @param threadPoolSize Number of network dispatcher threads to create |
||||||
|
* @param delivery A ResponseDelivery interface for posting responses and errors |
||||||
|
*/ |
||||||
|
public RequestQueue(Cache cache, Network network, int threadPoolSize, |
||||||
|
ResponseDelivery delivery) { |
||||||
|
mCache = cache; |
||||||
|
mNetwork = network; |
||||||
|
mDispatchers = new NetworkDispatcher[threadPoolSize]; |
||||||
|
mDelivery = delivery; |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* Creates the worker pool. Processing will not begin until {@link #start()} is called. |
||||||
|
* |
||||||
|
* @param cache A Cache to use for persisting responses to disk |
||||||
|
* @param network A Network interface for performing HTTP requests |
||||||
|
* @param threadPoolSize Number of network dispatcher threads to create |
||||||
|
*/ |
||||||
|
public RequestQueue(Cache cache, Network network, int threadPoolSize) { |
||||||
|
this(cache, network, threadPoolSize, |
||||||
|
new ExecutorDelivery(new Handler(Looper.getMainLooper()))); |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* Creates the worker pool. Processing will not begin until {@link #start()} is called. |
||||||
|
* |
||||||
|
* @param cache A Cache to use for persisting responses to disk |
||||||
|
* @param network A Network interface for performing HTTP requests |
||||||
|
*/ |
||||||
|
public RequestQueue(Cache cache, Network network) { |
||||||
|
this(cache, network, DEFAULT_NETWORK_THREAD_POOL_SIZE); |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* Starts the dispatchers in this queue. |
||||||
|
*/ |
||||||
|
public void start() { |
||||||
|
stop(); // Make sure any currently running dispatchers are stopped.
|
||||||
|
// Create the cache dispatcher and start it.
|
||||||
|
mCacheDispatcher = new CacheDispatcher(mCacheQueue, mNetworkQueue, mCache, mDelivery); |
||||||
|
mCacheDispatcher.start(); |
||||||
|
|
||||||
|
// Create network dispatchers (and corresponding threads) up to the pool size.
|
||||||
|
for (int i = 0; i < mDispatchers.length; i++) { |
||||||
|
NetworkDispatcher networkDispatcher = new NetworkDispatcher(mNetworkQueue, mNetwork, |
||||||
|
mCache, mDelivery); |
||||||
|
mDispatchers[i] = networkDispatcher; |
||||||
|
networkDispatcher.start(); |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* Stops the cache and network dispatchers. |
||||||
|
*/ |
||||||
|
public void stop() { |
||||||
|
if (mCacheDispatcher != null) { |
||||||
|
mCacheDispatcher.quit(); |
||||||
|
} |
||||||
|
for (int i = 0; i < mDispatchers.length; i++) { |
||||||
|
if (mDispatchers[i] != null) { |
||||||
|
mDispatchers[i].quit(); |
||||||
|
} |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* Gets a sequence number. |
||||||
|
*/ |
||||||
|
public int getSequenceNumber() { |
||||||
|
return mSequenceGenerator.incrementAndGet(); |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* Gets the {@link Cache} instance being used. |
||||||
|
*/ |
||||||
|
public Cache getCache() { |
||||||
|
return mCache; |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* A simple predicate or filter interface for Requests, for use by |
||||||
|
* {@link RequestQueue#cancelAll(RequestFilter)}. |
||||||
|
*/ |
||||||
|
public interface RequestFilter { |
||||||
|
public boolean apply(Request<?> request); |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* Cancels all requests in this queue for which the given filter applies. |
||||||
|
* @param filter The filtering function to use |
||||||
|
*/ |
||||||
|
public void cancelAll(RequestFilter filter) { |
||||||
|
synchronized (mCurrentRequests) { |
||||||
|
for (Request<?> request : mCurrentRequests) { |
||||||
|
if (filter.apply(request)) { |
||||||
|
request.cancel(); |
||||||
|
} |
||||||
|
} |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* Cancels all requests in this queue with the given tag. Tag must be non-null |
||||||
|
* and equality is by identity. |
||||||
|
*/ |
||||||
|
public void cancelAll(final Object tag) { |
||||||
|
if (tag == null) { |
||||||
|
throw new IllegalArgumentException("Cannot cancelAll with a null tag"); |
||||||
|
} |
||||||
|
cancelAll(new RequestFilter() { |
||||||
|
@Override |
||||||
|
public boolean apply(Request<?> request) { |
||||||
|
return request.getTag() == tag; |
||||||
|
} |
||||||
|
}); |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* Adds a Request to the dispatch queue. |
||||||
|
* @param request The request to service |
||||||
|
* @return The passed-in request |
||||||
|
*/ |
||||||
|
public Request add(Request request) { |
||||||
|
// Tag the request as belonging to this queue and add it to the set of current requests.
|
||||||
|
request.setRequestQueue(this); |
||||||
|
synchronized (mCurrentRequests) { |
||||||
|
mCurrentRequests.add(request); |
||||||
|
} |
||||||
|
|
||||||
|
// Process requests in the order they are added.
|
||||||
|
request.setSequence(getSequenceNumber()); |
||||||
|
request.addMarker("add-to-queue"); |
||||||
|
|
||||||
|
// If the request is uncacheable, skip the cache queue and go straight to the network.
|
||||||
|
if (!request.shouldCache()) { |
||||||
|
mNetworkQueue.add(request); |
||||||
|
return request; |
||||||
|
} |
||||||
|
|
||||||
|
// Insert request into stage if there's already a request with the same cache key in flight.
|
||||||
|
synchronized (mWaitingRequests) { |
||||||
|
String cacheKey = request.getCacheKey(); |
||||||
|
if (mWaitingRequests.containsKey(cacheKey)) { |
||||||
|
// There is already a request in flight. Queue up.
|
||||||
|
Queue<Request> stagedRequests = mWaitingRequests.get(cacheKey); |
||||||
|
if (stagedRequests == null) { |
||||||
|
stagedRequests = new LinkedList<Request>(); |
||||||
|
} |
||||||
|
stagedRequests.add(request); |
||||||
|
mWaitingRequests.put(cacheKey, stagedRequests); |
||||||
|
if (VolleyLog.DEBUG) { |
||||||
|
VolleyLog.v("Request for cacheKey=%s is in flight, putting on hold.", cacheKey); |
||||||
|
} |
||||||
|
} else { |
||||||
|
// Insert 'null' queue for this cacheKey, indicating there is now a request in
|
||||||
|
// flight.
|
||||||
|
mWaitingRequests.put(cacheKey, null); |
||||||
|
mCacheQueue.add(request); |
||||||
|
} |
||||||
|
return request; |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* Called from {@link Request#finish(String)}, indicating that processing of the given request |
||||||
|
* has finished. |
||||||
|
* |
||||||
|
* <p>Releases waiting requests for <code>request.getCacheKey()</code> if |
||||||
|
* <code>request.shouldCache()</code>.</p> |
||||||
|
*/ |
||||||
|
void finish(Request request) { |
||||||
|
// Remove from the set of requests currently being processed.
|
||||||
|
synchronized (mCurrentRequests) { |
||||||
|
mCurrentRequests.remove(request); |
||||||
|
} |
||||||
|
|
||||||
|
if (request.shouldCache()) { |
||||||
|
synchronized (mWaitingRequests) { |
||||||
|
String cacheKey = request.getCacheKey(); |
||||||
|
Queue<Request> waitingRequests = mWaitingRequests.remove(cacheKey); |
||||||
|
if (waitingRequests != null) { |
||||||
|
if (VolleyLog.DEBUG) { |
||||||
|
VolleyLog.v("Releasing %d waiting requests for cacheKey=%s.", |
||||||
|
waitingRequests.size(), cacheKey); |
||||||
|
} |
||||||
|
// Process all queued up requests. They won't be considered as in flight, but
|
||||||
|
// that's not a problem as the cache has been primed by 'request'.
|
||||||
|
mCacheQueue.addAll(waitingRequests); |
||||||
|
} |
||||||
|
} |
||||||
|
} |
||||||
|
} |
||||||
|
} |
@ -0,0 +1,85 @@ |
|||||||
|
/* |
||||||
|
* Copyright (C) 2011 The Android Open Source Project |
||||||
|
* |
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License"); |
||||||
|
* you may not use this file except in compliance with the License. |
||||||
|
* You may obtain a copy of the License at |
||||||
|
* |
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
* |
||||||
|
* Unless required by applicable law or agreed to in writing, software |
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS, |
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
||||||
|
* See the License for the specific language governing permissions and |
||||||
|
* limitations under the License. |
||||||
|
*/ |
||||||
|
|
||||||
|
package com.android.volley; |
||||||
|
|
||||||
|
/** |
||||||
|
* Encapsulates a parsed response for delivery. |
||||||
|
* |
||||||
|
* @param <T> Parsed type of this response |
||||||
|
*/ |
||||||
|
public class Response<T> { |
||||||
|
|
||||||
|
/** Callback interface for delivering parsed responses. */ |
||||||
|
public interface Listener<T> { |
||||||
|
/** Called when a response is received. */ |
||||||
|
public void onResponse(T response); |
||||||
|
} |
||||||
|
|
||||||
|
/** Callback interface for delivering error responses. */ |
||||||
|
public interface ErrorListener { |
||||||
|
/** |
||||||
|
* Callback method that an error has been occurred with the |
||||||
|
* provided error code and optional user-readable message. |
||||||
|
*/ |
||||||
|
public void onErrorResponse(VolleyError error); |
||||||
|
} |
||||||
|
|
||||||
|
/** Returns a successful response containing the parsed result. */ |
||||||
|
public static <T> Response<T> success(T result, Cache.Entry cacheEntry) { |
||||||
|
return new Response<T>(result, cacheEntry); |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* Returns a failed response containing the given error code and an optional |
||||||
|
* localized message displayed to the user. |
||||||
|
*/ |
||||||
|
public static <T> Response<T> error(VolleyError error) { |
||||||
|
return new Response<T>(error); |
||||||
|
} |
||||||
|
|
||||||
|
/** Parsed response, or null in the case of error. */ |
||||||
|
public final T result; |
||||||
|
|
||||||
|
/** Cache metadata for this response, or null in the case of error. */ |
||||||
|
public final Cache.Entry cacheEntry; |
||||||
|
|
||||||
|
/** Detailed error information if <code>errorCode != OK</code>. */ |
||||||
|
public final VolleyError error; |
||||||
|
|
||||||
|
/** True if this response was a soft-expired one and a second one MAY be coming. */ |
||||||
|
public boolean intermediate = false; |
||||||
|
|
||||||
|
/** |
||||||
|
* Returns whether this response is considered successful. |
||||||
|
*/ |
||||||
|
public boolean isSuccess() { |
||||||
|
return error == null; |
||||||
|
} |
||||||
|
|
||||||
|
|
||||||
|
private Response(T result, Cache.Entry cacheEntry) { |
||||||
|
this.result = result; |
||||||
|
this.cacheEntry = cacheEntry; |
||||||
|
this.error = null; |
||||||
|
} |
||||||
|
|
||||||
|
private Response(VolleyError error) { |
||||||
|
this.result = null; |
||||||
|
this.cacheEntry = null; |
||||||
|
this.error = error; |
||||||
|
} |
||||||
|
} |
@ -0,0 +1,35 @@ |
|||||||
|
/* |
||||||
|
* Copyright (C) 2011 The Android Open Source Project |
||||||
|
* |
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License"); |
||||||
|
* you may not use this file except in compliance with the License. |
||||||
|
* You may obtain a copy of the License at |
||||||
|
* |
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
* |
||||||
|
* Unless required by applicable law or agreed to in writing, software |
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS, |
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
||||||
|
* See the License for the specific language governing permissions and |
||||||
|
* limitations under the License. |
||||||
|
*/ |
||||||
|
|
||||||
|
package com.android.volley; |
||||||
|
|
||||||
|
public interface ResponseDelivery { |
||||||
|
/** |
||||||
|
* Parses a response from the network or cache and delivers it. |
||||||
|
*/ |
||||||
|
public void postResponse(Request<?> request, Response<?> response); |
||||||
|
|
||||||
|
/** |
||||||
|
* Parses a response from the network or cache and delivers it. The provided |
||||||
|
* Runnable will be executed after delivery. |
||||||
|
*/ |
||||||
|
public void postResponse(Request<?> request, Response<?> response, Runnable runnable); |
||||||
|
|
||||||
|
/** |
||||||
|
* Posts an error for the given request. |
||||||
|
*/ |
||||||
|
public void postError(Request<?> request, VolleyError error); |
||||||
|
} |
@ -0,0 +1,41 @@ |
|||||||
|
/* |
||||||
|
* Copyright (C) 2011 The Android Open Source Project |
||||||
|
* |
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License"); |
||||||
|
* you may not use this file except in compliance with the License. |
||||||
|
* You may obtain a copy of the License at |
||||||
|
* |
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
* |
||||||
|
* Unless required by applicable law or agreed to in writing, software |
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS, |
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
||||||
|
* See the License for the specific language governing permissions and |
||||||
|
* limitations under the License. |
||||||
|
*/ |
||||||
|
|
||||||
|
package com.android.volley; |
||||||
|
|
||||||
|
/** |
||||||
|
* Retry policy for a request. |
||||||
|
*/ |
||||||
|
public interface RetryPolicy { |
||||||
|
|
||||||
|
/** |
||||||
|
* Returns the current timeout (used for logging). |
||||||
|
*/ |
||||||
|
public int getCurrentTimeout(); |
||||||
|
|
||||||
|
/** |
||||||
|
* Returns the current retry count (used for logging). |
||||||
|
*/ |
||||||
|
public int getCurrentRetryCount(); |
||||||
|
|
||||||
|
/** |
||||||
|
* Prepares for the next retry by applying a backoff to the timeout. |
||||||
|
* @param error The error code of the last attempt. |
||||||
|
* @throws VolleyError In the event that the retry could not be performed (for example if we |
||||||
|
* ran out of attempts), the passed in error is thrown. |
||||||
|
*/ |
||||||
|
public void retry(VolleyError error) throws VolleyError; |
||||||
|
} |
@ -0,0 +1,32 @@ |
|||||||
|
/* |
||||||
|
* Copyright (C) 2011 The Android Open Source Project |
||||||
|
* |
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License"); |
||||||
|
* you may not use this file except in compliance with the License. |
||||||
|
* You may obtain a copy of the License at |
||||||
|
* |
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
* |
||||||
|
* Unless required by applicable law or agreed to in writing, software |
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS, |
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
||||||
|
* See the License for the specific language governing permissions and |
||||||
|
* limitations under the License. |
||||||
|
*/ |
||||||
|
|
||||||
|
package com.android.volley; |
||||||
|
|
||||||
|
|
||||||
|
/** |
||||||
|
* Indicates that the error responded with an error response. |
||||||
|
*/ |
||||||
|
@SuppressWarnings("serial") |
||||||
|
public class ServerError extends VolleyError { |
||||||
|
public ServerError(NetworkResponse networkResponse) { |
||||||
|
super(networkResponse); |
||||||
|
} |
||||||
|
|
||||||
|
public ServerError() { |
||||||
|
super(); |
||||||
|
} |
||||||
|
} |
@ -0,0 +1,23 @@ |
|||||||
|
/* |
||||||
|
* Copyright (C) 2011 The Android Open Source Project |
||||||
|
* |
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License"); |
||||||
|
* you may not use this file except in compliance with the License. |
||||||
|
* You may obtain a copy of the License at |
||||||
|
* |
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
* |
||||||
|
* Unless required by applicable law or agreed to in writing, software |
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS, |
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
||||||
|
* See the License for the specific language governing permissions and |
||||||
|
* limitations under the License. |
||||||
|
*/ |
||||||
|
|
||||||
|
package com.android.volley; |
||||||
|
|
||||||
|
/** |
||||||
|
* Indicates that the connection or the socket timed out. |
||||||
|
*/ |
||||||
|
@SuppressWarnings("serial") |
||||||
|
public class TimeoutError extends VolleyError { } |
@ -0,0 +1,48 @@ |
|||||||
|
/* |
||||||
|
* Copyright (C) 2011 The Android Open Source Project |
||||||
|
* |
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License"); |
||||||
|
* you may not use this file except in compliance with the License. |
||||||
|
* You may obtain a copy of the License at |
||||||
|
* |
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
* |
||||||
|
* Unless required by applicable law or agreed to in writing, software |
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS, |
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
||||||
|
* See the License for the specific language governing permissions and |
||||||
|
* limitations under the License. |
||||||
|
*/ |
||||||
|
|
||||||
|
package com.android.volley; |
||||||
|
|
||||||
|
/** |
||||||
|
* Exception style class encapsulating Volley errors |
||||||
|
*/ |
||||||
|
@SuppressWarnings("serial") |
||||||
|
public class VolleyError extends Exception { |
||||||
|
public final NetworkResponse networkResponse; |
||||||
|
|
||||||
|
public VolleyError() { |
||||||
|
networkResponse = null; |
||||||
|
} |
||||||
|
|
||||||
|
public VolleyError(NetworkResponse response) { |
||||||
|
networkResponse = response; |
||||||
|
} |
||||||
|
|
||||||
|
public VolleyError(String exceptionMessage) { |
||||||
|
super(exceptionMessage); |
||||||
|
networkResponse = null; |
||||||
|
} |
||||||
|
|
||||||
|
public VolleyError(String exceptionMessage, Throwable reason) { |
||||||
|
super(exceptionMessage, reason); |
||||||
|
networkResponse = null; |
||||||
|
} |
||||||
|
|
||||||
|
public VolleyError(Throwable cause) { |
||||||
|
super(cause); |
||||||
|
networkResponse = null; |
||||||
|
} |
||||||
|
} |
@ -0,0 +1,176 @@ |
|||||||
|
/* |
||||||
|
* Copyright (C) 2011 The Android Open Source Project |
||||||
|
* |
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License"); |
||||||
|
* you may not use this file except in compliance with the License. |
||||||
|
* You may obtain a copy of the License at |
||||||
|
* |
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
* |
||||||
|
* Unless required by applicable law or agreed to in writing, software |
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS, |
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
||||||
|
* See the License for the specific language governing permissions and |
||||||
|
* limitations under the License. |
||||||
|
*/ |
||||||
|
|
||||||
|
package com.android.volley; |
||||||
|
|
||||||
|
import java.util.ArrayList; |
||||||
|
import java.util.List; |
||||||
|
import java.util.Locale; |
||||||
|
|
||||||
|
import android.os.SystemClock; |
||||||
|
import android.util.Log; |
||||||
|
|
||||||
|
/** Logging helper class. */ |
||||||
|
public class VolleyLog { |
||||||
|
public static String TAG = "Volley"; |
||||||
|
|
||||||
|
public static boolean DEBUG = Log.isLoggable(TAG, Log.VERBOSE); |
||||||
|
|
||||||
|
/** |
||||||
|
* Customize the log tag for your application, so that other apps |
||||||
|
* using Volley don't mix their logs with yours. |
||||||
|
* <br /> |
||||||
|
* Enable the log property for your tag before starting your app: |
||||||
|
* <br /> |
||||||
|
* {@code adb shell setprop log.tag.<tag>} |
||||||
|
*/ |
||||||
|
public static void setTag(String tag) { |
||||||
|
d("Changing log tag to %s", tag); |
||||||
|
TAG = tag; |
||||||
|
|
||||||
|
// Reinitialize the DEBUG "constant"
|
||||||
|
DEBUG = Log.isLoggable(TAG, Log.VERBOSE); |
||||||
|
} |
||||||
|
|
||||||
|
public static void v(String format, Object... args) { |
||||||
|
if (DEBUG) { |
||||||
|
Log.v(TAG, buildMessage(format, args)); |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
public static void d(String format, Object... args) { |
||||||
|
Log.d(TAG, buildMessage(format, args)); |
||||||
|
} |
||||||
|
|
||||||
|
public static void e(String format, Object... args) { |
||||||
|
Log.e(TAG, buildMessage(format, args)); |
||||||
|
} |
||||||
|
|
||||||
|
public static void e(Throwable tr, String format, Object... args) { |
||||||
|
Log.e(TAG, buildMessage(format, args), tr); |
||||||
|
} |
||||||
|
|
||||||
|
public static void wtf(String format, Object... args) { |
||||||
|
Log.wtf(TAG, buildMessage(format, args)); |
||||||
|
} |
||||||
|
|
||||||
|
public static void wtf(Throwable tr, String format, Object... args) { |
||||||
|
Log.wtf(TAG, buildMessage(format, args), tr); |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* Formats the caller's provided message and prepends useful info like |
||||||
|
* calling thread ID and method name. |
||||||
|
*/ |
||||||
|
private static String buildMessage(String format, Object... args) { |
||||||
|
String msg = (args == null) ? format : String.format(Locale.US, format, args); |
||||||
|
StackTraceElement[] trace = new Throwable().fillInStackTrace().getStackTrace(); |
||||||
|
|
||||||
|
String caller = "<unknown>"; |
||||||
|
// Walk up the stack looking for the first caller outside of VolleyLog.
|
||||||
|
// It will be at least two frames up, so start there.
|
||||||
|
for (int i = 2; i < trace.length; i++) { |
||||||
|
Class<?> clazz = trace[i].getClass(); |
||||||
|
if (!clazz.equals(VolleyLog.class)) { |
||||||
|
String callingClass = trace[i].getClassName(); |
||||||
|
callingClass = callingClass.substring(callingClass.lastIndexOf('.') + 1); |
||||||
|
callingClass = callingClass.substring(callingClass.lastIndexOf('$') + 1); |
||||||
|
|
||||||
|
caller = callingClass + "." + trace[i].getMethodName(); |
||||||
|
break; |
||||||
|
} |
||||||
|
} |
||||||
|
return String.format(Locale.US, "[%d] %s: %s", |
||||||
|
Thread.currentThread().getId(), caller, msg); |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* A simple event log with records containing a name, thread ID, and timestamp. |
||||||
|
*/ |
||||||
|
static class MarkerLog { |
||||||
|
public static final boolean ENABLED = VolleyLog.DEBUG; |
||||||
|
|
||||||
|
/** Minimum duration from first marker to last in an marker log to warrant logging. */ |
||||||
|
private static final long MIN_DURATION_FOR_LOGGING_MS = 0; |
||||||
|
|
||||||
|
private static class Marker { |
||||||
|
public final String name; |
||||||
|
public final long thread; |
||||||
|
public final long time; |
||||||
|
|
||||||
|
public Marker(String name, long thread, long time) { |
||||||
|
this.name = name; |
||||||
|
this.thread = thread; |
||||||
|
this.time = time; |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
private final List<Marker> mMarkers = new ArrayList<Marker>(); |
||||||
|
private boolean mFinished = false; |
||||||
|
|
||||||
|
/** Adds a marker to this log with the specified name. */ |
||||||
|
public synchronized void add(String name, long threadId) { |
||||||
|
if (mFinished) { |
||||||
|
throw new IllegalStateException("Marker added to finished log"); |
||||||
|
} |
||||||
|
|
||||||
|
mMarkers.add(new Marker(name, threadId, SystemClock.elapsedRealtime())); |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* Closes the log, dumping it to logcat if the time difference between |
||||||
|
* the first and last markers is greater than {@link #MIN_DURATION_FOR_LOGGING_MS}. |
||||||
|
* @param header Header string to print above the marker log. |
||||||
|
*/ |
||||||
|
public synchronized void finish(String header) { |
||||||
|
mFinished = true; |
||||||
|
|
||||||
|
long duration = getTotalDuration(); |
||||||
|
if (duration <= MIN_DURATION_FOR_LOGGING_MS) { |
||||||
|
return; |
||||||
|
} |
||||||
|
|
||||||
|
long prevTime = mMarkers.get(0).time; |
||||||
|
d("(%-4d ms) %s", duration, header); |
||||||
|
for (Marker marker : mMarkers) { |
||||||
|
long thisTime = marker.time; |
||||||
|
d("(+%-4d) [%2d] %s", (thisTime - prevTime), marker.thread, marker.name); |
||||||
|
prevTime = thisTime; |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
@Override |
||||||
|
protected void finalize() throws Throwable { |
||||||
|
// Catch requests that have been collected (and hence end-of-lifed)
|
||||||
|
// but had no debugging output printed for them.
|
||||||
|
if (!mFinished) { |
||||||
|
finish("Request on the loose"); |
||||||
|
e("Marker log finalized without finish() - uncaught exit point for request"); |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
/** Returns the time difference between the first and last events in this log. */ |
||||||
|
private long getTotalDuration() { |
||||||
|
if (mMarkers.size() == 0) { |
||||||
|
return 0; |
||||||
|
} |
||||||
|
|
||||||
|
long first = mMarkers.get(0).time; |
||||||
|
long last = mMarkers.get(mMarkers.size() - 1).time; |
||||||
|
return last - first; |
||||||
|
} |
||||||
|
} |
||||||
|
} |
@ -0,0 +1,27 @@ |
|||||||
|
package com.android.volley.extra; |
||||||
|
|
||||||
|
import android.graphics.Bitmap; |
||||||
|
import android.util.LruCache; |
||||||
|
|
||||||
|
import com.android.volley.toolbox.ImageLoader.ImageCache; |
||||||
|
|
||||||
|
public class BitmapLruImageCache extends LruCache<String, Bitmap> implements ImageCache { |
||||||
|
public BitmapLruImageCache(int maxSize) { |
||||||
|
super(maxSize); |
||||||
|
} |
||||||
|
|
||||||
|
@Override |
||||||
|
protected int sizeOf(String key, Bitmap value) { |
||||||
|
return value.getRowBytes() * value.getHeight(); |
||||||
|
} |
||||||
|
|
||||||
|
@Override |
||||||
|
public Bitmap getBitmap(String url) { |
||||||
|
return get(url); |
||||||
|
} |
||||||
|
|
||||||
|
@Override |
||||||
|
public void putBitmap(String url, Bitmap bitmap) { |
||||||
|
put(url, bitmap); |
||||||
|
} |
||||||
|
} |
@ -0,0 +1,74 @@ |
|||||||
|
package com.android.volley.extra; |
||||||
|
|
||||||
|
import java.io.ByteArrayInputStream; |
||||||
|
import java.io.IOException; |
||||||
|
import java.io.InputStreamReader; |
||||||
|
import java.io.UnsupportedEncodingException; |
||||||
|
|
||||||
|
import android.util.JsonReader; |
||||||
|
|
||||||
|
import com.android.volley.NetworkResponse; |
||||||
|
import com.android.volley.ParseError; |
||||||
|
import com.android.volley.Request; |
||||||
|
import com.android.volley.Response; |
||||||
|
import com.android.volley.Response.ErrorListener; |
||||||
|
import com.android.volley.Response.Listener; |
||||||
|
import com.android.volley.VolleyError; |
||||||
|
import com.android.volley.toolbox.HttpHeaderParser; |
||||||
|
|
||||||
|
public abstract class JsonReaderRequest<T> extends Request<T> { |
||||||
|
protected final Listener<T> listener; |
||||||
|
private VolleyError error; |
||||||
|
|
||||||
|
public JsonReaderRequest(String url, Listener<T> listener, ErrorListener errorListener) { |
||||||
|
super(Method.GET, url, errorListener); |
||||||
|
|
||||||
|
this.listener = listener; |
||||||
|
} |
||||||
|
|
||||||
|
@Override |
||||||
|
protected void deliverResponse(T response) { |
||||||
|
listener.onResponse(response); |
||||||
|
} |
||||||
|
|
||||||
|
public void setError(VolleyError error) { |
||||||
|
this.error = error; |
||||||
|
} |
||||||
|
|
||||||
|
@Override |
||||||
|
protected Response<T> parseNetworkResponse(NetworkResponse response) { |
||||||
|
try { |
||||||
|
ByteArrayInputStream baos = new ByteArrayInputStream(response.data); |
||||||
|
|
||||||
|
JsonReader reader = new JsonReader(new InputStreamReader(baos, "UTF-8")); |
||||||
|
|
||||||
|
// long start = System.currentTimeMillis();
|
||||||
|
|
||||||
|
T read = readJson(reader); |
||||||
|
|
||||||
|
try { |
||||||
|
reader.close(); |
||||||
|
} catch (IOException e) { |
||||||
|
e.printStackTrace(); |
||||||
|
} |
||||||
|
|
||||||
|
// Log.e("Chan", "Total time: " + (System.currentTimeMillis() - start));
|
||||||
|
|
||||||
|
if (read == null) { |
||||||
|
return Response.error(new VolleyError()); |
||||||
|
} else if (error != null) { |
||||||
|
return Response.error(error); |
||||||
|
} else { |
||||||
|
return Response.success(read, HttpHeaderParser.parseCacheHeaders(response)); |
||||||
|
} |
||||||
|
} catch (UnsupportedEncodingException e) { |
||||||
|
return Response.error(new ParseError(e)); |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
public abstract T readJson(JsonReader reader); |
||||||
|
} |
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
@ -0,0 +1,101 @@ |
|||||||
|
/* |
||||||
|
* Copyright (C) 2011 The Android Open Source Project |
||||||
|
* |
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License"); |
||||||
|
* you may not use this file except in compliance with the License. |
||||||
|
* You may obtain a copy of the License at |
||||||
|
* |
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
* |
||||||
|
* Unless required by applicable law or agreed to in writing, software |
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS, |
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
||||||
|
* See the License for the specific language governing permissions and |
||||||
|
* limitations under the License. |
||||||
|
*/ |
||||||
|
|
||||||
|
package com.android.volley.toolbox; |
||||||
|
|
||||||
|
import android.accounts.Account; |
||||||
|
import android.accounts.AccountManager; |
||||||
|
import android.accounts.AccountManagerFuture; |
||||||
|
import android.content.Context; |
||||||
|
import android.content.Intent; |
||||||
|
import android.os.Bundle; |
||||||
|
|
||||||
|
import com.android.volley.AuthFailureError; |
||||||
|
|
||||||
|
/** |
||||||
|
* An Authenticator that uses {@link AccountManager} to get auth |
||||||
|
* tokens of a specified type for a specified account. |
||||||
|
*/ |
||||||
|
public class AndroidAuthenticator implements Authenticator { |
||||||
|
private final Context mContext; |
||||||
|
private final Account mAccount; |
||||||
|
private final String mAuthTokenType; |
||||||
|
private final boolean mNotifyAuthFailure; |
||||||
|
|
||||||
|
/** |
||||||
|
* Creates a new authenticator. |
||||||
|
* @param context Context for accessing AccountManager |
||||||
|
* @param account Account to authenticate as |
||||||
|
* @param authTokenType Auth token type passed to AccountManager |
||||||
|
*/ |
||||||
|
public AndroidAuthenticator(Context context, Account account, String authTokenType) { |
||||||
|
this(context, account, authTokenType, false); |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* Creates a new authenticator. |
||||||
|
* @param context Context for accessing AccountManager |
||||||
|
* @param account Account to authenticate as |
||||||
|
* @param authTokenType Auth token type passed to AccountManager |
||||||
|
* @param notifyAuthFailure Whether to raise a notification upon auth failure |
||||||
|
*/ |
||||||
|
public AndroidAuthenticator(Context context, Account account, String authTokenType, |
||||||
|
boolean notifyAuthFailure) { |
||||||
|
mContext = context; |
||||||
|
mAccount = account; |
||||||
|
mAuthTokenType = authTokenType; |
||||||
|
mNotifyAuthFailure = notifyAuthFailure; |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* Returns the Account being used by this authenticator. |
||||||
|
*/ |
||||||
|
public Account getAccount() { |
||||||
|
return mAccount; |
||||||
|
} |
||||||
|
|
||||||
|
@Override |
||||||
|
public String getAuthToken() throws AuthFailureError { |
||||||
|
final AccountManager accountManager = AccountManager.get(mContext); |
||||||
|
@SuppressWarnings("deprecation") |
||||||
|
AccountManagerFuture<Bundle> future = accountManager.getAuthToken(mAccount, |
||||||
|
mAuthTokenType, mNotifyAuthFailure, null, null); |
||||||
|
Bundle result; |
||||||
|
try { |
||||||
|
result = future.getResult(); |
||||||
|
} catch (Exception e) { |
||||||
|
throw new AuthFailureError("Error while retrieving auth token", e); |
||||||
|
} |
||||||
|
String authToken = null; |
||||||
|
if (future.isDone() && !future.isCancelled()) { |
||||||
|
if (result.containsKey(AccountManager.KEY_INTENT)) { |
||||||
|
Intent intent = result.getParcelable(AccountManager.KEY_INTENT); |
||||||
|
throw new AuthFailureError(intent); |
||||||
|
} |
||||||
|
authToken = result.getString(AccountManager.KEY_AUTHTOKEN); |
||||||
|
} |
||||||
|
if (authToken == null) { |
||||||
|
throw new AuthFailureError("Got null auth token for type: " + mAuthTokenType); |
||||||
|
} |
||||||
|
|
||||||
|
return authToken; |
||||||
|
} |
||||||
|
|
||||||
|
@Override |
||||||
|
public void invalidateAuthToken(String authToken) { |
||||||
|
AccountManager.get(mContext).invalidateAuthToken(mAccount.type, authToken); |
||||||
|
} |
||||||
|
} |