fixed media sharing on some crappy apps

This commit is contained in:
Mariotaku Lee 2016-04-04 22:09:30 +08:00
parent c0a4e64933
commit fec0f54a5f
6 changed files with 151 additions and 67 deletions

View File

@ -67,7 +67,7 @@ public interface TwidereConstants extends SharedPreferenceConstants, IntentConst
String PROTOCOL_CONTENT = SCHEME_CONTENT + "://"; String PROTOCOL_CONTENT = SCHEME_CONTENT + "://";
String PROTOCOL_TWIDERE = SCHEME_TWIDERE + "://"; String PROTOCOL_TWIDERE = SCHEME_TWIDERE + "://";
String AUTHORITY_TWIDERE_FILE = "twidere.file"; String AUTHORITY_TWIDERE_SHARE = "twidere.share";
String AUTHORITY_TWIDERE_CACHE = "twidere.cache"; String AUTHORITY_TWIDERE_CACHE = "twidere.cache";
String AUTHORITY_USER = "user"; String AUTHORITY_USER = "user";

View File

@ -502,19 +502,15 @@
android:name=".provider.RecentSearchProvider" android:name=".provider.RecentSearchProvider"
android:authorities="org.mariotaku.twidere.provider.SearchRecentSuggestions" android:authorities="org.mariotaku.twidere.provider.SearchRecentSuggestions"
tools:ignore="ExportedContentProvider"/> tools:ignore="ExportedContentProvider"/>
<provider
android:name="android.support.v4.content.FileProvider"
android:authorities="twidere.file"
android:exported="false"
android:grantUriPermissions="true">
<meta-data
android:name="android.support.FILE_PROVIDER_PATHS"
android:resource="@xml/file_paths"/>
</provider>
<provider <provider
android:name=".provider.CacheProvider" android:name=".provider.CacheProvider"
android:authorities="twidere.cache" android:authorities="twidere.cache"
android:exported="false"/> android:exported="false"/>
<provider
android:name=".provider.ShareProvider"
android:authorities="twidere.share"
android:exported="false"
android:grantUriPermissions="true"/>
<receiver android:name=".receiver.ConnectivityStateReceiver"> <receiver android:name=".receiver.ConnectivityStateReceiver">
<intent-filter> <intent-filter>

View File

