From fec0f54a5f3a7a4ba870039fece578c37ffec4b8 Mon Sep 17 00:00:00 2001 From: Mariotaku Lee Date: Mon, 4 Apr 2016 22:09:30 +0800 Subject: [PATCH] fixed media sharing on some crappy apps --- .../mariotaku/twidere/TwidereConstants.java | 2 +- twidere/src/main/AndroidManifest.xml | 14 +- .../twidere/activity/ComposeActivity.java | 38 ++---- .../twidere/activity/MediaViewerActivity.java | 31 +---- .../twidere/provider/CacheProvider.java | 5 + .../twidere/provider/ShareProvider.java | 128 ++++++++++++++++++ 6 files changed, 151 insertions(+), 67 deletions(-) create mode 100644 twidere/src/main/java/org/mariotaku/twidere/provider/ShareProvider.java diff --git a/twidere.component.common/src/main/java/org/mariotaku/twidere/TwidereConstants.java b/twidere.component.common/src/main/java/org/mariotaku/twidere/TwidereConstants.java index 7a37d511f..4086858c5 100644 --- a/twidere.component.common/src/main/java/org/mariotaku/twidere/TwidereConstants.java +++ b/twidere.component.common/src/main/java/org/mariotaku/twidere/TwidereConstants.java @@ -67,7 +67,7 @@ public interface TwidereConstants extends SharedPreferenceConstants, IntentConst String PROTOCOL_CONTENT = SCHEME_CONTENT + "://"; 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_USER = "user"; diff --git a/twidere/src/main/AndroidManifest.xml b/twidere/src/main/AndroidManifest.xml index 314baeb81..87695c2ca 100644 --- a/twidere/src/main/AndroidManifest.xml +++ b/twidere/src/main/AndroidManifest.xml @@ -502,19 +502,15 @@ android:name=".provider.RecentSearchProvider" android:authorities="org.mariotaku.twidere.provider.SearchRecentSuggestions" tools:ignore="ExportedContentProvider"/> - - - + diff --git a/twidere/src/main/java/org/mariotaku/twidere/activity/ComposeActivity.java b/twidere/src/main/java/org/mariotaku/twidere/activity/ComposeActivity.java index ba9d42257..3c8d3c58c 100644 --- a/twidere/src/main/java/org/mariotaku/twidere/activity/ComposeActivity.java +++ b/twidere/src/main/java/org/mariotaku/twidere/activity/ComposeActivity.java @@ -108,6 +108,7 @@ import org.mariotaku.multivalueswitch.library.MultiValueSwitch; import org.mariotaku.restfu.RestFuUtils; import org.mariotaku.twidere.BuildConfig; import org.mariotaku.twidere.R; +import org.mariotaku.twidere.activity.iface.IExtendedActivity; import org.mariotaku.twidere.adapter.ArrayRecyclerAdapter; import org.mariotaku.twidere.adapter.BaseRecyclerViewAdapter; import org.mariotaku.twidere.fragment.BaseSupportDialogFragment; @@ -205,7 +206,6 @@ public class ComposeActivity extends BaseActivity implements OnMenuItemClickList private AsyncTask mTask; private SupportMenuInflater mMenuInflater; private ItemTouchHelper mItemTouchHelper; - private SetProgressVisibleRunnable mSetProgressVisibleRunnable; // Views private RecyclerView mAttachedMediaPreview; @@ -238,7 +238,6 @@ public class ComposeActivity extends BaseActivity implements OnMenuItemClickList private boolean mImageUploaderUsed, mStatusShortenerUsed; private boolean mNavigateBackPressed; private boolean mTextChanged; - private boolean mFragmentResumed; private int mKeyMetaState; // Listeners @@ -1301,28 +1300,9 @@ public class ComposeActivity extends BaseActivity implements OnMenuItemClickList private void setProgressVisible(final boolean visible) { if (isFinishing()) return; - mSetProgressVisibleRunnable = new SetProgressVisibleRunnable(this, visible); - if (mFragmentResumed) { - runOnUiThread(mSetProgressVisibleRunnable); - mSetProgressVisibleRunnable = null; - } + executeAfterFragmentResumed(new SetProgressVisibleAction(visible)); } - @Override - protected void onResumeFragments() { - super.onResumeFragments(); - mFragmentResumed = true; - if (mSetProgressVisibleRunnable != null) { - runOnUiThread(mSetProgressVisibleRunnable); - mSetProgressVisibleRunnable = null; - } - } - - @Override - protected void onPause() { - super.onPause(); - mFragmentResumed = false; - } @Override 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 activityRef; final boolean visible; - SetProgressVisibleRunnable(ComposeActivity activity, boolean visible) { - this.activityRef = new WeakReference<>(activity); + SetProgressVisibleAction(boolean visible) { this.visible = visible; } @Override - public void run() { - final ComposeActivity activity = activityRef.get(); - if (activity == null) return; - final FragmentManager fm = activity.getSupportFragmentManager(); + public void execute(IExtendedActivity activity) { + final ComposeActivity composeActivity = (ComposeActivity) activity; + if (composeActivity == null) return; + final FragmentManager fm = composeActivity.getSupportFragmentManager(); final Fragment f = fm.findFragmentByTag(DISCARD_STATUS_DIALOG_FRAGMENT_TAG); if (!visible && f instanceof DialogFragment) { ((DialogFragment) f).dismiss(); diff --git a/twidere/src/main/java/org/mariotaku/twidere/activity/MediaViewerActivity.java b/twidere/src/main/java/org/mariotaku/twidere/activity/MediaViewerActivity.java index 56b7f004f..79c55cfb0 100644 --- a/twidere/src/main/java/org/mariotaku/twidere/activity/MediaViewerActivity.java +++ b/twidere/src/main/java/org/mariotaku/twidere/activity/MediaViewerActivity.java @@ -34,7 +34,6 @@ import android.support.v4.app.DialogFragment; import android.support.v4.app.Fragment; import android.support.v4.app.FragmentActivity; import android.support.v4.app.FragmentManager; -import android.support.v4.content.FileProvider; import android.support.v4.view.PagerAdapter; import android.support.v4.view.ViewPager; 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.UserKey; import org.mariotaku.twidere.provider.CacheProvider; +import org.mariotaku.twidere.provider.ShareProvider; import org.mariotaku.twidere.task.SaveFileTask; import org.mariotaku.twidere.task.SaveMediaToGalleryTask; import org.mariotaku.twidere.util.AsyncTaskUtils; @@ -110,10 +110,8 @@ public final class MediaViewerActivity extends BaseActivity implements Constants MediaDownloader mMediaDownloader; private ParcelableMedia[] mMedia; - private ActionHelper mActionHelper = new ActionHelper(this); private SaveFileTask mSaveFileTask; private int mSaveToStoragePosition = -1; - private File mShareFile; private Helper mHelper; @@ -129,27 +127,12 @@ public final class MediaViewerActivity extends BaseActivity implements Constants actionBar.setDisplayHomeAsUpEnabled(true); } - @Override - protected void onPause() { - mActionHelper.dispatchOnPause(); - super.onPause(); - } - - @Override - protected void onResumeFragments() { - super.onResumeFragments(); - mActionHelper.dispatchOnResumeFragments(); - } - @Override public void onActivityResult(int requestCode, int resultCode, Intent data) { switch (requestCode) { case REQUEST_SHARE_MEDIA: { - if (mShareFile != null) { - mShareFile.delete(); - mShareFile = null; - } + ShareProvider.clearTempFiles(this); break; } } @@ -299,11 +282,6 @@ public final class MediaViewerActivity extends BaseActivity implements Constants return actionBar != null && actionBar.isShowing(); } - @Override - public void executeAfterFragmentResumed(Action action) { - mActionHelper.executeAfterFragmentResumed(action); - } - @Override public void setBarVisibility(boolean visible) { final ActionBar actionBar = getSupportActionBar(); @@ -418,7 +396,7 @@ public final class MediaViewerActivity extends BaseActivity implements Constants // TODO show error return; } - final File destination = new File(getCacheDir(), "shared_files"); + final File destination = ShareProvider.getFilesDir(this); final SaveFileTask task = new SaveFileTask(this, result.cacheUri, destination, new CacheProvider.CacheFileTypeCallback(this, type)) { 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(); if (activity == null) return; - activity.mShareFile = savedFile; - final Uri fileUri = FileProvider.getUriForFile(activity, AUTHORITY_TWIDERE_FILE, + final Uri fileUri = ShareProvider.getUriForFile(activity, AUTHORITY_TWIDERE_SHARE, savedFile); final Intent intent = new Intent(Intent.ACTION_SEND); diff --git a/twidere/src/main/java/org/mariotaku/twidere/provider/CacheProvider.java b/twidere/src/main/java/org/mariotaku/twidere/provider/CacheProvider.java index 5721a522e..6b9c3aae6 100644 --- a/twidere/src/main/java/org/mariotaku/twidere/provider/CacheProvider.java +++ b/twidere/src/main/java/org/mariotaku/twidere/provider/CacheProvider.java @@ -5,16 +5,20 @@ 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.Files.FileColumns; import android.support.annotation.NonNull; import android.support.annotation.Nullable; import android.support.annotation.StringDef; +import android.util.Log; import android.webkit.MimeTypeMap; import com.bluelinelabs.logansquare.JsonMapper; import com.nostra13.universalimageloader.cache.disc.DiskCache; +import org.apache.commons.lang3.ArrayUtils; import org.mariotaku.restfu.RestFuUtils; import org.mariotaku.twidere.TwidereConstants; import org.mariotaku.twidere.model.CacheMetadata; @@ -27,6 +31,7 @@ import java.io.File; import java.io.FileInputStream; import java.io.FileNotFoundException; import java.io.IOException; +import java.util.Arrays; import java.util.Locale; import javax.inject.Inject; diff --git a/twidere/src/main/java/org/mariotaku/twidere/provider/ShareProvider.java b/twidere/src/main/java/org/mariotaku/twidere/provider/ShareProvider.java new file mode 100644 index 000000000..0fea6b77f --- /dev/null +++ b/twidere/src/main/java/org/mariotaku/twidere/provider/ShareProvider.java @@ -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; + } +}