diff --git a/AndroidManifest.xml b/AndroidManifest.xml index c45c59817..ec0cedd42 100644 --- a/AndroidManifest.xml +++ b/AndroidManifest.xml @@ -105,9 +105,6 @@ - - diff --git a/build.gradle b/build.gradle index 02feaea42..75d26497f 100644 --- a/build.gradle +++ b/build.gradle @@ -45,14 +45,14 @@ repositories { } dependencies { - compile 'com.android.support:appcompat-v7:25.4.0' - compile 'com.android.support:recyclerview-v7:25.4.0' - compile 'com.android.support:design:25.4.0' - compile 'com.android.support:support-v13:25.4.0' - compile 'com.android.support:cardview-v7:25.4.0' - compile 'com.android.support:preference-v7:25.4.0' - compile 'com.android.support:preference-v14:25.4.0' - compile 'com.android.support:gridlayout-v7:25.4.0' + compile 'com.android.support:appcompat-v7:26.1.0' + compile 'com.android.support:recyclerview-v7:26.1.0' + compile 'com.android.support:design:26.1.0' + compile 'com.android.support:support-v13:26.1.0' + compile 'com.android.support:cardview-v7:26.1.0' + compile 'com.android.support:preference-v7:26.1.0' + compile 'com.android.support:preference-v14:26.1.0' + compile 'com.android.support:gridlayout-v7:26.1.0' compile 'com.android.support:multidex:1.0.2' compile 'com.google.android.gms:play-services-gcm:9.6.1' @@ -70,8 +70,9 @@ dependencies { compile 'se.emilsjolander:stickylistheaders:2.7.0' compile 'com.jpardogo.materialtabstrip:library:1.0.9' compile 'org.apache.httpcomponents:httpclient-android:4.3.5' - compile 'com.github.chrisbanes.photoview:library:1.3.1' - compile 'com.github.bumptech.glide:glide:3.7.0' + compile 'com.github.chrisbanes:PhotoView:2.1.3' + compile 'com.github.bumptech.glide:glide:4.2.0' + annotationProcessor 'com.github.bumptech.glide:compiler:4.2.0' compile 'com.makeramen:roundedimageview:2.1.0' compile 'com.pnikosis:materialish-progress:1.5' compile 'org.greenrobot:eventbus:3.0.0' @@ -131,14 +132,14 @@ dependencies { dependencyVerification { verify = [ - 'com.android.support:appcompat-v7:70551e62660db15b790c5275f56b9de4dd9407d1494d07c8f3dd5698f3638677', - 'com.android.support:recyclerview-v7:a2fe121f9d01ed8980e97095b4a3fe9700a0aa0a7d4b0f8c594f765ad8455a0d', - 'com.android.support:design:3f409bf2019967ffc344cfaf11e52131fac982468a1707aaeb25bf3c52838966', - 'com.android.support:support-v13:f2dcf3eb3fe0271038dc78f6cff9cc3256a591f5c5198277b3887480e5b95e79', - 'com.android.support:cardview-v7:f3fbbe1fcfdbec7333c6a2c516c5fd511a909d1975271818e268d6fe297d8c70', - 'com.android.support:preference-v7:69bfa8e5527585dc51c02393b882c6b7e1e682995d5fbe45f57b7cb4e6970a7b', - 'com.android.support:preference-v14:aac6e6cb89b70e27859d88cf75410a5feab1c7a9364d420de42ddb7c154a866b', - 'com.android.support:gridlayout-v7:4c805b95e5b0a39c7244a0d1a14449cc54a9ab242b806f7379b38846f0539ce9', + 'com.android.support:appcompat-v7:9d44e7bf343dfd19a55e3e6f4c4e733b68d32509e0b0af5b32f2981f4f1dedd8', + 'com.android.support:recyclerview-v7:389cb47a7dabca4fb8c23657ff7c85ebc651428580d3a5ea0349eeb43ddea94b', + 'com.android.support:design:76f5fbb365bf2d622af5df8a4205904409250305685e38670bf654ac90c2494d', + 'com.android.support:support-v13:fc7ba35b0502a6168b350342779c6943100ace19cd6dd573707bddfa8e9e78a2', + 'com.android.support:cardview-v7:7ea56ed5560b629ee1c0f24af6693e32974fbc8b91b544052cd2c14b176c85e0', + 'com.android.support:preference-v7:42672e51c06c6e26a40798d3379ede97ee42076c84592d670c4e5c96630c50f1', + 'com.android.support:preference-v14:f340c88589184fd53ad46aebbba1ae5b88b5919f92ed085bf0f687a58d0e0e17', + 'com.android.support:gridlayout-v7:6fe57dd164f2e1d99ad650a56f686ddecd02bfbfabbfbd451e81a23eada5e564', 'com.android.support:multidex:7cd48755c7cfdb6dd2d21cbb02236ec390f6ac91cde87eb62f475b259ab5301d', 'com.google.android.gms:play-services-gcm:312e61253a236f2d9b750b9c04fc92fd190d23b0b2755c99de6ce4a28b259dae', 'com.google.android.gms:play-services-maps:45e8021e7ddac4a44a82a0e9698991389ded3023d35c58f38dbd86d54211ec0e', @@ -152,8 +153,8 @@ dependencyVerification { 'se.emilsjolander:stickylistheaders:a08ca948aa6b220f09d82f16bbbac395f6b78897e9eeac6a9f0b0ba755928eeb', 'com.jpardogo.materialtabstrip:library:c6ef812fba4f74be7dc4a905faa4c2908cba261a94c13d4f96d5e67e4aad4aaa', 'org.apache.httpcomponents:httpclient-android:6f56466a9bd0d42934b90bfbfe9977a8b654c058bf44a12bdc2877c4e1f033f1', - 'com.github.chrisbanes.photoview:library:f152dd0a87aca891aa182e42863fa05e0e8b2842e3b9fc512d7a3a6243c38ac4', - 'com.github.bumptech.glide:glide:76ef123957b5fbaebb05fcbe6606dd58c3bc3fcdadb257f99811d0ac9ea9b88b', + 'com.github.chrisbanes:PhotoView:ed06775308da260e1fd86d1d3288988fcd3d80db24ce0d7c9fcfedc39e622292', + 'com.github.bumptech.glide:glide:555350c4b9d163f1d3772a64a92119086073ed88340eb284391b1acc1bb5dd6c', 'com.makeramen:roundedimageview:1f5a1865796b308c6cdd114acc6e78408b110f0a62fc63553278fbeacd489cd1', 'com.pnikosis:materialish-progress:d71d80e00717a096784482aee21001a9d299fec3833e4ebd87739ed36cf77c54', 'org.greenrobot:eventbus:180d4212467df06f2fbc9c8d8a2984533ac79c87769ad883bc421612f0b4e17c', @@ -173,22 +174,26 @@ dependencyVerification { 'com.takisoft.fix:colorpicker:f5d0dbabe406a1800498ca9c1faf34db36e021d8488bf10360f29961fe3ab0d1', 'com.codewaves.stickyheadergrid:stickyheadergrid:5b4aa6a52a957cfd55f60f4220c11c0c371385a3cb9786cae03c260dcdef5794', 'com.github.dmytrodanylyk.circular-progress-button:library:635882453475181d737719b2adef658d80f9f85c9bdaf022f06cd22f387cdb16', - 'com.android.support:support-annotations:a774272036941b4e912eb426d70c848bde7f06a3bf5fb491f75a427dc6595270', - 'com.android.support:support-v4:ee44c481a1f4d6978568e223e8125379b52b2ececdd53450e09ebae144bd377d', - 'com.android.support:support-vector-drawable:077009d13882ee96f061e4bc2dbe7cce7ae1762d8297592a787ff741afbfb1f2', - 'com.android.support:animated-vector-drawable:628ab1d56a6ee4cbedf32617af8b2a1fe02964ed0628e8f898cc09ddba6e1835', - 'com.android.support:support-compat:54019c63614ce08b02d7b9605490cd2b29ba5b2505f394a9517450b5f72b30ca', - 'com.android.support:support-core-ui:e72ae29b823889686cff6fcb948d6745c2baf6d4c2af4fdffa1ec1e42e3833a3', - 'com.android.support:transition:848270144fb180efd2bf928a00ed176dbbc5290badfd638390ffba90088df8b3', + 'com.android.support:support-annotations:99d6199ad5a09a0e5e8a49a4cc08f818483ddcfd7eedea2f9923412daf982309', + 'com.android.support:support-v4:36d8385de1be7791231acb933b757198f97cb53bc7d046e8c4bc403d214caaca', + 'com.android.support:support-vector-drawable:1151b7f0ea29c9a9a8fee042a1dbe82f196632d801c438d08b279e131c767118', + 'com.android.support:animated-vector-drawable:d5905aee3c8a4ac75e069a73b914c0a41b9b36b0e6b04126719fca22659d3cc8', + 'com.android.support:support-compat:7d6da01cf9766b1705c6c80cfc12274a895b406c4c287900b07a56145ca6c030', + 'com.android.support:support-core-ui:82f538051599335ea881ec264407547cab52be750f16ce099cfb27754fc755ff', + 'com.android.support:transition:c5d3d1204997f80af1f4a3a315a54b1a23543c554963cef831da726aac34b56f', 'com.google.android.gms:play-services-base:0ca636a8fc9a5af45e607cdcd61783bf5d561cbbb0f862021ce69606eee5ad49', 'com.google.android.gms:play-services-basement:95dd882c5ffba15b9a99de3fefb05d3a01946623af67454ca00055d222f85a8d', 'com.google.android.gms:play-services-iid:54e919f9957b8b7820da7ee9b83471d00d0cac1cf08ddea8b5b41aea80bb1a70', 'org.whispersystems:signal-protocol-android:5b8acded7f2a40178eb90ab8e8cbfec89d170d91b3ff5e78487d1098df6185a1', 'org.whispersystems:signal-service-java:308d9e61b753760d0f3828eb3181db58469e75c763bdce5a8335df6c4af47695', + 'com.github.bumptech.glide:gifdecoder:217da4520c568a93aea9c7ce3b3cac2c61fabed5113b07ae38698054f6d2d8b6', + 'com.github.bumptech.glide:disklrucache:795c13245498c0cd806c3af71ee57b3f179cbd1609440a3021c211c364ef74d3', + 'com.github.bumptech.glide:annotations:057927a236f3229e72cfbac8bed0e9fb398473daf7d933390f59ea4cb79c137b', 'com.nineoldandroids:library:68025a14e3e7673d6ad2f95e4b46d78d7d068343aa99256b686fe59de1b3163a', 'javax.inject:javax.inject:91c77044a50c481636c32d916fd89c9118a72195390452c81065080f957de7ff', 'com.klinkerapps:logger:177e325259a8b111ad6745ec10db5861723c99f402222b80629f576f49408541', 'com.google.android:flexbox:a9989fd13ae2ee42765dfc515fe362edf4f326e74925d02a10369df8092a4935', + 'android.arch.lifecycle:runtime:e4e34e5d02bd102e8d39ddbc29f9ead8a15a61e367993d02238196ac48509ad8', 'com.google.android.gms:play-services-tasks:69ec265168e601d0203d04cd42e34bb019b2f029aa1e16fabd38a5153eea2086', 'org.whispersystems:curve25519-android:82595394422b957d4a5b5f1b27b75ba25cf6dc4db4d312418ca38cd6fff279ca', 'org.whispersystems:signal-protocol-java:5152c2b01a25147967d6bf82e540f947901bdfa79260be3eb3e96b03f787d6b5', @@ -197,19 +202,21 @@ dependencyVerification { 'com.fasterxml.jackson.core:jackson-databind:835097bcdd11f5bc8a08378c70d4c8054dfa4b911691cc2752063c75534d198d', 'com.squareup.okhttp3:okhttp:c1d57f913f74f61d424d4250a92723ba9a61affc12a0ab194d84cc179b472841', 'com.madgag.spongycastle:prov:b8c3fec3a59aac1aa04ccf4dad7179351e54ef7672f53f508151b614c131398a', + 'android.arch.lifecycle:common:86bf301a20ad0cd0a391e22a52e6fbf90575c096ff83233fa9fd0d52b3219121', + 'android.arch.core:common:5192934cd73df32e2c15722ed7fc488dde90baaec9ae030010dd1a80fb4e74e1', 'org.whispersystems:curve25519-java:7dd659d8822c06c3aea1a47f18fac9e5761e29cab8100030b877db445005f03e', 'com.fasterxml.jackson.core:jackson-annotations:0ca408c24202a7626ec8b861e99d85eca5e38b73311dd6dd12e3e9deecc3fe94', 'com.fasterxml.jackson.core:jackson-core:cbf4604784b4de226262845447a1ad3bb38a6728cebe86562e2c5afada8be2c0', 'com.squareup.okio:okio:734269c3ebc5090e3b23566db558f421f0b4027277c79ad5d176b8ec168bb850', 'com.madgag.spongycastle:core:8d6240b974b0aca4d3da9c7dd44d42339d8a374358aca5fc98e50a995764511f', - 'com.android.support:support-media-compat:566a161d9cb0083ef62a53e46b71ce5b3d455b8635b1a0a4ae28d96d4b583de8', - 'com.android.support:support-core-utils:34b8437dfa95ff28d29cf57ffa3b1354a9fa9bfe4059f0fd5ce2f5e4326a1748', - 'com.android.support:support-fragment:316d35d4d2d2902057efad104a73e4bdb50bee260a7075678185b8cd71170945', + 'com.android.support:support-media-compat:9d8cee7cd40eff22ebdeb90c8e70f5ee96c5bd25cb2c3e3b3940e27285a3e98a', + 'com.android.support:support-core-utils:4fda6d4eb430971e3b1dad7456988333f374b0f4ba15f99839ca1a0ab5155c8a', + 'com.android.support:support-fragment:a0ab3369ef40fe199160692f0463a5f63f1277ebfb64dd587c76fdb128d76b32', ] } android { - compileSdkVersion 25 + compileSdkVersion 26 buildToolsVersion '25.0.2' useLibrary 'org.apache.http.legacy' diff --git a/gradle/wrapper/gradle-wrapper.jar b/gradle/wrapper/gradle-wrapper.jar index 30d399d8d..fbd0c46ec 100644 Binary files a/gradle/wrapper/gradle-wrapper.jar and b/gradle/wrapper/gradle-wrapper.jar differ diff --git a/gradle/wrapper/gradle-wrapper.properties b/gradle/wrapper/gradle-wrapper.properties index ee02693a8..bd7127d9e 100644 --- a/gradle/wrapper/gradle-wrapper.properties +++ b/gradle/wrapper/gradle-wrapper.properties @@ -1,6 +1,6 @@ -#Thu Mar 09 11:46:40 PST 2017 +#Wed Oct 11 12:30:44 PDT 2017 distributionBase=GRADLE_USER_HOME distributionPath=wrapper/dists zipStoreBase=GRADLE_USER_HOME zipStorePath=wrapper/dists -distributionUrl=https\://services.gradle.org/distributions/gradle-3.3-all.zip +distributionUrl=https\://services.gradle.org/distributions/gradle-3.5.1-all.zip diff --git a/gradlew b/gradlew index 91a7e269e..4453ccea3 100755 --- a/gradlew +++ b/gradlew @@ -1,4 +1,4 @@ -#!/usr/bin/env bash +#!/usr/bin/env sh ############################################################################## ## @@ -6,12 +6,30 @@ ## ############################################################################## -# Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. -DEFAULT_JVM_OPTS="" +# 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\"`/" >/dev/null +APP_HOME="`pwd -P`" +cd "$SAVED" >/dev/null APP_NAME="Gradle" APP_BASE_NAME=`basename "$0"` +# Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. +DEFAULT_JVM_OPTS="" + # Use the maximum available, or set MAX_FD != -1 to use that value. MAX_FD="maximum" @@ -30,6 +48,7 @@ die ( ) { cygwin=false msys=false darwin=false +nonstop=false case "`uname`" in CYGWIN* ) cygwin=true @@ -40,31 +59,11 @@ case "`uname`" in MINGW* ) msys=true ;; + NONSTOP* ) + nonstop=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. @@ -90,7 +89,7 @@ location of your Java installation." fi # Increase the maximum file descriptors if we can. -if [ "$cygwin" = "false" -a "$darwin" = "false" ] ; then +if [ "$cygwin" = "false" -a "$darwin" = "false" -a "$nonstop" = "false" ] ; then MAX_FD_LIMIT=`ulimit -H -n` if [ $? -eq 0 ] ; then if [ "$MAX_FD" = "maximum" -o "$MAX_FD" = "max" ] ; then @@ -114,6 +113,7 @@ fi if $cygwin ; then APP_HOME=`cygpath --path --mixed "$APP_HOME"` CLASSPATH=`cygpath --path --mixed "$CLASSPATH"` + JAVACMD=`cygpath --unix "$JAVACMD"` # We build the pattern for arguments to be converted via cygpath ROOTDIRSRAW=`find -L / -maxdepth 1 -mindepth 1 -type d 2>/dev/null` @@ -154,11 +154,19 @@ if $cygwin ; then 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=("$@") +# Escape application args +save ( ) { + for i do printf %s\\n "$i" | sed "s/'/'\\\\''/g;1s/^/'/;\$s/\$/' \\\\/" ; done + echo " " } -eval splitJvmOpts $DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS -JVM_OPTS[${#JVM_OPTS[*]}]="-Dorg.gradle.appname=$APP_BASE_NAME" +APP_ARGS=$(save "$@") -exec "$JAVACMD" "${JVM_OPTS[@]}" -classpath "$CLASSPATH" org.gradle.wrapper.GradleWrapperMain "$@" +# Collect all arguments for the java command, following the shell quoting and substitution rules +eval set -- $DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS "\"-Dorg.gradle.appname=$APP_BASE_NAME\"" -classpath "\"$CLASSPATH\"" org.gradle.wrapper.GradleWrapperMain "$APP_ARGS" + +# by default we should be in the correct project dir, but when run from Finder on Mac, the cwd is wrong +if [ "$(uname)" = "Darwin" ] && [ "$HOME" = "$PWD" ]; then + cd "$(dirname "$0")" +fi + +exec "$JAVACMD" "$@" diff --git a/gradlew.bat b/gradlew.bat index aec99730b..e95643d6a 100644 --- a/gradlew.bat +++ b/gradlew.bat @@ -8,14 +8,14 @@ @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 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= + @rem Find java.exe if defined JAVA_HOME goto findJavaFromJavaHome @@ -46,10 +46,9 @@ echo location of your Java installation. goto fail :init -@rem Get command-line arguments, handling Windowz variants +@rem Get command-line arguments, handling Windows 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. @@ -60,11 +59,6 @@ set _SKIP=2 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 diff --git a/proguard-glide.pro b/proguard-glide.pro index 0588677f3..a5a3efcc1 100644 --- a/proguard-glide.pro +++ b/proguard-glide.pro @@ -1,3 +1,5 @@ +-keep public class * implements com.bumptech.glide.module.GlideModule +-keep public class * extends com.bumptech.glide.AppGlideModule -keep public enum com.bumptech.glide.load.resource.bitmap.ImageHeaderParser$** { **[] $VALUES; public *; diff --git a/res/layout/zooming_image_view.xml b/res/layout/zooming_image_view.xml index 6ad6c4dc5..da1c60519 100644 --- a/res/layout/zooming_image_view.xml +++ b/res/layout/zooming_image_view.xml @@ -3,10 +3,10 @@ xmlns:tools="http://schemas.android.com/tools" tools:context="org.thoughtcrime.securesms.components.ZoomingImageView"> - + () { - @Override - public void onResourceReady(Bitmap resource, GlideAnimation glideAnimation) { - setAvatar(Crop.getOutput(data), resource); - } - }); + GlideApp.with(this) + .asBitmap() + .load(Crop.getOutput(data)) + .skipMemoryCache(true) + .diskCacheStrategy(DiskCacheStrategy.NONE) + .centerCrop() + .override(AVATAR_SIZE, AVATAR_SIZE) + .into(new SimpleTarget() { + @Override + public void onResourceReady(Bitmap resource, Transition transition) { + setAvatar(Crop.getOutput(data), resource); + } + }); } } @@ -573,12 +577,12 @@ public class GroupCreateActivity extends PassphraseRequiredActionBarActivity private void setAvatar(T model, Bitmap bitmap) { avatarBmp = bitmap; - Glide.with(this) - .load(model) - .skipMemoryCache(true) - .diskCacheStrategy(DiskCacheStrategy.NONE) - .transform(new RoundedCorners(this, avatar.getWidth() / 2)) - .into(avatar); + GlideApp.with(this) + .load(model) + .circleCrop() + .skipMemoryCache(true) + .diskCacheStrategy(DiskCacheStrategy.NONE) + .into(avatar); } private static class GroupData { diff --git a/src/org/thoughtcrime/securesms/LogSubmitActivity.java b/src/org/thoughtcrime/securesms/LogSubmitActivity.java index 1455a274d..e7d1a4d7d 100644 --- a/src/org/thoughtcrime/securesms/LogSubmitActivity.java +++ b/src/org/thoughtcrime/securesms/LogSubmitActivity.java @@ -4,7 +4,6 @@ import android.content.ActivityNotFoundException; import android.content.Intent; import android.os.Bundle; import android.support.v4.app.FragmentTransaction; -import android.support.v7.app.ActionBarActivity; import android.util.Log; import android.view.MenuItem; import android.widget.Toast; diff --git a/src/org/thoughtcrime/securesms/components/RecentPhotoViewRail.java b/src/org/thoughtcrime/securesms/components/RecentPhotoViewRail.java index 7cfb44fd8..cca2462ec 100644 --- a/src/org/thoughtcrime/securesms/components/RecentPhotoViewRail.java +++ b/src/org/thoughtcrime/securesms/components/RecentPhotoViewRail.java @@ -20,7 +20,6 @@ import android.view.ViewGroup; import android.widget.FrameLayout; import android.widget.ImageView; -import com.bumptech.glide.Glide; import com.bumptech.glide.load.Key; import com.bumptech.glide.load.engine.DiskCacheStrategy; import com.bumptech.glide.signature.MediaStoreSignature; @@ -28,6 +27,7 @@ import com.bumptech.glide.signature.MediaStoreSignature; import org.thoughtcrime.securesms.R; import org.thoughtcrime.securesms.database.CursorRecyclerViewAdapter; import org.thoughtcrime.securesms.database.loaders.RecentPhotosLoader; +import org.thoughtcrime.securesms.mms.GlideApp; import org.thoughtcrime.securesms.util.ViewUtil; public class RecentPhotoViewRail extends FrameLayout implements LoaderManager.LoaderCallbacks { @@ -111,12 +111,11 @@ public class RecentPhotoViewRail extends FrameLayout implements LoaderManager.Lo Key signature = new MediaStoreSignature(mimeType, dateModified, orientation); - Glide.with(getContext()) - .fromMediaStore() - .load(uri) - .signature(signature) - .diskCacheStrategy(DiskCacheStrategy.NONE) - .into(viewHolder.imageView); + GlideApp.with(getContext()) + .load(uri) + .signature(signature) + .diskCacheStrategy(DiskCacheStrategy.NONE) + .into(viewHolder.imageView); viewHolder.imageView.setOnClickListener(new OnClickListener() { @Override diff --git a/src/org/thoughtcrime/securesms/components/ThumbnailView.java b/src/org/thoughtcrime/securesms/components/ThumbnailView.java index 54c926162..dacf71af4 100644 --- a/src/org/thoughtcrime/securesms/components/ThumbnailView.java +++ b/src/org/thoughtcrime/securesms/components/ThumbnailView.java @@ -15,22 +15,25 @@ import android.view.View; import android.widget.FrameLayout; import android.widget.ImageView; -import com.bumptech.glide.DrawableRequestBuilder; -import com.bumptech.glide.GenericRequestBuilder; import com.bumptech.glide.Glide; +import com.bumptech.glide.RequestBuilder; import com.bumptech.glide.load.engine.DiskCacheStrategy; +import com.bumptech.glide.load.resource.bitmap.RoundedCorners; +import com.bumptech.glide.request.RequestOptions; import org.thoughtcrime.securesms.R; import org.thoughtcrime.securesms.crypto.MasterSecret; import org.thoughtcrime.securesms.database.AttachmentDatabase; import org.thoughtcrime.securesms.mms.DecryptableStreamUriLoader.DecryptableUri; -import org.thoughtcrime.securesms.mms.RoundedCorners; +import org.thoughtcrime.securesms.mms.GlideApp; import org.thoughtcrime.securesms.mms.Slide; import org.thoughtcrime.securesms.mms.SlideClickListener; import org.thoughtcrime.securesms.util.Util; import org.thoughtcrime.securesms.util.ViewUtil; import org.whispersystems.libsignal.util.guava.Optional; +import static com.bumptech.glide.load.resource.drawable.DrawableTransitionOptions.withCrossFade; + public class ThumbnailView extends FrameLayout { private static final String TAG = ThumbnailView.class.getSimpleName(); @@ -141,18 +144,18 @@ public class ThumbnailView extends FrameLayout { if (slide.getThumbnailUri() != null) buildThumbnailGlideRequest(slide, masterSecret).into(image); else if (slide.hasPlaceholder()) buildPlaceholderGlideRequest(slide).into(image); - else Glide.clear(image); + else Glide.with(getContext()).clear(image); } public void setImageResource(@NonNull MasterSecret masterSecret, @NonNull Uri uri) { if (transferControls.isPresent()) getTransferControls().setVisibility(View.GONE); - Glide.with(getContext()) - .load(new DecryptableUri(masterSecret, uri)) - .diskCacheStrategy(DiskCacheStrategy.NONE) - .crossFade() - .transform(new RoundedCorners(getContext(), true, radius, backgroundColorHint)) - .into(image); + GlideApp.with(getContext()) + .load(new DecryptableUri(masterSecret, uri)) + .diskCacheStrategy(DiskCacheStrategy.NONE) + .transform(new RoundedCorners(radius)) + .transition(withCrossFade()) + .into(image); } public void setThumbnailClickListener(SlideClickListener listener) { @@ -164,7 +167,7 @@ public class ThumbnailView extends FrameLayout { } public void clear() { - if (isContextValid()) Glide.clear(image); + if (isContextValid()) Glide.with(getContext()).clear(image); if (transferControls.isPresent()) getTransferControls().clear(); slide = null; @@ -181,24 +184,23 @@ public class ThumbnailView extends FrameLayout { !((Activity)getContext()).isDestroyed(); } - private GenericRequestBuilder buildThumbnailGlideRequest(@NonNull Slide slide, @NonNull MasterSecret masterSecret) { - @SuppressWarnings("ConstantConditions") - DrawableRequestBuilder builder = Glide.with(getContext()) - .load(new DecryptableUri(masterSecret, slide.getThumbnailUri())) - .diskCacheStrategy(DiskCacheStrategy.NONE) - .crossFade() - .transform(new RoundedCorners(getContext(), true, radius, backgroundColorHint)); + private RequestBuilder buildThumbnailGlideRequest(@NonNull Slide slide, @NonNull MasterSecret masterSecret) { + RequestBuilder builder = GlideApp.with(getContext()) + .load(new DecryptableUri(masterSecret, slide.getThumbnailUri())) + .diskCacheStrategy(DiskCacheStrategy.NONE) + .transform(new RoundedCorners(radius)) + .transition(withCrossFade()); if (slide.isInProgress()) return builder; - else return builder.error(R.drawable.ic_missing_thumbnail_picture); + else return builder.apply(RequestOptions.errorOf(R.drawable.ic_missing_thumbnail_picture)); } - private GenericRequestBuilder buildPlaceholderGlideRequest(Slide slide) { - return Glide.with(getContext()) - .load(slide.getPlaceholderRes(getContext().getTheme())) - .asBitmap() - .diskCacheStrategy(DiskCacheStrategy.NONE) - .fitCenter(); + private RequestBuilder buildPlaceholderGlideRequest(Slide slide) { + return GlideApp.with(getContext()) + .asBitmap() + .load(slide.getPlaceholderRes(getContext().getTheme())) + .diskCacheStrategy(DiskCacheStrategy.NONE) + .fitCenter(); } private class ThumbnailClickDispatcher implements View.OnClickListener { diff --git a/src/org/thoughtcrime/securesms/components/ZoomingImageView.java b/src/org/thoughtcrime/securesms/components/ZoomingImageView.java index e2b12f4d0..3ff0fc02f 100644 --- a/src/org/thoughtcrime/securesms/components/ZoomingImageView.java +++ b/src/org/thoughtcrime/securesms/components/ZoomingImageView.java @@ -1,7 +1,6 @@ package org.thoughtcrime.securesms.components; import android.content.Context; -import android.graphics.Canvas; import android.net.Uri; import android.os.AsyncTask; import android.support.annotation.Nullable; @@ -10,37 +9,33 @@ import android.util.Log; import android.util.Pair; import android.view.View; import android.widget.FrameLayout; -import android.widget.ImageView; -import com.bumptech.glide.Glide; import com.bumptech.glide.load.engine.DiskCacheStrategy; -import com.bumptech.glide.load.resource.drawable.GlideDrawable; -import com.bumptech.glide.request.RequestListener; -import com.bumptech.glide.request.target.GlideDrawableImageViewTarget; import com.bumptech.glide.request.target.Target; import com.davemorrissey.labs.subscaleview.ImageSource; import com.davemorrissey.labs.subscaleview.SubsamplingScaleImageView; +import com.github.chrisbanes.photoview.PhotoView; import org.thoughtcrime.securesms.R; import org.thoughtcrime.securesms.components.subsampling.AttachmentBitmapDecoder; import org.thoughtcrime.securesms.components.subsampling.AttachmentRegionDecoder; import org.thoughtcrime.securesms.crypto.MasterSecret; import org.thoughtcrime.securesms.mms.DecryptableStreamUriLoader.DecryptableUri; +import org.thoughtcrime.securesms.mms.GlideApp; import org.thoughtcrime.securesms.mms.PartAuthority; import org.thoughtcrime.securesms.util.BitmapDecodingException; import org.thoughtcrime.securesms.util.BitmapUtil; +import org.thoughtcrime.securesms.util.MediaUtil; import java.io.IOException; import java.io.InputStream; -import uk.co.senab.photoview.PhotoViewAttacher; public class ZoomingImageView extends FrameLayout { private static final String TAG = ZoomingImageView.class.getName(); - private final ImageView imageView; - private final PhotoViewAttacher imageViewAttacher; + private final PhotoView photoView; private final SubsamplingScaleImageView subsamplingImageView; public ZoomingImageView(Context context) { @@ -56,9 +51,8 @@ public class ZoomingImageView extends FrameLayout { inflate(context, R.layout.zooming_image_view, this); - this.imageView = (ImageView) findViewById(R.id.image_view); - this.subsamplingImageView = (SubsamplingScaleImageView) findViewById(R.id.subsampling_image_view); - this.imageViewAttacher = new PhotoViewAttacher(imageView); + this.photoView = findViewById(R.id.image_view); + this.subsamplingImageView = findViewById(R.id.subsampling_image_view); this.subsamplingImageView.setBitmapDecoderClass(AttachmentBitmapDecoder.class); this.subsamplingImageView.setRegionDecoderClass(AttachmentRegionDecoder.class); @@ -74,7 +68,7 @@ public class ZoomingImageView extends FrameLayout { new AsyncTask>() { @Override protected @Nullable Pair doInBackground(Void... params) { - if (contentType.equals("image/gif")) return null; + if (MediaUtil.isGif(contentType)) return null; try { InputStream inputStream = PartAuthority.getAttachmentStream(context, masterSecret, uri); @@ -100,32 +94,26 @@ public class ZoomingImageView extends FrameLayout { } private void setImageViewUri(MasterSecret masterSecret, Uri uri) { + photoView.setVisibility(View.VISIBLE); subsamplingImageView.setVisibility(View.GONE); - imageView.setVisibility(View.VISIBLE); - Glide.with(getContext()) - .load(new DecryptableUri(masterSecret, uri)) - .diskCacheStrategy(DiskCacheStrategy.NONE) - .dontTransform() - .dontAnimate() - .into(new GlideDrawableImageViewTarget(imageView) { - @Override protected void setResource(GlideDrawable resource) { - super.setResource(resource); - imageViewAttacher.update(); - } - }); + GlideApp.with(getContext()) + .load(new DecryptableUri(masterSecret, uri)) + .diskCacheStrategy(DiskCacheStrategy.NONE) + .dontTransform() + .override(Target.SIZE_ORIGINAL, Target.SIZE_ORIGINAL) + .into(photoView); } private void setSubsamplingImageViewUri(Uri uri) { subsamplingImageView.setVisibility(View.VISIBLE); - imageView.setVisibility(View.GONE); + photoView.setVisibility(View.GONE); subsamplingImageView.setImage(ImageSource.uri(uri)); } - public void cleanup() { - imageView.setImageDrawable(null); + photoView.setImageDrawable(null); subsamplingImageView.recycle(); } } diff --git a/src/org/thoughtcrime/securesms/contacts/avatars/ContactPhotoFactory.java b/src/org/thoughtcrime/securesms/contacts/avatars/ContactPhotoFactory.java index cd622b4ca..990436177 100644 --- a/src/org/thoughtcrime/securesms/contacts/avatars/ContactPhotoFactory.java +++ b/src/org/thoughtcrime/securesms/contacts/avatars/ContactPhotoFactory.java @@ -11,13 +11,12 @@ import android.support.annotation.WorkerThread; import android.text.TextUtils; import android.util.Log; -import com.bumptech.glide.Glide; import com.bumptech.glide.load.engine.DiskCacheStrategy; import org.thoughtcrime.securesms.R; import org.thoughtcrime.securesms.database.Address; import org.thoughtcrime.securesms.mms.ContactPhotoUriLoader.ContactPhotoUri; -import org.thoughtcrime.securesms.profiles.AvatarPhotoUriLoader; +import org.thoughtcrime.securesms.mms.GlideApp; import org.thoughtcrime.securesms.profiles.AvatarPhotoUriLoader.AvatarPhotoUri; import java.util.concurrent.ExecutionException; @@ -58,10 +57,13 @@ public class ContactPhotoFactory { if (uri == null) return getSignalAvatarContactPhoto(context, address, name, targetSize); try { - Bitmap bitmap = Glide.with(context) - .load(new ContactPhotoUri(uri)).asBitmap() - .diskCacheStrategy(DiskCacheStrategy.NONE) - .centerCrop().into(targetSize, targetSize).get(); + Bitmap bitmap = GlideApp.with(context) + .asBitmap() + .load(new ContactPhotoUri(uri)) + .diskCacheStrategy(DiskCacheStrategy.NONE) + .centerCrop() + .submit(targetSize, targetSize) + .get(); return new BitmapContactPhoto(bitmap); } catch (ExecutionException e) { return getSignalAvatarContactPhoto(context, address, name, targetSize); @@ -83,14 +85,14 @@ public class ContactPhotoFactory { int targetSize) { try { - Bitmap bitmap = Glide.with(context) - .load(new AvatarPhotoUri(address)) - .asBitmap() - .diskCacheStrategy(DiskCacheStrategy.NONE) - .skipMemoryCache(true) - .centerCrop() - .into(targetSize, targetSize) - .get(); + Bitmap bitmap = GlideApp.with(context) + .asBitmap() + .load(new AvatarPhotoUri(address)) + .diskCacheStrategy(DiskCacheStrategy.NONE) + .skipMemoryCache(true) + .centerCrop() + .submit(targetSize, targetSize) + .get(); return new BitmapContactPhoto(bitmap); } catch (IllegalArgumentException e) { diff --git a/src/org/thoughtcrime/securesms/giph/ui/GiphyAdapter.java b/src/org/thoughtcrime/securesms/giph/ui/GiphyAdapter.java index 99a210902..5ebffa4ba 100644 --- a/src/org/thoughtcrime/securesms/giph/ui/GiphyAdapter.java +++ b/src/org/thoughtcrime/securesms/giph/ui/GiphyAdapter.java @@ -3,7 +3,9 @@ package org.thoughtcrime.securesms.giph.ui; import android.content.Context; import android.graphics.drawable.ColorDrawable; +import android.graphics.drawable.Drawable; import android.support.annotation.NonNull; +import android.support.annotation.Nullable; import android.support.v7.widget.RecyclerView; import android.util.Log; import android.view.LayoutInflater; @@ -11,16 +13,18 @@ import android.view.View; import android.view.ViewGroup; import android.widget.ProgressBar; -import com.bumptech.glide.DrawableRequestBuilder; import com.bumptech.glide.Glide; +import com.bumptech.glide.RequestBuilder; +import com.bumptech.glide.load.DataSource; import com.bumptech.glide.load.engine.DiskCacheStrategy; -import com.bumptech.glide.load.resource.drawable.GlideDrawable; +import com.bumptech.glide.load.engine.GlideException; import com.bumptech.glide.request.RequestListener; import com.bumptech.glide.request.target.Target; import org.thoughtcrime.securesms.R; import org.thoughtcrime.securesms.color.MaterialColor; import org.thoughtcrime.securesms.giph.model.GiphyImage; +import org.thoughtcrime.securesms.mms.GlideApp; import org.thoughtcrime.securesms.util.Util; import org.thoughtcrime.securesms.util.ViewUtil; @@ -37,7 +41,7 @@ public class GiphyAdapter extends RecyclerView.Adapter { + class GiphyViewHolder extends RecyclerView.ViewHolder implements View.OnClickListener, RequestListener { public AspectRatioImageView thumbnail; public GiphyImage image; @@ -58,7 +62,7 @@ public class GiphyAdapter extends RecyclerView.Adapter target, boolean isFirstResource) { + public boolean onLoadFailed(@Nullable GlideException e, Object model, Target target, boolean isFirstResource) { Log.w(TAG, e); synchronized (this) { @@ -69,10 +73,11 @@ public class GiphyAdapter extends RecyclerView.Adapter target, boolean isFromMemoryCache, boolean isFirstResource) { + public boolean onResourceReady(Drawable resource, Object model, Target target, DataSource dataSource, boolean isFirstResource) { synchronized (this) { if (image.getGifUrl().equals(model)) { this.modelReady = true; @@ -83,6 +88,7 @@ public class GiphyAdapter extends RecyclerView.Adapter images) { - this.context = context; + this.context = context.getApplicationContext(); this.images = images; } @@ -134,32 +140,33 @@ public class GiphyAdapter extends RecyclerView.Adapter thumbnailRequest = Glide.with(context) - .load(image.getStillUrl()); + RequestBuilder thumbnailRequest = GlideApp.with(context) + .load(image.getStillUrl()) + .diskCacheStrategy(DiskCacheStrategy.ALL); if (Util.isLowMemory(context)) { - Glide.with(context) - .load(image.getStillUrl()) - .placeholder(new ColorDrawable(Util.getRandomElement(MaterialColor.values()).toConversationColor(context))) - .diskCacheStrategy(DiskCacheStrategy.ALL) - .into(holder.thumbnail); + GlideApp.with(context) + .load(image.getStillUrl()) + .placeholder(new ColorDrawable(Util.getRandomElement(MaterialColor.values()).toConversationColor(context))) + .diskCacheStrategy(DiskCacheStrategy.ALL) + .into(holder.thumbnail); holder.setModelReady(); } else { - Glide.with(context) - .load(image.getGifUrl()) - .thumbnail(thumbnailRequest) - .placeholder(new ColorDrawable(Util.getRandomElement(MaterialColor.values()).toConversationColor(context))) - .diskCacheStrategy(DiskCacheStrategy.ALL) - .listener(holder) - .into(holder.thumbnail); + GlideApp.with(context) + .load(image.getGifUrl()) + .thumbnail(thumbnailRequest) + .placeholder(new ColorDrawable(Util.getRandomElement(MaterialColor.values()).toConversationColor(context))) + .diskCacheStrategy(DiskCacheStrategy.ALL) + .listener(holder) + .into(holder.thumbnail); } } @Override public void onViewRecycled(GiphyViewHolder holder) { super.onViewRecycled(holder); - Glide.clear(holder.thumbnail); + Glide.with(context).clear(holder.thumbnail); } @Override diff --git a/src/org/thoughtcrime/securesms/glide/OkHttpStreamFetcher.java b/src/org/thoughtcrime/securesms/glide/OkHttpStreamFetcher.java index 380f13023..5aeba7652 100644 --- a/src/org/thoughtcrime/securesms/glide/OkHttpStreamFetcher.java +++ b/src/org/thoughtcrime/securesms/glide/OkHttpStreamFetcher.java @@ -1,6 +1,9 @@ package org.thoughtcrime.securesms.glide; +import android.support.annotation.NonNull; + import com.bumptech.glide.Priority; +import com.bumptech.glide.load.DataSource; import com.bumptech.glide.load.data.DataFetcher; import com.bumptech.glide.load.model.GlideUrl; import com.bumptech.glide.util.ContentLengthInputStream; @@ -17,7 +20,7 @@ import okhttp3.ResponseBody; /** * Fetches an {@link InputStream} using the okhttp library. */ -public class OkHttpStreamFetcher implements DataFetcher { +class OkHttpStreamFetcher implements DataFetcher { private static final String TAG = OkHttpStreamFetcher.class.getName(); @@ -26,33 +29,38 @@ public class OkHttpStreamFetcher implements DataFetcher { private InputStream stream; private ResponseBody responseBody; - public OkHttpStreamFetcher(OkHttpClient client, GlideUrl url) { + OkHttpStreamFetcher(OkHttpClient client, GlideUrl url) { this.client = client; this.url = url; } @Override - public InputStream loadData(Priority priority) throws Exception { - Request.Builder requestBuilder = new Request.Builder() - .url(url.toStringUrl()); + public void loadData(Priority priority, DataCallback callback) { + try { + Request.Builder requestBuilder = new Request.Builder() + .url(url.toStringUrl()); - for (Map.Entry headerEntry : url.getHeaders().entrySet()) { - String key = headerEntry.getKey(); - requestBuilder.addHeader(key, headerEntry.getValue()); + for (Map.Entry headerEntry : url.getHeaders().entrySet()) { + String key = headerEntry.getKey(); + requestBuilder.addHeader(key, headerEntry.getValue()); + } + + Request request = requestBuilder.build(); + Response response = client.newCall(request).execute(); + + responseBody = response.body(); + + if (!response.isSuccessful()) { + throw new IOException("Request failed with code: " + response.code()); + } + + long contentLength = responseBody.contentLength(); + stream = ContentLengthInputStream.obtain(responseBody.byteStream(), contentLength); + + callback.onDataReady(stream); + } catch (IOException e) { + callback.onLoadFailed(e); } - - Request request = requestBuilder.build(); - Response response = client.newCall(request).execute(); - - responseBody = response.body(); - - if (!response.isSuccessful()) { - throw new IOException("Request failed with code: " + response.code()); - } - - long contentLength = responseBody.contentLength(); - stream = ContentLengthInputStream.obtain(responseBody.byteStream(), contentLength); - return stream; } @Override @@ -69,13 +77,20 @@ public class OkHttpStreamFetcher implements DataFetcher { } } - @Override - public String getId() { - return url.getCacheKey(); - } - @Override public void cancel() { // TODO: call cancel on the client when this method is called on a background thread. See #257 } + + @NonNull + @Override + public Class getDataClass() { + return InputStream.class; + } + + @NonNull + @Override + public DataSource getDataSource() { + return DataSource.REMOTE; + } } \ No newline at end of file diff --git a/src/org/thoughtcrime/securesms/glide/OkHttpUrlLoader.java b/src/org/thoughtcrime/securesms/glide/OkHttpUrlLoader.java index 7ee19060f..a2aecc0fa 100644 --- a/src/org/thoughtcrime/securesms/glide/OkHttpUrlLoader.java +++ b/src/org/thoughtcrime/securesms/glide/OkHttpUrlLoader.java @@ -1,12 +1,12 @@ package org.thoughtcrime.securesms.glide; -import android.content.Context; +import android.support.annotation.Nullable; -import com.bumptech.glide.load.data.DataFetcher; -import com.bumptech.glide.load.model.GenericLoaderFactory; +import com.bumptech.glide.load.Options; import com.bumptech.glide.load.model.GlideUrl; import com.bumptech.glide.load.model.ModelLoader; import com.bumptech.glide.load.model.ModelLoaderFactory; +import com.bumptech.glide.load.model.MultiModelLoaderFactory; import org.thoughtcrime.securesms.giph.net.GiphyProxySelector; @@ -19,9 +19,23 @@ import okhttp3.OkHttpClient; */ public class OkHttpUrlLoader implements ModelLoader { - /** - * The default factory for {@link OkHttpUrlLoader}s. - */ + private final OkHttpClient client; + + private OkHttpUrlLoader(OkHttpClient client) { + this.client = client; + } + + @Nullable + @Override + public LoadData buildLoadData(GlideUrl glideUrl, int width, int height, Options options) { + return new LoadData<>(glideUrl, new OkHttpStreamFetcher(client, glideUrl)); + } + + @Override + public boolean handles(GlideUrl glideUrl) { + return true; + } + public static class Factory implements ModelLoaderFactory { private static volatile OkHttpClient internalClient; private OkHttpClient client; @@ -39,22 +53,16 @@ public class OkHttpUrlLoader implements ModelLoader { return internalClient; } - /** - * Constructor for a new Factory that runs requests using a static singleton client. - */ public Factory() { this(getInternalClient()); } - /** - * Constructor for a new Factory that runs requests using given client. - */ private Factory(OkHttpClient client) { this.client = client; } @Override - public ModelLoader build(Context context, GenericLoaderFactory factories) { + public ModelLoader build(MultiModelLoaderFactory multiFactory) { return new OkHttpUrlLoader(client); } @@ -63,15 +71,4 @@ public class OkHttpUrlLoader implements ModelLoader { // Do nothing, this instance doesn't own the client. } } - - private final OkHttpClient client; - - private OkHttpUrlLoader(OkHttpClient client) { - this.client = client; - } - - @Override - public DataFetcher getResourceFetcher(GlideUrl model, int width, int height) { - return new OkHttpStreamFetcher(client, model); - } } \ No newline at end of file diff --git a/src/org/thoughtcrime/securesms/mms/AttachmentStreamLocalUriFetcher.java b/src/org/thoughtcrime/securesms/mms/AttachmentStreamLocalUriFetcher.java index b9144d45a..a0462632f 100644 --- a/src/org/thoughtcrime/securesms/mms/AttachmentStreamLocalUriFetcher.java +++ b/src/org/thoughtcrime/securesms/mms/AttachmentStreamLocalUriFetcher.java @@ -1,40 +1,45 @@ package org.thoughtcrime.securesms.mms; -import android.content.ContentResolver; -import android.content.Context; -import android.net.Uri; +import android.support.annotation.NonNull; import android.util.Log; import com.bumptech.glide.Priority; +import com.bumptech.glide.load.DataSource; import com.bumptech.glide.load.data.DataFetcher; -import com.bumptech.glide.load.data.StreamLocalUriFetcher; -import org.thoughtcrime.securesms.crypto.MasterSecret; +import org.whispersystems.libsignal.InvalidMessageException; import org.whispersystems.libsignal.util.guava.Optional; import org.whispersystems.signalservice.api.crypto.AttachmentCipherInputStream; import java.io.File; -import java.io.FileNotFoundException; import java.io.IOException; import java.io.InputStream; -public class AttachmentStreamLocalUriFetcher implements DataFetcher { +class AttachmentStreamLocalUriFetcher implements DataFetcher { + private static final String TAG = AttachmentStreamLocalUriFetcher.class.getSimpleName(); + private File attachment; private byte[] key; private InputStream is; - public AttachmentStreamLocalUriFetcher(File attachment, byte[] key) { + AttachmentStreamLocalUriFetcher(File attachment, byte[] key) { this.attachment = attachment; this.key = key; } - @Override public InputStream loadData(Priority priority) throws Exception { - is = new AttachmentCipherInputStream(attachment, key, Optional.absent()); - return is; + @Override + public void loadData(Priority priority, DataCallback callback) { + try { + is = new AttachmentCipherInputStream(attachment, key, Optional.absent()); + callback.onDataReady(is); + } catch (IOException | InvalidMessageException e) { + callback.onLoadFailed(e); + } } - @Override public void cleanup() { + @Override + public void cleanup() { try { if (is != null) is.close(); is = null; @@ -43,11 +48,20 @@ public class AttachmentStreamLocalUriFetcher implements DataFetcher } } - @Override public String getId() { - return AttachmentStreamLocalUriFetcher.class.getCanonicalName() + "::" + attachment.getAbsolutePath(); + @Override + public void cancel() {} + + @NonNull + @Override + public Class getDataClass() { + return InputStream.class; } - @Override public void cancel() { - + @NonNull + @Override + public DataSource getDataSource() { + return DataSource.LOCAL; } + + } diff --git a/src/org/thoughtcrime/securesms/mms/AttachmentStreamUriLoader.java b/src/org/thoughtcrime/securesms/mms/AttachmentStreamUriLoader.java index 2db01844a..253724ccb 100644 --- a/src/org/thoughtcrime/securesms/mms/AttachmentStreamUriLoader.java +++ b/src/org/thoughtcrime/securesms/mms/AttachmentStreamUriLoader.java @@ -1,39 +1,38 @@ package org.thoughtcrime.securesms.mms; -import android.content.Context; -import android.net.Uri; import android.support.annotation.NonNull; +import android.support.annotation.Nullable; -import com.bumptech.glide.load.data.DataFetcher; -import com.bumptech.glide.load.model.GenericLoaderFactory; +import com.bumptech.glide.load.Key; +import com.bumptech.glide.load.Options; import com.bumptech.glide.load.model.ModelLoader; import com.bumptech.glide.load.model.ModelLoaderFactory; -import com.bumptech.glide.load.model.stream.StreamModelLoader; +import com.bumptech.glide.load.model.MultiModelLoaderFactory; -import org.thoughtcrime.securesms.crypto.MasterSecret; import org.thoughtcrime.securesms.mms.AttachmentStreamUriLoader.AttachmentModel; -import org.thoughtcrime.securesms.mms.DecryptableStreamUriLoader.DecryptableUri; -import org.thoughtcrime.securesms.util.SaveAttachmentTask.Attachment; import java.io.File; import java.io.InputStream; +import java.security.MessageDigest; -/** - * A {@link ModelLoader} for translating uri models into {@link InputStream} data. Capable of handling 'http', - * 'https', 'android.resource', 'content', and 'file' schemes. Unsupported schemes will throw an exception in - * {@link #getResourceFetcher(Uri, int, int)}. - */ -public class AttachmentStreamUriLoader implements StreamModelLoader { - private final Context context; +public class AttachmentStreamUriLoader implements ModelLoader { - /** - * THe default factory for {@link com.bumptech.glide.load.model.stream.StreamUriLoader}s. - */ - public static class Factory implements ModelLoaderFactory { + @Nullable + @Override + public LoadData buildLoadData(AttachmentModel attachmentModel, int width, int height, Options options) { + return new LoadData<>(attachmentModel, new AttachmentStreamLocalUriFetcher(attachmentModel.attachment, attachmentModel.key)); + } + + @Override + public boolean handles(AttachmentModel attachmentModel) { + return true; + } + + static class Factory implements ModelLoaderFactory { @Override - public StreamModelLoader build(Context context, GenericLoaderFactory factories) { - return new AttachmentStreamUriLoader(context); + public ModelLoader build(MultiModelLoaderFactory multiFactory) { + return new AttachmentStreamUriLoader(); } @Override @@ -42,16 +41,7 @@ public class AttachmentStreamUriLoader implements StreamModelLoader getResourceFetcher(AttachmentModel model, int width, int height) { - return new AttachmentStreamLocalUriFetcher(model.attachment, model.key); - } - - public static class AttachmentModel { + public static class AttachmentModel implements Key { public @NonNull File attachment; public @NonNull byte[] key; @@ -60,6 +50,11 @@ public class AttachmentStreamUriLoader implements StreamModelLoader { -public class ContactPhotoUriLoader implements StreamModelLoader { private final Context context; - /** - * THe default factory for {@link com.bumptech.glide.load.model.stream.StreamUriLoader}s. - */ - public static class Factory implements ModelLoaderFactory { + private ContactPhotoUriLoader(Context context) { + this.context = context; + } + + @Nullable + @Override + public LoadData buildLoadData(ContactPhotoUri contactPhotoUri, int width, int height, Options options) { + return new LoadData<>(contactPhotoUri, new StreamLocalUriFetcher(context.getContentResolver(), contactPhotoUri.uri)); + } + + @Override + public boolean handles(ContactPhotoUri contactPhotoUri) { + return true; + } + + static class Factory implements ModelLoaderFactory { + + private final Context context; + + Factory(Context context) { + this.context = context.getApplicationContext(); + } @Override - public StreamModelLoader build(Context context, GenericLoaderFactory factories) { + public ModelLoader build(MultiModelLoaderFactory multiFactory) { return new ContactPhotoUriLoader(context); } @@ -32,21 +55,28 @@ public class ContactPhotoUriLoader implements StreamModelLoader } } - public ContactPhotoUriLoader(Context context) { - this.context = context; - } - - @Override - public DataFetcher getResourceFetcher(ContactPhotoUri model, int width, int height) { - return new ContactPhotoLocalUriFetcher(context, model.uri); - } - - public static class ContactPhotoUri { + public static class ContactPhotoUri implements Key { public @NonNull Uri uri; public ContactPhotoUri(@NonNull Uri uri) { this.uri = uri; } + + @Override + public void updateDiskCacheKey(MessageDigest messageDigest) { + messageDigest.update(uri.toString().getBytes()); + } + + @Override + public boolean equals(Object other) { + if (other == null || !(other instanceof ContactPhotoUri)) return false; + + return this.uri.equals(((ContactPhotoUri)other).uri); + } + + public int hashCode() { + return uri.hashCode(); + } } } diff --git a/src/org/thoughtcrime/securesms/mms/DecryptableStreamLocalUriFetcher.java b/src/org/thoughtcrime/securesms/mms/DecryptableStreamLocalUriFetcher.java index f87f12232..e49d075ff 100644 --- a/src/org/thoughtcrime/securesms/mms/DecryptableStreamLocalUriFetcher.java +++ b/src/org/thoughtcrime/securesms/mms/DecryptableStreamLocalUriFetcher.java @@ -17,15 +17,15 @@ import java.io.FileNotFoundException; import java.io.IOException; import java.io.InputStream; -public class DecryptableStreamLocalUriFetcher extends StreamLocalUriFetcher { +class DecryptableStreamLocalUriFetcher extends StreamLocalUriFetcher { private static final String TAG = DecryptableStreamLocalUriFetcher.class.getSimpleName(); private Context context; private MasterSecret masterSecret; - public DecryptableStreamLocalUriFetcher(Context context, MasterSecret masterSecret, Uri uri) { - super(context, uri); + DecryptableStreamLocalUriFetcher(Context context, MasterSecret masterSecret, Uri uri) { + super(context.getContentResolver(), uri); this.context = context; this.masterSecret = masterSecret; } diff --git a/src/org/thoughtcrime/securesms/mms/DecryptableStreamUriLoader.java b/src/org/thoughtcrime/securesms/mms/DecryptableStreamUriLoader.java index b5fcdf314..2f67e3653 100644 --- a/src/org/thoughtcrime/securesms/mms/DecryptableStreamUriLoader.java +++ b/src/org/thoughtcrime/securesms/mms/DecryptableStreamUriLoader.java @@ -3,33 +3,49 @@ package org.thoughtcrime.securesms.mms; import android.content.Context; import android.net.Uri; import android.support.annotation.NonNull; +import android.support.annotation.Nullable; -import com.bumptech.glide.load.data.DataFetcher; -import com.bumptech.glide.load.model.GenericLoaderFactory; +import com.bumptech.glide.load.Key; +import com.bumptech.glide.load.Options; import com.bumptech.glide.load.model.ModelLoader; import com.bumptech.glide.load.model.ModelLoaderFactory; -import com.bumptech.glide.load.model.stream.StreamModelLoader; +import com.bumptech.glide.load.model.MultiModelLoaderFactory; import org.thoughtcrime.securesms.crypto.MasterSecret; import org.thoughtcrime.securesms.mms.DecryptableStreamUriLoader.DecryptableUri; import java.io.InputStream; +import java.security.MessageDigest; + +public class DecryptableStreamUriLoader implements ModelLoader { -/** - * A {@link ModelLoader} for translating uri models into {@link InputStream} data. Capable of handling 'http', - * 'https', 'android.resource', 'content', and 'file' schemes. Unsupported schemes will throw an exception in - * {@link #getResourceFetcher(Uri, int, int)}. - */ -public class DecryptableStreamUriLoader implements StreamModelLoader { private final Context context; - /** - * THe default factory for {@link com.bumptech.glide.load.model.stream.StreamUriLoader}s. - */ - public static class Factory implements ModelLoaderFactory { + private DecryptableStreamUriLoader(Context context) { + this.context = context; + } + + @Nullable + @Override + public LoadData buildLoadData(DecryptableUri decryptableUri, int width, int height, Options options) { + return new LoadData<>(decryptableUri, new DecryptableStreamLocalUriFetcher(context, decryptableUri.masterSecret, decryptableUri.uri)); + } + + @Override + public boolean handles(DecryptableUri decryptableUri) { + return true; + } + + static class Factory implements ModelLoaderFactory { + + private final Context context; + + Factory(Context context) { + this.context = context.getApplicationContext(); + } @Override - public StreamModelLoader build(Context context, GenericLoaderFactory factories) { + public ModelLoader build(MultiModelLoaderFactory multiFactory) { return new DecryptableStreamUriLoader(context); } @@ -39,16 +55,7 @@ public class DecryptableStreamUriLoader implements StreamModelLoader getResourceFetcher(DecryptableUri model, int width, int height) { - return new DecryptableStreamLocalUriFetcher(context, model.masterSecret, model.uri); - } - - public static class DecryptableUri { + public static class DecryptableUri implements Key { public @NonNull MasterSecret masterSecret; public @NonNull Uri uri; @@ -57,6 +64,11 @@ public class DecryptableStreamUriLoader implements StreamModelLoader " + routeToHostObtained); - return routeToHostObtained; + return false; } protected static byte[] parseResponse(InputStream is) throws IOException { diff --git a/src/org/thoughtcrime/securesms/mms/MmsRadio.java b/src/org/thoughtcrime/securesms/mms/MmsRadio.java index 7a3bcc63e..5f61ad822 100644 --- a/src/org/thoughtcrime/securesms/mms/MmsRadio.java +++ b/src/org/thoughtcrime/securesms/mms/MmsRadio.java @@ -11,8 +11,13 @@ import android.util.Log; import org.thoughtcrime.securesms.util.Util; +import java.lang.reflect.InvocationTargetException; +import java.lang.reflect.Method; + public class MmsRadio { + private static final String TAG = MmsRadio.class.getSimpleName(); + private static MmsRadio instance; public static synchronized MmsRadio getInstance(Context context) { @@ -52,8 +57,17 @@ public class MmsRadio { if (connectedCounter == 0) { Log.w("MmsRadio", "Turning off MMS radio..."); - connectivityManager.stopUsingNetworkFeature(ConnectivityManager.TYPE_MOBILE, FEATURE_ENABLE_MMS); - + try { + final Method stopUsingNetworkFeatureMethod = connectivityManager.getClass().getMethod("stopUsingNetworkFeature", Integer.TYPE, String.class); + stopUsingNetworkFeatureMethod.invoke(connectivityManager, ConnectivityManager.TYPE_MOBILE, FEATURE_ENABLE_MMS); + } catch (NoSuchMethodException nsme) { + Log.w(TAG, nsme); + } catch (IllegalAccessException iae) { + Log.w(TAG, iae); + } catch (InvocationTargetException ite) { + Log.w(TAG, ite); + } + if (connectivityListener != null) { Log.w("MmsRadio", "Unregistering receiver..."); context.unregisterReceiver(connectivityListener); @@ -63,8 +77,18 @@ public class MmsRadio { } public synchronized void connect() throws MmsRadioException { - int status = connectivityManager.startUsingNetworkFeature(ConnectivityManager.TYPE_MOBILE, - FEATURE_ENABLE_MMS); + int status; + + try { + final Method startUsingNetworkFeatureMethod = connectivityManager.getClass().getMethod("startUsingNetworkFeature", Integer.TYPE, String.class); + status = (int)startUsingNetworkFeatureMethod.invoke(connectivityManager, ConnectivityManager.TYPE_MOBILE, FEATURE_ENABLE_MMS); + } catch (NoSuchMethodException nsme) { + throw new MmsRadioException(nsme); + } catch (IllegalAccessException iae) { + throw new MmsRadioException(iae); + } catch (InvocationTargetException ite) { + throw new MmsRadioException(ite); + } Log.w("MmsRadio", "startUsingNetworkFeature status: " + status); diff --git a/src/org/thoughtcrime/securesms/mms/MmsRadioException.java b/src/org/thoughtcrime/securesms/mms/MmsRadioException.java index 9919541b8..dfac78dee 100644 --- a/src/org/thoughtcrime/securesms/mms/MmsRadioException.java +++ b/src/org/thoughtcrime/securesms/mms/MmsRadioException.java @@ -4,4 +4,8 @@ public class MmsRadioException extends Throwable { public MmsRadioException(String s) { super(s); } + + public MmsRadioException(Exception e) { + super(e); + } } diff --git a/src/org/thoughtcrime/securesms/mms/RoundedCorners.java b/src/org/thoughtcrime/securesms/mms/RoundedCorners.java deleted file mode 100644 index 4b1117010..000000000 --- a/src/org/thoughtcrime/securesms/mms/RoundedCorners.java +++ /dev/null @@ -1,105 +0,0 @@ -package org.thoughtcrime.securesms.mms; - -import android.content.Context; -import android.graphics.Bitmap; -import android.graphics.Bitmap.Config; -import android.graphics.BitmapShader; -import android.graphics.Canvas; -import android.graphics.Paint; -import android.graphics.RectF; -import android.graphics.Shader.TileMode; -import android.support.annotation.NonNull; -import android.support.annotation.Nullable; - -import com.bumptech.glide.load.engine.bitmap_recycle.BitmapPool; -import com.bumptech.glide.load.resource.bitmap.BitmapTransformation; -import com.bumptech.glide.load.resource.bitmap.TransformationUtils; - -import org.thoughtcrime.securesms.util.ResUtil; - -public class RoundedCorners extends BitmapTransformation { - private final boolean crop; - private final int radius; - private final int colorHint; - - public RoundedCorners(@NonNull Context context, boolean crop, int radius, int colorHint) { - super(context); - this.crop = crop; - this.radius = radius; - this.colorHint = colorHint; - } - - public RoundedCorners(@NonNull Context context, int radius) { - this(context, true, radius, ResUtil.getColor(context, android.R.attr.windowBackground)); - } - - @Override protected Bitmap transform(BitmapPool pool, Bitmap toTransform, int outWidth, - int outHeight) - { - final Bitmap toRound = crop ? centerCrop(pool, toTransform, outWidth, outHeight) - : fitCenter(pool, toTransform, outWidth, outHeight); - - final Bitmap rounded = round(pool, toRound); - - if (toRound != null && toRound != rounded && toRound != toTransform && !pool.put(toRound)) { - toRound.recycle(); - } - - return rounded; - } - - private Bitmap centerCrop(BitmapPool pool, Bitmap toTransform, int outWidth, int outHeight) { - final Bitmap toReuse = pool.get(outWidth, outHeight, getSafeConfig(toTransform)); - final Bitmap transformed = TransformationUtils.centerCrop(toReuse, toTransform, outWidth, outHeight); - - if (toReuse != null && toReuse != transformed && !pool.put(toReuse)) { - toReuse.recycle(); - } - - return transformed; - } - - private Bitmap fitCenter(BitmapPool pool, Bitmap toTransform, int outWidth, int outHeight) { - return TransformationUtils.fitCenter(toTransform, pool, outWidth, outHeight); - } - - private Bitmap round(@NonNull BitmapPool pool, @Nullable Bitmap toRound) { - if (toRound == null) { - return null; - } - - Bitmap result = pool.get(toRound.getWidth(), toRound.getHeight(), getSafeConfig(toRound)); - - if (result == null) { - result = Bitmap.createBitmap(toRound.getWidth(), toRound.getHeight(), getSafeConfig(toRound)); - } - - Canvas canvas = new Canvas(result); - - if (Config.RGB_565.equals(result.getConfig())) { - Paint cornerPaint = new Paint(); - cornerPaint.setColor(colorHint); - - canvas.drawRect(0, 0, radius, radius, cornerPaint); - canvas.drawRect(0, toRound.getHeight() - radius, radius, toRound.getHeight(), cornerPaint); - canvas.drawRect(toRound.getWidth() - radius, 0, toRound.getWidth(), radius, cornerPaint); - canvas.drawRect(toRound.getWidth() - radius, toRound.getHeight() - radius, toRound.getWidth(), toRound.getHeight(), cornerPaint); - } - - Paint shaderPaint = new Paint(Paint.ANTI_ALIAS_FLAG); - shaderPaint.setShader(new BitmapShader(toRound, TileMode.CLAMP, TileMode.CLAMP)); - - canvas.drawRoundRect(new RectF(0, 0, toRound.getWidth(), toRound.getHeight()), radius, radius, shaderPaint); - - return result; - } - - private static Bitmap.Config getSafeConfig(Bitmap bitmap) { - return bitmap.getConfig() != null ? bitmap.getConfig() : Bitmap.Config.ARGB_8888; - } - - @Override - public String getId() { - return RoundedCorners.class.getCanonicalName(); - } -} diff --git a/src/org/thoughtcrime/securesms/mms/TextSecureGlideModule.java b/src/org/thoughtcrime/securesms/mms/SignalGlideModule.java similarity index 54% rename from src/org/thoughtcrime/securesms/mms/TextSecureGlideModule.java rename to src/org/thoughtcrime/securesms/mms/SignalGlideModule.java index 9ce5b075d..495d659ae 100644 --- a/src/org/thoughtcrime/securesms/mms/TextSecureGlideModule.java +++ b/src/org/thoughtcrime/securesms/mms/SignalGlideModule.java @@ -1,13 +1,16 @@ package org.thoughtcrime.securesms.mms; import android.content.Context; +import android.util.Log; import com.bumptech.glide.Glide; import com.bumptech.glide.GlideBuilder; +import com.bumptech.glide.Registry; +import com.bumptech.glide.annotation.GlideModule; import com.bumptech.glide.load.engine.cache.DiskCache; import com.bumptech.glide.load.engine.cache.DiskCacheAdapter; import com.bumptech.glide.load.model.GlideUrl; -import com.bumptech.glide.module.GlideModule; +import com.bumptech.glide.module.AppGlideModule; import org.thoughtcrime.securesms.glide.OkHttpUrlLoader; import org.thoughtcrime.securesms.mms.AttachmentStreamUriLoader.AttachmentModel; @@ -18,19 +21,27 @@ import org.thoughtcrime.securesms.profiles.AvatarPhotoUriLoader.AvatarPhotoUri; import java.io.InputStream; -public class TextSecureGlideModule implements GlideModule { +@GlideModule +public class SignalGlideModule extends AppGlideModule { + + @Override + public boolean isManifestParsingEnabled() { + return false; + } + @Override public void applyOptions(Context context, GlideBuilder builder) { + builder.setLogLevel(Log.ERROR); // builder.setDiskCache(new NoopDiskCacheFactory()); } @Override - public void registerComponents(Context context, Glide glide) { - glide.register(DecryptableUri.class, InputStream.class, new DecryptableStreamUriLoader.Factory()); - glide.register(ContactPhotoUri.class, InputStream.class, new ContactPhotoUriLoader.Factory()); - glide.register(AttachmentModel.class, InputStream.class, new AttachmentStreamUriLoader.Factory()); - glide.register(GlideUrl.class, InputStream.class, new OkHttpUrlLoader.Factory()); - glide.register(AvatarPhotoUri.class, InputStream.class, new AvatarPhotoUriLoader.Factory()); + public void registerComponents(Context context, Glide glide, Registry registry) { + registry.append(DecryptableUri.class, InputStream.class, new DecryptableStreamUriLoader.Factory(context)); + registry.append(ContactPhotoUri.class, InputStream.class, new ContactPhotoUriLoader.Factory(context)); + registry.append(AttachmentModel.class, InputStream.class, new AttachmentStreamUriLoader.Factory()); + registry.replace(GlideUrl.class, InputStream.class, new OkHttpUrlLoader.Factory()); + registry.append(AvatarPhotoUri.class, InputStream.class, new AvatarPhotoUriLoader.Factory(context)); } public static class NoopDiskCacheFactory implements DiskCache.Factory { diff --git a/src/org/thoughtcrime/securesms/notifications/SingleRecipientNotificationBuilder.java b/src/org/thoughtcrime/securesms/notifications/SingleRecipientNotificationBuilder.java index bd33f778a..653b2f837 100644 --- a/src/org/thoughtcrime/securesms/notifications/SingleRecipientNotificationBuilder.java +++ b/src/org/thoughtcrime/securesms/notifications/SingleRecipientNotificationBuilder.java @@ -14,7 +14,6 @@ import android.support.v4.app.NotificationCompat.Action; import android.support.v4.app.RemoteInput; import android.text.SpannableStringBuilder; -import com.bumptech.glide.Glide; import com.bumptech.glide.load.engine.DiskCacheStrategy; import org.thoughtcrime.securesms.R; @@ -22,6 +21,7 @@ import org.thoughtcrime.securesms.contacts.avatars.ContactColors; import org.thoughtcrime.securesms.contacts.avatars.ContactPhotoFactory; import org.thoughtcrime.securesms.crypto.MasterSecret; import org.thoughtcrime.securesms.mms.DecryptableStreamUriLoader; +import org.thoughtcrime.securesms.mms.GlideApp; import org.thoughtcrime.securesms.mms.Slide; import org.thoughtcrime.securesms.mms.SlideDeck; import org.thoughtcrime.securesms.preferences.widgets.NotificationPrivacyPreference; @@ -225,12 +225,12 @@ public class SingleRecipientNotificationBuilder extends AbstractNotificationBuil @SuppressWarnings("ConstantConditions") Uri uri = slideDeck.getThumbnailSlide().getThumbnailUri(); - return Glide.with(context) - .load(new DecryptableStreamUriLoader.DecryptableUri(masterSecret, uri)) - .asBitmap() - .diskCacheStrategy(DiskCacheStrategy.NONE) - .into(500, 500) - .get(); + return GlideApp.with(context) + .asBitmap() + .load(new DecryptableStreamUriLoader.DecryptableUri(masterSecret, uri)) + .diskCacheStrategy(DiskCacheStrategy.NONE) + .submit(500, 500) + .get(); } catch (InterruptedException | ExecutionException e) { throw new AssertionError(e); } diff --git a/src/org/thoughtcrime/securesms/profiles/AvatarPhotoUriFetcher.java b/src/org/thoughtcrime/securesms/profiles/AvatarPhotoUriFetcher.java index b0818e7c5..b028e3e77 100644 --- a/src/org/thoughtcrime/securesms/profiles/AvatarPhotoUriFetcher.java +++ b/src/org/thoughtcrime/securesms/profiles/AvatarPhotoUriFetcher.java @@ -5,32 +5,34 @@ import android.content.Context; import android.support.annotation.NonNull; import com.bumptech.glide.Priority; +import com.bumptech.glide.load.DataSource; import com.bumptech.glide.load.data.DataFetcher; import org.thoughtcrime.securesms.database.Address; -import java.io.File; -import java.io.FileInputStream; -import java.io.FileNotFoundException; import java.io.IOException; import java.io.InputStream; -public class AvatarPhotoUriFetcher implements DataFetcher { +class AvatarPhotoUriFetcher implements DataFetcher { private final Context context; private final Address address; private InputStream inputStream; - public AvatarPhotoUriFetcher(@NonNull Context context, @NonNull Address address) { + AvatarPhotoUriFetcher(@NonNull Context context, @NonNull Address address) { this.context = context.getApplicationContext(); this.address = address; } @Override - public InputStream loadData(Priority priority) throws IOException { - inputStream = AvatarHelper.getInputStreamFor(context, address); - return inputStream; + public void loadData(Priority priority, DataCallback callback) { + try { + inputStream = AvatarHelper.getInputStreamFor(context, address); + callback.onDataReady(inputStream); + } catch (IOException e) { + callback.onLoadFailed(e); + } } @Override @@ -40,13 +42,20 @@ public class AvatarPhotoUriFetcher implements DataFetcher { } catch (IOException e) {} } - @Override - public String getId() { - return address.serialize(); - } - @Override public void cancel() { } + + @NonNull + @Override + public Class getDataClass() { + return InputStream.class; + } + + @NonNull + @Override + public DataSource getDataSource() { + return DataSource.LOCAL; + } } diff --git a/src/org/thoughtcrime/securesms/profiles/AvatarPhotoUriLoader.java b/src/org/thoughtcrime/securesms/profiles/AvatarPhotoUriLoader.java index 3b301ddff..9209f80c3 100644 --- a/src/org/thoughtcrime/securesms/profiles/AvatarPhotoUriLoader.java +++ b/src/org/thoughtcrime/securesms/profiles/AvatarPhotoUriLoader.java @@ -3,24 +3,48 @@ package org.thoughtcrime.securesms.profiles; import android.content.Context; import android.support.annotation.NonNull; +import android.support.annotation.Nullable; -import com.bumptech.glide.load.data.DataFetcher; -import com.bumptech.glide.load.model.GenericLoaderFactory; +import com.bumptech.glide.load.Key; +import com.bumptech.glide.load.Options; +import com.bumptech.glide.load.model.ModelLoader; import com.bumptech.glide.load.model.ModelLoaderFactory; -import com.bumptech.glide.load.model.stream.StreamModelLoader; +import com.bumptech.glide.load.model.MultiModelLoaderFactory; import org.thoughtcrime.securesms.database.Address; import java.io.InputStream; +import java.security.MessageDigest; -public class AvatarPhotoUriLoader implements StreamModelLoader { +public class AvatarPhotoUriLoader implements ModelLoader { private final Context context; + private AvatarPhotoUriLoader(Context context) { + this.context = context; + } + + @Nullable + @Override + public LoadData buildLoadData(AvatarPhotoUri avatarPhotoUri, int width, int height, Options options) { + return new LoadData<>(avatarPhotoUri, new AvatarPhotoUriFetcher(context, avatarPhotoUri.address)); + } + + @Override + public boolean handles(AvatarPhotoUri avatarPhotoUri) { + return true; + } + public static class Factory implements ModelLoaderFactory { + private final Context context; + + public Factory(Context context) { + this.context = context.getApplicationContext(); + } + @Override - public StreamModelLoader build(Context context, GenericLoaderFactory factories) { + public ModelLoader build(MultiModelLoaderFactory multiFactory) { return new AvatarPhotoUriLoader(context); } @@ -28,21 +52,29 @@ public class AvatarPhotoUriLoader implements StreamModelLoader getResourceFetcher(AvatarPhotoUri model, int width, int height) { - return new AvatarPhotoUriFetcher(context, model.address); - } - - public static class AvatarPhotoUri { + public static class AvatarPhotoUri implements Key { public @NonNull Address address; public AvatarPhotoUri(@NonNull Address address) { this.address = address; } + + @Override + public void updateDiskCacheKey(MessageDigest messageDigest) { + messageDigest.update(address.serialize().getBytes()); + } + + @Override + public boolean equals(Object other) { + if (other == null || !(other instanceof AvatarPhotoUri)) return false; + + return this.address.equals(((AvatarPhotoUri)other).address); + } + + @Override + public int hashCode() { + return address.hashCode(); + } } } diff --git a/src/org/thoughtcrime/securesms/scribbles/StickerSelectFragment.java b/src/org/thoughtcrime/securesms/scribbles/StickerSelectFragment.java index e297cdcdd..a02975755 100644 --- a/src/org/thoughtcrime/securesms/scribbles/StickerSelectFragment.java +++ b/src/org/thoughtcrime/securesms/scribbles/StickerSelectFragment.java @@ -1,4 +1,4 @@ -/** +/* * Copyright (C) 2016 Open Whisper Systems * * This program is free software: you can redistribute it and/or modify @@ -31,10 +31,10 @@ import android.view.View; import android.view.ViewGroup; import android.widget.ImageView; -import com.bumptech.glide.Glide; import com.bumptech.glide.load.engine.DiskCacheStrategy; import org.thoughtcrime.securesms.R; +import org.thoughtcrime.securesms.mms.GlideApp; public class StickerSelectFragment extends Fragment implements LoaderManager.LoaderCallbacks { @@ -58,7 +58,7 @@ public class StickerSelectFragment extends Fragment implements LoaderManager.Loa @Nullable Bundle savedInstanceState) { View view = inflater.inflate(R.layout.scribble_select_sticker_fragment, container, false); - this.recyclerView = (RecyclerView)view.findViewById(R.id.stickers_recycler_view); + this.recyclerView = view.findViewById(R.id.stickers_recycler_view); return view; } @@ -113,10 +113,10 @@ public class StickerSelectFragment extends Fragment implements LoaderManager.Loa public void onBindViewHolder(StickerViewHolder holder, int position) { holder.fileName = stickerFiles[position]; - Glide.with(context) - .load(Uri.parse("file:///android_asset/" + holder.fileName)) - .diskCacheStrategy(DiskCacheStrategy.NONE) - .into(holder.image); + GlideApp.with(context) + .load(Uri.parse("file:///android_asset/" + holder.fileName)) + .diskCacheStrategy(DiskCacheStrategy.NONE) + .into(holder.image); } @Override @@ -127,7 +127,7 @@ public class StickerSelectFragment extends Fragment implements LoaderManager.Loa @Override public void onViewRecycled(StickerViewHolder holder) { super.onViewRecycled(holder); - Glide.clear(holder.image); + GlideApp.with(context).clear(holder.image); } private void onStickerSelected(String fileName) { @@ -141,22 +141,19 @@ public class StickerSelectFragment extends Fragment implements LoaderManager.Loa StickerViewHolder(View itemView) { super(itemView); - image = (ImageView) itemView.findViewById(R.id.sticker_image); - itemView.setOnClickListener(new View.OnClickListener() { - @Override - public void onClick(View view) { - int pos = getAdapterPosition(); - if (pos >= 0) { - onStickerSelected(fileName); - } + image = itemView.findViewById(R.id.sticker_image); + itemView.setOnClickListener(view -> { + int pos = getAdapterPosition(); + if (pos >= 0) { + onStickerSelected(fileName); } }); } } } - public interface StickerSelectionListener { - public void onStickerSelected(String name); + interface StickerSelectionListener { + void onStickerSelected(String name); } diff --git a/src/org/thoughtcrime/securesms/scribbles/widget/ScribbleView.java b/src/org/thoughtcrime/securesms/scribbles/widget/ScribbleView.java index 960f91ac7..1363c8305 100644 --- a/src/org/thoughtcrime/securesms/scribbles/widget/ScribbleView.java +++ b/src/org/thoughtcrime/securesms/scribbles/widget/ScribbleView.java @@ -30,13 +30,13 @@ import android.util.Log; import android.widget.FrameLayout; import android.widget.ImageView; -import com.bumptech.glide.Glide; import com.bumptech.glide.load.engine.DiskCacheStrategy; import com.bumptech.glide.request.target.Target; import org.thoughtcrime.securesms.R; import org.thoughtcrime.securesms.crypto.MasterSecret; import org.thoughtcrime.securesms.mms.DecryptableStreamUriLoader.DecryptableUri; +import org.thoughtcrime.securesms.mms.GlideApp; import org.thoughtcrime.securesms.scribbles.widget.entity.MotionEntity; import org.thoughtcrime.securesms.scribbles.widget.entity.TextEntity; import org.thoughtcrime.securesms.util.Util; @@ -81,11 +81,11 @@ public class ScribbleView extends FrameLayout { this.imageUri = uri; this.masterSecret = masterSecret; - Glide.with(getContext()) - .load(new DecryptableUri(masterSecret, uri)) - .diskCacheStrategy(DiskCacheStrategy.NONE) - .fitCenter() - .into(imageView); + GlideApp.with(getContext()) + .load(new DecryptableUri(masterSecret, uri)) + .diskCacheStrategy(DiskCacheStrategy.NONE) + .fitCenter() + .into(imageView); } public @NonNull ListenableFuture getRenderedImage() { @@ -110,13 +110,13 @@ public class ScribbleView extends FrameLayout { height = 768; } - return Glide.with(context) - .load(new DecryptableUri(masterSecret, imageUri)) - .asBitmap() - .diskCacheStrategy(DiskCacheStrategy.NONE) - .skipMemoryCache(true) - .into(width, height) - .get(); + return GlideApp.with(context) + .asBitmap() + .load(new DecryptableUri(masterSecret, imageUri)) + .diskCacheStrategy(DiskCacheStrategy.NONE) + .skipMemoryCache(true) + .into(width, height) + .get(); } catch (InterruptedException | ExecutionException e) { Log.w(TAG, e); return null; diff --git a/src/org/thoughtcrime/securesms/util/BitmapUtil.java b/src/org/thoughtcrime/securesms/util/BitmapUtil.java index 62f8a5491..9319b38cd 100644 --- a/src/org/thoughtcrime/securesms/util/BitmapUtil.java +++ b/src/org/thoughtcrime/securesms/util/BitmapUtil.java @@ -15,14 +15,9 @@ import android.support.annotation.Nullable; import android.util.Log; import android.util.Pair; -import com.bumptech.glide.Glide; -import com.bumptech.glide.Priority; -import com.bumptech.glide.load.DecodeFormat; -import com.bumptech.glide.load.engine.Resource; -import com.bumptech.glide.load.resource.bitmap.BitmapResource; -import com.bumptech.glide.load.resource.bitmap.Downsampler; -import com.bumptech.glide.load.resource.bitmap.FitCenter; +import com.bumptech.glide.load.resource.bitmap.DownsampleStrategy; +import org.thoughtcrime.securesms.mms.GlideApp; import org.thoughtcrime.securesms.mms.MediaConstraints; import java.io.BufferedInputStream; @@ -30,6 +25,7 @@ import java.io.ByteArrayInputStream; import java.io.ByteArrayOutputStream; import java.io.IOException; import java.io.InputStream; +import java.util.concurrent.ExecutionException; import java.util.concurrent.atomic.AtomicBoolean; import javax.microedition.khronos.egl.EGL10; @@ -49,90 +45,80 @@ public class BitmapUtil { public static byte[] createScaledBytes(Context context, T model, MediaConstraints constraints) throws BitmapDecodingException { - int quality = MAX_COMPRESSION_QUALITY; - int attempts = 0; - byte[] bytes; - - Bitmap scaledBitmap = Downsampler.AT_MOST.decode(getInputStreamForModel(context, model), - Glide.get(context).getBitmapPool(), - constraints.getImageMaxWidth(context), - constraints.getImageMaxHeight(context), - DecodeFormat.PREFER_RGB_565); - - if (scaledBitmap == null) { - throw new BitmapDecodingException("Unable to decode image"); - } - try { - do { - ByteArrayOutputStream baos = new ByteArrayOutputStream(); - scaledBitmap.compress(CompressFormat.JPEG, quality, baos); - bytes = baos.toByteArray(); + int quality = MAX_COMPRESSION_QUALITY; + int attempts = 0; + byte[] bytes; - Log.w(TAG, "iteration with quality " + quality + " size " + (bytes.length / 1024) + "kb"); - if (quality == MIN_COMPRESSION_QUALITY) break; + Bitmap scaledBitmap = GlideApp.with(context) + .asBitmap() + .load(model) + .downsample(DownsampleStrategy.AT_MOST) + .submit(constraints.getImageMaxWidth(context), + constraints.getImageMaxWidth(context)) + .get(); + + if (scaledBitmap == null) { + throw new BitmapDecodingException("Unable to decode image"); + } - int nextQuality = (int)Math.floor(quality * Math.sqrt((double)constraints.getImageMaxSize(context) / bytes.length)); - if (quality - nextQuality < MIN_COMPRESSION_QUALITY_DECREASE) { - nextQuality = quality - MIN_COMPRESSION_QUALITY_DECREASE; + try { + do { + ByteArrayOutputStream baos = new ByteArrayOutputStream(); + scaledBitmap.compress(CompressFormat.JPEG, quality, baos); + bytes = baos.toByteArray(); + + Log.w(TAG, "iteration with quality " + quality + " size " + (bytes.length / 1024) + "kb"); + if (quality == MIN_COMPRESSION_QUALITY) break; + + int nextQuality = (int)Math.floor(quality * Math.sqrt((double)constraints.getImageMaxSize(context) / bytes.length)); + if (quality - nextQuality < MIN_COMPRESSION_QUALITY_DECREASE) { + nextQuality = quality - MIN_COMPRESSION_QUALITY_DECREASE; + } + quality = Math.max(nextQuality, MIN_COMPRESSION_QUALITY); } - quality = Math.max(nextQuality, MIN_COMPRESSION_QUALITY); + while (bytes.length > constraints.getImageMaxSize(context) && attempts++ < MAX_COMPRESSION_ATTEMPTS); + if (bytes.length > constraints.getImageMaxSize(context)) { + throw new BitmapDecodingException("Unable to scale image below: " + bytes.length); + } + Log.w(TAG, "createScaledBytes(" + model.toString() + ") -> quality " + Math.min(quality, MAX_COMPRESSION_QUALITY) + ", " + attempts + " attempt(s)"); + return bytes; + } finally { + if (scaledBitmap != null) scaledBitmap.recycle(); } - while (bytes.length > constraints.getImageMaxSize(context) && attempts++ < MAX_COMPRESSION_ATTEMPTS); - if (bytes.length > constraints.getImageMaxSize(context)) { - throw new BitmapDecodingException("Unable to scale image below: " + bytes.length); - } - Log.w(TAG, "createScaledBytes(" + model.toString() + ") -> quality " + Math.min(quality, MAX_COMPRESSION_QUALITY) + ", " + attempts + " attempt(s)"); - return bytes; - } finally { - if (scaledBitmap != null) scaledBitmap.recycle(); + } catch (InterruptedException | ExecutionException e) { + throw new BitmapDecodingException(e); } } public static Bitmap createScaledBitmap(Context context, T model, int maxWidth, int maxHeight) throws BitmapDecodingException - { - final Pair dimensions = getDimensions(getInputStreamForModel(context, model)); - final Pair clamped = clampDimensions(dimensions.first, dimensions.second, - maxWidth, maxHeight); - return createScaledBitmapInto(context, model, clamped.first, clamped.second); - } - - private static InputStream getInputStreamForModel(Context context, T model) - throws BitmapDecodingException { try { - return Glide.buildStreamModelLoader(model, context) - .getResourceFetcher(model, -1, -1) - .loadData(Priority.NORMAL); - } catch (Exception e) { + return GlideApp.with(context) + .asBitmap() + .load(model) + .downsample(DownsampleStrategy.AT_MOST) + .submit(maxWidth, maxHeight) + .get(); + } catch (InterruptedException | ExecutionException e) { throw new BitmapDecodingException(e); } } - private static Bitmap createScaledBitmapInto(Context context, T model, int width, int height) - throws BitmapDecodingException - { - final Bitmap rough = Downsampler.AT_LEAST.decode(getInputStreamForModel(context, model), - Glide.get(context).getBitmapPool(), - width, height, - DecodeFormat.PREFER_RGB_565); - - final Resource resource = BitmapResource.obtain(rough, Glide.get(context).getBitmapPool()); - final Resource result = new FitCenter(context).transform(resource, width, height); - - if (result == null) { - throw new BitmapDecodingException("unable to transform Bitmap"); - } - return result.get(); - } - public static Bitmap createScaledBitmap(Context context, T model, float scale) throws BitmapDecodingException { - Pair dimens = getDimensions(getInputStreamForModel(context, model)); - return createScaledBitmapInto(context, model, - (int)(dimens.first * scale), (int)(dimens.second * scale)); + try { + return GlideApp.with(context) + .asBitmap() + .load(model) + .sizeMultiplier(scale) + .submit() + .get(); + } catch (InterruptedException | ExecutionException e) { + throw new BitmapDecodingException(e); + } } private static BitmapFactory.Options getImageDimensions(InputStream inputStream) @@ -253,27 +239,6 @@ public class BitmapUtil { return output; } - private static Pair clampDimensions(int inWidth, int inHeight, int maxWidth, int maxHeight) { - if (inWidth > maxWidth || inHeight > maxHeight) { - final float aspectWidth, aspectHeight; - - if (inWidth == 0 || inHeight == 0) { - aspectWidth = maxWidth; - aspectHeight = maxHeight; - } else if (inWidth >= inHeight) { - aspectWidth = maxWidth; - aspectHeight = (aspectWidth / inWidth) * inHeight; - } else { - aspectHeight = maxHeight; - aspectWidth = (aspectHeight / inHeight) * inWidth; - } - - return new Pair<>(Math.round(aspectWidth), Math.round(aspectHeight)); - } else { - return new Pair<>(inWidth, inHeight); - } - } - public static Bitmap createFromDrawable(final Drawable drawable, final int width, final int height) { final AtomicBoolean created = new AtomicBoolean(false); final Bitmap[] result = new Bitmap[1]; diff --git a/src/org/thoughtcrime/securesms/util/MediaUtil.java b/src/org/thoughtcrime/securesms/util/MediaUtil.java index 6a6da5de9..72ff87647 100644 --- a/src/org/thoughtcrime/securesms/util/MediaUtil.java +++ b/src/org/thoughtcrime/securesms/util/MediaUtil.java @@ -11,8 +11,6 @@ import android.text.TextUtils; import android.util.Log; import android.webkit.MimeTypeMap; -import com.bumptech.glide.Glide; - import org.thoughtcrime.securesms.R; import org.thoughtcrime.securesms.attachments.Attachment; import org.thoughtcrime.securesms.crypto.MasterSecret; @@ -20,6 +18,7 @@ import org.thoughtcrime.securesms.mms.AudioSlide; import org.thoughtcrime.securesms.mms.DecryptableStreamUriLoader.DecryptableUri; import org.thoughtcrime.securesms.mms.DocumentSlide; import org.thoughtcrime.securesms.mms.GifSlide; +import org.thoughtcrime.securesms.mms.GlideApp; import org.thoughtcrime.securesms.mms.ImageSlide; import org.thoughtcrime.securesms.mms.MmsSlide; import org.thoughtcrime.securesms.mms.PartAuthority; @@ -67,12 +66,12 @@ public class MediaUtil { { try { int maxSize = context.getResources().getDimensionPixelSize(R.dimen.media_bubble_height); - return Glide.with(context) - .load(new DecryptableUri(masterSecret, uri)) - .asBitmap() - .centerCrop() - .into(maxSize, maxSize) - .get(); + return GlideApp.with(context) + .asBitmap() + .load(new DecryptableUri(masterSecret, uri)) + .centerCrop() + .into(maxSize, maxSize) + .get(); } catch (InterruptedException | ExecutionException e) { Log.w(TAG, e); throw new BitmapDecodingException(e);