quote is also available for users not using official keys!

fixed black background issue
This commit is contained in:
Mariotaku Lee 2015-05-13 23:40:56 +08:00
parent a6dd10a3f9
commit 64b7692391
13 changed files with 114 additions and 24 deletions

View File

@ -39,6 +39,7 @@ android {
dependencies { dependencies {
apt 'com.bluelinelabs:logansquare-compiler:1.0.6' apt 'com.bluelinelabs:logansquare-compiler:1.0.6'
compile 'com.android.support:support-annotations:22.1.1' compile 'com.android.support:support-annotations:22.1.1'
compile 'com.android.support:support-v4:22.1.1'
compile 'com.bluelinelabs:logansquare:1.0.6' compile 'com.bluelinelabs:logansquare:1.0.6'
compile 'org.apache.commons:commons-lang3:3.4' compile 'org.apache.commons:commons-lang3:3.4'
compile project(':twidere.component.querybuilder') compile project(':twidere.component.querybuilder')

View File

@ -56,4 +56,8 @@ public interface TweetResources {
@Body(BodyType.FORM) @Body(BodyType.FORM)
Status updateStatus(@Form StatusUpdate latestStatus) throws TwitterException; Status updateStatus(@Form StatusUpdate latestStatus) throws TwitterException;
@POST("/statuses/lookup.json")
@Body(BodyType.FORM)
ResponseList<Status> lookupStatuses(@Form("id") long[] ids) throws TwitterException;
} }

View File

@ -24,11 +24,6 @@ import android.support.annotation.NonNull;
import com.bluelinelabs.logansquare.annotation.JsonField; import com.bluelinelabs.logansquare.annotation.JsonField;
import com.bluelinelabs.logansquare.annotation.JsonObject; import com.bluelinelabs.logansquare.annotation.JsonObject;
import org.mariotaku.twidere.api.twitter.util.TwitterDateConverter;
import java.util.Arrays;
import java.util.Date;
import org.mariotaku.twidere.api.twitter.model.CardEntity; import org.mariotaku.twidere.api.twitter.model.CardEntity;
import org.mariotaku.twidere.api.twitter.model.GeoLocation; import org.mariotaku.twidere.api.twitter.model.GeoLocation;
import org.mariotaku.twidere.api.twitter.model.HashtagEntity; import org.mariotaku.twidere.api.twitter.model.HashtagEntity;
@ -38,6 +33,10 @@ import org.mariotaku.twidere.api.twitter.model.Status;
import org.mariotaku.twidere.api.twitter.model.UrlEntity; import org.mariotaku.twidere.api.twitter.model.UrlEntity;
import org.mariotaku.twidere.api.twitter.model.User; import org.mariotaku.twidere.api.twitter.model.User;
import org.mariotaku.twidere.api.twitter.model.UserMentionEntity; import org.mariotaku.twidere.api.twitter.model.UserMentionEntity;
import org.mariotaku.twidere.api.twitter.util.TwitterDateConverter;
import java.util.Arrays;
import java.util.Date;
/** /**
* Created by mariotaku on 15/5/5. * Created by mariotaku on 15/5/5.
@ -326,6 +325,11 @@ public class StatusImpl extends TwitterResponseImpl implements Status {
'}'; '}';
} }
public static void setQuotedStatus(Status status, Status quoted) {
if (!(status instanceof StatusImpl)) return;
((StatusImpl) status).quotedStatus = quoted;
}
@JsonObject @JsonObject
static class CurrentUserRetweet { static class CurrentUserRetweet {
@JsonField(name = "id") @JsonField(name = "id")

View File

@ -22,12 +22,8 @@ package org.mariotaku.twidere.util;
import android.content.Context; import android.content.Context;
import android.support.annotation.NonNull; import android.support.annotation.NonNull;
import org.mariotaku.twidere.common.R; import org.mariotaku.twidere.api.twitter.Twitter;
import org.mariotaku.twidere.model.ConsumerKeyType; import org.mariotaku.twidere.api.twitter.TwitterException;
import java.nio.charset.Charset;
import java.util.zip.CRC32;
import org.mariotaku.twidere.api.twitter.model.DirectMessage; import org.mariotaku.twidere.api.twitter.model.DirectMessage;
import org.mariotaku.twidere.api.twitter.model.EntitySupport; import org.mariotaku.twidere.api.twitter.model.EntitySupport;
import org.mariotaku.twidere.api.twitter.model.MediaEntity; import org.mariotaku.twidere.api.twitter.model.MediaEntity;
@ -35,6 +31,17 @@ import org.mariotaku.twidere.api.twitter.model.Status;
import org.mariotaku.twidere.api.twitter.model.UrlEntity; import org.mariotaku.twidere.api.twitter.model.UrlEntity;
import org.mariotaku.twidere.api.twitter.model.User; import org.mariotaku.twidere.api.twitter.model.User;
import org.mariotaku.twidere.api.twitter.model.UserMentionEntity; import org.mariotaku.twidere.api.twitter.model.UserMentionEntity;
import org.mariotaku.twidere.api.twitter.model.impl.StatusImpl;
import org.mariotaku.twidere.common.R;
import org.mariotaku.twidere.model.ConsumerKeyType;
import org.mariotaku.twidere.util.collection.LongSparseMap;
import java.nio.charset.Charset;
import java.util.List;
import java.util.Set;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import java.util.zip.CRC32;
import static org.mariotaku.twidere.util.HtmlEscapeHelper.toPlainText; import static org.mariotaku.twidere.util.HtmlEscapeHelper.toPlainText;
@ -42,6 +49,9 @@ import static org.mariotaku.twidere.util.HtmlEscapeHelper.toPlainText;
* Created by mariotaku on 15/1/11. * Created by mariotaku on 15/1/11.
*/ */
public class TwitterContentUtils { public class TwitterContentUtils {
public static final int TWITTER_BULK_QUERY_COUNT = 100;
public static String formatDirectMessageText(final DirectMessage message) { public static String formatDirectMessageText(final DirectMessage message) {
if (message == null) return null; if (message == null) return null;
final HtmlBuilder builder = new HtmlBuilder(message.getText(), false, true, true); final HtmlBuilder builder = new HtmlBuilder(message.getText(), false, true, true);
@ -159,6 +169,43 @@ public class TwitterContentUtils {
return str.replace("&amp;", "&").replace("&lt;", "<").replace("&gt;", ">"); return str.replace("&amp;", "&").replace("&lt;", "<").replace("&gt;", ">");
} }
private static final Pattern PATTERN_TWITTER_STATUS_LINK = Pattern.compile("https?://twitter\\.com/(?:#!/)?(\\w+)/status(es)?/(\\d+)");
public static <T extends List<Status>> T getStatusesWithQuoteData(Twitter twitter, @NonNull T list) throws TwitterException {
LongSparseMap<Status> quotes = new LongSparseMap<>();
// Phase 1: collect all statuses contains a status link, and put it in the map
for (Status status : list) {
if (status.isQuote()) continue;
final UrlEntity[] entities = status.getUrlEntities();
if (entities == null || entities.length <= 0) continue;
// Seems Twitter will find last status link for quote target, so we search backward
for (int i = entities.length - 1; i >= 0; i--) {
final Matcher m = PATTERN_TWITTER_STATUS_LINK.matcher(entities[i].getExpandedUrl());
if (!m.matches()) continue;
quotes.put(Long.parseLong(m.group(3)), status);
break;
}
}
// Phase 2: look up quoted tweets. Each lookup can fetch up to 100 tweets, so we split quote
// ids into batches
final long[] quoteIds = quotes.keys();
for (int currentBulkIdx = 0, totalLength = quoteIds.length; currentBulkIdx < totalLength; currentBulkIdx += TWITTER_BULK_QUERY_COUNT) {
final int currentBulkCount = Math.min(totalLength, currentBulkIdx + TWITTER_BULK_QUERY_COUNT);
final long[] ids = new long[currentBulkCount];
System.arraycopy(quoteIds, currentBulkIdx, ids, 0, currentBulkCount);
// Lookup quoted statuses, then set each status into original status
for (Status quoted : twitter.lookupStatuses(ids)) {
final Set<Status> orig = quotes.get(quoted.getId());
// This set shouldn't be null here, add null check to make inspector happy.
if (orig == null) continue;
for (Status status : orig) {
StatusImpl.setQuotedStatus(status, quoted);
}
}
}
return list;
}
private static void parseEntities(final HtmlBuilder builder, final EntitySupport entities) { private static void parseEntities(final HtmlBuilder builder, final EntitySupport entities) {
// Format media. // Format media.
final MediaEntity[] mediaEntities = entities.getMediaEntities(); final MediaEntity[] mediaEntities = entities.getMediaEntities();

View File

@ -19,16 +19,17 @@
package org.mariotaku.twidere.util.collection; package org.mariotaku.twidere.util.collection;
import android.support.annotation.Nullable;
import android.support.v4.util.LongSparseArray; import android.support.v4.util.LongSparseArray;
import java.util.HashSet; import java.util.Set;
/** /**
* Created by mariotaku on 14/12/12. * Created by mariotaku on 14/12/12.
*/ */
public class LongSparseMap<T> { public class LongSparseMap<T> {
private final LongSparseArray<HashSet<T>> internalArray; private final LongSparseArray<CompactHashSet<T>> internalArray;
public LongSparseMap() { public LongSparseMap() {
internalArray = new LongSparseArray<>(); internalArray = new LongSparseArray<>();
@ -36,9 +37,9 @@ public class LongSparseMap<T> {
public boolean put(long key, T value) { public boolean put(long key, T value) {
final int idx = internalArray.indexOfKey(key); final int idx = internalArray.indexOfKey(key);
final HashSet<T> set; final CompactHashSet<T> set;
if (idx < 0) { if (idx < 0) {
set = new HashSet<>(); set = new CompactHashSet<>();
internalArray.put(key, set); internalArray.put(key, set);
} else { } else {
set = internalArray.valueAt(idx); set = internalArray.valueAt(idx);
@ -46,6 +47,11 @@ public class LongSparseMap<T> {
return set.add(value); return set.add(value);
} }
@Nullable
public Set<T> get(long key) {
return internalArray.get(key);
}
public boolean clear(long key) { public boolean clear(long key) {
final int idx = internalArray.indexOfKey(key); final int idx = internalArray.indexOfKey(key);
if (idx < 0) return false; if (idx < 0) return false;
@ -63,4 +69,12 @@ public class LongSparseMap<T> {
return idx >= 0 && internalArray.valueAt(idx).contains(value); return idx >= 0 && internalArray.valueAt(idx).contains(value);
} }
public long[] keys() {
final long[] keys = new long[internalArray.size()];
for (int i = 0, j = internalArray.size(); i < j; i++) {
keys[i] = internalArray.keyAt(i);
}
return keys;
}
} }

View File

@ -42,6 +42,7 @@ import android.widget.Toast;
import org.mariotaku.simplerestapi.http.Authorization; import org.mariotaku.simplerestapi.http.Authorization;
import org.mariotaku.simplerestapi.http.Endpoint; import org.mariotaku.simplerestapi.http.Endpoint;
import org.mariotaku.twidere.R; import org.mariotaku.twidere.R;
import org.mariotaku.twidere.api.twitter.TwitterOAuth;
import org.mariotaku.twidere.api.twitter.auth.OAuthAuthorization; import org.mariotaku.twidere.api.twitter.auth.OAuthAuthorization;
import org.mariotaku.twidere.api.twitter.auth.OAuthToken; import org.mariotaku.twidere.api.twitter.auth.OAuthToken;
import org.mariotaku.twidere.app.TwidereApplication; import org.mariotaku.twidere.app.TwidereApplication;
@ -55,9 +56,6 @@ import org.xmlpull.v1.XmlPullParserException;
import java.io.IOException; import java.io.IOException;
import java.io.StringReader; import java.io.StringReader;
import org.mariotaku.twidere.api.twitter.TwitterConstants;
import org.mariotaku.twidere.api.twitter.TwitterOAuth;
import static android.text.TextUtils.isEmpty; import static android.text.TextUtils.isEmpty;
import static org.mariotaku.twidere.util.Utils.getNonEmptyString; import static org.mariotaku.twidere.util.Utils.getNonEmptyString;

View File

@ -124,7 +124,7 @@ public abstract class ThemedAppCompatActivity extends AppCompatActivity implemen
public void setTheme(int resid) { public void setTheme(int resid) {
super.setTheme(mCurrentThemeResource = getThemeResourceId()); super.setTheme(mCurrentThemeResource = getThemeResourceId());
if (shouldApplyWindowBackground()) { if (shouldApplyWindowBackground()) {
ThemeUtils.applyWindowBackground(this, getWindow(), resid, mCurrentThemeBackgroundOption, ThemeUtils.applyWindowBackground(this, getWindow(), mCurrentThemeResource, mCurrentThemeBackgroundOption,
mCurrentThemeBackgroundAlpha); mCurrentThemeBackgroundAlpha);
} }
} }

View File

@ -37,6 +37,7 @@ import android.view.MenuItem;
import android.view.View; import android.view.View;
import com.rengwuxian.materialedittext.MaterialEditText; import com.rengwuxian.materialedittext.MaterialEditText;
import com.rengwuxian.materialedittext.validation.METLengthChecker;
import org.mariotaku.twidere.R; import org.mariotaku.twidere.R;
import org.mariotaku.twidere.model.ParcelableStatus; import org.mariotaku.twidere.model.ParcelableStatus;
@ -44,6 +45,7 @@ import org.mariotaku.twidere.util.AsyncTwitterWrapper;
import org.mariotaku.twidere.util.LinkCreator; import org.mariotaku.twidere.util.LinkCreator;
import org.mariotaku.twidere.util.MenuUtils; import org.mariotaku.twidere.util.MenuUtils;
import org.mariotaku.twidere.util.ThemeUtils; import org.mariotaku.twidere.util.ThemeUtils;
import org.mariotaku.twidere.util.TwidereValidator;
import org.mariotaku.twidere.view.holder.StatusViewHolder; import org.mariotaku.twidere.view.holder.StatusViewHolder;
import org.mariotaku.twidere.view.holder.StatusViewHolder.DummyStatusHolderAdapter; import org.mariotaku.twidere.view.holder.StatusViewHolder.DummyStatusHolderAdapter;
@ -56,6 +58,7 @@ public class RetweetQuoteDialogFragment extends BaseSupportDialogFragment implem
private MaterialEditText mEditComment; private MaterialEditText mEditComment;
private PopupMenu mPopupMenu; private PopupMenu mPopupMenu;
private View mCommentMenu; private View mCommentMenu;
private TwidereValidator mValidator;
@Override @Override
public void onClick(final DialogInterface dialog, final int which) { public void onClick(final DialogInterface dialog, final int which) {
@ -108,6 +111,7 @@ public class RetweetQuoteDialogFragment extends BaseSupportDialogFragment implem
final Context wrapped = ThemeUtils.getDialogThemedContext(getActivity()); final Context wrapped = ThemeUtils.getDialogThemedContext(getActivity());
final AlertDialog.Builder builder = new AlertDialog.Builder(wrapped); final AlertDialog.Builder builder = new AlertDialog.Builder(wrapped);
final Context context = builder.getContext(); final Context context = builder.getContext();
mValidator = new TwidereValidator(context);
final LayoutInflater inflater = LayoutInflater.from(context); final LayoutInflater inflater = LayoutInflater.from(context);
@SuppressLint("InflateParams") final View view = inflater.inflate(R.layout.dialog_status_quote_retweet, null); @SuppressLint("InflateParams") final View view = inflater.inflate(R.layout.dialog_status_quote_retweet, null);
final StatusViewHolder holder = new StatusViewHolder(new DummyStatusHolderAdapter(context), view.findViewById(R.id.item_content)); final StatusViewHolder holder = new StatusViewHolder(new DummyStatusHolderAdapter(context), view.findViewById(R.id.item_content));
@ -127,6 +131,16 @@ public class RetweetQuoteDialogFragment extends BaseSupportDialogFragment implem
view.findViewById(R.id.action_buttons).setVisibility(View.GONE); view.findViewById(R.id.action_buttons).setVisibility(View.GONE);
view.findViewById(R.id.item_content).setFocusable(false); view.findViewById(R.id.item_content).setFocusable(false);
mEditComment = (MaterialEditText) view.findViewById(R.id.edit_comment); mEditComment = (MaterialEditText) view.findViewById(R.id.edit_comment);
mEditComment.setLengthChecker(new METLengthChecker() {
final String statusLink = LinkCreator.getTwitterStatusLink(status.user_screen_name, status.quote_id).toString();
@Override
public int getLength(CharSequence text) {
return mValidator.getTweetLength(text + " " + statusLink);
}
});
mEditComment.setMaxCharacters(mValidator.getMaxTweetLength());
mCommentMenu = view.findViewById(R.id.comment_menu); mCommentMenu = view.findViewById(R.id.comment_menu);
mPopupMenu = new PopupMenu(context, mCommentMenu, Gravity.NO_GRAVITY, mPopupMenu = new PopupMenu(context, mCommentMenu, Gravity.NO_GRAVITY,

View File

@ -37,6 +37,8 @@ import org.mariotaku.twidere.app.TwidereApplication;
import org.mariotaku.twidere.model.ParcelableStatus; import org.mariotaku.twidere.model.ParcelableStatus;
import org.mariotaku.twidere.util.LoganSquareWrapper; import org.mariotaku.twidere.util.LoganSquareWrapper;
import org.mariotaku.twidere.util.TwitterAPIUtils; import org.mariotaku.twidere.util.TwitterAPIUtils;
import org.mariotaku.twidere.util.TwitterContentUtils;
import org.mariotaku.twidere.util.Utils;
import java.io.File; import java.io.File;
import java.io.FileOutputStream; import java.io.FileOutputStream;
@ -108,6 +110,9 @@ public abstract class TwitterAPIStatusesLoader extends ParcelableStatusesLoader
} }
statuses = new ArrayList<>(); statuses = new ArrayList<>();
truncated = truncateStatuses(getStatuses(twitter, paging), statuses, mSinceId); truncated = truncateStatuses(getStatuses(twitter, paging), statuses, mSinceId);
if (!Utils.isOfficialTwitterInstance(context, twitter)) {
TwitterContentUtils.getStatusesWithQuoteData(twitter, statuses);
}
} catch (final TwitterException e) { } catch (final TwitterException e) {
// mHandler.post(new ShowErrorRunnable(e)); // mHandler.post(new ShowErrorRunnable(e));
Log.w(LOGTAG, e); Log.w(LOGTAG, e);

View File

@ -141,7 +141,6 @@ import org.mariotaku.twidere.activity.support.MediaViewerActivity;
import org.mariotaku.twidere.adapter.iface.IBaseAdapter; import org.mariotaku.twidere.adapter.iface.IBaseAdapter;
import org.mariotaku.twidere.adapter.iface.IBaseCardAdapter; import org.mariotaku.twidere.adapter.iface.IBaseCardAdapter;
import org.mariotaku.twidere.api.twitter.Twitter; import org.mariotaku.twidere.api.twitter.Twitter;
import org.mariotaku.twidere.api.twitter.TwitterConstants;
import org.mariotaku.twidere.api.twitter.TwitterException; import org.mariotaku.twidere.api.twitter.TwitterException;
import org.mariotaku.twidere.api.twitter.auth.OAuthSupport; import org.mariotaku.twidere.api.twitter.auth.OAuthSupport;
import org.mariotaku.twidere.api.twitter.model.DirectMessage; import org.mariotaku.twidere.api.twitter.model.DirectMessage;

View File

@ -20,7 +20,6 @@
package org.mariotaku.twidere.view; package org.mariotaku.twidere.view;
import android.content.Context; import android.content.Context;
import android.support.v7.widget.AppCompatMultiAutoCompleteTextView;
import android.text.InputType; import android.text.InputType;
import android.text.SpannableString; import android.text.SpannableString;
import android.text.Spanned; import android.text.Spanned;
@ -28,10 +27,12 @@ import android.text.TextUtils;
import android.text.method.ArrowKeyMovementMethod; import android.text.method.ArrowKeyMovementMethod;
import android.util.AttributeSet; import android.util.AttributeSet;
import com.rengwuxian.materialedittext.MaterialMultiAutoCompleteTextView;
import org.mariotaku.twidere.R; import org.mariotaku.twidere.R;
import org.mariotaku.twidere.adapter.UserHashtagAutoCompleteAdapter; import org.mariotaku.twidere.adapter.UserHashtagAutoCompleteAdapter;
public class StatusComposeEditText extends AppCompatMultiAutoCompleteTextView { public class StatusComposeEditText extends MaterialMultiAutoCompleteTextView {
private UserHashtagAutoCompleteAdapter mAdapter; private UserHashtagAutoCompleteAdapter mAdapter;
private long mAccountId; private long mAccountId;

View File

@ -126,7 +126,10 @@
android:maxHeight="140dp" android:maxHeight="140dp"
android:singleLine="false" android:singleLine="false"
android:textAppearance="?android:textAppearanceMedium" android:textAppearance="?android:textAppearanceMedium"
android:textColor="?android:textColorPrimary"> app:met_baseColor="?android:textColorSecondary"
app:met_helperTextColor="?android:textColorSecondary"
app:met_textColor="?android:textColorPrimary"
app:met_textColorHint="?android:textColorSecondary">
<requestFocus /> <requestFocus />
</org.mariotaku.twidere.view.StatusComposeEditText> </org.mariotaku.twidere.view.StatusComposeEditText>