using file provider to share files

changed all classes ended with Trojan 'cause some anti-virus apps indicates this a virus, LMFAO
This commit is contained in:
Mariotaku Lee 2015-12-28 12:33:46 +08:00
parent e1f1e5299b
commit 54a1bcdc9c
29 changed files with 536 additions and 223 deletions

View File

@ -24,7 +24,7 @@ import java.lang.reflect.Type;
/**
* Created by mariotaku on 15/12/13.
*/
public class ParameterizedTypeTrojan {
public class ParameterizedTypeAccessor {
public static <T> ParameterizedType<T> create(Type type) {
return new ParameterizedType.ConcreteParameterizedType<>(type);

View File

@ -67,6 +67,8 @@ public interface TwidereConstants extends SharedPreferenceConstants, IntentConst
String PROTOCOL_CONTENT = SCHEME_CONTENT + "://";
String PROTOCOL_TWIDERE = SCHEME_TWIDERE + "://";
String AUTHORITY_TWIDERE_FILE = "twidere.file";
String AUTHORITY_USER = "user";
String AUTHORITY_HOME = "home";
String AUTHORITY_MENTIONS = "mentions";

View File

@ -23,7 +23,7 @@ import android.support.v4.util.SimpleArrayMap;
import com.bluelinelabs.logansquare.LoganSquare;
import com.bluelinelabs.logansquare.ParameterizedType;
import com.bluelinelabs.logansquare.ParameterizedTypeTrojan;
import com.bluelinelabs.logansquare.ParameterizedTypeAccessor;
import com.fasterxml.jackson.core.JsonParseException;
import org.mariotaku.restfu.Converter;
@ -69,7 +69,7 @@ public class TwitterConverter implements Converter {
private static <T> T parseOrThrow(RestHttpResponse resp, InputStream stream, Type type) throws IOException, TwitterException {
try {
final ParameterizedType<T> parameterizedType = ParameterizedTypeTrojan.create(type);
final ParameterizedType<T> parameterizedType = ParameterizedTypeAccessor.create(type);
final T parse = LoganSquare.parse(stream, parameterizedType);
if (TwitterException.class == type && parse == null) {
throw new TwitterException();

View File

@ -114,6 +114,7 @@ dependencies {
compile fileTree(dir: 'libs/main', include: ['*.jar'])
provided 'javax.annotation:jsr250-api:1.0'
// googleCompile fileTree(dir: 'libs/google', include: ['*.jar'])
compile 'com.google.android.gms:play-services-appindexing:8.1.0'
}
task svgToDrawable(type: SvgDrawableTask) {

View File

@ -526,6 +526,15 @@
android:name=".provider.RecentSearchProvider"
android:authorities="org.mariotaku.twidere.provider.SearchRecentSuggestions"
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>
<receiver android:name=".receiver.ConnectivityStateReceiver">
<intent-filter>
@ -558,6 +567,11 @@
<action android:name="android.intent.action.ACTION_POWER_DISCONNECTED"/>
</intent-filter>
</receiver>
<!-- ATTENTION: This was auto-generated to add Google Play services to your project for
App Indexing. See https://g.co/AppIndexing/AndroidStudio for more information. -->
<meta-data
android:name="com.google.android.gms.version"
android:value="@integer/google_play_services_version"/>
</application>

View File

@ -1,6 +1,6 @@
package android.support.v4.app;
public class BackStackEntryTrojan {
public class BackStackEntryAccessor {
public static Fragment getFragmentInBackStackRecord(final FragmentManager.BackStackEntry entry) {
if (entry instanceof BackStackRecord) return ((BackStackRecord) entry).mHead.fragment;

View File

@ -2,7 +2,7 @@ package android.support.v4.app;
import android.os.Bundle;
public class FragmentTrojan {
public class FragmentAccessor {
public static Bundle getSavedFragmentState(final Fragment f) {
return f.mSavedFragmentState;

View File

@ -2,7 +2,7 @@ package android.support.v4.app;
import android.support.v4.view.LayoutInflaterFactory;
public class FragmentManagerTrojan {
public class FragmentManagerAccessor {
public static boolean isStateSaved(final FragmentManager fm) {
if (fm instanceof FragmentManagerImpl) return ((FragmentManagerImpl) fm).mStateSaved;

View File

@ -22,7 +22,7 @@ package android.support.v4.content;
/**
* Created by mariotaku on 15/7/5.
*/
public class LoaderTrojan {
public class LoaderAccessor {
public static <T> boolean isContentChanged(final Loader<T> loader) {
return loader.mContentChanged;
}

View File

@ -24,7 +24,7 @@ import android.view.View;
/**
* Created by mariotaku on 15/7/18.
*/
public class DrawerLayoutTrojan {
public class DrawerLayoutAccessor {
public static View findDrawerWithGravity(DrawerLayout layout, int gravity) {
return layout.findDrawerWithGravity(gravity);

View File

@ -24,7 +24,7 @@ import android.support.annotation.Nullable;
/**
* Created by mariotaku on 15/4/27.
*/
public class AppCompatDelegateTrojan {
public class AppCompatDelegateAccessor {
@Nullable
public static ActionBar peekActionBar(@Nullable AppCompatDelegate delegate) {

View File

@ -24,7 +24,7 @@ import android.support.v7.widget.RecyclerView.ViewHolder;
/**
* Created by mariotaku on 14/12/6.
*/
public class ViewHolderTrojan {
public class ViewHolderAccessor {
public static boolean isRemoved(ViewHolder holder) {
return holder.isRemoved();

View File

@ -0,0 +1,32 @@
/*
* Twidere - Twitter client for Android
*
* Copyright (C) 2012-2015 Mariotaku Lee <mariotaku.lee@gmail.com>
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package org.mariotaku.twidere.activity.iface;
/**
* Created by mariotaku on 15/12/28.
*/
public interface IExtendedActivity {
void executeAfterFragmentResumed(Action action);
interface Action {
void execute(IExtendedActivity activity);
}
}

View File

@ -31,6 +31,7 @@ import com.squareup.otto.Bus;
import org.mariotaku.twidere.Constants;
import org.mariotaku.twidere.activity.iface.IControlBarActivity;
import org.mariotaku.twidere.activity.iface.IExtendedActivity;
import org.mariotaku.twidere.app.TwidereApplication;
import org.mariotaku.twidere.fragment.iface.IBaseFragment.SystemWindowsInsetsCallback;
import org.mariotaku.twidere.util.ActivityTracker;
@ -46,13 +47,15 @@ import org.mariotaku.twidere.util.dagger.DaggerGeneralComponent;
import org.mariotaku.twidere.view.iface.IExtendedView.OnFitSystemWindowsListener;
import java.util.ArrayList;
import java.util.LinkedList;
import java.util.Queue;
import javax.inject.Inject;
@SuppressLint("Registered")
public class BaseAppCompatActivity extends ThemedAppCompatActivity implements Constants,
OnFitSystemWindowsListener, SystemWindowsInsetsCallback, IControlBarActivity,
KeyboardShortcutCallback {
KeyboardShortcutCallback, IExtendedActivity {
// Utility classes
@Inject
@ -78,6 +81,8 @@ public class BaseAppCompatActivity extends ThemedAppCompatActivity implements Co
private boolean mIsVisible;
private Rect mSystemWindowsInsets;
private int mKeyMetaState;
private boolean mFragmentResumed;
private Queue<Action> mActionQueue = new LinkedList<>();
@Override
public boolean getSystemWindowsInsets(Rect insets) {
@ -183,6 +188,7 @@ public class BaseAppCompatActivity extends ThemedAppCompatActivity implements Co
@Override
protected void onPause() {
mFragmentResumed = false;
super.onPause();
}
@ -250,4 +256,24 @@ public class BaseAppCompatActivity extends ThemedAppCompatActivity implements Co
return mKeyMetaState;
}
@Override
protected void onResumeFragments() {
super.onResumeFragments();
mFragmentResumed = true;
executePending();
}
@Override
public void executeAfterFragmentResumed(Action action) {
mActionQueue.add(action);
executePending();
}
private void executePending() {
if (!mFragmentResumed) return;
Action action;
while ((action = mActionQueue.poll()) != null) {
action.execute(this);
}
}
}

View File

@ -43,7 +43,7 @@ import android.support.v4.view.ViewCompat;
import android.support.v4.view.ViewPager;
import android.support.v4.view.ViewPager.OnPageChangeListener;
import android.support.v4.widget.DrawerLayout;
import android.support.v4.widget.DrawerLayoutTrojan;
import android.support.v4.widget.DrawerLayoutAccessor;
import android.support.v7.widget.Toolbar;
import android.view.Gravity;
import android.view.KeyEvent;
@ -257,7 +257,7 @@ public class HomeActivity extends BaseAppCompatActivity implements OnClickListen
switch (action) {
case ACTION_NAVIGATION_PREVIOUS_TAB: {
final int previous = mViewPager.getCurrentItem() - 1;
if (previous < 0 && DrawerLayoutTrojan.findDrawerWithGravity(mDrawerLayout, Gravity.START) != null) {
if (previous < 0 && DrawerLayoutAccessor.findDrawerWithGravity(mDrawerLayout, Gravity.START) != null) {
mDrawerLayout.openDrawer(GravityCompat.START);
setControlBarVisibleAnimate(true);
} else if (previous < mPagerAdapter.getCount()) {
@ -271,7 +271,7 @@ public class HomeActivity extends BaseAppCompatActivity implements OnClickListen
}
case ACTION_NAVIGATION_NEXT_TAB: {
final int next = mViewPager.getCurrentItem() + 1;
if (next >= mPagerAdapter.getCount() && DrawerLayoutTrojan.findDrawerWithGravity(mDrawerLayout, Gravity.END) != null) {
if (next >= mPagerAdapter.getCount() && DrawerLayoutAccessor.findDrawerWithGravity(mDrawerLayout, Gravity.END) != null) {
mDrawerLayout.openDrawer(GravityCompat.END);
setControlBarVisibleAnimate(true);
} else if (next >= 0) {

View File

@ -110,7 +110,7 @@ public class LinkHandlerActivity extends BaseAppCompatActivity implements System
@Override
public int getThemeResourceId() {
return ThemeUtils.getDialogWhenLargeThemeResource(this);
return ThemeUtils.getNoActionBarThemeResource(this);
}
@Override

View File

@ -17,6 +17,9 @@
package org.mariotaku.twidere.activity.support;
import android.Manifest;
import android.app.Activity;
import android.app.DialogFragment;
import android.app.FragmentManager;
import android.content.Intent;
import android.media.AudioManager;
import android.media.MediaPlayer;
@ -32,8 +35,10 @@ import android.os.Handler;
import android.support.annotation.NonNull;
import android.support.annotation.Nullable;
import android.support.v4.app.Fragment;
import android.support.v4.app.FragmentActivity;
import android.support.v4.app.FragmentTransaction;
import android.support.v4.app.LoaderManager.LoaderCallbacks;
import android.support.v4.content.FileProvider;
import android.support.v4.content.Loader;
import android.support.v4.util.Pair;
import android.support.v4.view.ViewPager;
@ -58,15 +63,15 @@ import android.widget.Toast;
import com.davemorrissey.labs.subscaleview.ImageSource;
import com.davemorrissey.labs.subscaleview.SubsamplingScaleImageView;
import com.desmond.asyncmanager.AsyncManager;
import com.desmond.asyncmanager.TaskRunnable;
import com.pnikosis.materialishprogress.ProgressWheel;
import com.sprylab.android.widget.TextureVideoView;
import org.apache.commons.lang3.ArrayUtils;
import org.mariotaku.twidere.Constants;
import org.mariotaku.twidere.R;
import org.mariotaku.twidere.activity.iface.IExtendedActivity;
import org.mariotaku.twidere.adapter.support.SupportFixedFragmentStatePagerAdapter;
import org.mariotaku.twidere.fragment.ProgressDialogFragment;
import org.mariotaku.twidere.fragment.support.BaseSupportFragment;
import org.mariotaku.twidere.fragment.support.ViewStatusDialogFragment;
import org.mariotaku.twidere.loader.support.TileImageLoader;
@ -75,11 +80,13 @@ import org.mariotaku.twidere.loader.support.TileImageLoader.Result;
import org.mariotaku.twidere.model.ParcelableMedia;
import org.mariotaku.twidere.model.ParcelableMedia.VideoInfo.Variant;
import org.mariotaku.twidere.model.ParcelableStatus;
import org.mariotaku.twidere.task.ProgressSaveFileTask;
import org.mariotaku.twidere.task.SaveFileTask;
import org.mariotaku.twidere.task.SaveImageToGalleryTask;
import org.mariotaku.twidere.util.AsyncTaskUtils;
import org.mariotaku.twidere.util.KeyboardShortcutsHandler;
import org.mariotaku.twidere.util.MenuUtils;
import org.mariotaku.twidere.util.PermissionUtils;
import org.mariotaku.twidere.util.SaveFileTask;
import org.mariotaku.twidere.util.ThemeUtils;
import org.mariotaku.twidere.util.Utils;
import org.mariotaku.twidere.util.VideoLoader.VideoLoadingListener;
@ -258,6 +265,8 @@ public final class MediaViewerActivity extends BaseAppCompatActivity implements
public static class BaseImagePageFragment extends AbsMediaPageFragment
implements DownloadListener, LoaderCallbacks<Result>, OnClickListener {
private static final int REQUEST_SHARE_IMAGE = 201;
private SubsamplingScaleImageView mImageView;
private ProgressWheel mProgressBar;
private boolean mLoaderInitialized;
@ -265,6 +274,7 @@ public final class MediaViewerActivity extends BaseAppCompatActivity implements
private SaveFileTask mSaveFileTask;
private File mImageFile;
private File mShareImageFile;
@Override
public void onBaseViewCreated(View view, @Nullable Bundle savedInstanceState) {
@ -394,7 +404,7 @@ public final class MediaViewerActivity extends BaseAppCompatActivity implements
final File file = mImageFile;
final boolean hasImage = file != null && file.exists();
if (!hasImage) return;
mSaveFileTask = SaveFileTask.saveImage(getActivity(), file);
mSaveFileTask = SaveImageToGalleryTask.create(getActivity(), file);
AsyncTaskUtils.executeTask(mSaveFileTask);
}
@ -402,44 +412,10 @@ public final class MediaViewerActivity extends BaseAppCompatActivity implements
public void onPrepareOptionsMenu(Menu menu) {
super.onPrepareOptionsMenu(menu);
final boolean isLoading = getLoaderManager().hasRunningLoaders();
final TaskRunnable<File, Pair<Boolean, Intent>, Pair<Fragment, Menu>> checkState
= new TaskRunnable<File, Pair<Boolean, Intent>, Pair<Fragment, Menu>>() {
@Override
public Pair<Boolean, Intent> doLongOperation(File file) throws InterruptedException {
final boolean hasImage = file != null && file.exists();
if (!hasImage) {
return Pair.create(false, null);
}
final Intent intent = new Intent(Intent.ACTION_SEND);
final Uri fileUri = Uri.fromFile(file);
final String imageMimeType = Utils.getImageMimeType(file);
intent.setDataAndType(fileUri, imageMimeType);
intent.putExtra(Intent.EXTRA_STREAM, fileUri);
final MediaViewerActivity activity = (MediaViewerActivity) getActivity();
if (activity.hasStatus()) {
final ParcelableStatus status = activity.getStatus();
intent.putExtra(Intent.EXTRA_TEXT, Utils.getStatusShareText(activity, status));
intent.putExtra(Intent.EXTRA_SUBJECT, Utils.getStatusShareSubject(activity, status));
}
return Pair.create(true, intent);
}
@Override
public void callback(Pair<Fragment, Menu> callback, Pair<Boolean, Intent> result) {
if (callback.first.isDetached() || callback.first.getActivity() == null) return;
final Menu menu = callback.second;
final boolean hasImage = result.first;
MenuUtils.setMenuItemAvailability(menu, R.id.refresh, !hasImage && !isLoading);
MenuUtils.setMenuItemAvailability(menu, R.id.share, hasImage && !isLoading);
MenuUtils.setMenuItemAvailability(menu, R.id.save, hasImage && !isLoading);
if (!hasImage) return;
final MenuItem shareItem = menu.findItem(R.id.share);
shareItem.setIntent(Intent.createChooser(result.second, callback.first.getString(R.string.share)));
}
};
checkState.setParams(mImageFile);
checkState.setResultHandler(Pair.<Fragment, Menu>create(this, menu));
AsyncManager.runBackgroundTask(checkState);
final boolean hasImage = mImageFile != null;
MenuUtils.setMenuItemAvailability(menu, R.id.refresh, !hasImage && !isLoading);
MenuUtils.setMenuItemAvailability(menu, R.id.share, hasImage && !isLoading);
MenuUtils.setMenuItemAvailability(menu, R.id.save, hasImage && !isLoading);
}
@ -464,6 +440,65 @@ public final class MediaViewerActivity extends BaseAppCompatActivity implements
loadImage();
return true;
}
case R.id.share: {
final FragmentActivity activity = getActivity();
final File destination = new File(activity.getCacheDir(), "shared_files");
final SaveFileTask task = new SaveFileTask(activity, mImageFile, destination,
new SaveImageToGalleryTask.ImageMimeTypeCallback()) {
private static final String PROGRESS_FRAGMENT_TAG = "progress";
protected void dismissProgress() {
final MediaViewerActivity activity = (MediaViewerActivity) getActivity();
if (activity == null) return;
activity.executeAfterFragmentResumed(new IExtendedActivity.Action() {
@Override
public void execute(IExtendedActivity activity) {
final FragmentManager fm = ((Activity) activity).getFragmentManager();
final DialogFragment fragment = (DialogFragment) fm.findFragmentByTag(PROGRESS_FRAGMENT_TAG);
if (fragment != null) {
fragment.dismiss();
}
}
});
}
protected void showProgress() {
final MediaViewerActivity activity = (MediaViewerActivity) getActivity();
if (activity == null) return;
activity.executeAfterFragmentResumed(new IExtendedActivity.Action() {
@Override
public void execute(IExtendedActivity activity) {
final DialogFragment fragment = new ProgressDialogFragment();
fragment.setCancelable(false);
fragment.show(((Activity) activity).getFragmentManager(), PROGRESS_FRAGMENT_TAG);
}
});
}
protected void onFileSaved(File savedFile, String mimeType) {
final MediaViewerActivity activity = (MediaViewerActivity) getActivity();
if (activity == null) return;
final Uri fileUri = FileProvider.getUriForFile(activity,
AUTHORITY_TWIDERE_FILE, savedFile);
final Intent intent = new Intent(Intent.ACTION_SEND);
intent.setDataAndType(fileUri, mimeType);
intent.putExtra(Intent.EXTRA_STREAM, fileUri);
intent.addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION);
if (activity.hasStatus()) {
final ParcelableStatus status = activity.getStatus();
intent.putExtra(Intent.EXTRA_TEXT, Utils.getStatusShareText(activity, status));
intent.putExtra(Intent.EXTRA_SUBJECT, Utils.getStatusShareSubject(activity, status));
}
startActivityForResult(Intent.createChooser(intent, activity.getString(R.string.share)),
REQUEST_SHARE_IMAGE);
}
};
task.execute();
return true;
}
}
return super.onOptionsItemSelected(item);
}
@ -484,7 +519,18 @@ public final class MediaViewerActivity extends BaseAppCompatActivity implements
loadImage();
}
@Override
public void onActivityResult(final int requestCode, final int resultCode, final Intent data) {
switch (requestCode) {
case REQUEST_SHARE_IMAGE: {
if (mShareImageFile != null) {
mShareImageFile.delete();
}
return;
}
}
super.onActivityResult(requestCode, resultCode, data);
}
}
public static final class ImagePageFragment extends BaseImagePageFragment
@ -840,7 +886,13 @@ public final class MediaViewerActivity extends BaseAppCompatActivity implements
if (extension == null) return;
final File pubDir = Environment.getExternalStoragePublicDirectory(Environment.DIRECTORY_MOVIES);
final File saveDir = new File(pubDir, "Twidere");
mSaveFileTask = AsyncTaskUtils.executeTask(new SaveFileTask(getActivity(), file, mimeType, saveDir));
mSaveFileTask = AsyncTaskUtils.executeTask(new ProgressSaveFileTask(getActivity(), file, saveDir,
new SaveFileTask.StringMimeTypeCallback(mimeType)) {
@Override
protected void onFileSaved(File savedFile, String mimeType) {
}
});
}
@Override
@ -938,6 +990,7 @@ public final class MediaViewerActivity extends BaseAppCompatActivity implements
final Intent intent = new Intent(Intent.ACTION_SEND);
final Uri fileUri = Uri.fromFile(file);
intent.setDataAndType(fileUri, linkAndType.second);
intent.addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION);
intent.putExtra(Intent.EXTRA_STREAM, fileUri);
final MediaViewerActivity activity = (MediaViewerActivity) getActivity();
if (activity.hasStatus()) {

View File

@ -23,7 +23,7 @@ import android.os.Bundle;
import android.support.v4.app.Fragment;
import android.support.v4.app.FragmentManager;
import android.support.v4.app.FragmentStatePagerAdapter;
import android.support.v4.app.FragmentTrojan;
import android.support.v4.app.FragmentAccessor;
import android.view.ViewGroup;
public abstract class SupportFixedFragmentStatePagerAdapter extends FragmentStatePagerAdapter {
@ -35,7 +35,7 @@ public abstract class SupportFixedFragmentStatePagerAdapter extends FragmentStat
@Override
public Object instantiateItem(final ViewGroup container, final int position) {
final Fragment f = (Fragment) super.instantiateItem(container, position);
final Bundle savedFragmentState = f != null ? FragmentTrojan.getSavedFragmentState(f) : null;
final Bundle savedFragmentState = f != null ? FragmentAccessor.getSavedFragmentState(f) : null;
if (savedFragmentState != null) {
savedFragmentState.setClassLoader(f.getClass().getClassLoader());
}

View File

@ -30,7 +30,7 @@ import android.os.Bundle;
import android.support.annotation.Nullable;
import android.support.v4.app.Fragment;
import android.support.v4.app.FragmentActivity;
import android.support.v4.app.FragmentManagerTrojan;
import android.support.v4.app.FragmentManagerAccessor;
import android.support.v4.view.LayoutInflaterCompat;
import android.support.v4.view.LayoutInflaterFactory;
import android.view.LayoutInflater;
@ -193,7 +193,7 @@ public class BaseSupportFragment extends Fragment implements IBaseFragment, Cons
}
final LayoutInflater inflater = activity.getLayoutInflater().cloneInContext(getThemedContext());
getChildFragmentManager(); // Init if needed; use raw implementation below.
final LayoutInflaterFactory delegate = FragmentManagerTrojan.getLayoutInflaterFactory(getChildFragmentManager());
final LayoutInflaterFactory delegate = FragmentManagerAccessor.getLayoutInflaterFactory(getChildFragmentManager());
LayoutInflaterCompat.setFactory(inflater, new ThemedLayoutInflaterFactory((IThemedActivity) activity, delegate));
return inflater;
}

View File

@ -43,7 +43,7 @@ import android.support.annotation.Nullable;
import android.support.v4.app.Fragment;
import android.support.v4.app.FragmentActivity;
import android.support.v4.app.FragmentManager;
import android.support.v4.app.FragmentManagerTrojan;
import android.support.v4.app.FragmentManagerAccessor;
import android.support.v4.app.FragmentTransaction;
import android.support.v4.app.LoaderManager.LoaderCallbacks;
import android.support.v4.content.AsyncTaskLoader;
@ -1167,7 +1167,7 @@ public class StatusFragment extends BaseSupportFragment implements LoaderCallbac
}
final Fragment cardFragment = TwitterCardUtils.createCardFragment(status);
final FragmentManager fm = fragment.getChildFragmentManager();
if (cardFragment != null && !FragmentManagerTrojan.isStateSaved(fm)) {
if (cardFragment != null && !FragmentManagerAccessor.isStateSaved(fm)) {
final FragmentTransaction ft = fm.beginTransaction();
ft.replace(R.id.twitter_card, cardFragment);
ft.commit();

View File

@ -24,7 +24,7 @@ import android.database.Cursor;
import android.net.Uri;
import android.support.annotation.NonNull;
import android.support.v4.content.AsyncTaskLoader;
import android.support.v4.content.LoaderTrojan;
import android.support.v4.content.LoaderAccessor;
import org.mariotaku.library.objectcursor.ObjectCursor;
@ -247,7 +247,7 @@ public class ObjectCursorLoader<T> extends AsyncTaskLoader<List<T>> {
writer.println(mObjects);
writer.print(prefix);
writer.print("mContentChanged=");
writer.println(LoaderTrojan.isContentChanged(this));
writer.println(LoaderAccessor.isContentChanged(this));
}
}

View File

@ -0,0 +1,69 @@
/*
* Twidere - Twitter client for Android
*
* Copyright (C) 2012-2015 Mariotaku Lee <mariotaku.lee@gmail.com>
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package org.mariotaku.twidere.task;
import android.app.Activity;
import android.app.DialogFragment;
import android.app.FragmentManager;
import android.content.Context;
import org.mariotaku.twidere.activity.iface.IExtendedActivity;
import org.mariotaku.twidere.fragment.ProgressDialogFragment;
import java.io.File;
/**
* Created by mariotaku on 15/12/28.
*/
public abstract class ProgressSaveFileTask extends SaveFileTask {
private static final String PROGRESS_FRAGMENT_TAG = "progress";
public ProgressSaveFileTask(Context context, File source, File destination, MimeTypeCallback getMimeType) {
super(context, source, destination, getMimeType);
}
protected void showProgress() {
final Context context = getContext();
if (context == null) return;
((IExtendedActivity) context).executeAfterFragmentResumed(new IExtendedActivity.Action() {
@Override
public void execute(IExtendedActivity activity) {
final DialogFragment fragment = new ProgressDialogFragment();
fragment.setCancelable(false);
fragment.show(((Activity) activity).getFragmentManager(), PROGRESS_FRAGMENT_TAG);
}
});
}
protected void dismissProgress() {
final Context context = getContext();
if (context == null) return;
((IExtendedActivity) context).executeAfterFragmentResumed(new IExtendedActivity.Action() {
@Override
public void execute(IExtendedActivity activity) {
final FragmentManager fm = ((Activity) activity).getFragmentManager();
final DialogFragment fragment = (DialogFragment) fm.findFragmentByTag(PROGRESS_FRAGMENT_TAG);
if (fragment != null) {
fragment.dismiss();
}
}
});
}
}

View File

@ -0,0 +1,171 @@
/*
* Twidere - Twitter client for Android
*
* Copyright (C) 2012-2014 Mariotaku Lee <mariotaku.lee@gmail.com>
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package org.mariotaku.twidere.task;
import android.content.Context;
import android.os.AsyncTask;
import android.support.annotation.NonNull;
import android.support.annotation.Nullable;
import android.util.Log;
import android.webkit.MimeTypeMap;
import org.mariotaku.twidere.Constants;
import org.mariotaku.twidere.util.Utils;
import java.io.File;
import java.io.IOException;
import java.lang.ref.WeakReference;
import okio.BufferedSink;
import okio.Okio;
import okio.Source;
import static android.text.TextUtils.isEmpty;
public abstract class SaveFileTask extends AsyncTask<Object, Object, SaveFileTask.SaveFileResult> implements Constants {
private final WeakReference<Context> contextRef;
@NonNull
private final File source, destination;
@NonNull
private final MimeTypeCallback getMimeType;
public SaveFileTask(@NonNull final Context context, @NonNull final File source,
@NonNull final File destination, @NonNull final MimeTypeCallback getMimeType) {
this.contextRef = new WeakReference<>(context);
this.source = source;
this.getMimeType = getMimeType;
this.destination = destination;
}
@Nullable
public static SaveFileResult saveFile(@NonNull final Context context, @NonNull final File source,
@NonNull final MimeTypeCallback mimeTypeCallback,
@NonNull final File destinationDir) {
Source ioSrc = null;
BufferedSink sink = null;
try {
final String name = source.getName();
if (isEmpty(name)) return null;
final String mimeType = mimeTypeCallback.getMimeType(source);
final String extension = mimeTypeCallback.getExtension(mimeType);
if (extension == null) return null;
final String nameToSave = getFileNameWithExtension(name, extension);
if (!destinationDir.isDirectory() && !destinationDir.mkdirs()) return null;
final File saveFile = new File(destinationDir, nameToSave);
ioSrc = Okio.source(source);
sink = Okio.buffer(Okio.sink(saveFile));
sink.writeAll(ioSrc);
sink.flush();
return new SaveFileResult(saveFile, mimeType);
} catch (final IOException e) {
Log.w(LOGTAG, "Failed to save file", e);
return null;
} finally {
Utils.closeSilently(sink);
Utils.closeSilently(ioSrc);
}
}
@Override
protected final SaveFileResult doInBackground(final Object... args) {
final Context context = contextRef.get();
if (context == null) return null;
return saveFile(context, source, getMimeType, destination);
}
@Override
protected void onCancelled() {
dismissProgress();
}
@Override
protected final void onPreExecute() {
showProgress();
}
@Override
protected final void onPostExecute(@Nullable final SaveFileResult result) {
dismissProgress();
if (result != null) {
onFileSaved(result.savedFile, result.mimeType);
}
}
protected abstract void onFileSaved(File savedFile, String mimeType);
protected abstract void showProgress();
protected abstract void dismissProgress();
protected final Context getContext() {
return contextRef.get();
}
private static String getFileNameWithExtension(String name, String extension) {
int lastDotIdx = name.lastIndexOf('.');
if (lastDotIdx < 0) return name + "." + extension;
return name.substring(0, lastDotIdx) + "." + extension;
}
public interface MimeTypeCallback {
String getMimeType(File source);
String getExtension(String mimeType);
}
public static final class SaveFileResult {
File savedFile;
String mimeType;
public SaveFileResult(File savedFile, String mimeType) {
this.savedFile = savedFile;
this.mimeType = mimeType;
}
public File getSavedFile() {
return savedFile;
}
public String getMimeType() {
return mimeType;
}
}
public static class StringMimeTypeCallback implements MimeTypeCallback {
private final String mimeType;
public StringMimeTypeCallback(String mimeType) {
this.mimeType = mimeType;
}
@Override
public String getMimeType(File source) {
return mimeType;
}
@Override
public String getExtension(String mimeType) {
return MimeTypeMap.getSingleton().getExtensionFromMimeType(mimeType);
}
}
}

View File

@ -0,0 +1,73 @@
/*
* Twidere - Twitter client for Android
*
* Copyright (C) 2012-2015 Mariotaku Lee <mariotaku.lee@gmail.com>
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package org.mariotaku.twidere.task;
import android.app.Activity;
import android.content.Context;
import android.media.MediaScannerConnection;
import android.os.Environment;
import android.support.annotation.NonNull;
import android.webkit.MimeTypeMap;
import android.widget.Toast;
import org.mariotaku.twidere.R;
import org.mariotaku.twidere.util.Utils;
import java.io.File;
/**
* Created by mariotaku on 15/12/28.
*/
public class SaveImageToGalleryTask extends ProgressSaveFileTask {
public SaveImageToGalleryTask(@NonNull Activity activity, @NonNull File source, @NonNull File destination) {
super(activity, source, destination, new ImageMimeTypeCallback());
}
public static SaveFileTask create(final Activity activity, final File source) {
final File pubDir = Environment.getExternalStoragePublicDirectory(Environment.DIRECTORY_PICTURES);
final File saveDir = new File(pubDir, "Twidere");
return new SaveImageToGalleryTask(activity, source, saveDir);
}
protected void onFileSaved(File savedFile, String mimeType) {
final Context context = getContext();
if (context == null) return;
if (savedFile != null && savedFile.exists()) {
MediaScannerConnection.scanFile(context, new String[]{savedFile.getPath()},
new String[]{mimeType}, null);
Toast.makeText(context, R.string.saved_to_gallery, Toast.LENGTH_SHORT).show();
} else {
Toast.makeText(context, R.string.error_occurred, Toast.LENGTH_SHORT).show();
}
}
public static final class ImageMimeTypeCallback implements MimeTypeCallback {
@Override
public String getMimeType(File source) {
return Utils.getImageMimeType(source);
}
@Override
public String getExtension(String mimeType) {
return MimeTypeMap.getSingleton().getExtensionFromMimeType(mimeType);
}
}
}

View File

@ -1,151 +0,0 @@
/*
* Twidere - Twitter client for Android
*
* Copyright (C) 2012-2014 Mariotaku Lee <mariotaku.lee@gmail.com>
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package org.mariotaku.twidere.util;
import android.app.Activity;
import android.app.DialogFragment;
import android.app.FragmentManager;
import android.content.Context;
import android.media.MediaScannerConnection;
import android.os.AsyncTask;
import android.os.Environment;
import android.system.ErrnoException;
import android.system.OsConstants;
import android.util.Log;
import android.webkit.MimeTypeMap;
import android.widget.Toast;
import org.apache.commons.lang3.exception.ExceptionUtils;
import org.mariotaku.twidere.Constants;
import org.mariotaku.twidere.R;
import org.mariotaku.twidere.fragment.ProgressDialogFragment;
import java.io.File;
import java.io.IOException;
import okio.BufferedSink;
import okio.Okio;
import okio.Source;
import static android.text.TextUtils.isEmpty;
public class SaveFileTask extends AsyncTask<Object, Object, File> implements Constants {
private static final String PROGRESS_FRAGMENT_TAG = "progress";
private final File source, destination;
private final Activity activity;
private final String mimeType;
public SaveFileTask(final Activity activity, final File source, final String mimeType, final File destination) {
this.activity = activity;
this.source = source;
this.mimeType = mimeType;
this.destination = destination;
}
public static SaveFileTask saveImage(final Activity activity, final File source) {
final String mimeType = Utils.getImageMimeType(source);
final MimeTypeMap map = MimeTypeMap.getSingleton();
final String extension = map.getExtensionFromMimeType(mimeType);
if (extension == null) return null;
final File pubDir = Environment.getExternalStoragePublicDirectory(Environment.DIRECTORY_PICTURES);
final File saveDir = new File(pubDir, "Twidere");
return new SaveFileTask(activity, source, mimeType, saveDir);
}
public static File saveFile(final Context context, final File source, final String mimeType, final File destination) {
if (context == null && source == null) return null;
Source ioSrc = null;
BufferedSink sink = null;
try {
final String name = source.getName();
if (isEmpty(name)) return null;
final MimeTypeMap map = MimeTypeMap.getSingleton();
final String extension = map.getExtensionFromMimeType(mimeType);
if (extension == null) return null;
final String nameToSave = getFileNameWithExtension(name, extension);
if (!destination.isDirectory() && !destination.mkdirs()) return null;
final File saveFile = new File(destination, nameToSave);
ioSrc = Okio.source(source);
sink = Okio.buffer(Okio.sink(saveFile));
sink.writeAll(ioSrc);
sink.flush();
if (mimeType != null) {
MediaScannerConnection.scanFile(context, new String[]{saveFile.getPath()},
new String[]{mimeType}, null);
}
return saveFile;
} catch (final IOException e) {
final int errno = Utils.getErrorNo(e.getCause());
Log.w(LOGTAG, "Failed to save file", e);
return null;
} finally {
Utils.closeSilently(sink);
Utils.closeSilently(ioSrc);
}
}
private static String getFileNameWithExtension(String name, String extension) {
int lastDotIdx = name.lastIndexOf('.');
if (lastDotIdx < 0) return name + "." + extension;
return name.substring(0, lastDotIdx) + "." + extension;
}
@Override
protected File doInBackground(final Object... args) {
if (source == null) return null;
return saveFile(activity, source, mimeType, destination);
}
@Override
protected void onCancelled() {
final FragmentManager fm = activity.getFragmentManager();
final DialogFragment fragment = (DialogFragment) fm.findFragmentByTag(PROGRESS_FRAGMENT_TAG);
if (fragment != null && fragment.isVisible()) {
fragment.dismiss();
}
super.onCancelled();
}
@Override
protected void onPostExecute(final File result) {
final FragmentManager fm = activity.getFragmentManager();
final DialogFragment fragment = (DialogFragment) fm.findFragmentByTag(PROGRESS_FRAGMENT_TAG);
if (fragment != null) {
fragment.dismiss();
}
super.onPostExecute(result);
if (result != null && result.exists()) {
Toast.makeText(activity, R.string.saved_to_gallery, Toast.LENGTH_SHORT).show();
} else {
Toast.makeText(activity, R.string.error_occurred, Toast.LENGTH_SHORT).show();
}
}
@Override
protected void onPreExecute() {
final DialogFragment fragment = new ProgressDialogFragment();
fragment.setCancelable(false);
fragment.show(activity.getFragmentManager(), PROGRESS_FRAGMENT_TAG);
super.onPreExecute();
}
}

View File

@ -34,7 +34,7 @@ import android.support.v4.view.ViewCompat;
import android.support.v7.app.ActionBar;
import android.support.v7.app.AppCompatActivity;
import android.support.v7.app.AppCompatDelegate;
import android.support.v7.app.AppCompatDelegateTrojan;
import android.support.v7.app.AppCompatDelegateAccessor;
import android.support.v7.view.ContextThemeWrapper;
import android.support.v7.widget.TwidereToolbar;
import android.util.AttributeSet;
@ -257,13 +257,13 @@ public class ThemedLayoutInflaterFactory implements LayoutInflaterFactory {
Context actionBarContext = null;
if (activity instanceof AppCompatActivity) {
final AppCompatDelegate delegate = ((AppCompatActivity) activity).getDelegate();
final ActionBar actionBar = AppCompatDelegateTrojan.peekActionBar(delegate);
final ActionBar actionBar = AppCompatDelegateAccessor.peekActionBar(delegate);
if (actionBar != null) {
actionBarContext = actionBar.getThemedContext();
}
} else if (activity instanceof AppCompatPreferenceActivity) {
final AppCompatDelegate delegate = ((AppCompatPreferenceActivity) activity).getDelegate();
final ActionBar actionBar = AppCompatDelegateTrojan.peekActionBar(delegate);
final ActionBar actionBar = AppCompatDelegateAccessor.peekActionBar(delegate);
if (actionBar != null) {
actionBarContext = actionBar.getThemedContext();
}

View File

@ -2,7 +2,6 @@
<resources>
<bool name="shadow_slidable">false</bool>
<bool name="is_large_screen">true</bool>
<bool name="relative_behind_width">false</bool>
</resources>

View File

@ -3,7 +3,6 @@
<bool name="default_display_tab_label">false</bool>
<bool name="home_display_icon">false</bool>
<bool name="is_large_screen">false</bool>
<bool name="default_shadow_slidable">true</bool>
<bool name="shadow_slidable">true</bool>
<bool name="has_font_family">false</bool>

View File

@ -0,0 +1,25 @@
<?xml version="1.0" encoding="utf-8"?>
<!--
~ Twidere - Twitter client for Android
~
~ Copyright (C) 2012-2015 Mariotaku Lee <mariotaku.lee@gmail.com>
~
~ This program is free software: you can redistribute it and/or modify
~ it under the terms of the GNU General Public License as published by
~ the Free Software Foundation, either version 3 of the License, or
~ (at your option) any later version.
~
~ This program is distributed in the hope that it will be useful,
~ but WITHOUT ANY WARRANTY; without even the implied warranty of
~ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
~ GNU General Public License for more details.
~
~ You should have received a copy of the GNU General Public License
~ along with this program. If not, see <http://www.gnu.org/licenses/>.
-->
<paths>
<cache-path
name="shares"
path="shared_files/"/>
</paths>