made bandwidth saving mode default off

improved cache
This commit is contained in:
Mariotaku Lee 2016-02-22 23:01:54 +08:00
parent be05ac0775
commit dc4cdc63fd
10 changed files with 186 additions and 78 deletions

View File

@ -287,7 +287,7 @@ public interface SharedPreferenceConstants {
@Preference(type = STRING, hasDefault = true) @Preference(type = STRING, hasDefault = true)
String KEY_PROFILE_IMAGE_STYLE = "profile_image_style"; 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"; String KEY_BANDWIDTH_SAVING_MODE = "bandwidth_saving_mode";
@Preference(type = STRING) @Preference(type = STRING)

View File

@ -109,9 +109,8 @@ 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.j256.simplemagic:simplemagic:1.6' compile 'com.github.mariotaku.MediaViewerLibrary:base:0.9.10'
compile 'com.github.mariotaku.MediaViewerLibrary:base:0.9.9' compile 'com.github.mariotaku.MediaViewerLibrary:subsample-image-view:0.9.10'
compile 'com.github.mariotaku.MediaViewerLibrary:subsample-image-view:0.9.9'
compile 'com.github.mariotaku:SQLiteQB:0.9.4' compile 'com.github.mariotaku:SQLiteQB:0.9.4'
compile 'com.github.mariotaku.ObjectCursor:core:0.9.4' compile 'com.github.mariotaku.ObjectCursor:core:0.9.4'
compile project(':twidere.component.common') compile project(':twidere.component.common')

View File

@ -321,7 +321,11 @@ public final class MediaViewerActivity extends AbsMediaViewerActivity implements
} }
case R.id.share: { case R.id.share: {
if (object instanceof CacheDownloadMediaViewerFragment) { if (object instanceof CacheDownloadMediaViewerFragment) {
shareMedia(); if (object instanceof VideoPageFragment) {
shareMedia(CacheProvider.Type.VIDEO);
} else if (object instanceof ImagePageFragment) {
shareMedia(CacheProvider.Type.IMAGE);
}
} else { } else {
final ParcelableMedia media = getMedia()[currentItem]; final ParcelableMedia media = getMedia()[currentItem];
final Intent intent = new Intent(Intent.ACTION_SEND); 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 ViewPager viewPager = findViewPager();
final PagerAdapter adapter = viewPager.getAdapter(); final PagerAdapter adapter = viewPager.getAdapter();
final Object object = adapter.instantiateItem(viewPager, viewPager.getCurrentItem()); 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 File destination = new File(getCacheDir(), "shared_files");
final SaveFileTask task = new SaveFileTask(this, result.cacheUri, destination, 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"; private static final String PROGRESS_FRAGMENT_TAG = "progress";
protected void dismissProgress() { protected void dismissProgress() {
@ -462,9 +466,15 @@ public final class MediaViewerActivity extends AbsMediaViewerActivity implements
if (result == null) return; if (result == null) return;
if (mSaveFileTask != null && mSaveFileTask.getStatus() == AsyncTask.Status.RUNNING) return; if (mSaveFileTask != null && mSaveFileTask.getStatus() == AsyncTask.Status.RUNNING) return;
final Uri cacheUri = result.cacheUri; final Uri cacheUri = result.cacheUri;
final boolean hasImage = cacheUri != null; final boolean hasMedia = cacheUri != null;
if (!hasImage) return; if (!hasMedia) return;
mSaveFileTask = SaveImageToGalleryTask.create(this, cacheUri); 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); AsyncTaskUtils.executeTask(mSaveFileTask);
} }

View File

@ -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 + '\'' +
'}';
}
}

View File

@ -9,16 +9,23 @@ import android.net.Uri;
import android.os.ParcelFileDescriptor; import android.os.ParcelFileDescriptor;
import android.support.annotation.NonNull; import android.support.annotation.NonNull;
import android.support.annotation.Nullable; import android.support.annotation.Nullable;
import android.support.annotation.StringDef;
import android.webkit.MimeTypeMap; import android.webkit.MimeTypeMap;
import com.j256.simplemagic.ContentInfoUtil; 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.twidere.TwidereConstants; import org.mariotaku.twidere.TwidereConstants;
import org.mariotaku.twidere.model.CacheMetadata;
import org.mariotaku.twidere.task.SaveFileTask; 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 org.mariotaku.twidere.util.dagger.GeneralComponentHelper;
import java.io.File; import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException; import java.io.FileNotFoundException;
import java.io.IOException; import java.io.IOException;
@ -29,16 +36,19 @@ import okio.ByteString;
/** /**
* Created by mariotaku on 16/1/1. * Created by mariotaku on 16/1/1.
*/ */
public class CacheProvider extends ContentProvider { public class CacheProvider extends ContentProvider implements TwidereConstants {
@Inject @Inject
DiskCache mSimpleDiskCache; DiskCache mSimpleDiskCache;
private ContentInfoUtil mContentInfoUtil;
public static Uri getCacheUri(String key) { public static Uri getCacheUri(String key, @Type String type) {
return new Uri.Builder().scheme(ContentResolver.SCHEME_CONTENT) final Uri.Builder builder = new Uri.Builder();
.authority(TwidereConstants.AUTHORITY_TWIDERE_CACHE) builder.scheme(ContentResolver.SCHEME_CONTENT);
.appendPath(ByteString.encodeUtf8(key).base64Url()) builder.authority(TwidereConstants.AUTHORITY_TWIDERE_CACHE);
.build(); builder.appendPath(ByteString.encodeUtf8(key).base64Url());
if (type != null) {
builder.appendQueryParameter(QUERY_PARAM_TYPE, type);
}
return builder.build();
} }
public static String getCacheKey(Uri uri) { public static String getCacheKey(Uri uri) {
@ -49,11 +59,19 @@ public class CacheProvider extends ContentProvider {
return ByteString.decodeBase64(uri.getLastPathSegment()).utf8(); 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 @Override
public boolean onCreate() { public boolean onCreate() {
final Context context = getContext(); final Context context = getContext();
assert context != null; assert context != null;
mContentInfoUtil = new ContentInfoUtil();
GeneralComponentHelper.build(context).inject(this); GeneralComponentHelper.build(context).inject(this);
return true; return true;
} }
@ -67,12 +85,41 @@ public class CacheProvider extends ContentProvider {
@Nullable @Nullable
@Override @Override
public String getType(@NonNull Uri uri) { 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; if (file == null) return null;
FileInputStream is = null;
try { 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) { } catch (IOException e) {
return null; return null;
} finally {
RestFuUtils.closeSilently(is);
} }
} }
@ -137,9 +184,11 @@ public class CacheProvider extends ContentProvider {
public static final class CacheFileTypeCallback implements SaveFileTask.FileInfoCallback { public static final class CacheFileTypeCallback implements SaveFileTask.FileInfoCallback {
private final Context context; private final Context context;
private final String type;
public CacheFileTypeCallback(Context context) { public CacheFileTypeCallback(Context context, @Type String type) {
this.context = context; this.context = context;
this.type = type;
} }
@Nullable @Nullable
@ -151,13 +200,27 @@ public class CacheProvider extends ContentProvider {
} }
@Override @Override
@Nullable
public String getMimeType(@NonNull Uri source) { 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 @Override
public String getExtension(String mimeType) { public String getExtension(@Nullable String mimeType) {
if (mimeType == null) return null;
return MimeTypeMap.getSingleton().getExtensionFromMimeType(mimeType); return MimeTypeMap.getSingleton().getExtensionFromMimeType(mimeType);
} }
} }
@StringDef({Type.IMAGE, Type.VIDEO, Type.JSON})
public @interface Type {
String IMAGE = "image";
String VIDEO = "video";
String JSON = "json";
}
} }

View File

@ -54,10 +54,10 @@ public abstract class SaveFileTask extends AsyncTask<Object, Object, SaveFileTas
private final FileInfoCallback getMimeType; private final FileInfoCallback getMimeType;
public SaveFileTask(@NonNull final Context context, @NonNull final Uri source, 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.contextRef = new WeakReference<>(context);
this.source = source; this.source = source;
this.getMimeType = getMimeType; this.getMimeType = callback;
this.destination = destination; 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);
}
}
} }

View File

@ -38,14 +38,29 @@ import java.io.File;
*/ */
public class SaveImageToGalleryTask extends ProgressSaveFileTask { public class SaveImageToGalleryTask extends ProgressSaveFileTask {
public SaveImageToGalleryTask(@NonNull Activity activity, @NonNull Uri source, @NonNull File destination) { public SaveImageToGalleryTask(@NonNull Activity activity, @NonNull Uri source, @NonNull File destination, String type) {
super(activity, source, destination, new CacheProvider.CacheFileTypeCallback(activity)); super(activity, source, destination, new CacheProvider.CacheFileTypeCallback(activity, type));
} }
public static SaveFileTask create(final Activity activity, final Uri source) { public static SaveFileTask create(final Activity activity, final Uri source,
final File pubDir = Environment.getExternalStoragePublicDirectory(Environment.DIRECTORY_PICTURES); @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"); final File saveDir = new File(pubDir, "Twidere");
return new SaveImageToGalleryTask(activity, source, saveDir); return new SaveImageToGalleryTask(activity, source, saveDir, type);
} }
@Override @Override

View File

@ -25,9 +25,11 @@ import org.mariotaku.restfu.http.mime.Body;
import org.mariotaku.twidere.Constants; import org.mariotaku.twidere.Constants;
import org.mariotaku.twidere.api.twitter.auth.OAuthAuthorization; import org.mariotaku.twidere.api.twitter.auth.OAuthAuthorization;
import org.mariotaku.twidere.api.twitter.auth.OAuthEndpoint; 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.ParcelableCredentials;
import org.mariotaku.twidere.model.ParcelableMedia; import org.mariotaku.twidere.model.ParcelableMedia;
import org.mariotaku.twidere.util.DataStoreUtils; import org.mariotaku.twidere.util.DataStoreUtils;
import org.mariotaku.twidere.util.JsonSerializer;
import org.mariotaku.twidere.util.SharedPreferencesWrapper; import org.mariotaku.twidere.util.SharedPreferencesWrapper;
import org.mariotaku.twidere.util.TwitterAPIFactory; import org.mariotaku.twidere.util.TwitterAPIFactory;
import org.mariotaku.twidere.util.UserAgentUtils; 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()); throw new IOException("Unable to get media, response code: " + resp.getStatus());
} }
final Body body = resp.getBody(); final Body body = resp.getBody();
final long length = body.length(); final CacheMetadata metadata = new CacheMetadata();
final InputStream stream = body.stream(); metadata.setContentType(body.contentType().getContentType());
return new CacheDownloadLoader.DownloadResult() { return new TwidereDownloadResult(body, metadata);
@Override
public void close() throws IOException {
body.close();
}
@Override
public long getLength() {
return length;
}
@Override
public InputStream getStream() {
return stream;
}
};
} }
private String getEndpoint(Uri uri) { private String getEndpoint(Uri uri) {
@ -206,4 +193,37 @@ public class TwidereMediaDownloader implements MediaDownloader, Constants {
} }
return uri; 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();
}
}
} }

View File

@ -45,7 +45,7 @@ public class UILFileCache implements FileCache {
@Override @Override
public Uri toUri(final String key) { public Uri toUri(final String key) {
return CacheProvider.getCacheUri(key); return CacheProvider.getCacheUri(key, null);
} }
@Override @Override

View File

@ -13,7 +13,7 @@
android:title="@string/preload_wifi_only"/> android:title="@string/preload_wifi_only"/>
<org.mariotaku.twidere.preference.AutoFixSwitchPreference <org.mariotaku.twidere.preference.AutoFixSwitchPreference
android:defaultValue="true" android:defaultValue="false"
android:key="bandwidth_saving_mode" android:key="bandwidth_saving_mode"
android:summary="@string/bandwidth_saving_mode_summary" android:summary="@string/bandwidth_saving_mode_summary"
android:title="@string/bandwidth_saving_mode"/> android:title="@string/bandwidth_saving_mode"/>