parent
fcc7abf5e3
commit
b1fd5c7fdc
|
@ -15,16 +15,7 @@ import java.util.Locale;
|
||||||
public class TwitterMediaProvider implements Provider {
|
public class TwitterMediaProvider implements Provider {
|
||||||
@Override
|
@Override
|
||||||
public boolean supports(@NonNull String link) {
|
public boolean supports(@NonNull String link) {
|
||||||
final String authority = PreviewMediaExtractor.getAuthority(link);
|
return isSupported(link);
|
||||||
if (authority == null || !authority.endsWith(".twimg.com")) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
final String path = PreviewMediaExtractor.getPath(link);
|
|
||||||
if (path == null) return false;
|
|
||||||
if (path.startsWith("/media/")) {
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
return false;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Nullable
|
@Nullable
|
||||||
|
@ -55,4 +46,17 @@ public class TwitterMediaProvider implements Provider {
|
||||||
return from(link);
|
return from(link);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public static boolean isSupported(@NonNull String link) {
|
||||||
|
final String authority = PreviewMediaExtractor.getAuthority(link);
|
||||||
|
if (authority == null || !authority.endsWith(".twimg.com")) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
final String path = PreviewMediaExtractor.getPath(link);
|
||||||
|
if (path == null) return false;
|
||||||
|
if (path.startsWith("/media/")) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -19,10 +19,10 @@ android {
|
||||||
|
|
||||||
defaultConfig {
|
defaultConfig {
|
||||||
applicationId "org.mariotaku.twidere"
|
applicationId "org.mariotaku.twidere"
|
||||||
minSdkVersion 21
|
minSdkVersion 14
|
||||||
targetSdkVersion 23
|
targetSdkVersion 23
|
||||||
versionCode 155
|
versionCode 156
|
||||||
versionName "3.0.6.4"
|
versionName "3.0.6.5"
|
||||||
multiDexEnabled true
|
multiDexEnabled true
|
||||||
|
|
||||||
buildConfigField 'boolean', 'LEAK_CANARY_ENABLED', 'Boolean.parseBoolean("false")'
|
buildConfigField 'boolean', 'LEAK_CANARY_ENABLED', 'Boolean.parseBoolean("false")'
|
||||||
|
@ -109,13 +109,10 @@ dependencies {
|
||||||
compile 'com.lnikkila:extendedtouchview:0.1.0'
|
compile 'com.lnikkila:extendedtouchview:0.1.0'
|
||||||
compile 'com.google.dagger:dagger:2.0.2'
|
compile 'com.google.dagger:dagger:2.0.2'
|
||||||
compile 'org.attoparser:attoparser:1.4.0.RELEASE'
|
compile 'org.attoparser:attoparser:1.4.0.RELEASE'
|
||||||
compile 'com.github.mariotaku.MediaViewerLibrary:base:0.9.11'
|
compile 'com.github.mariotaku.MediaViewerLibrary:base:0.9.12'
|
||||||
compile 'com.github.mariotaku.MediaViewerLibrary:subsample-image-view:0.9.11'
|
compile 'com.github.mariotaku.MediaViewerLibrary:subsample-image-view:0.9.12'
|
||||||
compile 'com.github.mariotaku.SQLiteQB:library:0.9.5'
|
compile 'com.github.mariotaku.SQLiteQB:library:0.9.5'
|
||||||
compile 'com.github.mariotaku.ObjectCursor:core:0.9.5'
|
compile 'com.github.mariotaku.ObjectCursor:core:0.9.5'
|
||||||
compile 'rapid.decoder:library:0.3.0'
|
|
||||||
compile 'rapid.decoder:jpeg-decoder:0.3.0'
|
|
||||||
compile 'rapid.decoder:png-decoder:0.3.0'
|
|
||||||
|
|
||||||
compile project(':twidere.component.common')
|
compile project(':twidere.component.common')
|
||||||
compile project(':twidere.component.nyan')
|
compile project(':twidere.component.nyan')
|
||||||
|
|
|
@ -0,0 +1,38 @@
|
||||||
|
package org.mariotaku.twidere.activity.support;
|
||||||
|
|
||||||
|
import android.net.Uri;
|
||||||
|
|
||||||
|
import org.junit.Test;
|
||||||
|
|
||||||
|
import static org.junit.Assert.assertEquals;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Created by mariotaku on 16/3/3.
|
||||||
|
*/
|
||||||
|
public class ImagePageFragmentTest {
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testReplaceTwitterMediaUri() throws Exception {
|
||||||
|
assertEquals("https://pbs.twimg.com/media/DEADBEEF.png:large",
|
||||||
|
MediaViewerActivity.ImagePageFragment.replaceTwitterMediaUri(Uri.parse(
|
||||||
|
"https://pbs.twimg.com/media/DEADBEEF.png:large")).toString());
|
||||||
|
assertEquals("https://pbs.twimg.com/media/DEADBEEF.png:orig",
|
||||||
|
MediaViewerActivity.ImagePageFragment.replaceTwitterMediaUri(Uri.parse(
|
||||||
|
"https://pbs.twimg.com/media/DEADBEEF.png:orig")).toString());
|
||||||
|
assertEquals("https://pbs.twimg.com/media/DEADBEEF.png:large",
|
||||||
|
MediaViewerActivity.ImagePageFragment.replaceTwitterMediaUri(Uri.parse(
|
||||||
|
"https://pbs.twimg.com/media/DEADBEEF.jpg:large")).toString());
|
||||||
|
assertEquals("https://pbs.twimg.com/media/DEADBEEF.png:large",
|
||||||
|
MediaViewerActivity.ImagePageFragment.replaceTwitterMediaUri(Uri.parse(
|
||||||
|
"https://pbs.twimg.com/media/DEADBEEF.jpg:orig")).toString());
|
||||||
|
assertEquals("https://pbs.twimg.com/media/DEADBEEF.png",
|
||||||
|
MediaViewerActivity.ImagePageFragment.replaceTwitterMediaUri(Uri.parse(
|
||||||
|
"https://pbs.twimg.com/media/DEADBEEF.jpg")).toString());
|
||||||
|
assertEquals("https://pbs.twimg.com/media/DEADBEEF.png:",
|
||||||
|
MediaViewerActivity.ImagePageFragment.replaceTwitterMediaUri(Uri.parse(
|
||||||
|
"https://pbs.twimg.com/media/DEADBEEF.jpg:")).toString());
|
||||||
|
assertEquals("https://example.com/media/DEADBEEF.jpg",
|
||||||
|
MediaViewerActivity.ImagePageFragment.replaceTwitterMediaUri(Uri.parse(
|
||||||
|
"https://example.com/media/DEADBEEF.jpg")).toString());
|
||||||
|
}
|
||||||
|
}
|
|
@ -93,9 +93,8 @@ import org.mariotaku.twidere.util.PermissionUtils;
|
||||||
import org.mariotaku.twidere.util.ThemeUtils;
|
import org.mariotaku.twidere.util.ThemeUtils;
|
||||||
import org.mariotaku.twidere.util.Utils;
|
import org.mariotaku.twidere.util.Utils;
|
||||||
import org.mariotaku.twidere.util.dagger.GeneralComponentHelper;
|
import org.mariotaku.twidere.util.dagger.GeneralComponentHelper;
|
||||||
import org.mariotaku.twidere.util.imageviewer.RapidImageDecoder;
|
|
||||||
import org.mariotaku.twidere.util.imageviewer.RapidImageRegionDecoder;
|
|
||||||
import org.mariotaku.twidere.util.media.MediaExtra;
|
import org.mariotaku.twidere.util.media.MediaExtra;
|
||||||
|
import org.mariotaku.twidere.util.media.preview.provider.TwitterMediaProvider;
|
||||||
|
|
||||||
import java.io.File;
|
import java.io.File;
|
||||||
import java.util.concurrent.TimeUnit;
|
import java.util.concurrent.TimeUnit;
|
||||||
|
@ -826,6 +825,13 @@ public final class MediaViewerActivity extends AbsMediaViewerActivity implements
|
||||||
protected Object getDownloadExtra() {
|
protected Object getDownloadExtra() {
|
||||||
final MediaExtra mediaExtra = new MediaExtra();
|
final MediaExtra mediaExtra = new MediaExtra();
|
||||||
mediaExtra.setAccountId(getArguments().getLong(EXTRA_ACCOUNT_ID, -1));
|
mediaExtra.setAccountId(getArguments().getLong(EXTRA_ACCOUNT_ID, -1));
|
||||||
|
final Uri origDownloadUri = super.getDownloadUri();
|
||||||
|
final Uri downloadUri = getDownloadUri();
|
||||||
|
if (origDownloadUri != null && downloadUri != null) {
|
||||||
|
final String fallbackUrl = origDownloadUri.toString();
|
||||||
|
mediaExtra.setFallbackUrl(fallbackUrl);
|
||||||
|
mediaExtra.setSkipUrlReplacing(!fallbackUrl.equals(downloadUri.toString()));
|
||||||
|
}
|
||||||
return mediaExtra;
|
return mediaExtra;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -844,6 +850,36 @@ public final class MediaViewerActivity extends AbsMediaViewerActivity implements
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Nullable
|
||||||
|
@Override
|
||||||
|
protected Uri getDownloadUri() {
|
||||||
|
final Uri downloadUri = super.getDownloadUri();
|
||||||
|
if (downloadUri == null) return null;
|
||||||
|
return replaceTwitterMediaUri(downloadUri);
|
||||||
|
}
|
||||||
|
|
||||||
|
static Uri replaceTwitterMediaUri(Uri downloadUri) {
|
||||||
|
String uriString = downloadUri.toString();
|
||||||
|
if (TwitterMediaProvider.isSupported(uriString)) {
|
||||||
|
final String suffix = ".jpg";
|
||||||
|
int lastIndexOfJpegSuffix = uriString.lastIndexOf(suffix);
|
||||||
|
if (lastIndexOfJpegSuffix == -1) return downloadUri;
|
||||||
|
final int endOfSuffix = lastIndexOfJpegSuffix + suffix.length();
|
||||||
|
if (endOfSuffix == uriString.length()) {
|
||||||
|
return Uri.parse(uriString.substring(0, lastIndexOfJpegSuffix) + ".png");
|
||||||
|
} else {
|
||||||
|
// Seems :orig suffix won't work jpegs -> pngs
|
||||||
|
String sizeSuffix = uriString.substring(endOfSuffix);
|
||||||
|
if (":orig".equals(sizeSuffix)) {
|
||||||
|
sizeSuffix = ":large";
|
||||||
|
}
|
||||||
|
return Uri.parse(uriString.substring(0, lastIndexOfJpegSuffix) + ".png" +
|
||||||
|
sizeSuffix);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return downloadUri;
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public boolean hasDownloadedData() {
|
public boolean hasDownloadedData() {
|
||||||
return super.hasDownloadedData() && mMediaLoadState != State.ERROR;
|
return super.hasDownloadedData() && mMediaLoadState != State.ERROR;
|
||||||
|
@ -860,8 +896,7 @@ public final class MediaViewerActivity extends AbsMediaViewerActivity implements
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected void setupImageView(SubsamplingScaleImageView imageView) {
|
protected void setupImageView(SubsamplingScaleImageView imageView) {
|
||||||
imageView.setRegionDecoderClass(RapidImageRegionDecoder.class);
|
imageView.setMaxScale(getResources().getDisplayMetrics().density);
|
||||||
imageView.setBitmapDecoderClass(RapidImageDecoder.class);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -15,7 +15,6 @@ import android.webkit.MimeTypeMap;
|
||||||
import com.bluelinelabs.logansquare.JsonMapper;
|
import com.bluelinelabs.logansquare.JsonMapper;
|
||||||
import com.nostra13.universalimageloader.cache.disc.DiskCache;
|
import com.nostra13.universalimageloader.cache.disc.DiskCache;
|
||||||
|
|
||||||
import org.mariotaku.mediaviewer.library.CacheDownloadLoader;
|
|
||||||
import org.mariotaku.restfu.RestFuUtils;
|
import org.mariotaku.restfu.RestFuUtils;
|
||||||
import org.mariotaku.twidere.TwidereConstants;
|
import org.mariotaku.twidere.TwidereConstants;
|
||||||
import org.mariotaku.twidere.model.CacheMetadata;
|
import org.mariotaku.twidere.model.CacheMetadata;
|
||||||
|
@ -65,7 +64,11 @@ public class CacheProvider extends ContentProvider implements TwidereConstants {
|
||||||
throw new IllegalArgumentException(uri.toString());
|
throw new IllegalArgumentException(uri.toString());
|
||||||
if (!TwidereConstants.AUTHORITY_TWIDERE_CACHE.equals(uri.getAuthority()))
|
if (!TwidereConstants.AUTHORITY_TWIDERE_CACHE.equals(uri.getAuthority()))
|
||||||
throw new IllegalArgumentException(uri.toString());
|
throw new IllegalArgumentException(uri.toString());
|
||||||
return CacheDownloadLoader.getExtraKey(ByteString.decodeBase64(uri.getLastPathSegment()).utf8());
|
return getExtraKey(ByteString.decodeBase64(uri.getLastPathSegment()).utf8());
|
||||||
|
}
|
||||||
|
|
||||||
|
public static String getExtraKey(String key) {
|
||||||
|
return key + ".extra";
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
|
|
@ -1,26 +0,0 @@
|
||||||
package org.mariotaku.twidere.util.imageviewer;
|
|
||||||
|
|
||||||
import android.content.Context;
|
|
||||||
import android.graphics.Bitmap;
|
|
||||||
import android.net.Uri;
|
|
||||||
|
|
||||||
import com.davemorrissey.labs.subscaleview.decoder.ImageDecoder;
|
|
||||||
|
|
||||||
import rapid.decoder.BitmapDecoder;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* A very simple implementation of {@link com.davemorrissey.labs.subscaleview.decoder.ImageRegionDecoder}
|
|
||||||
* using the RapidDecoder library (https://github.com/suckgamony/RapidDecoder). For PNGs, this can
|
|
||||||
* give more reliable decoding and better performance. For JPGs, it is slower and can run out of
|
|
||||||
* memory with large images, but has better support for grayscale and CMYK images.
|
|
||||||
*
|
|
||||||
* This is an incomplete and untested implementation provided as an example only.
|
|
||||||
*/
|
|
||||||
public class RapidImageDecoder implements ImageDecoder {
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public Bitmap decode(Context context, Uri uri) throws Exception {
|
|
||||||
return BitmapDecoder.from(context, uri).useBuiltInDecoder(true).config(Bitmap.Config.RGB_565).decode();
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
|
@ -1,57 +0,0 @@
|
||||||
package org.mariotaku.twidere.util.imageviewer;
|
|
||||||
|
|
||||||
import android.content.Context;
|
|
||||||
import android.graphics.Bitmap;
|
|
||||||
import android.graphics.Point;
|
|
||||||
import android.graphics.Rect;
|
|
||||||
import android.net.Uri;
|
|
||||||
|
|
||||||
import com.davemorrissey.labs.subscaleview.decoder.ImageRegionDecoder;
|
|
||||||
|
|
||||||
import rapid.decoder.BitmapDecoder;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* A very simple implementation of {@link com.davemorrissey.labs.subscaleview.decoder.ImageRegionDecoder}
|
|
||||||
* using the RapidDecoder library (https://github.com/suckgamony/RapidDecoder). For PNGs, this can
|
|
||||||
* give more reliable decoding and better performance. For JPGs, it is slower and can run out of
|
|
||||||
* memory with large images, but has better support for grayscale and CMYK images.
|
|
||||||
* <p/>
|
|
||||||
* This is an incomplete and untested implementation provided as an example only.
|
|
||||||
*/
|
|
||||||
public class RapidImageRegionDecoder implements ImageRegionDecoder {
|
|
||||||
|
|
||||||
private BitmapDecoder decoder;
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public Point init(Context context, Uri uri) throws Exception {
|
|
||||||
decoder = BitmapDecoder.from(context, uri);
|
|
||||||
decoder.useBuiltInDecoder(true);
|
|
||||||
return new Point(decoder.sourceWidth(), decoder.sourceHeight());
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public synchronized Bitmap decodeRegion(Rect sRect, int sampleSize) {
|
|
||||||
try {
|
|
||||||
return decoder.reset().region(sRect).scale(sRect.width() / sampleSize, sRect.height() / sampleSize).decode();
|
|
||||||
} catch (Exception e) {
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public boolean isReady() {
|
|
||||||
return decoder != null;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void recycle() {
|
|
||||||
BitmapDecoder.destroyMemoryCache();
|
|
||||||
BitmapDecoder.destroyDiskCache();
|
|
||||||
try {
|
|
||||||
decoder.reset();
|
|
||||||
} catch (UnsupportedOperationException ignore) {
|
|
||||||
|
|
||||||
}
|
|
||||||
decoder = null;
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -7,6 +7,7 @@ public class MediaExtra {
|
||||||
long accountId;
|
long accountId;
|
||||||
boolean useThumbor = true;
|
boolean useThumbor = true;
|
||||||
String fallbackUrl;
|
String fallbackUrl;
|
||||||
|
boolean skipUrlReplacing;
|
||||||
|
|
||||||
public long getAccountId() {
|
public long getAccountId() {
|
||||||
return accountId;
|
return accountId;
|
||||||
|
@ -31,4 +32,12 @@ public class MediaExtra {
|
||||||
public void setFallbackUrl(String fallbackUrl) {
|
public void setFallbackUrl(String fallbackUrl) {
|
||||||
this.fallbackUrl = fallbackUrl;
|
this.fallbackUrl = fallbackUrl;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public boolean isSkipUrlReplacing() {
|
||||||
|
return skipUrlReplacing;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setSkipUrlReplacing(boolean skipUrlReplacing) {
|
||||||
|
this.skipUrlReplacing = skipUrlReplacing;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -82,12 +82,17 @@ public class TwidereMediaDownloader implements MediaDownloader, Constants {
|
||||||
@Override
|
@Override
|
||||||
public CacheDownloadLoader.DownloadResult get(@NonNull String url, Object extra) throws IOException {
|
public CacheDownloadLoader.DownloadResult get(@NonNull String url, Object extra) throws IOException {
|
||||||
try {
|
try {
|
||||||
|
boolean skipUrlReplacing = false;
|
||||||
|
if (extra instanceof MediaExtra) {
|
||||||
|
skipUrlReplacing = ((MediaExtra) extra).isSkipUrlReplacing();
|
||||||
|
}
|
||||||
|
if (!skipUrlReplacing) {
|
||||||
final ParcelableMedia media = PreviewMediaExtractor.fromLink(url, mClient, extra);
|
final ParcelableMedia media = PreviewMediaExtractor.fromLink(url, mClient, extra);
|
||||||
if (media != null && media.media_url != null) {
|
if (media != null && media.media_url != null) {
|
||||||
return getInternal(media.media_url, extra);
|
return getInternal(media.media_url, extra);
|
||||||
} else {
|
|
||||||
return getInternal(url, extra);
|
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
return getInternal(url, extra);
|
||||||
} catch (IOException e) {
|
} catch (IOException e) {
|
||||||
if (extra instanceof MediaExtra) {
|
if (extra instanceof MediaExtra) {
|
||||||
final String fallbackUrl = ((MediaExtra) extra).getFallbackUrl();
|
final String fallbackUrl = ((MediaExtra) extra).getFallbackUrl();
|
||||||
|
|
|
@ -8,6 +8,7 @@ import com.nostra13.universalimageloader.utils.IoUtils;
|
||||||
import org.mariotaku.mediaviewer.library.FileCache;
|
import org.mariotaku.mediaviewer.library.FileCache;
|
||||||
import org.mariotaku.twidere.provider.CacheProvider;
|
import org.mariotaku.twidere.provider.CacheProvider;
|
||||||
|
|
||||||
|
import java.io.ByteArrayInputStream;
|
||||||
import java.io.File;
|
import java.io.File;
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
import java.io.InputStream;
|
import java.io.InputStream;
|
||||||
|
@ -34,13 +35,17 @@ public class UILFileCache implements FileCache {
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void save(final String key, final InputStream is, final CopyListener listener) throws IOException {
|
public void save(final String key, final InputStream is, byte[] extra,
|
||||||
|
final CopyListener listener) throws IOException {
|
||||||
cache.save(key, is, new IoUtils.CopyListener() {
|
cache.save(key, is, new IoUtils.CopyListener() {
|
||||||
@Override
|
@Override
|
||||||
public boolean onBytesCopied(final int current, final int total) {
|
public boolean onBytesCopied(final int current, final int total) {
|
||||||
return listener == null || listener.onCopied(current);
|
return listener == null || listener.onCopied(current);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
if (extra != null) {
|
||||||
|
cache.save(CacheProvider.getExtraKey(key), new ByteArrayInputStream(extra), null);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
|
Loading…
Reference in New Issue