@ -108,6 +108,7 @@ import org.mariotaku.multivalueswitch.library.MultiValueSwitch;
import org.mariotaku.restfu.RestFuUtils; import org.mariotaku.restfu.RestFuUtils;
import org.mariotaku.twidere.BuildConfig; import org.mariotaku.twidere.BuildConfig;
import org.mariotaku.twidere.R; import org.mariotaku.twidere.R;
import org.mariotaku.twidere.activity.iface.IExtendedActivity;
import org.mariotaku.twidere.adapter.ArrayRecyclerAdapter; import org.mariotaku.twidere.adapter.ArrayRecyclerAdapter;
import org.mariotaku.twidere.adapter.BaseRecyclerViewAdapter; import org.mariotaku.twidere.adapter.BaseRecyclerViewAdapter;
import org.mariotaku.twidere.fragment.BaseSupportDialogFragment; import org.mariotaku.twidere.fragment.BaseSupportDialogFragment;
@ -205,7 +206,6 @@ public class ComposeActivity extends BaseActivity implements OnMenuItemClickList
private AsyncTask<Object, Object, ?> mTask; private AsyncTask<Object, Object, ?> mTask;
private SupportMenuInflater mMenuInflater; private SupportMenuInflater mMenuInflater;
private ItemTouchHelper mItemTouchHelper; private ItemTouchHelper mItemTouchHelper;
private SetProgressVisibleRunnable mSetProgressVisibleRunnable;
// Views // Views
private RecyclerView mAttachedMediaPreview; private RecyclerView mAttachedMediaPreview;
@ -238,7 +238,6 @@ public class ComposeActivity extends BaseActivity implements OnMenuItemClickList
private boolean mImageUploaderUsed, mStatusShortenerUsed; private boolean mImageUploaderUsed, mStatusShortenerUsed;
private boolean mNavigateBackPressed; private boolean mNavigateBackPressed;
private boolean mTextChanged; private boolean mTextChanged;
private boolean mFragmentResumed;
private int mKeyMetaState; private int mKeyMetaState;
// Listeners // Listeners
@ -1301,28 +1300,9 @@ public class ComposeActivity extends BaseActivity implements OnMenuItemClickList
private void setProgressVisible(final boolean visible) { private void setProgressVisible(final boolean visible) {
if (isFinishing()) return; if (isFinishing()) return;
mSetProgressVisibleRunnable = new SetProgressVisibleRunnable(this, visible); executeAfterFragmentResumed(new SetProgressVisibleAction(visible));
if (mFragmentResumed) {
runOnUiThread(mSetProgressVisibleRunnable);
mSetProgressVisibleRunnable = null;
}
} }
@Override
protected void onResumeFragments() {
super.onResumeFragments();
mFragmentResumed = true;
if (mSetProgressVisibleRunnable != null) {
runOnUiThread(mSetProgressVisibleRunnable);
mSetProgressVisibleRunnable = null;
}
}
@Override
protected void onPause() {
super.onPause();
mFragmentResumed = false;
}
@Override @Override
public void onRequestPermissionsResult(int requestCode, @NonNull String[] permissions, @NonNull int[] grantResults) { public void onRequestPermissionsResult(int requestCode, @NonNull String[] permissions, @NonNull int[] grantResults) {
@ -1538,21 +1518,19 @@ public class ComposeActivity extends BaseActivity implements OnMenuItemClickList
} }
static class SetProgressVisibleRunnable implements Runnable { static class SetProgressVisibleAction implements Action {
final WeakReference<ComposeActivity> activityRef;
final boolean visible; final boolean visible;
SetProgressVisibleRunnable(ComposeActivity activity, boolean visible) { SetProgressVisibleAction(boolean visible) {
this.activityRef = new WeakReference<>(activity);
this.visible = visible; this.visible = visible;
} }
@Override @Override
public void run() { public void execute(IExtendedActivity activity) {
final ComposeActivity activity = activityRef.get(); final ComposeActivity composeActivity = (ComposeActivity) activity;
if (activity == null) return; if (composeActivity == null) return;
final FragmentManager fm = activity.getSupportFragmentManager(); final FragmentManager fm = composeActivity.getSupportFragmentManager();
final Fragment f = fm.findFragmentByTag(DISCARD_STATUS_DIALOG_FRAGMENT_TAG); final Fragment f = fm.findFragmentByTag(DISCARD_STATUS_DIALOG_FRAGMENT_TAG);
if (!visible && f instanceof DialogFragment) { if (!visible && f instanceof DialogFragment) {
((DialogFragment) f).dismiss(); ((DialogFragment) f).dismiss();

View File

@ -34,7 +34,6 @@ import android.support.v4.app.DialogFragment;
import android.support.v4.app.Fragment; import android.support.v4.app.Fragment;
import android.support.v4.app.FragmentActivity; import android.support.v4.app.FragmentActivity;
import android.support.v4.app.FragmentManager; import android.support.v4.app.FragmentManager;
import android.support.v4.content.FileProvider;
import android.support.v4.view.PagerAdapter; import android.support.v4.view.PagerAdapter;
import android.support.v4.view.ViewPager; import android.support.v4.view.ViewPager;
import android.support.v7.app.ActionBar; import android.support.v7.app.ActionBar;
@ -76,6 +75,7 @@ import org.mariotaku.twidere.model.ParcelableMedia;
import org.mariotaku.twidere.model.ParcelableStatus; import org.mariotaku.twidere.model.ParcelableStatus;
import org.mariotaku.twidere.model.UserKey; import org.mariotaku.twidere.model.UserKey;
import org.mariotaku.twidere.provider.CacheProvider; import org.mariotaku.twidere.provider.CacheProvider;
import org.mariotaku.twidere.provider.ShareProvider;
import org.mariotaku.twidere.task.SaveFileTask; import org.mariotaku.twidere.task.SaveFileTask;
import org.mariotaku.twidere.task.SaveMediaToGalleryTask; import org.mariotaku.twidere.task.SaveMediaToGalleryTask;
import org.mariotaku.twidere.util.AsyncTaskUtils; import org.mariotaku.twidere.util.AsyncTaskUtils;
@ -110,10 +110,8 @@ public final class MediaViewerActivity extends BaseActivity implements Constants
MediaDownloader mMediaDownloader; MediaDownloader mMediaDownloader;
private ParcelableMedia[] mMedia; private ParcelableMedia[] mMedia;
private ActionHelper mActionHelper = new ActionHelper(this);
private SaveFileTask mSaveFileTask; private SaveFileTask mSaveFileTask;
private int mSaveToStoragePosition = -1; private int mSaveToStoragePosition = -1;
private File mShareFile;
private Helper mHelper; private Helper mHelper;
@ -129,27 +127,12 @@ public final class MediaViewerActivity extends BaseActivity implements Constants
actionBar.setDisplayHomeAsUpEnabled(true); actionBar.setDisplayHomeAsUpEnabled(true);
} }
@Override
protected void onPause() {
mActionHelper.dispatchOnPause();
super.onPause();
}
@Override
protected void onResumeFragments() {
super.onResumeFragments();
mActionHelper.dispatchOnResumeFragments();
}
@Override @Override
public void onActivityResult(int requestCode, int resultCode, Intent data) { public void onActivityResult(int requestCode, int resultCode, Intent data) {
switch (requestCode) { switch (requestCode) {
case REQUEST_SHARE_MEDIA: { case REQUEST_SHARE_MEDIA: {
if (mShareFile != null) { ShareProvider.clearTempFiles(this);
mShareFile.delete();
mShareFile = null;
}
break; break;
} }
} }
@ -299,11 +282,6 @@ public final class MediaViewerActivity extends BaseActivity implements Constants
return actionBar != null && actionBar.isShowing(); return actionBar != null && actionBar.isShowing();
} }
@Override
public void executeAfterFragmentResumed(Action action) {
mActionHelper.executeAfterFragmentResumed(action);
}
@Override @Override
public void setBarVisibility(boolean visible) { public void setBarVisibility(boolean visible) {
final ActionBar actionBar = getSupportActionBar(); final ActionBar actionBar = getSupportActionBar();
@ -418,7 +396,7 @@ public final class MediaViewerActivity extends BaseActivity implements Constants
// TODO show error // TODO show error
return; return;
} }
final File destination = new File(getCacheDir(), "shared_files"); final File destination = ShareProvider.getFilesDir(this);
final SaveFileTask task = new SaveFileTask(this, result.cacheUri, destination, final SaveFileTask task = new SaveFileTask(this, result.cacheUri, destination,
new CacheProvider.CacheFileTypeCallback(this, type)) { new CacheProvider.CacheFileTypeCallback(this, type)) {
private static final String PROGRESS_FRAGMENT_TAG = "progress"; private static final String PROGRESS_FRAGMENT_TAG = "progress";
@ -458,8 +436,7 @@ public final class MediaViewerActivity extends BaseActivity implements Constants
final MediaViewerActivity activity = (MediaViewerActivity) getContext(); final MediaViewerActivity activity = (MediaViewerActivity) getContext();
if (activity == null) return; if (activity == null) return;
activity.mShareFile = savedFile; final Uri fileUri = ShareProvider.getUriForFile(activity, AUTHORITY_TWIDERE_SHARE,
final Uri fileUri = FileProvider.getUriForFile(activity, AUTHORITY_TWIDERE_FILE,
savedFile); savedFile);
final Intent intent = new Intent(Intent.ACTION_SEND); final Intent intent = new Intent(Intent.ACTION_SEND);

View File

@ -5,16 +5,20 @@ import android.content.ContentResolver;
import android.content.ContentValues; import android.content.ContentValues;
import android.content.Context; import android.content.Context;
import android.database.Cursor; import android.database.Cursor;
import android.database.MatrixCursor;
import android.net.Uri; import android.net.Uri;
import android.os.ParcelFileDescriptor; import android.os.ParcelFileDescriptor;
import android.provider.MediaStore.Files.FileColumns;
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.support.annotation.StringDef;
import android.util.Log;
import android.webkit.MimeTypeMap; 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.apache.commons.lang3.ArrayUtils;
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;
@ -27,6 +31,7 @@ import java.io.File;
import java.io.FileInputStream; import java.io.FileInputStream;
import java.io.FileNotFoundException; import java.io.FileNotFoundException;
import java.io.IOException; import java.io.IOException;
import java.util.Arrays;
import java.util.Locale; import java.util.Locale;
import javax.inject.Inject; import javax.inject.Inject;

View File

@ -0,0 +1,128 @@
package org.mariotaku.twidere.provider;
import android.annotation.SuppressLint;
import android.content.ContentProvider;
import android.content.ContentResolver;
import android.content.ContentValues;
import android.content.Context;
import android.database.Cursor;
import android.database.MatrixCursor;
import android.net.Uri;
import android.os.ParcelFileDescriptor;
import android.provider.MediaStore.MediaColumns;
import android.support.annotation.NonNull;
import android.support.annotation.Nullable;
import org.apache.commons.lang3.ArrayUtils;
import java.io.File;
import java.io.FileNotFoundException;
import java.io.IOException;
/**
* Created by mariotaku on 16/4/4.
*/
public class ShareProvider extends ContentProvider {
public static final String[] COLUMNS = {MediaColumns.DATA, MediaColumns.DISPLAY_NAME,
MediaColumns.SIZE, MediaColumns.MIME_TYPE};
@Override
public boolean onCreate() {
return true;
}
@SuppressLint("SetWorldReadable")
@Nullable
@Override
public Cursor query(@NonNull Uri uri, String[] projection, String selection, String[] selectionArgs, String sortOrder) {
try {
final File file = getFile(uri);
if (file == null) return null;
// Make world-readable intentionally since it will be deleted shortly
//noinspection ResultOfMethodCallIgnored
file.setReadable(true, false);
if (projection == null) {
projection = COLUMNS;
}
MatrixCursor cursor = new MatrixCursor(projection, 1);
Object[] values = new Object[projection.length];
writeValue(projection, values, MediaColumns.DATA, file.getAbsolutePath());
cursor.addRow(values);
return cursor;
} catch (IOException e) {
return null;
}
}
@Nullable
@Override
public ParcelFileDescriptor openFile(@NonNull Uri uri, @NonNull String mode) throws FileNotFoundException {
if (!mode.equals("r")) throw new IllegalArgumentException();
final File file = getFile(uri);
return ParcelFileDescriptor.open(file,
ParcelFileDescriptor.MODE_READ_ONLY);
}
private void writeValue(String[] columns, Object[] values, String column, Object value) {
int idx = ArrayUtils.indexOf(columns, column);
if (idx != ArrayUtils.INDEX_NOT_FOUND) {
values[idx] = value;
}
}
private File getFile(@NonNull Uri uri) throws FileNotFoundException {
final String lastPathSegment = uri.getLastPathSegment();
if (lastPathSegment == null) throw new FileNotFoundException(uri.toString());
return new File(getFilesDir(getContext()), lastPathSegment);
}
@Nullable
@Override
public String getType(@NonNull Uri uri) {
return null;
}
@Nullable
@Override
public Uri insert(@NonNull Uri uri, ContentValues values) {
return null;
}
@Override
public int delete(@NonNull Uri uri, String selection, String[] selectionArgs) {
return 0;
}
@Override
public int update(@NonNull Uri uri, ContentValues values, String selection, String[] selectionArgs) {
return 0;
}
@Nullable
public static File getFilesDir(Context context) {
final File externalCacheDir = context.getExternalCacheDir();
if (externalCacheDir == null) return null;
return new File(externalCacheDir, "shared_files");
}
@Nullable
public static Uri getUriForFile(@NonNull Context context, @NonNull String authority, @NonNull File file) {
final File filesDir = getFilesDir(context);
if (filesDir == null) return null;
if (!filesDir.equals(file.getParentFile())) return null;
return new Uri.Builder().scheme(ContentResolver.SCHEME_CONTENT).authority(authority).appendPath(file.getName()).build();
}
public static boolean clearTempFiles(Context context) {
final File externalCacheDir = context.getExternalCacheDir();
if (externalCacheDir == null) return false;
File[] files = externalCacheDir.listFiles();
for (File file : files) {
if (file.isFile()) {
//noinspection ResultOfMethodCallIgnored
file.delete();
}
}
return true;
}
}