parent
be05ac0775
commit
dc4cdc63fd
|
@ -287,7 +287,7 @@ public interface SharedPreferenceConstants {
|
|||
@Preference(type = STRING, hasDefault = true)
|
||||
String KEY_PROFILE_IMAGE_STYLE = "profile_image_style";
|
||||
|
||||
@Preference(type = BOOLEAN, hasDefault = true, defaultBoolean = true)
|
||||
@Preference(type = BOOLEAN, hasDefault = true, defaultBoolean = false)
|
||||
String KEY_BANDWIDTH_SAVING_MODE = "bandwidth_saving_mode";
|
||||
|
||||
@Preference(type = STRING)
|
||||
|
|
|
@ -109,9 +109,8 @@ dependencies {
|
|||
compile 'com.lnikkila:extendedtouchview:0.1.0'
|
||||
compile 'com.google.dagger:dagger:2.0.2'
|
||||
compile 'org.attoparser:attoparser:1.4.0.RELEASE'
|
||||
compile 'com.j256.simplemagic:simplemagic:1.6'
|
||||
compile 'com.github.mariotaku.MediaViewerLibrary:base:0.9.9'
|
||||
compile 'com.github.mariotaku.MediaViewerLibrary:subsample-image-view:0.9.9'
|
||||
compile 'com.github.mariotaku.MediaViewerLibrary:base:0.9.10'
|
||||
compile 'com.github.mariotaku.MediaViewerLibrary:subsample-image-view:0.9.10'
|
||||
compile 'com.github.mariotaku:SQLiteQB:0.9.4'
|
||||
compile 'com.github.mariotaku.ObjectCursor:core:0.9.4'
|
||||
compile project(':twidere.component.common')
|
||||
|
|
|
@ -321,7 +321,11 @@ public final class MediaViewerActivity extends AbsMediaViewerActivity implements
|
|||
}
|
||||
case R.id.share: {
|
||||
if (object instanceof CacheDownloadMediaViewerFragment) {
|
||||
shareMedia();
|
||||
if (object instanceof VideoPageFragment) {
|
||||
shareMedia(CacheProvider.Type.VIDEO);
|
||||
} else if (object instanceof ImagePageFragment) {
|
||||
shareMedia(CacheProvider.Type.IMAGE);
|
||||
}
|
||||
} else {
|
||||
final ParcelableMedia media = getMedia()[currentItem];
|
||||
final Intent intent = new Intent(Intent.ACTION_SEND);
|
||||
|
@ -364,7 +368,7 @@ public final class MediaViewerActivity extends AbsMediaViewerActivity implements
|
|||
}
|
||||
|
||||
|
||||
protected final void shareMedia() {
|
||||
protected final void shareMedia(@CacheProvider.Type final String type) {
|
||||
final ViewPager viewPager = findViewPager();
|
||||
final PagerAdapter adapter = viewPager.getAdapter();
|
||||
final Object object = adapter.instantiateItem(viewPager, viewPager.getCurrentItem());
|
||||
|
@ -376,7 +380,7 @@ public final class MediaViewerActivity extends AbsMediaViewerActivity implements
|
|||
}
|
||||
final File destination = new File(getCacheDir(), "shared_files");
|
||||
final SaveFileTask task = new SaveFileTask(this, result.cacheUri, destination,
|
||||
new CacheProvider.CacheFileTypeCallback(this)) {
|
||||
new CacheProvider.CacheFileTypeCallback(this, type)) {
|
||||
private static final String PROGRESS_FRAGMENT_TAG = "progress";
|
||||
|
||||
protected void dismissProgress() {
|
||||
|
@ -462,9 +466,15 @@ public final class MediaViewerActivity extends AbsMediaViewerActivity implements
|
|||
if (result == null) return;
|
||||
if (mSaveFileTask != null && mSaveFileTask.getStatus() == AsyncTask.Status.RUNNING) return;
|
||||
final Uri cacheUri = result.cacheUri;
|
||||
final boolean hasImage = cacheUri != null;
|
||||
if (!hasImage) return;
|
||||
mSaveFileTask = SaveImageToGalleryTask.create(this, cacheUri);
|
||||
final boolean hasMedia = cacheUri != null;
|
||||
if (!hasMedia) return;
|
||||
if (f instanceof ImagePageFragment) {
|
||||
mSaveFileTask = SaveImageToGalleryTask.create(this, cacheUri, CacheProvider.Type.IMAGE);
|
||||
} else if (f instanceof VideoPageFragment) {
|
||||
mSaveFileTask = SaveImageToGalleryTask.create(this, cacheUri, CacheProvider.Type.VIDEO);
|
||||
} else {
|
||||
throw new UnsupportedOperationException();
|
||||
}
|
||||
AsyncTaskUtils.executeTask(mSaveFileTask);
|
||||
}
|
||||
|
||||
|
|
|
@ -0,0 +1,28 @@
|
|||
package org.mariotaku.twidere.model;
|
||||
|
||||
import com.bluelinelabs.logansquare.annotation.JsonField;
|
||||
import com.bluelinelabs.logansquare.annotation.JsonObject;
|
||||
|
||||
/**
|
||||
* Created by mariotaku on 16/2/22.
|
||||
*/
|
||||
@JsonObject
|
||||
public class CacheMetadata {
|
||||
@JsonField(name = "content_type")
|
||||
String contentType;
|
||||
|
||||
public String getContentType() {
|
||||
return contentType;
|
||||
}
|
||||
|
||||
public void setContentType(String contentType) {
|
||||
this.contentType = contentType;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return "CacheMetadata{" +
|
||||
"contentType='" + contentType + '\'' +
|
||||
'}';
|
||||
}
|
||||
}
|
|
@ -9,16 +9,23 @@ import android.net.Uri;
|
|||
import android.os.ParcelFileDescriptor;
|
||||
import android.support.annotation.NonNull;
|
||||
import android.support.annotation.Nullable;
|
||||
import android.support.annotation.StringDef;
|
||||
import android.webkit.MimeTypeMap;
|
||||
|
||||
import com.j256.simplemagic.ContentInfoUtil;
|
||||
import com.bluelinelabs.logansquare.JsonMapper;
|
||||
import com.nostra13.universalimageloader.cache.disc.DiskCache;
|
||||
|
||||
import org.mariotaku.mediaviewer.library.CacheDownloadLoader;
|
||||
import org.mariotaku.restfu.RestFuUtils;
|
||||
import org.mariotaku.twidere.TwidereConstants;
|
||||
import org.mariotaku.twidere.model.CacheMetadata;
|
||||
import org.mariotaku.twidere.task.SaveFileTask;
|
||||
import org.mariotaku.twidere.util.LoganSquareMapperFinder;
|
||||
import org.mariotaku.twidere.util.Utils;
|
||||
import org.mariotaku.twidere.util.dagger.GeneralComponentHelper;
|
||||
|
||||
import java.io.File;
|
||||
import java.io.FileInputStream;
|
||||
import java.io.FileNotFoundException;
|
||||
import java.io.IOException;
|
||||
|
||||
|
@ -29,16 +36,19 @@ import okio.ByteString;
|
|||
/**
|
||||
* Created by mariotaku on 16/1/1.
|
||||
*/
|
||||
public class CacheProvider extends ContentProvider {
|
||||
public class CacheProvider extends ContentProvider implements TwidereConstants {
|
||||
@Inject
|
||||
DiskCache mSimpleDiskCache;
|
||||
private ContentInfoUtil mContentInfoUtil;
|
||||
|
||||
public static Uri getCacheUri(String key) {
|
||||
return new Uri.Builder().scheme(ContentResolver.SCHEME_CONTENT)
|
||||
.authority(TwidereConstants.AUTHORITY_TWIDERE_CACHE)
|
||||
.appendPath(ByteString.encodeUtf8(key).base64Url())
|
||||
.build();
|
||||
public static Uri getCacheUri(String key, @Type String type) {
|
||||
final Uri.Builder builder = new Uri.Builder();
|
||||
builder.scheme(ContentResolver.SCHEME_CONTENT);
|
||||
builder.authority(TwidereConstants.AUTHORITY_TWIDERE_CACHE);
|
||||
builder.appendPath(ByteString.encodeUtf8(key).base64Url());
|
||||
if (type != null) {
|
||||
builder.appendQueryParameter(QUERY_PARAM_TYPE, type);
|
||||
}
|
||||
return builder.build();
|
||||
}
|
||||
|
||||
public static String getCacheKey(Uri uri) {
|
||||
|
@ -49,11 +59,19 @@ public class CacheProvider extends ContentProvider {
|
|||
return ByteString.decodeBase64(uri.getLastPathSegment()).utf8();
|
||||
}
|
||||
|
||||
|
||||
public static String getMetadataKey(Uri uri) {
|
||||
if (!ContentResolver.SCHEME_CONTENT.equals(uri.getScheme()))
|
||||
throw new IllegalArgumentException(uri.toString());
|
||||
if (!TwidereConstants.AUTHORITY_TWIDERE_CACHE.equals(uri.getAuthority()))
|
||||
throw new IllegalArgumentException(uri.toString());
|
||||
return CacheDownloadLoader.getExtraKey(ByteString.decodeBase64(uri.getLastPathSegment()).utf8());
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean onCreate() {
|
||||
final Context context = getContext();
|
||||
assert context != null;
|
||||
mContentInfoUtil = new ContentInfoUtil();
|
||||
GeneralComponentHelper.build(context).inject(this);
|
||||
return true;
|
||||
}
|
||||
|
@ -67,12 +85,41 @@ public class CacheProvider extends ContentProvider {
|
|||
@Nullable
|
||||
@Override
|
||||
public String getType(@NonNull Uri uri) {
|
||||
final File file = mSimpleDiskCache.get(getCacheKey(uri));
|
||||
final CacheMetadata metadata = getMetadata(uri);
|
||||
if (metadata != null) {
|
||||
return metadata.getContentType();
|
||||
}
|
||||
final String type = uri.getQueryParameter(QUERY_PARAM_TYPE);
|
||||
if (type != null) {
|
||||
switch (type) {
|
||||
case Type.IMAGE: {
|
||||
final File file = mSimpleDiskCache.get(getCacheKey(uri));
|
||||
if (file == null) return null;
|
||||
return Utils.getImageMimeType(file);
|
||||
}
|
||||
case Type.VIDEO: {
|
||||
return "video/mp4";
|
||||
}
|
||||
case Type.JSON: {
|
||||
return "application/json";
|
||||
}
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
public CacheMetadata getMetadata(@NonNull Uri uri) {
|
||||
final File file = mSimpleDiskCache.get(getMetadataKey(uri));
|
||||
if (file == null) return null;
|
||||
FileInputStream is = null;
|
||||
try {
|
||||
return mContentInfoUtil.findMatch(file).getMimeType();
|
||||
final JsonMapper<CacheMetadata> mapper = LoganSquareMapperFinder.mapperFor(CacheMetadata.class);
|
||||
is = new FileInputStream(file);
|
||||
return mapper.parse(is);
|
||||
} catch (IOException e) {
|
||||
return null;
|
||||
} finally {
|
||||
RestFuUtils.closeSilently(is);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -137,9 +184,11 @@ public class CacheProvider extends ContentProvider {
|
|||
|
||||
public static final class CacheFileTypeCallback implements SaveFileTask.FileInfoCallback {
|
||||
private final Context context;
|
||||
private final String type;
|
||||
|
||||
public CacheFileTypeCallback(Context context) {
|
||||
public CacheFileTypeCallback(Context context, @Type String type) {
|
||||
this.context = context;
|
||||
this.type = type;
|
||||
}
|
||||
|
||||
@Nullable
|
||||
|
@ -151,13 +200,27 @@ public class CacheProvider extends ContentProvider {
|
|||
}
|
||||
|
||||
@Override
|
||||
@Nullable
|
||||
public String getMimeType(@NonNull Uri source) {
|
||||
return context.getContentResolver().getType(source);
|
||||
if (type == null || source.getQueryParameter(QUERY_PARAM_TYPE) != null) {
|
||||
return context.getContentResolver().getType(source);
|
||||
}
|
||||
final Uri.Builder builder = source.buildUpon();
|
||||
builder.appendQueryParameter(QUERY_PARAM_TYPE, type);
|
||||
return context.getContentResolver().getType(builder.build());
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getExtension(String mimeType) {
|
||||
public String getExtension(@Nullable String mimeType) {
|
||||
if (mimeType == null) return null;
|
||||
return MimeTypeMap.getSingleton().getExtensionFromMimeType(mimeType);
|
||||
}
|
||||
}
|
||||
|
||||
@StringDef({Type.IMAGE, Type.VIDEO, Type.JSON})
|
||||
public @interface Type {
|
||||
String IMAGE = "image";
|
||||
String VIDEO = "video";
|
||||
String JSON = "json";
|
||||
}
|
||||
}
|
||||
|
|
|
@ -54,10 +54,10 @@ public abstract class SaveFileTask extends AsyncTask<Object, Object, SaveFileTas
|
|||
private final FileInfoCallback getMimeType;
|
||||
|
||||
public SaveFileTask(@NonNull final Context context, @NonNull final Uri source,
|
||||
@NonNull final File destination, @NonNull final FileInfoCallback getMimeType) {
|
||||
@NonNull final File destination, @NonNull final FileInfoCallback callback) {
|
||||
this.contextRef = new WeakReference<>(context);
|
||||
this.source = source;
|
||||
this.getMimeType = getMimeType;
|
||||
this.getMimeType = callback;
|
||||
this.destination = destination;
|
||||
}
|
||||
|
||||
|
@ -175,31 +175,4 @@ public abstract class SaveFileTask extends AsyncTask<Object, Object, SaveFileTas
|
|||
}
|
||||
}
|
||||
|
||||
public static class StringFileInfoCallback implements FileInfoCallback {
|
||||
private final String filename;
|
||||
private final String mimeType;
|
||||
|
||||
public StringFileInfoCallback(String filename, String mimeType) {
|
||||
this.filename = filename;
|
||||
this.mimeType = mimeType;
|
||||
}
|
||||
|
||||
@Nullable
|
||||
@Override
|
||||
public String getFilename(@NonNull Uri source) {
|
||||
return filename;
|
||||
}
|
||||
|
||||
@Override
|
||||
@Nullable
|
||||
public String getMimeType(@NonNull Uri source) {
|
||||
return mimeType;
|
||||
}
|
||||
|
||||
@Override
|
||||
@Nullable
|
||||
public String getExtension(@Nullable String mimeType) {
|
||||
return MimeTypeMap.getSingleton().getExtensionFromMimeType(mimeType);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -38,14 +38,29 @@ import java.io.File;
|
|||
*/
|
||||
public class SaveImageToGalleryTask extends ProgressSaveFileTask {
|
||||
|
||||
public SaveImageToGalleryTask(@NonNull Activity activity, @NonNull Uri source, @NonNull File destination) {
|
||||
super(activity, source, destination, new CacheProvider.CacheFileTypeCallback(activity));
|
||||
public SaveImageToGalleryTask(@NonNull Activity activity, @NonNull Uri source, @NonNull File destination, String type) {
|
||||
super(activity, source, destination, new CacheProvider.CacheFileTypeCallback(activity, type));
|
||||
}
|
||||
|
||||
public static SaveFileTask create(final Activity activity, final Uri source) {
|
||||
final File pubDir = Environment.getExternalStoragePublicDirectory(Environment.DIRECTORY_PICTURES);
|
||||
public static SaveFileTask create(final Activity activity, final Uri source,
|
||||
@NonNull @CacheProvider.Type final String type) {
|
||||
final File pubDir;
|
||||
switch (type) {
|
||||
case CacheProvider.Type.VIDEO: {
|
||||
pubDir = Environment.getExternalStoragePublicDirectory(Environment.DIRECTORY_MOVIES);
|
||||
break;
|
||||
}
|
||||
case CacheProvider.Type.IMAGE: {
|
||||
pubDir = Environment.getExternalStoragePublicDirectory(Environment.DIRECTORY_PICTURES);
|
||||
break;
|
||||
}
|
||||
default: {
|
||||
pubDir = Environment.getExternalStoragePublicDirectory(Environment.DIRECTORY_DOWNLOADS);
|
||||
break;
|
||||
}
|
||||
}
|
||||
final File saveDir = new File(pubDir, "Twidere");
|
||||
return new SaveImageToGalleryTask(activity, source, saveDir);
|
||||
return new SaveImageToGalleryTask(activity, source, saveDir, type);
|
||||
}
|
||||
|
||||
@Override
|
||||
|
|
|
@ -25,9 +25,11 @@ import org.mariotaku.restfu.http.mime.Body;
|
|||
import org.mariotaku.twidere.Constants;
|
||||
import org.mariotaku.twidere.api.twitter.auth.OAuthAuthorization;
|
||||
import org.mariotaku.twidere.api.twitter.auth.OAuthEndpoint;
|
||||
import org.mariotaku.twidere.model.CacheMetadata;
|
||||
import org.mariotaku.twidere.model.ParcelableCredentials;
|
||||
import org.mariotaku.twidere.model.ParcelableMedia;
|
||||
import org.mariotaku.twidere.util.DataStoreUtils;
|
||||
import org.mariotaku.twidere.util.JsonSerializer;
|
||||
import org.mariotaku.twidere.util.SharedPreferencesWrapper;
|
||||
import org.mariotaku.twidere.util.TwitterAPIFactory;
|
||||
import org.mariotaku.twidere.util.UserAgentUtils;
|
||||
|
@ -138,24 +140,9 @@ public class TwidereMediaDownloader implements MediaDownloader, Constants {
|
|||
throw new IOException("Unable to get media, response code: " + resp.getStatus());
|
||||
}
|
||||
final Body body = resp.getBody();
|
||||
final long length = body.length();
|
||||
final InputStream stream = body.stream();
|
||||
return new CacheDownloadLoader.DownloadResult() {
|
||||
@Override
|
||||
public void close() throws IOException {
|
||||
body.close();
|
||||
}
|
||||
|
||||
@Override
|
||||
public long getLength() {
|
||||
return length;
|
||||
}
|
||||
|
||||
@Override
|
||||
public InputStream getStream() {
|
||||
return stream;
|
||||
}
|
||||
};
|
||||
final CacheMetadata metadata = new CacheMetadata();
|
||||
metadata.setContentType(body.contentType().getContentType());
|
||||
return new TwidereDownloadResult(body, metadata);
|
||||
}
|
||||
|
||||
private String getEndpoint(Uri uri) {
|
||||
|
@ -206,4 +193,37 @@ public class TwidereMediaDownloader implements MediaDownloader, Constants {
|
|||
}
|
||||
return uri;
|
||||
}
|
||||
|
||||
private static class TwidereDownloadResult implements CacheDownloadLoader.DownloadResult {
|
||||
private final Body mBody;
|
||||
private final CacheMetadata mMetadata;
|
||||
|
||||
public TwidereDownloadResult(Body body, CacheMetadata metadata) {
|
||||
mBody = body;
|
||||
mMetadata = metadata;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void close() throws IOException {
|
||||
mBody.close();
|
||||
}
|
||||
|
||||
@Override
|
||||
public long getLength() throws IOException {
|
||||
return mBody.length();
|
||||
}
|
||||
|
||||
@Override
|
||||
public InputStream getStream() throws IOException {
|
||||
return mBody.stream();
|
||||
}
|
||||
|
||||
@Override
|
||||
public byte[] getExtra() {
|
||||
if (mMetadata == null) return null;
|
||||
final String serialize = JsonSerializer.serialize(mMetadata, CacheMetadata.class);
|
||||
if (serialize == null) return null;
|
||||
return serialize.getBytes();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -45,7 +45,7 @@ public class UILFileCache implements FileCache {
|
|||
|
||||
@Override
|
||||
public Uri toUri(final String key) {
|
||||
return CacheProvider.getCacheUri(key);
|
||||
return CacheProvider.getCacheUri(key, null);
|
||||
}
|
||||
|
||||
@Override
|
||||
|
|
|
@ -13,7 +13,7 @@
|
|||
android:title="@string/preload_wifi_only"/>
|
||||
|
||||
<org.mariotaku.twidere.preference.AutoFixSwitchPreference
|
||||
android:defaultValue="true"
|
||||
android:defaultValue="false"
|
||||
android:key="bandwidth_saving_mode"
|
||||
android:summary="@string/bandwidth_saving_mode_summary"
|
||||
android:title="@string/bandwidth_saving_mode"/>
|
||||
|
|
Loading…
Reference in New Issue