1
0
mirror of https://github.com/TwidereProject/Twidere-Android synced 2025-01-30 16:35:00 +01:00

fixed user fragment banner flashing

This commit is contained in:
Mariotaku Lee 2016-02-01 14:58:34 +08:00
parent 36f92bda17
commit 95354b3e5c
32 changed files with 696 additions and 538 deletions

View File

@ -82,8 +82,6 @@ public interface IntentConstants {
String BROADCAST_USER_LIST_MEMBERS_ADDED = INTENT_PACKAGE_PREFIX + "USER_LIST_MEMBER_ADDED";
String BROADCAST_USER_LIST_SUBSCRIBED = INTENT_PACKAGE_PREFIX + "USER_LIST_SUBSRCIBED";
String BROADCAST_USER_LIST_UNSUBSCRIBED = INTENT_PACKAGE_PREFIX + "USER_LIST_UNSUBSCRIBED";
String BROADCAST_USER_LIST_CREATED = INTENT_PACKAGE_PREFIX + "USER_LIST_CREATED";
String BROADCAST_USER_LIST_DELETED = INTENT_PACKAGE_PREFIX + "USER_LIST_DELETED";
String BROADCAST_FILTERS_UPDATED = INTENT_PACKAGE_PREFIX + "FILTERS_UPDATED";
String BROADCAST_REFRESH_HOME_TIMELINE = INTENT_PACKAGE_PREFIX + "REFRESH_HOME_TIMELINE";
String BROADCAST_REFRESH_NOTIFICATIONS = INTENT_PACKAGE_PREFIX + "REFRESH_NOTIFICATIONS";

View File

@ -47,10 +47,13 @@ import org.mariotaku.twidere.util.TwitterContentUtils;
@CursorObject(valuesCreator = true)
public class ParcelableUser implements Parcelable, Comparable<ParcelableUser> {
@ParcelableThisPlease
@JsonField(name = "account_id")
public long account_id;
@ParcelableThisPlease
public int account_color;
@ParcelableThisPlease
@JsonField(name = "id")
@CursorField(CachedUsers.USER_ID)
@ -205,13 +208,6 @@ public class ParcelableUser implements Parcelable, Comparable<ParcelableUser> {
is_basic = true;
}
public ParcelableUser(final Cursor cursor, ParcelableUserCursorIndices indices, final long accountId) {
indices.callBeforeCreated(this);
indices.parseFields(this, cursor);
indices.callAfterCreated(this);
this.account_id = accountId;
}
@AfterCursorObjectCreated
void afterCursorObjectCreated() {
is_cache = true;

View File

@ -63,6 +63,8 @@ public class TwitterContentUtils {
public static final int TWITTER_BULK_QUERY_COUNT = 100;
private static final Pattern PATTERN_TWITTER_STATUS_LINK = Pattern.compile("https?://twitter\\.com/(?:#!/)?(\\w+)/status(es)?/(\\d+)");
private static final CharSequenceTranslator UNESCAPE_TWITTER_RAW_TEXT = new LookupTranslator(EntityArrays.BASIC_UNESCAPE());
private static final CharSequenceTranslator ESCAPE_TWITTER_RAW_TEXT = new LookupTranslator(EntityArrays.BASIC_ESCAPE());
public static String formatDirectMessageText(final DirectMessage message) {
if (message == null) return null;
@ -176,9 +178,6 @@ public class TwitterContentUtils {
return ConsumerKeyType.UNKNOWN;
}
private static final CharSequenceTranslator UNESCAPE_TWITTER_RAW_TEXT = new LookupTranslator(EntityArrays.BASIC_UNESCAPE());
private static final CharSequenceTranslator ESCAPE_TWITTER_RAW_TEXT = new LookupTranslator(EntityArrays.BASIC_ESCAPE());
public static String unescapeTwitterStatusText(final CharSequence text) {
if (text == null) return null;
return UNESCAPE_TWITTER_RAW_TEXT.translate(text);

View File

@ -0,0 +1,5 @@
#Mon Feb 01 00:57:24 CST 2016
minimal.os.version=
application.category=
custom.manifest=false
manifest.additions=true

View File

@ -131,8 +131,10 @@ import org.mariotaku.twidere.util.TwidereValidator;
import org.mariotaku.twidere.util.TwidereViewUtils;
import org.mariotaku.twidere.util.TwitterContentUtils;
import org.mariotaku.twidere.util.Utils;
import org.mariotaku.twidere.util.dagger.GeneralComponentHelper;
import org.mariotaku.twidere.view.ActionIconView;
import org.mariotaku.twidere.view.BadgeView;
import org.mariotaku.twidere.view.CheckableLinearLayout;
import org.mariotaku.twidere.view.ComposeEditText;
import org.mariotaku.twidere.view.ShapedImageView;
import org.mariotaku.twidere.view.StatusTextCountView;
@ -151,6 +153,8 @@ import java.util.Collections;
import java.util.List;
import java.util.TreeSet;
import javax.inject.Inject;
public class ComposeActivity extends ThemedFragmentActivity implements OnMenuItemClickListener,
OnClickListener, OnLongClickListener, Callback {
@ -163,13 +167,15 @@ public class ComposeActivity extends ThemedFragmentActivity implements OnMenuIte
private static final String DISCARD_STATUS_DIALOG_FRAGMENT_TAG = "discard_status";
// Utility classes
private final Extractor mExtractor = new Extractor();
private TwidereValidator mValidator;
@Inject
Extractor mExtractor;
@Inject
TwidereValidator mValidator;
private LocationManager mLocationManager;
private ContentResolver mResolver;
private AsyncTask<Object, Object, ?> mTask;
private SupportMenuInflater mMenuInflater;
private ItemTouchHelper mItemTouchHelper;
private SetProgressVisibleRunnable mSetProgressVisibleRunnable;
// Views
private RecyclerView mAttachedMediaPreview;
@ -201,12 +207,11 @@ public class ComposeActivity extends ThemedFragmentActivity implements OnMenuIte
private boolean mImageUploaderUsed, mStatusShortenerUsed;
private boolean mNavigateBackPressed;
private boolean mTextChanged;
private SetProgressVisibleRunnable mSetProgressVisibleRunnable;
private boolean mFragmentResumed;
private int mKeyMetaState;
// Listeners
private final LocationListener mLocationListener = new ComposeLocationListener(this);
private LocationListener mLocationListener;
@Override
public int getThemeColor() {
@ -317,8 +322,12 @@ public class ComposeActivity extends ThemedFragmentActivity implements OnMenuIte
protected void onStop() {
saveAccountSelection();
try {
mLocationManager.removeUpdates(mLocationListener);
if (mLocationListener != null) {
mLocationManager.removeUpdates(mLocationListener);
mLocationListener = null;
}
} catch (SecurityException ignore) {
// That should not happen
}
super.onStop();
}
@ -580,7 +589,7 @@ public class ComposeActivity extends ThemedFragmentActivity implements OnMenuIte
builder.media(getMedia());
}
final ContentValues values = ContentValuesCreator.createStatusDraft(builder.build());
final Uri draftUri = mResolver.insert(Drafts.CONTENT_URI, values);
final Uri draftUri = getContentResolver().insert(Drafts.CONTENT_URI, values);
displayNewDraftNotification(text, draftUri);
}
@ -608,19 +617,19 @@ public class ComposeActivity extends ThemedFragmentActivity implements OnMenuIte
@Override
protected void onCreate(final Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
GeneralComponentHelper.build(this).inject(this);
mLocationManager = (LocationManager) getSystemService(Context.LOCATION_SERVICE);
mResolver = getContentResolver();
mValidator = new TwidereValidator(this);
setContentView(R.layout.activity_compose);
setFinishOnTouchOutside(false);
final long[] defaultAccountIds = DataStoreUtils.getAccountIds(this);
if (defaultAccountIds.length <= 0) {
final ParcelableCredentials[] accounts = ParcelableCredentials.getCredentialsArray(this, false, false);
if (accounts.length <= 0) {
final Intent intent = new Intent(INTENT_ACTION_TWITTER_LOGIN);
intent.setClass(this, SignInActivity.class);
startActivity(intent);
finish();
return;
}
final long[] defaultAccountIds = ParcelableAccount.getAccountIds(accounts);
mMenuBar.setOnMenuItemClickListener(this);
setupEditText();
mAccountSelectorContainer.setOnClickListener(this);
@ -635,15 +644,10 @@ public class ComposeActivity extends ThemedFragmentActivity implements OnMenuIte
mAccountSelector.setItemAnimator(new DefaultItemAnimator());
mAccountsAdapter = new AccountIconsAdapter(this);
mAccountSelector.setAdapter(mAccountsAdapter);
mAccountsAdapter.setAccounts(ParcelableCredentials.getCredentialsArray(this, false, false));
mAccountsAdapter.setAccounts(accounts);
mMediaPreviewAdapter = new MediaPreviewAdapter(this, new SimpleItemTouchHelperCallback.OnStartDragListener() {
@Override
public void onStartDrag(ViewHolder viewHolder) {
mItemTouchHelper.startDrag(viewHolder);
}
});
mMediaPreviewAdapter = new MediaPreviewAdapter(this, new PreviewGridOnStartDragListener(mItemTouchHelper));
mItemTouchHelper = new ItemTouchHelper(new AttachedMediaItemTouchHelperCallback(mMediaPreviewAdapter));
final LinearLayoutManager layoutManager = new LinearLayoutManager(this);
layoutManager.setOrientation(LinearLayoutManager.HORIZONTAL);
@ -651,12 +655,7 @@ public class ComposeActivity extends ThemedFragmentActivity implements OnMenuIte
mAttachedMediaPreview.setAdapter(mMediaPreviewAdapter);
mItemTouchHelper.attachToRecyclerView(mAttachedMediaPreview);
final int previewGridSpacing = getResources().getDimensionPixelSize(R.dimen.element_spacing_small);
mAttachedMediaPreview.addItemDecoration(new ItemDecoration() {
@Override
public void getItemOffsets(Rect outRect, View view, RecyclerView parent, State state) {
outRect.left = outRect.right = previewGridSpacing;
}
});
mAttachedMediaPreview.addItemDecoration(new PreviewGridItemDecoration(previewGridSpacing));
final Intent intent = getIntent();
@ -685,8 +684,8 @@ public class ComposeActivity extends ThemedFragmentActivity implements OnMenuIte
if (!handleIntent(intent)) {
handleDefaultIntent(intent);
}
final long[] accountIds = mAccountsAdapter.getSelectedAccountIds();
if (accountIds.length == 0) {
final long[] selectedAccountIds = mAccountsAdapter.getSelectedAccountIds();
if (selectedAccountIds.length == 0) {
final long[] idsInPrefs = TwidereArrayUtils.parseLongArray(
mPreferences.getString(KEY_COMPOSE_ACCOUNTS, null), ',');
final long[] intersection = TwidereArrayUtils.intersection(idsInPrefs, defaultAccountIds);
@ -736,18 +735,7 @@ public class ComposeActivity extends ThemedFragmentActivity implements OnMenuIte
private void setupEditText() {
final boolean sendByEnter = mPreferences.getBoolean(KEY_QUICK_SEND);
EditTextEnterHandler.attach(mEditText, new EnterListener() {
@Override
public boolean shouldCallListener() {
return mKeyMetaState == 0;
}
@Override
public boolean onHitEnter() {
confirmAndUpdateStatus();
return true;
}
}, sendByEnter);
EditTextEnterHandler.attach(mEditText, new ComposeEnterListener(this), sendByEnter);
mEditText.addTextChangedListener(new TextWatcher() {
@Override
@ -1146,6 +1134,7 @@ public class ComposeActivity extends ThemedFragmentActivity implements OnMenuIte
}
private void setProgressVisible(final boolean visible) {
if (isFinishing()) return;
mSetProgressVisibleRunnable = new SetProgressVisibleRunnable(this, visible);
if (mFragmentResumed) {
runOnUiThread(mSetProgressVisibleRunnable);
@ -1174,7 +1163,11 @@ public class ComposeActivity extends ThemedFragmentActivity implements OnMenuIte
super.onRequestPermissionsResult(requestCode, permissions, grantResults);
if (PermissionUtils.getPermission(permissions, grantResults, Manifest.permission.ACCESS_FINE_LOCATION) == PackageManager.PERMISSION_GRANTED ||
PermissionUtils.getPermission(permissions, grantResults, Manifest.permission.ACCESS_COARSE_LOCATION) == PackageManager.PERMISSION_GRANTED) {
startLocationUpdateIfEnabled();
try {
startLocationUpdateIfEnabled();
} catch (SecurityException e) {
// That should not happen
}
} else {
Toast.makeText(this, R.string.cannot_get_location, Toast.LENGTH_SHORT).show();
}
@ -1194,29 +1187,22 @@ public class ComposeActivity extends ThemedFragmentActivity implements OnMenuIte
* the best provider of data (GPS, WiFi/cell phone tower lookup, some other
* mechanism) and finds the last known location.
*/
private boolean startLocationUpdateIfEnabled() {
final LocationManager lm = mLocationManager;
try {
lm.removeUpdates(mLocationListener);
} catch (SecurityException ignore) {
}
private boolean startLocationUpdateIfEnabled() throws SecurityException {
if (mLocationListener != null) return true;
final boolean attachLocation = mPreferences.getBoolean(KEY_ATTACH_LOCATION);
if (!attachLocation) {
return false;
}
final Criteria criteria = new Criteria();
criteria.setAccuracy(Criteria.ACCURACY_FINE);
final String provider = lm.getBestProvider(criteria, true);
final String provider = mLocationManager.getBestProvider(criteria, true);
if (provider != null) {
try {
mLocationText.setText(R.string.getting_location);
lm.requestLocationUpdates(provider, 0, 0, mLocationListener);
final Location location = Utils.getCachedLocation(this);
if (location != null) {
mLocationListener.onLocationChanged(location);
}
} catch (SecurityException e) {
return false;
mLocationText.setText(R.string.getting_location);
mLocationListener = new ComposeLocationListener(this);
mLocationManager.requestLocationUpdates(provider, 0, 0, mLocationListener);
final Location location = Utils.getCachedLocation(this);
if (location != null) {
mLocationListener.onLocationChanged(location);
}
} else {
Toast.makeText(this, R.string.cannot_get_location, Toast.LENGTH_SHORT).show();
@ -1351,16 +1337,18 @@ public class ComposeActivity extends ThemedFragmentActivity implements OnMenuIte
static class SetProgressVisibleRunnable implements Runnable {
final ComposeActivity activity;
final WeakReference<ComposeActivity> activityRef;
final boolean visible;
SetProgressVisibleRunnable(ComposeActivity activity, boolean visible) {
this.activity = activity;
this.activityRef = new WeakReference<>(activity);
this.visible = visible;
}
@Override
public void run() {
final ComposeActivity activity = activityRef.get();
if (activity == null) return;
final FragmentManager fm = activity.getSupportFragmentManager();
final Fragment f = fm.findFragmentByTag(DISCARD_STATUS_DIALOG_FRAGMENT_TAG);
if (!visible && f instanceof DialogFragment) {
@ -1389,6 +1377,7 @@ public class ComposeActivity extends ThemedFragmentActivity implements OnMenuIte
public void showAccount(AccountIconsAdapter adapter, ParcelableAccount account, boolean isSelected) {
itemView.setAlpha(isSelected ? 1 : 0.33f);
((CheckableLinearLayout) itemView).setChecked(isSelected);
final MediaLoaderWrapper loader = adapter.getImageLoader();
if (ObjectUtils.notEqual(account.profile_image_url, iconView.getTag()) || iconView.getDrawable() == null) {
loader.displayProfileImage(iconView, account.profile_image_url);
@ -1400,6 +1389,7 @@ public class ComposeActivity extends ThemedFragmentActivity implements OnMenuIte
@Override
public void onClick(View v) {
((CheckableLinearLayout) itemView).toggle();
adapter.toggleSelection(getAdapterPosition());
}
@ -1660,11 +1650,11 @@ public class ComposeActivity extends ThemedFragmentActivity implements OnMenuIte
static class DiscardTweetTask extends AsyncTask<Object, Object, Object> {
final ComposeActivity activity;
final WeakReference<ComposeActivity> activityRef;
private final List<ParcelableMediaUpdate> media;
DiscardTweetTask(final ComposeActivity activity) {
this.activity = activity;
this.activityRef = new WeakReference<>(activity);
this.media = activity.getMediaList();
}
@ -1684,17 +1674,21 @@ public class ComposeActivity extends ThemedFragmentActivity implements OnMenuIte
@Override
protected void onPostExecute(final Object result) {
final ComposeActivity activity = activityRef.get();
if (activity == null) return;
activity.setProgressVisible(false);
activity.finish();
}
@Override
protected void onPreExecute() {
final ComposeActivity activity = activityRef.get();
if (activity == null) return;
activity.setProgressVisible(true);
}
}
public static class MediaPreviewAdapter extends ArrayRecyclerAdapter<ParcelableMediaUpdate, MediaPreviewViewHolder>
static class MediaPreviewAdapter extends ArrayRecyclerAdapter<ParcelableMediaUpdate, MediaPreviewViewHolder>
implements SimpleItemTouchHelperCallback.ItemTouchHelperAdapter {
final LayoutInflater mInflater;
@ -1884,4 +1878,54 @@ public class ComposeActivity extends ThemedFragmentActivity implements OnMenuIte
viewHolder.itemView.setAlpha(ALPHA_FULL);
}
}
private static class PreviewGridItemDecoration extends ItemDecoration {
private final int mPreviewGridSpacing;
public PreviewGridItemDecoration(int previewGridSpacing) {
mPreviewGridSpacing = previewGridSpacing;
}
@Override
public void getItemOffsets(Rect outRect, View view, RecyclerView parent, State state) {
outRect.left = outRect.right = mPreviewGridSpacing;
}
}
private static class PreviewGridOnStartDragListener implements SimpleItemTouchHelperCallback.OnStartDragListener {
private final WeakReference<ItemTouchHelper> helperRef;
public PreviewGridOnStartDragListener(ItemTouchHelper helper) {
helperRef = new WeakReference<>(helper);
}
@Override
public void onStartDrag(ViewHolder viewHolder) {
final ItemTouchHelper helper = helperRef.get();
if (helper == null) return;
helper.startDrag(viewHolder);
}
}
private static class ComposeEnterListener implements EnterListener {
private final WeakReference<ComposeActivity> activityRef;
public ComposeEnterListener(ComposeActivity activity) {
activityRef = new WeakReference<>(activity);
}
@Override
public boolean shouldCallListener() {
final ComposeActivity activity = activityRef.get();
return activity != null && activity.mKeyMetaState == 0;
}
@Override
public boolean onHitEnter() {
final ComposeActivity activity = activityRef.get();
if (activity == null) return false;
activity.confirmAndUpdateStatus();
return true;
}
}
}

View File

@ -104,8 +104,11 @@ public final class MediaViewerActivity extends AbsMediaViewerActivity implements
}
@Override
@NonNull
protected ViewPager findViewPager() {
return (ViewPager) findViewById(R.id.view_pager);
final ViewPager viewPager = (ViewPager) findViewById(R.id.view_pager);
if (viewPager == null) throw new NullPointerException();
return viewPager;
}
@Override

View File

@ -31,6 +31,8 @@ import android.view.KeyEvent;
import android.view.LayoutInflater;
import android.view.View;
import com.squareup.otto.Bus;
import org.mariotaku.twidere.BuildConfig;
import org.mariotaku.twidere.Constants;
import org.mariotaku.twidere.activity.iface.IThemedActivity;
@ -66,6 +68,8 @@ public abstract class ThemedFragmentActivity extends FragmentActivity implements
protected UserColorNameManager mUserColorNameManager;
@Inject
protected SharedPreferencesWrapper mPreferences;
@Inject
protected Bus mBus;
// Data fields
private int mCurrentThemeResource, mCurrentThemeColor, mCurrentThemeBackgroundAlpha;

View File

@ -19,10 +19,7 @@
package org.mariotaku.twidere.activity.support;
import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.Intent;
import android.content.IntentFilter;
import android.os.AsyncTask;
import android.os.Bundle;
import android.support.v4.app.DialogFragment;
@ -36,6 +33,8 @@ import android.widget.AdapterView.OnItemClickListener;
import android.widget.AutoCompleteTextView;
import android.widget.ListView;
import com.squareup.otto.Subscribe;
import org.mariotaku.twidere.R;
import org.mariotaku.twidere.adapter.SimpleParcelableUserListsAdapter;
import org.mariotaku.twidere.adapter.SimpleParcelableUsersAdapter;
@ -55,6 +54,7 @@ import org.mariotaku.twidere.model.SingleResponse;
import org.mariotaku.twidere.util.AsyncTaskUtils;
import org.mariotaku.twidere.util.ParseUtils;
import org.mariotaku.twidere.util.TwitterAPIFactory;
import org.mariotaku.twidere.util.message.UserListCreatedEvent;
import java.util.ArrayList;
import java.util.List;
@ -72,16 +72,6 @@ public class UserListSelectorActivity extends BaseSupportDialogActivity implemen
private String mScreenName;
private final BroadcastReceiver mStatusReceiver = new BroadcastReceiver() {
@Override
public void onReceive(final Context context, final Intent intent) {
final String action = intent.getAction();
if (BROADCAST_USER_LIST_CREATED.equals(action)) {
getUserLists(mScreenName);
}
}
};
private Runnable mResumeFragmentRunnable;
private boolean mFragmentsResumed;
@ -196,16 +186,20 @@ public class UserListSelectorActivity extends BaseSupportDialogActivity implemen
@Override
protected void onStart() {
super.onStart();
final IntentFilter filter = new IntentFilter(BROADCAST_USER_LIST_CREATED);
registerReceiver(mStatusReceiver, filter);
mBus.register(this);
}
@Override
protected void onStop() {
unregisterReceiver(mStatusReceiver);
mBus.unregister(this);
super.onStop();
}
@Subscribe
void onUserListCreated(UserListCreatedEvent event) {
getUserLists(mScreenName);
}
private long getAccountId() {
return getIntent().getLongExtra(EXTRA_ACCOUNT_ID, -1);
}

View File

@ -32,6 +32,7 @@ import org.mariotaku.twidere.app.TwidereApplication;
import org.mariotaku.twidere.util.AsyncTwitterWrapper;
import org.mariotaku.twidere.util.DebugModeUtils;
import org.mariotaku.twidere.util.SharedPreferencesWrapper;
import org.mariotaku.twidere.util.TwidereValidator;
import org.mariotaku.twidere.util.UserColorNameManager;
import org.mariotaku.twidere.util.dagger.GeneralComponentHelper;
@ -45,6 +46,8 @@ public class BaseSupportDialogFragment extends DialogFragment implements Constan
protected UserColorNameManager mUserColorNameManager;
@Inject
protected SharedPreferencesWrapper mPreferences;
@Inject
protected TwidereValidator mValidator;
public TwidereApplication getApplication() {
final Activity activity = getActivity();

View File

@ -24,7 +24,6 @@ import android.content.BroadcastReceiver;
import android.content.ContentResolver;
import android.content.Context;
import android.content.IntentFilter;
import android.content.SharedPreferences;
import android.graphics.Rect;
import android.os.Bundle;
import android.support.annotation.Nullable;
@ -53,6 +52,7 @@ import org.mariotaku.twidere.util.NotificationManagerWrapper;
import org.mariotaku.twidere.util.ReadStateManager;
import org.mariotaku.twidere.util.SharedPreferencesWrapper;
import org.mariotaku.twidere.util.ThemedLayoutInflaterFactory;
import org.mariotaku.twidere.util.TwidereValidator;
import org.mariotaku.twidere.util.UserColorNameManager;
import org.mariotaku.twidere.util.dagger.GeneralComponentHelper;
@ -83,6 +83,8 @@ public class BaseSupportFragment extends Fragment implements IBaseFragment, Cons
protected BidiFormatter mBidiFormatter;
@Inject
protected ErrorInfoStore mErrorInfoStore;
@Inject
TwidereValidator mValidator;
public BaseSupportFragment() {
@ -107,19 +109,6 @@ public class BaseSupportFragment extends Fragment implements IBaseFragment, Cons
return null;
}
public SharedPreferences getSharedPreferences(final String name, final int mode) {
final Activity activity = getActivity();
if (activity != null) return activity.getSharedPreferences(name, mode);
return null;
}
public Object getSystemService(final String name) {
final Activity activity = getActivity();
if (activity != null) return activity.getSystemService(name);
return null;
}
public void invalidateOptionsMenu() {
final FragmentActivity activity = getActivity();
if (activity == null) return;

View File

@ -104,7 +104,6 @@ import org.mariotaku.twidere.util.KeyboardShortcutsHandler.TakeAllKeyboardShortc
import org.mariotaku.twidere.util.MenuUtils;
import org.mariotaku.twidere.util.ParseUtils;
import org.mariotaku.twidere.util.ReadStateManager;
import org.mariotaku.twidere.util.TwidereValidator;
import org.mariotaku.twidere.util.UserColorNameManager;
import org.mariotaku.twidere.util.Utils;
import org.mariotaku.twidere.util.dagger.GeneralComponentHelper;
@ -162,8 +161,6 @@ public class MessagesConversationFragment extends BaseSupportFragment implements
};
private PanelShowHideListener mScrollListener;
// Utility classes
private TwidereValidator mValidator;
private SharedPreferences mMessageDrafts;
private EffectViewHelper mEffectHelper;
@ -228,8 +225,7 @@ public class MessagesConversationFragment extends BaseSupportFragment implements
super.onActivityCreated(savedInstanceState);
final BaseAppCompatActivity activity = (BaseAppCompatActivity) getActivity();
mMessageDrafts = getSharedPreferences(MESSAGE_DRAFTS_PREFERENCES_NAME, Context.MODE_PRIVATE);
mValidator = new TwidereValidator(activity);
mMessageDrafts = activity.getSharedPreferences(MESSAGE_DRAFTS_PREFERENCES_NAME, Context.MODE_PRIVATE);
final View view = getView();
assert view != null;

View File

@ -62,7 +62,6 @@ public class RetweetQuoteDialogFragment extends BaseSupportDialogFragment implem
public static final String FRAGMENT_TAG = "retweet_quote";
private PopupMenu mPopupMenu;
private TwidereValidator mValidator;
@Override
public void onClick(final DialogInterface dialog, final int which) {
@ -95,7 +94,6 @@ public class RetweetQuoteDialogFragment extends BaseSupportDialogFragment implem
final Context wrapped = ThemeUtils.getDialogThemedContext(getActivity());
final AlertDialog.Builder builder = new AlertDialog.Builder(wrapped);
final Context context = builder.getContext();
mValidator = new TwidereValidator(context);
final LayoutInflater inflater = LayoutInflater.from(context);
@SuppressLint("InflateParams") final View view = inflater.inflate(R.layout.dialog_status_quote_retweet, null);
final DummyStatusHolderAdapter adapter = new DummyStatusHolderAdapter(context);

View File

@ -24,11 +24,8 @@ import android.annotation.TargetApi;
import android.app.Activity;
import android.content.ActivityNotFoundException;
import android.content.ContentResolver;
import android.content.ContentValues;
import android.content.Context;
import android.content.Intent;
import android.content.SharedPreferences;
import android.content.SharedPreferences.OnSharedPreferenceChangeListener;
import android.content.res.Resources;
import android.graphics.Color;
import android.graphics.Outline;
@ -45,6 +42,7 @@ import android.nfc.NfcEvent;
import android.os.Build;
import android.os.Bundle;
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.LoaderManager;
@ -65,6 +63,7 @@ import android.support.v7.widget.Toolbar;
import android.support.v7.widget.TwidereToolbar;
import android.text.TextUtils;
import android.util.Log;
import android.util.Pair;
import android.view.KeyEvent;
import android.view.LayoutInflater;
import android.view.Menu;
@ -81,9 +80,12 @@ import android.widget.ImageView;
import android.widget.ProgressBar;
import android.widget.TextView;
import com.desmond.asyncmanager.AsyncManager;
import com.desmond.asyncmanager.TaskRunnable;
import com.meizu.flyme.reflect.StatusBarProxy;
import com.squareup.otto.Subscribe;
import org.apache.commons.lang3.ObjectUtils;
import org.apache.commons.lang3.math.NumberUtils;
import org.mariotaku.sqliteqb.library.Expression;
import org.mariotaku.twidere.R;
@ -98,13 +100,14 @@ import org.mariotaku.twidere.api.twitter.Twitter;
import org.mariotaku.twidere.api.twitter.TwitterException;
import org.mariotaku.twidere.api.twitter.model.FriendshipUpdate;
import org.mariotaku.twidere.api.twitter.model.Relationship;
import org.mariotaku.twidere.app.TwidereApplication;
import org.mariotaku.twidere.fragment.iface.IBaseFragment.SystemWindowsInsetsCallback;
import org.mariotaku.twidere.fragment.iface.RefreshScrollTopInterface;
import org.mariotaku.twidere.fragment.iface.SupportFragmentCallback;
import org.mariotaku.twidere.graphic.ActionBarColorDrawable;
import org.mariotaku.twidere.graphic.ActionIconDrawable;
import org.mariotaku.twidere.loader.support.ParcelableUserLoader;
import org.mariotaku.twidere.model.CachedRelationship;
import org.mariotaku.twidere.model.CachedRelationshipValuesCreator;
import org.mariotaku.twidere.model.ConsumerKeyType;
import org.mariotaku.twidere.model.ParcelableCredentials;
import org.mariotaku.twidere.model.ParcelableMedia;
@ -112,6 +115,7 @@ import org.mariotaku.twidere.model.ParcelableUser;
import org.mariotaku.twidere.model.ParcelableUserList;
import org.mariotaku.twidere.model.SingleResponse;
import org.mariotaku.twidere.model.SupportTabSpec;
import org.mariotaku.twidere.provider.TwidereDataStore.CachedRelationships;
import org.mariotaku.twidere.provider.TwidereDataStore.CachedUsers;
import org.mariotaku.twidere.provider.TwidereDataStore.Filters;
import org.mariotaku.twidere.util.AsyncTwitterWrapper;
@ -122,13 +126,13 @@ import org.mariotaku.twidere.util.KeyboardShortcutsHandler;
import org.mariotaku.twidere.util.KeyboardShortcutsHandler.KeyboardShortcutCallback;
import org.mariotaku.twidere.util.LinkCreator;
import org.mariotaku.twidere.util.MenuUtils;
import org.mariotaku.twidere.util.ParseUtils;
import org.mariotaku.twidere.util.ThemeUtils;
import org.mariotaku.twidere.util.TwidereColorUtils;
import org.mariotaku.twidere.util.TwidereLinkify;
import org.mariotaku.twidere.util.TwidereLinkify.OnLinkClickListener;
import org.mariotaku.twidere.util.TwidereMathUtils;
import org.mariotaku.twidere.util.TwitterAPIFactory;
import org.mariotaku.twidere.util.UserColorNameManager;
import org.mariotaku.twidere.util.Utils;
import org.mariotaku.twidere.util.menu.TwidereMenuInfo;
import org.mariotaku.twidere.util.message.FriendshipUpdatedEvent;
@ -151,17 +155,14 @@ import java.util.Calendar;
import java.util.List;
import java.util.Locale;
public class UserFragment extends BaseSupportFragment implements OnClickListener,
OnLinkClickListener, OnSizeChangedListener, OnSharedPreferenceChangeListener,
OnTouchListener, DrawerCallback, SupportFragmentCallback, SystemWindowsInsetsCallback,
RefreshScrollTopInterface, OnPageChangeListener, KeyboardShortcutCallback {
private static final ArgbEvaluator sArgbEvaluator = new ArgbEvaluator();
public class UserFragment extends BaseSupportFragment implements OnClickListener, OnLinkClickListener,
OnSizeChangedListener, OnTouchListener, DrawerCallback, SupportFragmentCallback,
SystemWindowsInsetsCallback, RefreshScrollTopInterface, OnPageChangeListener, KeyboardShortcutCallback,
UserColorNameManager.UserColorChangedListener, UserColorNameManager.UserNicknameChangedListener {
public static final String TRANSITION_NAME_PROFILE_IMAGE = "profile_image";
public static final String TRANSITION_NAME_PROFILE_TYPE = "profile_type";
public static final String TRANSITION_NAME_CARD = "card";
private static final ArgbEvaluator sArgbEvaluator = new ArgbEvaluator();
private static final int LOADER_ID_USER = 1;
private static final int LOADER_ID_FRIENDSHIP = 2;
@ -172,6 +173,7 @@ public class UserFragment extends BaseSupportFragment implements OnClickListener
private static final String TAB_TYPE_MEDIA = "media";
private static final String TAB_TYPE_FAVORITES = "favorites";
// Views
private ShapedImageView mProfileImageView;
private ImageView mProfileTypeView;
private ProfileBannerImageView mProfileBannerView;
@ -201,12 +203,12 @@ public class UserFragment extends BaseSupportFragment implements OnClickListener
private View mProfileDetailsContainer;
private View mFollowingYouIndicator;
private ActionBarDrawable mActionBarBackground;
private SupportTabsAdapter mPagerAdapter;
// Data fields
private ParcelableUser mUser;
private Relationship mRelationship;
private UserRelationship mRelationship;
private Locale mLocale;
private boolean mGetUserInfoLoaderInitialized, mGetFriendShipLoaderInitialized;
private int mBannerWidth;
@ -217,85 +219,35 @@ public class UserFragment extends BaseSupportFragment implements OnClickListener
private int mPreviousTabItemIsDark, mPreviousActionBarItemIsDark;
private boolean mHideBirthdayView;
private final LoaderCallbacks<SingleResponse<UserRelationship>> mFriendshipLoaderCallbacks =
new LoaderCallbacks<SingleResponse<UserRelationship>>() {
private final LoaderCallbacks<SingleResponse<Relationship>> mFriendshipLoaderCallbacks = new LoaderCallbacks<SingleResponse<Relationship>>() {
@Override
public Loader<SingleResponse<UserRelationship>> onCreateLoader(final int id, final Bundle args) {
invalidateOptionsMenu();
mFollowButton.setVisibility(View.GONE);
mFollowProgress.setVisibility(View.VISIBLE);
mFollowingYouIndicator.setVisibility(View.GONE);
final long accountId = args.getLong(EXTRA_ACCOUNT_ID, -1);
final long userId = args.getLong(EXTRA_USER_ID, -1);
return new UserRelationshipLoader(getActivity(), accountId, userId);
}
@Override
public Loader<SingleResponse<Relationship>> onCreateLoader(final int id, final Bundle args) {
invalidateOptionsMenu();
mFollowButton.setVisibility(View.GONE);
mFollowProgress.setVisibility(View.VISIBLE);
mFollowingYouIndicator.setVisibility(View.GONE);
final long accountId = args.getLong(EXTRA_ACCOUNT_ID, -1);
final long userId = args.getLong(EXTRA_USER_ID, -1);
return new RelationshipLoader(getActivity(), accountId, userId);
}
@Override
public void onLoaderReset(final Loader<SingleResponse<UserRelationship>> loader) {
@Override
public void onLoaderReset(final Loader<SingleResponse<Relationship>> loader) {
}
}
@Override
public void onLoadFinished(final Loader<SingleResponse<Relationship>> loader,
final SingleResponse<Relationship> data) {
mFollowProgress.setVisibility(View.GONE);
final ParcelableUser user = getUser();
final Relationship relationship = data.getData();
showRelationship(user, relationship);
}
};
private void showRelationship(ParcelableUser user, Relationship relationship) {
mRelationship = relationship;
if (user == null) return;
invalidateOptionsMenu();
final boolean isMyself = user.account_id == user.id;
if (isMyself) {
mFollowButton.setText(R.string.edit);
mFollowButton.setVisibility(View.VISIBLE);
} else if (relationship != null) {
mFollowButton.setEnabled(!relationship.isSourceBlockedByTarget());
if (relationship.isSourceBlockedByTarget()) {
mPagesErrorContainer.setVisibility(View.GONE);
mPagesErrorText.setText(null);
mPagesContent.setVisibility(View.VISIBLE);
} else if (!relationship.isSourceFollowingTarget() && user.is_protected) {
mPagesErrorContainer.setVisibility(View.VISIBLE);
final String displayName = mUserColorNameManager.getDisplayName(user, mNameFirst, true);
mPagesErrorText.setText(getString(R.string.user_protected_summary, displayName));
mPagesErrorIcon.setImageResource(R.drawable.ic_info_locked);
mPagesContent.setVisibility(View.GONE);
} else {
mPagesErrorContainer.setVisibility(View.GONE);
mPagesErrorText.setText(null);
mPagesContent.setVisibility(View.VISIBLE);
}
if (relationship.isSourceBlockingTarget()) {
mFollowButton.setText(R.string.unblock);
} else if (relationship.isSourceFollowingTarget()) {
mFollowButton.setText(R.string.unfollow);
} else if (user.is_follow_request_sent) {
mFollowButton.setText(R.string.requested);
} else {
mFollowButton.setText(R.string.follow);
}
mFollowButton.setCompoundDrawablePadding(Math.round(mFollowButton.getTextSize() * 0.25f));
mFollowingYouIndicator.setVisibility(relationship.isTargetFollowingSource() ? View.VISIBLE : View.GONE);
final ContentResolver resolver = getContentResolver();
final ContentValues cachedValues = ContentValuesCreator.makeCachedUserContentValues(user);
resolver.insert(CachedUsers.CONTENT_URI, cachedValues);
mFollowButton.setVisibility(View.VISIBLE);
} else {
mFollowButton.setText(null);
mFollowButton.setVisibility(View.GONE);
mPagesErrorContainer.setVisibility(View.GONE);
mPagesContent.setVisibility(View.VISIBLE);
}
}
@Override
public void onLoadFinished(final Loader<SingleResponse<UserRelationship>> loader,
final SingleResponse<UserRelationship> data) {
mFollowProgress.setVisibility(View.GONE);
final ParcelableUser user = getUser();
final UserRelationship relationship = data.getData();
showRelationship(user, relationship);
}
};
private final LoaderCallbacks<SingleResponse<ParcelableUser>> mUserInfoLoaderCallbacks = new LoaderCallbacks<SingleResponse<ParcelableUser>>() {
@Override
@ -362,6 +314,69 @@ public class UserFragment extends BaseSupportFragment implements OnClickListener
};
private static void setCompatToolbarOverlayAlpha(FragmentActivity activity, float alpha) {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) return;
final View windowOverlay = activity.findViewById(R.id.window_overlay);
if (windowOverlay != null) {
windowOverlay.setAlpha(alpha);
return;
}
final Drawable drawable = ThemeUtils.getCompatToolbarOverlay(activity);
if (drawable == null) return;
drawable.setAlpha(Math.round(alpha * 255));
}
private void showRelationship(@Nullable ParcelableUser user, @Nullable UserRelationship userRelationship) {
if (user == null) {
mRelationship = null;
return;
} else if (userRelationship != null && userRelationship.check(user)) {
mRelationship = userRelationship;
} else {
mRelationship = null;
}
invalidateOptionsMenu();
if (mRelationship == null && user.account_id == user.id) {
mFollowButton.setText(R.string.edit);
mFollowButton.setVisibility(View.VISIBLE);
return;
}
final Relationship relationship = mRelationship.relationship;
mFollowButton.setEnabled(relationship.isSourceBlockingTarget() ||
!relationship.isSourceBlockedByTarget());
if (relationship.isSourceBlockedByTarget()) {
mPagesErrorContainer.setVisibility(View.GONE);
mPagesErrorText.setText(null);
mPagesContent.setVisibility(View.VISIBLE);
} else if (!relationship.isSourceFollowingTarget() && user.is_protected) {
mPagesErrorContainer.setVisibility(View.VISIBLE);
final String displayName = mUserColorNameManager.getDisplayName(user, mNameFirst, true);
mPagesErrorText.setText(getString(R.string.user_protected_summary, displayName));
mPagesErrorIcon.setImageResource(R.drawable.ic_info_locked);
mPagesContent.setVisibility(View.GONE);
} else {
mPagesErrorContainer.setVisibility(View.GONE);
mPagesErrorText.setText(null);
mPagesContent.setVisibility(View.VISIBLE);
}
if (relationship.isSourceBlockingTarget()) {
mFollowButton.setText(R.string.unblock);
} else if (relationship.isSourceFollowingTarget()) {
mFollowButton.setText(R.string.unfollow);
} else if (user.is_follow_request_sent) {
mFollowButton.setText(R.string.requested);
} else {
mFollowButton.setText(R.string.follow);
}
mFollowButton.setCompoundDrawablePadding(Math.round(mFollowButton.getTextSize() * 0.25f));
mFollowingYouIndicator.setVisibility(relationship.isTargetFollowingSource() ? View.VISIBLE : View.GONE);
final CacheUserInfoRunnable task = new CacheUserInfoRunnable(getContext().getApplicationContext());
task.setParams(Pair.create(user, relationship));
AsyncManager.runBackgroundTask(task);
mFollowButton.setVisibility(View.VISIBLE);
}
@Override
public boolean canScroll(float dy) {
final Fragment fragment = getCurrentVisibleFragment();
@ -487,14 +502,17 @@ public class UserFragment extends BaseSupportFragment implements OnClickListener
final LoaderManager lm = getLoaderManager();
lm.destroyLoader(LOADER_ID_USER);
lm.destroyLoader(LOADER_ID_FRIENDSHIP);
final boolean userIsMe = user.account_id == user.id;
mCardContent.setVisibility(View.VISIBLE);
mHeaderErrorContainer.setVisibility(View.GONE);
mProgressContainer.setVisibility(View.GONE);
mUser = user;
final int userColor = mUserColorNameManager.getUserColor(user.id, true);
mProfileImageView.setBorderColor(userColor != 0 ? userColor : Color.WHITE);
mProfileNameContainer.drawEnd(DataStoreUtils.getAccountColor(activity, user.account_id));
if (user.account_color != 0) {
mProfileNameContainer.drawEnd(user.account_color);
} else {
mProfileNameContainer.drawEnd(DataStoreUtils.getAccountColor(activity, user.account_id));
}
final String nick = mUserColorNameManager.getUserNickname(user.id, true);
mNameView.setText(mBidiFormatter.unicodeWrap(TextUtils.isEmpty(nick) ? user.name : getString(R.string.name_with_nickname, user.name, nick)));
final int typeIconRes = Utils.getUserTypeIconRes(user.is_verified, user.is_protected);
@ -533,9 +551,13 @@ public class UserFragment extends BaseSupportFragment implements OnClickListener
}
final int defWidth = resources.getDisplayMetrics().widthPixels;
final int width = mBannerWidth > 0 ? mBannerWidth : defWidth;
mMediaLoader.displayProfileBanner(mProfileBannerView, user.profile_banner_url, width);
final Relationship relationship = mRelationship;
if (relationship == null || relationship.getTargetUserId() != user.id) {
if (ObjectUtils.notEqual(mProfileBannerView.getTag(), user.profile_banner_url) ||
mProfileBannerView.getDrawable() == null) {
mProfileBannerView.setTag(user.profile_banner_url);
mMediaLoader.displayProfileBanner(mProfileBannerView, user.profile_banner_url, width);
}
final UserRelationship relationship = mRelationship;
if (relationship == null) {
getFriendship();
}
activity.setTitle(mUserColorNameManager.getDisplayName(user, mNameFirst, true));
@ -570,6 +592,7 @@ public class UserFragment extends BaseSupportFragment implements OnClickListener
return false;
}
@Nullable
public ParcelableUser getUser() {
return mUser;
}
@ -678,17 +701,14 @@ public class UserFragment extends BaseSupportFragment implements OnClickListener
super.onActivityCreated(savedInstanceState);
final FragmentActivity activity = getActivity();
setHasOptionsMenu(true);
getSharedPreferences(USER_COLOR_PREFERENCES_NAME, Context.MODE_PRIVATE)
.registerOnSharedPreferenceChangeListener(this);
getSharedPreferences(USER_NICKNAME_PREFERENCES_NAME, Context.MODE_PRIVATE)
.registerOnSharedPreferenceChangeListener(this);
mUserColorNameManager.registerColorChangedListener(this);
mUserColorNameManager.registerNicknameChangedListener(this);
mNameFirst = mPreferences.getBoolean(KEY_NAME_FIRST);
mLocale = getResources().getConfiguration().locale;
mCardBackgroundColor = ThemeUtils.getCardBackgroundColor(activity,
ThemeUtils.getThemeBackgroundOption(activity),
ThemeUtils.getUserThemeBackgroundAlpha(activity));
mActionBarShadowColor = 0xA0000000;
final TwidereApplication app = TwidereApplication.getInstance(activity);
final Bundle args = getArguments();
long accountId = -1, userId = -1;
String screenName = null;
@ -777,16 +797,14 @@ public class UserFragment extends BaseSupportFragment implements OnClickListener
final float actionBarElevation = ThemeUtils.getSupportActionBarElevation(activity);
ViewCompat.setElevation(mPagerIndicator, actionBarElevation);
setupBaseActionBar();
setupUserPages();
if (activity instanceof IThemedActivity) {
ViewSupport.setBackground(mPagerOverlay, ThemeUtils.getNormalWindowContentOverlay(activity,
((IThemedActivity) activity).getCurrentThemeResourceId()));
ViewSupport.setBackground(mErrorOverlay, ThemeUtils.getNormalWindowContentOverlay(activity,
((IThemedActivity) activity).getCurrentThemeResourceId()));
}
setupBaseActionBar();
setupUserPages();
if (activity instanceof IThemedActivity) {
setUiColor(((IThemedActivity) activity).getCurrentThemeColor());
}
@ -830,8 +848,9 @@ public class UserFragment extends BaseSupportFragment implements OnClickListener
public void onPrepareOptionsMenu(final Menu menu) {
final AsyncTwitterWrapper twitter = mTwitterWrapper;
final ParcelableUser user = getUser();
final Relationship relationship = mRelationship;
if (twitter == null || user == null) return;
final UserRelationship userRelationship = mRelationship;
if (twitter == null || user == null || userRelationship == null) return;
final Relationship relationship = userRelationship.relationship;
final boolean isMyself = user.account_id == user.id;
final MenuItem mentionItem = menu.findItem(R.id.mention);
@ -857,7 +876,7 @@ public class UserFragment extends BaseSupportFragment implements OnClickListener
// followItem.setTitle(null);
// followItem.setIcon(null);
// }
if (!isMyself && relationship != null) {
if (!isMyself) {
MenuUtils.setMenuItemAvailability(menu, R.id.send_direct_message, relationship.canSourceDMTarget());
MenuUtils.setMenuItemAvailability(menu, R.id.block, true);
MenuUtils.setMenuItemAvailability(menu, R.id.mute_user, true);
@ -873,9 +892,8 @@ public class UserFragment extends BaseSupportFragment implements OnClickListener
}
final MenuItem filterItem = menu.findItem(R.id.add_to_filter);
if (filterItem != null) {
final boolean filtering = Utils.isFilteringUser(getActivity(), user.id);
ActionIconDrawable.setMenuHighlight(filterItem, new TwidereMenuInfo(filtering));
filterItem.setTitle(filtering ? R.string.remove_from_filter : R.string.add_to_filter);
ActionIconDrawable.setMenuHighlight(filterItem, new TwidereMenuInfo(userRelationship.isFiltering));
filterItem.setTitle(userRelationship.isFiltering ? R.string.remove_from_filter : R.string.add_to_filter);
}
final MenuItem wantRetweetsItem = menu.findItem(R.id.enable_retweets);
if (wantRetweetsItem != null) {
@ -909,16 +927,15 @@ public class UserFragment extends BaseSupportFragment implements OnClickListener
public boolean onOptionsItemSelected(final MenuItem item) {
final AsyncTwitterWrapper twitter = mTwitterWrapper;
final ParcelableUser user = getUser();
final Relationship relationship = mRelationship;
final UserRelationship userRelationship = mRelationship;
if (user == null || twitter == null) return false;
switch (item.getItemId()) {
case R.id.block: {
if (mRelationship != null) {
if (mRelationship.isSourceBlockingTarget()) {
twitter.destroyBlockAsync(user.account_id, user.id);
} else {
CreateUserBlockDialogFragment.show(getFragmentManager(), user);
}
if (userRelationship == null) return true;
if (userRelationship.relationship.isSourceBlockingTarget()) {
twitter.destroyBlockAsync(user.account_id, user.id);
} else {
CreateUserBlockDialogFragment.show(getFragmentManager(), user);
}
break;
}
@ -927,9 +944,9 @@ public class UserFragment extends BaseSupportFragment implements OnClickListener
break;
}
case R.id.add_to_filter: {
final boolean filtering = Utils.isFilteringUser(getActivity(), user.id);
if (userRelationship == null) return true;
final ContentResolver cr = getContentResolver();
if (filtering) {
if (userRelationship.isFiltering) {
final Expression where = Expression.equals(Filters.Users.USER_ID, user.id);
cr.delete(Filters.Users.CONTENT_URI, where.getSQL(), null);
Utils.showInfoMessage(getActivity(), R.string.message_user_unmuted, false);
@ -940,12 +957,11 @@ public class UserFragment extends BaseSupportFragment implements OnClickListener
break;
}
case R.id.mute_user: {
if (mRelationship != null) {
if (mRelationship.isSourceMutingTarget()) {
twitter.destroyMuteAsync(user.account_id, user.id);
} else {
CreateUserMuteDialogFragment.show(getFragmentManager(), user);
}
if (userRelationship == null) return true;
if (userRelationship.relationship.isSourceMutingTarget()) {
twitter.destroyMuteAsync(user.account_id, user.id);
} else {
CreateUserMuteDialogFragment.show(getFragmentManager(), user);
}
break;
}
@ -1002,8 +1018,8 @@ public class UserFragment extends BaseSupportFragment implements OnClickListener
break;
}
case R.id.follow: {
if (relationship == null) return false;
final boolean isFollowing = relationship.isSourceFollowingTarget();
if (userRelationship == null) return true;
final boolean isFollowing = userRelationship.relationship.isSourceFollowingTarget();
final boolean isCreatingFriendship = twitter.isCreatingFriendship(user.account_id, user.id);
final boolean isDestroyingFriendship = twitter.isDestroyingFriendship(user.account_id, user.id);
if (!isCreatingFriendship && !isDestroyingFriendship) {
@ -1211,12 +1227,12 @@ public class UserFragment extends BaseSupportFragment implements OnClickListener
Utils.openProfileEditor(getActivity(), user.account_id);
break;
}
final Relationship relationship = mRelationship;
final UserRelationship userRelationship = mRelationship;
final AsyncTwitterWrapper twitter = mTwitterWrapper;
if (relationship == null || twitter == null) return;
if (relationship.isSourceBlockingTarget()) {
if (userRelationship == null || twitter == null) return;
if (userRelationship.relationship.isSourceBlockingTarget()) {
twitter.destroyBlockAsync(user.account_id, user.id);
} else if (relationship.isSourceFollowingTarget()) {
} else if (userRelationship.relationship.isSourceFollowingTarget()) {
DestroyFriendshipDialogFragment.show(getFragmentManager(), user);
} else {
twitter.createFriendshipAsync(user.account_id, user.id);
@ -1310,8 +1326,14 @@ public class UserFragment extends BaseSupportFragment implements OnClickListener
}
@Override
public void onSharedPreferenceChanged(final SharedPreferences sharedPreferences, final String key) {
if (mUser == null || !ParseUtils.parseString(mUser.id).equals(key)) return;
public void onUserNicknameChanged(long userId, String nick) {
if (mUser == null || mUser.id != userId) return;
displayUser(mUser);
}
@Override
public void onUserColorChanged(long userId, int color) {
if (mUser == null || mUser.id != userId) return;
displayUser(mUser);
}
@ -1354,8 +1376,9 @@ public class UserFragment extends BaseSupportFragment implements OnClickListener
}
private void getFriendship() {
mRelationship = null;
final ParcelableUser user = getUser();
if (user == null) return;
mRelationship = null;
final LoaderManager lm = getLoaderManager();
lm.destroyLoader(LOADER_ID_FRIENDSHIP);
final Bundle args = new Bundle();
@ -1375,20 +1398,10 @@ public class UserFragment extends BaseSupportFragment implements OnClickListener
getUserInfo(user.account_id, user.id, user.screen_name, omitIntentExtra);
}
private static void setCompatToolbarOverlayAlpha(FragmentActivity activity, float alpha) {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) return;
final View windowOverlay = activity.findViewById(R.id.window_overlay);
if (windowOverlay != null) {
windowOverlay.setAlpha(alpha);
return;
}
final Drawable drawable = ThemeUtils.getCompatToolbarOverlay(activity);
if (drawable == null) return;
drawable.setAlpha(Math.round(alpha * 255));
}
private void setUiColor(int color) {
mUiColor = color;
mPreviousActionBarItemIsDark = 0;
mPreviousTabItemIsDark = 0;
if (mActionBarBackground == null) {
setupBaseActionBar();
}
@ -1674,32 +1687,34 @@ public class UserFragment extends BaseSupportFragment implements OnClickListener
}
static class RelationshipLoader extends AsyncTaskLoader<SingleResponse<Relationship>> {
static class UserRelationshipLoader extends AsyncTaskLoader<SingleResponse<UserRelationship>> {
private final Context context;
private final long account_id, user_id;
private final long accountId, userId;
public RelationshipLoader(final Context context, final long account_id, final long user_id) {
public UserRelationshipLoader(final Context context, final long accountId, final long userId) {
super(context);
this.context = context;
this.account_id = account_id;
this.user_id = user_id;
this.accountId = accountId;
this.userId = userId;
}
@Override
public SingleResponse<Relationship> loadInBackground() {
if (account_id == user_id) return SingleResponse.getInstance();
final Twitter twitter = TwitterAPIFactory.getTwitterInstance(context, account_id, false);
public SingleResponse<UserRelationship> loadInBackground() {
final boolean isFiltering = Utils.isFilteringUser(context, userId);
if (accountId == userId)
return SingleResponse.getInstance();
final Twitter twitter = TwitterAPIFactory.getTwitterInstance(context, accountId, false);
if (twitter == null) return SingleResponse.getInstance();
try {
final Relationship relationship = twitter.showFriendship(user_id);
final Relationship relationship = twitter.showFriendship(userId);
if (relationship.isSourceBlockingTarget() || relationship.isSourceBlockedByTarget()) {
Utils.setLastSeen(context, user_id, -1);
Utils.setLastSeen(context, userId, -1);
} else {
Utils.setLastSeen(context, user_id, System.currentTimeMillis());
Utils.setLastSeen(context, userId, System.currentTimeMillis());
}
Utils.updateRelationship(context, relationship, account_id);
return SingleResponse.getInstance(relationship);
Utils.updateRelationship(context, relationship, accountId);
return SingleResponse.getInstance(new UserRelationship(relationship, isFiltering));
} catch (final TwitterException e) {
return SingleResponse.getInstance(e);
}
@ -1711,4 +1726,37 @@ public class UserFragment extends BaseSupportFragment implements OnClickListener
}
}
static class UserRelationship {
@NonNull
Relationship relationship;
boolean isFiltering;
public UserRelationship(@NonNull Relationship relationship, boolean isFiltering) {
this.relationship = relationship;
this.isFiltering = isFiltering;
}
public boolean check(@NonNull ParcelableUser user) {
return relationship.getSourceUserId() == user.account_id
&& relationship.getTargetUserId() == user.id;
}
}
private static class CacheUserInfoRunnable extends TaskRunnable<Pair<ParcelableUser, Relationship>, Object, Object> {
private final Context context;
public CacheUserInfoRunnable(Context context) {
this.context = context;
}
@Override
public Object doLongOperation(Pair<ParcelableUser, Relationship> args) throws InterruptedException {
final ContentResolver resolver = context.getContentResolver();
final ParcelableUser user = args.first;
resolver.insert(CachedUsers.CONTENT_URI, ContentValuesCreator.makeCachedUserContentValues(user));
resolver.insert(CachedRelationships.CONTENT_URI, CachedRelationshipValuesCreator.create(
new CachedRelationship(user.account_id, user.id, args.second)));
return null;
}
}
}

View File

@ -19,10 +19,7 @@
package org.mariotaku.twidere.fragment.support;
import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.Intent;
import android.content.IntentFilter;
import android.os.Bundle;
import android.support.v4.app.DialogFragment;
import android.support.v4.content.Loader;
@ -30,32 +27,20 @@ import android.view.Menu;
import android.view.MenuInflater;
import android.view.MenuItem;
import com.squareup.otto.Subscribe;
import org.mariotaku.twidere.R;
import org.mariotaku.twidere.adapter.AbsUserListsAdapter;
import org.mariotaku.twidere.loader.support.UserListsLoader;
import org.mariotaku.twidere.model.ParcelableUserList;
import org.mariotaku.twidere.util.MenuUtils;
import org.mariotaku.twidere.util.Utils;
import org.mariotaku.twidere.util.message.UserListDestroyedEvent;
import java.util.List;
public class UserListsFragment extends ParcelableUserListsFragment {
private final BroadcastReceiver mStatusReceiver = new BroadcastReceiver() {
@Override
public void onReceive(final Context context, final Intent intent) {
if (getActivity() == null || !isAdded() || isDetached()) return;
final String action = intent.getAction();
if (BROADCAST_USER_LIST_DELETED.equals(action)) {
final ParcelableUserList list = intent.getParcelableExtra(EXTRA_USER_LIST);
if (list != null) {
removeUserList(list.id);
}
}
}
};
@Override
public Loader<List<ParcelableUserList>> onCreateUserListsLoader(final Context context,
final Bundle args, final boolean fromUser) {
@ -114,15 +99,20 @@ public class UserListsFragment extends ParcelableUserListsFragment {
@Override
public void onStart() {
super.onStart();
registerReceiver(mStatusReceiver, new IntentFilter(BROADCAST_USER_LIST_DELETED));
mBus.register(this);
}
@Override
public void onStop() {
unregisterReceiver(mStatusReceiver);
mBus.unregister(this);
super.onStop();
}
@Subscribe
void onUserListDestroyed(UserListDestroyedEvent event) {
removeUserList(event.userList.id);
}
private void removeUserList(final long id) {
final AbsUserListsAdapter<List<ParcelableUserList>> adapter = getAdapter();
// final int listsIdx = adapter.findItemPosition(id);

View File

@ -38,6 +38,7 @@ import org.mariotaku.twidere.model.SingleResponse;
import org.mariotaku.twidere.provider.TwidereDataStore.Accounts;
import org.mariotaku.twidere.provider.TwidereDataStore.CachedUsers;
import org.mariotaku.twidere.util.ContentValuesCreator;
import org.mariotaku.twidere.util.DataStoreUtils;
import org.mariotaku.twidere.util.TwitterAPIFactory;
import org.mariotaku.twidere.util.TwitterWrapper;
@ -67,11 +68,13 @@ public final class ParcelableUserLoader extends AsyncTaskLoader<SingleResponse<P
public SingleResponse<ParcelableUser> loadInBackground() {
final Context context = getContext();
final ContentResolver resolver = context.getContentResolver();
int accountColor = DataStoreUtils.getAccountColor(context, mAccountId);
if (!mOmitIntentExtra && mExtras != null) {
final ParcelableUser user = mExtras.getParcelable(EXTRA_USER);
if (user != null) {
final ContentValues values = ContentValuesCreator.makeCachedUserContentValues(user);
resolver.insert(CachedUsers.CONTENT_URI, values);
user.account_color = accountColor;
return SingleResponse.getInstance(user);
}
}
@ -89,33 +92,37 @@ public final class ParcelableUserLoader extends AsyncTaskLoader<SingleResponse<P
}
final Cursor cur = resolver.query(CachedUsers.CONTENT_URI, CachedUsers.COLUMNS,
where.getSQL(), whereArgs, null);
final int count = cur.getCount();
try {
if (count > 0) {
final ParcelableUserCursorIndices indices = new ParcelableUserCursorIndices(cur);
cur.moveToFirst();
return SingleResponse.getInstance(new ParcelableUser(cur, indices, mAccountId));
if (cur != null) {
try {
if (cur.moveToFirst()) {
final ParcelableUserCursorIndices indices = new ParcelableUserCursorIndices(cur);
final ParcelableUser user = indices.newObject(cur);
user.account_id = mAccountId;
user.account_color = accountColor;
return SingleResponse.getInstance(user);
}
} finally {
cur.close();
}
} finally {
cur.close();
}
}
try {
final User user = TwitterWrapper.tryShowUser(twitter, mUserId, mScreenName);
final ContentValues cachedUserValues = createCachedUser(user);
final long userId = user.getId();
final User twitterUser = TwitterWrapper.tryShowUser(twitter, mUserId, mScreenName);
final ContentValues cachedUserValues = createCachedUser(twitterUser);
final long userId = twitterUser.getId();
resolver.insert(CachedUsers.CONTENT_URI, cachedUserValues);
final ParcelableUser result = new ParcelableUser(user, mAccountId);
final ParcelableUser user = new ParcelableUser(twitterUser, mAccountId);
if (isMyAccount(context, userId)) {
final ContentValues accountValues = new ContentValues();
accountValues.put(Accounts.NAME, result.name);
accountValues.put(Accounts.SCREEN_NAME, result.screen_name);
accountValues.put(Accounts.PROFILE_IMAGE_URL, result.profile_image_url);
accountValues.put(Accounts.PROFILE_BANNER_URL, result.profile_banner_url);
accountValues.put(Accounts.NAME, user.name);
accountValues.put(Accounts.SCREEN_NAME, user.screen_name);
accountValues.put(Accounts.PROFILE_IMAGE_URL, user.profile_image_url);
accountValues.put(Accounts.PROFILE_BANNER_URL, user.profile_banner_url);
final String accountWhere = Expression.equals(Accounts.ACCOUNT_ID, userId).getSQL();
resolver.update(Accounts.CONTENT_URI, accountValues, accountWhere, null);
}
return SingleResponse.getInstance(result);
user.account_color = accountColor;
return SingleResponse.getInstance(user);
} catch (final TwitterException e) {
Log.w(LOGTAG, e);
return SingleResponse.getInstance(e);

View File

@ -0,0 +1,54 @@
package org.mariotaku.twidere.model;
import android.support.annotation.NonNull;
import org.mariotaku.library.objectcursor.annotation.CursorField;
import org.mariotaku.library.objectcursor.annotation.CursorObject;
import org.mariotaku.twidere.api.twitter.model.Relationship;
import org.mariotaku.twidere.provider.TwidereDataStore.CachedRelationships;
/**
* Created by mariotaku on 16/2/1.
*/
@CursorObject(valuesCreator = true)
public class CachedRelationship {
@CursorField(CachedRelationships.ACCOUNT_ID)
public long account_id;
@CursorField(CachedRelationships.USER_ID)
public long user_id;
@CursorField(CachedRelationships.FOLLOWING)
public boolean following;
@CursorField(CachedRelationships.FOLLOWED_BY)
public boolean followed_by;
@CursorField(CachedRelationships.BLOCKING)
public boolean blocking;
@CursorField(CachedRelationships.BLOCKED_BY)
public boolean blocked_by;
@CursorField(CachedRelationships.MUTING)
public boolean muting;
@CursorField(CachedRelationships.RETWEET_ENABLED)
public boolean retweet_enabled;
public CachedRelationship() {
}
public CachedRelationship(long accountId, long userId, @NonNull Relationship relationship) {
account_id = accountId;
user_id = userId;
following = relationship.isSourceFollowingTarget();
following = relationship.isSourceFollowedByTarget();
blocking = relationship.isSourceBlockingTarget();
blocked_by = relationship.isSourceBlockedByTarget();
muting = relationship.isSourceMutingTarget();
retweet_enabled = relationship.isSourceWantRetweetsFromTarget();
}
}

View File

@ -26,7 +26,6 @@ import android.content.ContentResolver;
import android.content.ContentValues;
import android.content.Context;
import android.content.Intent;
import android.content.SharedPreferences;
import android.content.pm.PackageManager;
import android.database.Cursor;
import android.graphics.BitmapFactory;
@ -81,6 +80,7 @@ import org.mariotaku.twidere.util.BitmapUtils;
import org.mariotaku.twidere.util.ContentValuesCreator;
import org.mariotaku.twidere.util.MediaUploaderInterface;
import org.mariotaku.twidere.util.NotificationManagerWrapper;
import org.mariotaku.twidere.util.SharedPreferencesWrapper;
import org.mariotaku.twidere.util.StatusShortenerInterface;
import org.mariotaku.twidere.util.TwidereListUtils;
import org.mariotaku.twidere.util.TwidereValidator;
@ -112,16 +112,18 @@ import static org.mariotaku.twidere.util.Utils.getImageUploadStatus;
public class BackgroundOperationService extends IntentService implements Constants {
private TwidereValidator mValidator;
private final Extractor extractor = new Extractor();
private Handler mHandler;
private SharedPreferences mPreferences;
private ContentResolver mResolver;
@Inject
SharedPreferencesWrapper mPreferences;
@Inject
AsyncTwitterWrapper mTwitter;
@Inject
NotificationManagerWrapper mNotificationManager;
@Inject
TwidereValidator mValidator;
@Inject
Extractor mExtractor;
private MediaUploaderInterface mUploader;
private StatusShortenerInterface mShortener;
@ -138,9 +140,6 @@ public class BackgroundOperationService extends IntentService implements Constan
GeneralComponentHelper.build(this).inject(this);
final TwidereApplication app = TwidereApplication.getInstance(this);
mHandler = new Handler();
mPreferences = getSharedPreferences(SHARED_PREFERENCES_NAME, MODE_PRIVATE);
mValidator = new TwidereValidator(this);
mResolver = getContentResolver();
final String uploaderComponent = mPreferences.getString(KEY_MEDIA_UPLOADER, null);
final String shortenerComponent = mPreferences.getString(KEY_STATUS_SHORTENER, null);
mUseUploader = !ServicePickerPreference.isNoneValue(uploaderComponent);
@ -292,16 +291,17 @@ public class BackgroundOperationService extends IntentService implements Constan
final SingleResponse<ParcelableDirectMessage> result = sendDirectMessage(builder, accountId, recipientId, text,
imageUri);
final ContentResolver resolver = getContentResolver();
if (result.getData() != null && result.getData().id > 0) {
final ContentValues values = ContentValuesCreator.createDirectMessage(result.getData());
final String delete_where = DirectMessages.ACCOUNT_ID + " = " + accountId + " AND "
+ DirectMessages.MESSAGE_ID + " = " + result.getData().id;
mResolver.delete(DirectMessages.Outbox.CONTENT_URI, delete_where, null);
mResolver.insert(DirectMessages.Outbox.CONTENT_URI, values);
resolver.delete(DirectMessages.Outbox.CONTENT_URI, delete_where, null);
resolver.insert(DirectMessages.Outbox.CONTENT_URI, values);
showOkMessage(R.string.direct_message_sent, false);
} else {
final ContentValues values = createMessageDraft(accountId, recipientId, text, imageUri);
mResolver.insert(Drafts.CONTENT_URI, values);
resolver.insert(Drafts.CONTENT_URI, values);
showErrorMessage(R.string.action_sending_direct_message, result.getException(), true);
}
stopForeground(false);
@ -333,7 +333,8 @@ public class BackgroundOperationService extends IntentService implements Constan
updateUpdateStatusNotification(this, builder, 0, item));
final ContentValues draftValues = ContentValuesCreator.createStatusDraft(item,
ParcelableAccount.getAccountIds(item.accounts));
final Uri draftUri = mResolver.insert(Drafts.CONTENT_URI, draftValues);
final ContentResolver resolver = getContentResolver();
final Uri draftUri = resolver.insert(Drafts.CONTENT_URI, draftValues);
final long def = -1;
final long draftId = draftUri != null ? NumberUtils.toLong(draftUri.getLastPathSegment(), def) : -1;
mTwitter.addSendingDraftId(draftId);
@ -371,15 +372,15 @@ public class BackgroundOperationService extends IntentService implements Constan
} else {
final ContentValues accountIdsValues = new ContentValues();
accountIdsValues.put(Drafts.ACCOUNT_IDS, TwidereListUtils.toString(failedAccountIds, ',', false));
mResolver.update(Drafts.CONTENT_URI, accountIdsValues, where.getSQL(), null);
resolver.update(Drafts.CONTENT_URI, accountIdsValues, where.getSQL(), null);
showErrorMessage(R.string.action_updating_status, exception, true);
final ContentValues notifValues = new ContentValues();
notifValues.put(BaseColumns._ID, draftId);
mResolver.insert(Drafts.CONTENT_URI_NOTIFICATIONS, notifValues);
resolver.insert(Drafts.CONTENT_URI_NOTIFICATIONS, notifValues);
}
} else {
showOkMessage(R.string.status_updated, false);
mResolver.delete(Drafts.CONTENT_URI, where.getSQL(), null);
resolver.delete(Drafts.CONTENT_URI, where.getSQL(), null);
if (item.media != null) {
for (final ParcelableMediaUpdate media : item.media) {
final String path = getImagePathFromUri(this, Uri.parse(media.uri));
@ -453,7 +454,7 @@ public class BackgroundOperationService extends IntentService implements Constan
private List<SingleResponse<ParcelableStatus>> updateStatus(final Builder builder,
final ParcelableStatusUpdate statusUpdate) {
final ArrayList<ContentValues> hashTagValues = new ArrayList<>();
final Collection<String> hashTags = extractor.extractHashtags(statusUpdate.text);
final Collection<String> hashTags = mExtractor.extractHashtags(statusUpdate.text);
for (final String hashTag : hashTags) {
final ContentValues values = new ContentValues();
values.put(CachedHashtags.NAME, hashTag);
@ -464,7 +465,8 @@ public class BackgroundOperationService extends IntentService implements Constan
&& statusUpdate.text.contains(EASTER_EGG_RESTORE_TEXT_PART2)
&& statusUpdate.text.contains(EASTER_EGG_RESTORE_TEXT_PART3);
boolean mentionedHondaJOJO = false, notReplyToOther = false;
mResolver.bulkInsert(CachedHashtags.CONTENT_URI,
final ContentResolver resolver = getContentResolver();
resolver.bulkInsert(CachedHashtags.CONTENT_URI,
hashTagValues.toArray(new ContentValues[hashTagValues.size()]));
final List<SingleResponse<ParcelableStatus>> results = new ArrayList<>();
@ -547,8 +549,8 @@ public class BackgroundOperationService extends IntentService implements Constan
for (int i = 0, j = mediaIds.length; i < j; i++) {
final ParcelableMediaUpdate media = statusUpdate.media[i];
final Uri mediaUri = Uri.parse(media.uri);
final String mediaType = mResolver.getType(mediaUri);
final InputStream is = mResolver.openInputStream(mediaUri);
final String mediaType = resolver.getType(mediaUri);
final InputStream is = resolver.openInputStream(mediaUri);
final long length = is.available();
cis = new ContentLengthInputStream(is, length);
cis.setReadListener(new StatusMediaUploadListener(this, mNotificationManager, builder,

View File

@ -93,6 +93,8 @@ import org.mariotaku.twidere.util.message.ProfileUpdatedEvent;
import org.mariotaku.twidere.util.message.StatusDestroyedEvent;
import org.mariotaku.twidere.util.message.StatusListChangedEvent;
import org.mariotaku.twidere.util.message.StatusRetweetedEvent;
import org.mariotaku.twidere.util.message.UserListCreatedEvent;
import org.mariotaku.twidere.util.message.UserListDestroyedEvent;
import java.io.FileNotFoundException;
import java.util.ArrayList;
@ -207,9 +209,10 @@ public class AsyncTwitterWrapper extends TwitterWrapper {
return mAsyncTaskManager.add(task, true);
}
public int createUserListAsync(final long accountId, final String list_name, final boolean is_public,
public int createUserListAsync(final long accountId, final String listName, final boolean isPublic,
final String description) {
final CreateUserListTask task = new CreateUserListTask(accountId, list_name, is_public, description);
final CreateUserListTask task = new CreateUserListTask(mContext, accountId, listName, isPublic,
description);
return mAsyncTaskManager.add(task, true);
}
@ -269,7 +272,7 @@ public class AsyncTwitterWrapper extends TwitterWrapper {
}
public int destroyUserListAsync(final long accountId, final long listId) {
final DestroyUserListTask task = new DestroyUserListTask(accountId, listId);
final DestroyUserListTask task = new DestroyUserListTask(mContext, accountId, listId);
return mAsyncTaskManager.add(task, true);
}
@ -1245,48 +1248,47 @@ public class AsyncTwitterWrapper extends TwitterWrapper {
}
class CreateUserListTask extends ManagedAsyncTask<Object, Object, SingleResponse<UserList>> {
static class CreateUserListTask extends ManagedAsyncTask<Object, Object, SingleResponse<ParcelableUserList>> {
private final long account_id;
private final String list_name, description;
private final boolean is_public;
private final long accountId;
private final String listName, description;
private final boolean isPublic;
public CreateUserListTask(final long account_id, final String list_name, final boolean is_public,
public CreateUserListTask(Context context, final long accountId, final String listName, final boolean isPublic,
final String description) {
super(mContext);
this.account_id = account_id;
this.list_name = list_name;
super(context);
this.accountId = accountId;
this.listName = listName;
this.description = description;
this.is_public = is_public;
this.isPublic = isPublic;
}
@Override
protected SingleResponse<UserList> doInBackground(final Object... params) {
final Twitter twitter = TwitterAPIFactory.getTwitterInstance(mContext, account_id, false);
if (twitter == null || list_name == null) return SingleResponse.getInstance();
protected SingleResponse<ParcelableUserList> doInBackground(final Object... params) {
final Twitter twitter = TwitterAPIFactory.getTwitterInstance(getContext(), accountId, false);
if (twitter == null || listName == null) return SingleResponse.getInstance();
try {
final UserListUpdate userListUpdate = new UserListUpdate();
userListUpdate.setName(list_name);
userListUpdate.setMode(is_public ? UserList.Mode.PUBLIC : UserList.Mode.PRIVATE);
userListUpdate.setName(listName);
userListUpdate.setMode(isPublic ? UserList.Mode.PUBLIC : UserList.Mode.PRIVATE);
userListUpdate.setDescription(description);
final UserList list = twitter.createUserList(userListUpdate);
return SingleResponse.getInstance(list, null);
return SingleResponse.getInstance(new ParcelableUserList(list, accountId), null);
} catch (final TwitterException e) {
return SingleResponse.getInstance(null, e);
}
}
@Override
protected void onPostExecute(final SingleResponse<UserList> result) {
protected void onPostExecute(final SingleResponse<ParcelableUserList> result) {
final Context context = getContext();
if (result.hasData()) {
final UserList userList = result.getData();
final String message = mContext.getString(R.string.created_list, userList.getName());
Utils.showOkMessage(mContext, message, false);
final Intent intent = new Intent(BROADCAST_USER_LIST_CREATED);
intent.putExtra(EXTRA_USER_LIST, new ParcelableUserList(userList, account_id));
mContext.sendBroadcast(intent);
final ParcelableUserList userList = result.getData();
final String message = context.getString(R.string.created_list, userList.name);
Utils.showOkMessage(context, message, false);
bus.post(new UserListCreatedEvent(userList));
} else {
Utils.showErrorMessage(mContext, R.string.action_creating_list, result.getException(), true);
Utils.showErrorMessage(context, R.string.action_creating_list, result.getException(), true);
}
super.onPostExecute(result);
}
@ -1870,13 +1872,13 @@ public class AsyncTwitterWrapper extends TwitterWrapper {
}
class DestroyUserListTask extends ManagedAsyncTask<Object, Object, SingleResponse<ParcelableUserList>> {
static class DestroyUserListTask extends ManagedAsyncTask<Object, Object, SingleResponse<ParcelableUserList>> {
private final long mAccountId;
private final long mListId;
public DestroyUserListTask(final long accountId, final long listId) {
super(mContext);
public DestroyUserListTask(Context context, final long accountId, final long listId) {
super(context);
mAccountId = accountId;
mListId = listId;
}
@ -1884,7 +1886,7 @@ public class AsyncTwitterWrapper extends TwitterWrapper {
@Override
protected SingleResponse<ParcelableUserList> doInBackground(final Object... params) {
final Twitter twitter = TwitterAPIFactory.getTwitterInstance(mContext, mAccountId, false);
final Twitter twitter = TwitterAPIFactory.getTwitterInstance(getContext(), mAccountId, false);
if (twitter != null) {
try {
if (mListId > 0) {
@ -1902,14 +1904,13 @@ public class AsyncTwitterWrapper extends TwitterWrapper {
@Override
protected void onPostExecute(final SingleResponse<ParcelableUserList> result) {
final boolean succeed = result.hasData();
final Context context = getContext();
if (succeed) {
final String message = mContext.getString(R.string.deleted_list, result.getData().name);
Utils.showInfoMessage(mContext, message, false);
final Intent intent = new Intent(BROADCAST_USER_LIST_DELETED);
intent.putExtra(EXTRA_USER_LIST, result.getData());
mContext.sendBroadcast(intent);
final String message = context.getString(R.string.deleted_list, result.getData().name);
Utils.showInfoMessage(context, message, false);
bus.post(new UserListDestroyedEvent(result.getData()));
} else {
Utils.showErrorMessage(mContext, R.string.action_deleting, result.getException(), true);
Utils.showErrorMessage(context, R.string.action_deleting, result.getException(), true);
}
super.onPostExecute(result);
}

View File

@ -19,7 +19,6 @@
package org.mariotaku.twidere.util;
import android.content.Context;
import android.text.TextUtils;
import com.twitter.Validator;
@ -29,35 +28,29 @@ import org.mariotaku.twidere.Constants;
public class TwidereValidator implements Constants {
private final int mMaxTweetLength;
private final Validator mValidator;
private final int mMaxTweetLength;
private final Validator mValidator;
public TwidereValidator(final Context context) {
final SharedPreferencesWrapper prefs = SharedPreferencesWrapper.getInstance(context, SHARED_PREFERENCES_NAME,
Context.MODE_PRIVATE);
mValidator = new Validator();
if (prefs != null) {
final String textLimit = prefs.getString(KEY_STATUS_TEXT_LIMIT, null);
mMaxTweetLength = NumberUtils.toInt(textLimit, Validator.MAX_TWEET_LENGTH);
} else {
mMaxTweetLength = Validator.MAX_TWEET_LENGTH;
}
}
public TwidereValidator(final SharedPreferencesWrapper preferences) {
mValidator = new Validator();
final String textLimit = preferences.getString(KEY_STATUS_TEXT_LIMIT, null);
mMaxTweetLength = NumberUtils.toInt(textLimit, Validator.MAX_TWEET_LENGTH);
}
public int getMaxTweetLength() {
return mMaxTweetLength;
}
public int getMaxTweetLength() {
return mMaxTweetLength;
}
public int getTweetLength(final String text) {
return mValidator.getTweetLength(text);
}
public int getTweetLength(final String text) {
return mValidator.getTweetLength(text);
}
public boolean isValidTweet(final String text) {
return !TextUtils.isEmpty(text) && getTweetLength(text) <= getMaxTweetLength();
}
public boolean isValidTweet(final String text) {
return !TextUtils.isEmpty(text) && getTweetLength(text) <= getMaxTweetLength();
}
public boolean isValidDirectMessage(final CharSequence text) {
return !TextUtils.isEmpty(text);
}
public boolean isValidDirectMessage(final CharSequence text) {
return !TextUtils.isEmpty(text);
}
}

View File

@ -49,19 +49,14 @@ public class UserColorNameManager implements TwidereConstants {
mNicknamePreferences = context.getSharedPreferences(USER_NICKNAME_PREFERENCES_NAME, Context.MODE_PRIVATE);
}
public static void registerOnUserColorChangedListener(final Context context,
final OnUserColorChangedListener listener) {
public void registerColorChangedListener(final UserColorChangedListener listener) {
final SharedPreferences prefs = context.getSharedPreferences(USER_COLOR_PREFERENCES_NAME, Context.MODE_PRIVATE);
prefs.registerOnSharedPreferenceChangeListener(new OnColorPreferenceChangeListener(listener));
mColorPreferences.registerOnSharedPreferenceChangeListener(new OnColorPreferenceChangeListener(listener));
}
public static void registerOnUserNicknameChangedListener(final Context context,
final OnUserNicknameChangedListener listener) {
public void registerNicknameChangedListener(final UserNicknameChangedListener listener) {
final SharedPreferences prefs = context.getSharedPreferences(USER_NICKNAME_PREFERENCES_NAME,
Context.MODE_PRIVATE);
prefs.registerOnSharedPreferenceChangeListener(new OnNickPreferenceChangeListener(listener));
mNicknamePreferences.registerOnSharedPreferenceChangeListener(new OnNickPreferenceChangeListener(listener));
}
public void clearUserColor(final long userId) {
@ -159,19 +154,19 @@ public class UserColorNameManager implements TwidereConstants {
return TextUtils.isEmpty(nick) ? name : nick;
}
public interface OnUserColorChangedListener {
public interface UserColorChangedListener {
void onUserColorChanged(long userId, int color);
}
public interface OnUserNicknameChangedListener {
public interface UserNicknameChangedListener {
void onUserNicknameChanged(long userId, String nick);
}
private static final class OnColorPreferenceChangeListener implements OnSharedPreferenceChangeListener {
private final OnUserColorChangedListener mListener;
private final UserColorChangedListener mListener;
OnColorPreferenceChangeListener(final OnUserColorChangedListener listener) {
OnColorPreferenceChangeListener(final UserColorChangedListener listener) {
mListener = listener;
}
@ -188,9 +183,9 @@ public class UserColorNameManager implements TwidereConstants {
private static final class OnNickPreferenceChangeListener implements OnSharedPreferenceChangeListener {
private final OnUserNicknameChangedListener mListener;
private final UserNicknameChangedListener mListener;
OnNickPreferenceChangeListener(final OnUserNicknameChangedListener listener) {
OnNickPreferenceChangeListener(final UserNicknameChangedListener listener) {
mListener = listener;
}

View File

@ -33,6 +33,7 @@ import com.nostra13.universalimageloader.utils.L;
import com.squareup.okhttp.Dns;
import com.squareup.otto.Bus;
import com.squareup.otto.ThreadEnforcer;
import com.twitter.Extractor;
import org.mariotaku.mediaviewer.library.FileCache;
import org.mariotaku.mediaviewer.library.MediaDownloader;
@ -54,6 +55,7 @@ import org.mariotaku.twidere.util.NotificationManagerWrapper;
import org.mariotaku.twidere.util.ReadStateManager;
import org.mariotaku.twidere.util.SharedPreferencesWrapper;
import org.mariotaku.twidere.util.TwidereMathUtils;
import org.mariotaku.twidere.util.TwidereValidator;
import org.mariotaku.twidere.util.UserColorNameManager;
import org.mariotaku.twidere.util.Utils;
import org.mariotaku.twidere.util.imageloader.ReadOnlyDiskLRUNameCache;
@ -216,6 +218,18 @@ public class ApplicationModule implements Constants {
return new TwidereMediaDownloader(application, preferences, client);
}
@Provides
@Singleton
public TwidereValidator twidereValidator(SharedPreferencesWrapper preferences) {
return new TwidereValidator(preferences);
}
@Provides
@Singleton
public Extractor extractor() {
return new Extractor();
}
@Provides
@Singleton
public ErrorInfoStore errorInfoStore() {

View File

@ -27,6 +27,7 @@ import org.mariotaku.restfu.http.RestHttpClient;
import org.mariotaku.twidere.util.ActivityTracker;
import org.mariotaku.twidere.util.ExternalThemeManager;
import org.mariotaku.twidere.util.ReadStateManager;
import org.mariotaku.twidere.util.TwidereValidator;
import javax.inject.Inject;
@ -50,6 +51,8 @@ public class DependencyHolder {
ActivityTracker mActivityTracker;
@Inject
Dns mDns;
@Inject
TwidereValidator mValidator;
DependencyHolder(Context context) {
GeneralComponentHelper.build(context).inject(this);
@ -83,4 +86,8 @@ public class DependencyHolder {
public Dns getDns() {
return mDns;
}
public TwidereValidator getValidator() {
return mValidator;
}
}

View File

@ -24,6 +24,7 @@ import android.support.v7.widget.RecyclerView;
import org.mariotaku.twidere.activity.BasePreferenceActivity;
import org.mariotaku.twidere.activity.BaseThemedActivity;
import org.mariotaku.twidere.activity.support.BaseAppCompatActivity;
import org.mariotaku.twidere.activity.support.ComposeActivity;
import org.mariotaku.twidere.activity.support.MediaViewerActivity;
import org.mariotaku.twidere.activity.support.ThemedFragmentActivity;
import org.mariotaku.twidere.adapter.AccountsAdapter;
@ -85,6 +86,8 @@ public interface GeneralComponent {
void inject(ThemedFragmentActivity object);
void inject(ComposeActivity object);
void inject(TwidereDataProvider object);
void inject(BaseListFragment object);

View File

@ -0,0 +1,37 @@
/*
* 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.message;
import android.support.annotation.NonNull;
import org.mariotaku.twidere.model.ParcelableUserList;
/**
* Created by mariotaku on 14/12/10.
*/
public class UserListCreatedEvent {
@NonNull
public final ParcelableUserList userList;
public UserListCreatedEvent(@NonNull ParcelableUserList userList) {
this.userList = userList;
}
}

View File

@ -0,0 +1,37 @@
/*
* 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.message;
import android.support.annotation.NonNull;
import org.mariotaku.twidere.model.ParcelableUserList;
/**
* Created by mariotaku on 14/12/10.
*/
public class UserListDestroyedEvent {
@NonNull
public final ParcelableUserList userList;
public UserListDestroyedEvent(@NonNull ParcelableUserList userList) {
this.userList = userList;
}
}

View File

@ -0,0 +1,64 @@
package org.mariotaku.twidere.view;
import android.annotation.TargetApi;
import android.content.Context;
import android.os.Build;
import android.support.v4.view.accessibility.AccessibilityEventCompat;
import android.util.AttributeSet;
import android.view.accessibility.AccessibilityEvent;
import android.view.accessibility.AccessibilityNodeInfo;
import android.widget.Checkable;
import android.widget.LinearLayout;
/**
* Created by mariotaku on 16/2/1.
*/
public class CheckableLinearLayout extends LinearLayout implements Checkable {
private boolean mChecked;
public CheckableLinearLayout(Context context) {
super(context);
}
public CheckableLinearLayout(Context context, AttributeSet attrs) {
super(context, attrs);
}
public CheckableLinearLayout(Context context, AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
}
@TargetApi(Build.VERSION_CODES.LOLLIPOP)
public CheckableLinearLayout(Context context, AttributeSet attrs, int defStyleAttr, int defStyleRes) {
super(context, attrs, defStyleAttr, defStyleRes);
}
@Override
public void onInitializeAccessibilityEvent(AccessibilityEvent event) {
super.onInitializeAccessibilityEvent(event);
event.setChecked(isChecked());
}
@Override
public void onInitializeAccessibilityNodeInfo(AccessibilityNodeInfo info) {
super.onInitializeAccessibilityNodeInfo(info);
info.setCheckable(true);
info.setChecked(isChecked());
}
@Override
public boolean isChecked() {
return mChecked;
}
@Override
public void setChecked(boolean checked) {
mChecked = checked;
sendAccessibilityEvent(AccessibilityEventCompat.CONTENT_CHANGE_TYPE_UNDEFINED);
}
@Override
public void toggle() {
setChecked(!isChecked());
}
}

View File

@ -27,6 +27,7 @@ import android.util.AttributeSet;
import org.mariotaku.twidere.util.ThemeUtils;
import org.mariotaku.twidere.util.TwidereValidator;
import org.mariotaku.twidere.util.dagger.DependencyHolder;
import java.util.Locale;
@ -48,7 +49,7 @@ public class StatusTextCountView extends AppCompatTextView {
public StatusTextCountView(final Context context, final AttributeSet attrs, final int defStyle) {
super(context, attrs, defStyle);
mValidator = new TwidereValidator(context);
mValidator = DependencyHolder.get(context).getValidator();
if (isInEditMode()) {
mTextColor = 0;
mLocale = Locale.getDefault();

View File

@ -409,7 +409,7 @@ public class StatusViewHolder extends ViewHolder implements Constants, IStatusVi
} else if (TwitterCardUtils.CARD_NAME_PLAYER.equals(cardName)) {
extraTypeView.setImageResource(sensitive ? R.drawable.ic_action_warning : R.drawable.ic_action_play_circle);
extraTypeView.setVisibility(View.VISIBLE);
} else if (media != null && media.length > 0) {
} else if (!ArrayUtils.isEmpty(media)) {
if (hasVideo(media)) {
extraTypeView.setImageResource(sensitive ? R.drawable.ic_action_warning : R.drawable.ic_action_movie);
} else {
@ -426,9 +426,11 @@ public class StatusViewHolder extends ViewHolder implements Constants, IStatusVi
boolean hasVideo(ParcelableMedia[] media) {
for (ParcelableMedia mediaItem : media) {
if (mediaItem.type == ParcelableMedia.Type.TYPE_VIDEO
|| mediaItem.type == ParcelableMedia.Type.TYPE_ANIMATED_GIF)
return true;
switch (mediaItem.type) {
case ParcelableMedia.Type.TYPE_VIDEO:
case ParcelableMedia.Type.TYPE_ANIMATED_GIF:
return true;
}
}
return false;
}

View File

@ -168,6 +168,7 @@
android:layout_alignStart="@id/account_profile_image_frame"
android:layout_alignTop="@id/account_profile_image_frame"
android:layout_gravity="center"
android:contentDescription="@string/select_accounts_for_compose"
android:textColor="?android:colorForeground"/>
</RelativeLayout>

View File

@ -1,128 +0,0 @@
<?xml version="1.0" encoding="utf-8"?><!--
~ 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/>.
-->
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="?actionBarSize"
android:baselineAligned="false">
<RelativeLayout
android:id="@+id/account_selector_button"
style="?actionButtonStyle"
android:layout_width="wrap_content"
android:layout_height="match_parent"
android:layout_alignParentLeft="true"
android:layout_alignParentStart="true"
android:clickable="true"
android:paddingBottom="@dimen/element_spacing_msmall"
android:paddingEnd="@dimen/element_spacing_msmall"
android:paddingLeft="@dimen/element_spacing_msmall"
android:paddingRight="@dimen/element_spacing_msmall"
android:paddingStart="@dimen/element_spacing_msmall"
android:paddingTop="@dimen/element_spacing_msmall">
<org.mariotaku.twidere.view.SquareFrameLayout
android:id="@+id/account_profile_image_frame"
android:layout_width="wrap_content"
android:layout_height="match_parent">
<org.mariotaku.twidere.view.ShapedImageView
android:id="@+id/account_profile_image"
style="?profileImageStyle"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:layout_gravity="center"
app:sivBackgroundColor="?android:colorBackground"
app:sivBorder="true"
app:sivBorderWidth="@dimen/line_width_compose_account_profile_image" />
</org.mariotaku.twidere.view.SquareFrameLayout>
<org.mariotaku.twidere.view.BadgeView
android:id="@+id/accounts_count"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:layout_alignBottom="@id/account_profile_image_frame"
android:layout_alignEnd="@id/account_profile_image_frame"
android:layout_alignLeft="@id/account_profile_image_frame"
android:layout_alignRight="@id/account_profile_image_frame"
android:layout_alignStart="@id/account_profile_image_frame"
android:layout_alignTop="@id/account_profile_image_frame"
android:layout_gravity="center"
android:textColor="?android:colorForeground" />
</RelativeLayout>
<FrameLayout
android:layout_width="match_parent"
android:layout_height="match_parent"
android:layout_toEndOf="@+id/account_selector_button"
android:layout_toLeftOf="@+id/send"
android:layout_toRightOf="@+id/account_selector_button"
android:layout_toStartOf="@+id/send"
android:scrollbars="none">
<android.support.v7.widget.ActionMenuView
android:id="@+id/menu_bar"
android:layout_width="wrap_content"
android:layout_height="match_parent"
android:layout_gravity="center"
android:background="@null" />
</FrameLayout>
<LinearLayout
android:id="@+id/send"
style="?actionButtonStyle"
android:layout_width="wrap_content"
android:layout_height="match_parent"
android:layout_alignParentEnd="true"
android:layout_alignParentRight="true"
android:contentDescription="@string/send"
android:gravity="center"
android:orientation="horizontal"
android:paddingBottom="@dimen/element_spacing_normal"
android:paddingEnd="@dimen/element_spacing_normal"
android:paddingLeft="@dimen/element_spacing_large"
android:paddingRight="@dimen/element_spacing_normal"
android:paddingStart="@dimen/element_spacing_large"
android:paddingTop="@dimen/element_spacing_normal">
<org.mariotaku.twidere.view.StatusTextCountView
android:id="@+id/status_text_count"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:gravity="center"
android:minWidth="@dimen/element_size_small"
android:textAppearance="?android:textAppearanceSmall"
tools:text="140" />
<org.mariotaku.twidere.view.ActionIconView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:color="?android:textColorSecondary"
android:contentDescription="@string/send"
android:cropToPadding="false"
android:padding="@dimen/element_spacing_xsmall"
android:scaleType="centerCrop"
android:src="@drawable/ic_action_send" />
</LinearLayout>
</RelativeLayout>

View File

@ -16,7 +16,7 @@
~ You should have received a copy of the GNU General Public License
~ along with this program. If not, see <http://www.gnu.org/licenses/>.
-->
<LinearLayout
<org.mariotaku.twidere.view.CheckableLinearLayout
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tool="http://schemas.android.com/tools"
@ -57,4 +57,4 @@
tool:text="Name"/>
</android.support.v7.widget.CardView>
</LinearLayout>
</org.mariotaku.twidere.view.CheckableLinearLayout>

View File

@ -841,4 +841,5 @@
<string name="title_summary_line_format"><xliff:g id="title">%1$s</xliff:g>: <xliff:g id="summary">%2$s</xliff:g></string>
<string name="mentions_only">Mentions only</string>
<string name="error_no_dm_permission">No direct message permission, check your Twitter application permission setting.</string>
<string name="select_accounts_for_compose">Select accounts for compose</string>
</resources>