1
0
mirror of https://github.com/TwidereProject/Twidere-Android synced 2025-02-17 04:00:48 +01:00

implemented twitter gif (actually video) support

This commit is contained in:
Mariotaku Lee 2015-04-15 15:32:20 +08:00
parent 765d14312b
commit ae1ab617ca
11 changed files with 183 additions and 49 deletions

View File

@ -2,35 +2,53 @@ package org.mariotaku.twidere.model;
import android.os.Parcel;
import android.os.Parcelable;
import android.support.annotation.IntDef;
import android.support.annotation.NonNull;
import android.support.annotation.Nullable;
import org.mariotaku.jsonserializer.JSONParcel;
import org.mariotaku.jsonserializer.JSONParcelable;
import org.mariotaku.twidere.util.MediaPreviewUtils;
import org.mariotaku.twidere.util.ParseUtils;
import org.mariotaku.twidere.util.SimpleValueSerializer;
import org.mariotaku.twidere.util.SimpleValueSerializer.Reader;
import org.mariotaku.twidere.util.SimpleValueSerializer.SerializationException;
import org.mariotaku.twidere.util.SimpleValueSerializer.SimpleValueSerializable;
import org.mariotaku.twidere.util.SimpleValueSerializer.Writer;
import org.mariotaku.twidere.util.TwidereArrayUtils;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import twitter4j.CardEntity;
import twitter4j.CardEntity.BindingValue;
import twitter4j.CardEntity.ImageValue;
import twitter4j.CardEntity.StringValue;
import twitter4j.EntitySupport;
import twitter4j.ExtendedEntitySupport;
import twitter4j.MediaEntity;
import twitter4j.MediaEntity.Size;
import twitter4j.MediaEntity.Type;
import twitter4j.Status;
import twitter4j.URLEntity;
@SuppressWarnings("unused")
public class ParcelableMedia implements Parcelable, JSONParcelable, SimpleValueSerializable {
@IntDef({TYPE_UNKNOWN, TYPE_IMAGE, TYPE_VIDEO, TYPE_CARD_ANIMATED_GIF})
public @interface MediaType {
}
@MediaType
public static final int TYPE_UNKNOWN = 0;
@MediaType
public static final int TYPE_IMAGE = 1;
@MediaType
public static final int TYPE_VIDEO = 2;
@MediaType
public static final int TYPE_CARD_ANIMATED_GIF = 3;
public static final Parcelable.Creator<ParcelableMedia> CREATOR = new Parcelable.Creator<ParcelableMedia>() {
@Override
@ -72,8 +90,12 @@ public class ParcelableMedia implements Parcelable, JSONParcelable, SimpleValueS
public String media_url;
@Nullable
public String page_url;
public int start, end, type;
public String page_url, preview_url;
public int start;
public int end;
@MediaType
public int type;
public int width, height;
public VideoInfo video_info;
@ -86,8 +108,10 @@ public class ParcelableMedia implements Parcelable, JSONParcelable, SimpleValueS
public ParcelableMedia(final JSONParcel in) {
media_url = in.readString("media_url");
page_url = in.readString("page_url");
preview_url = in.readString("preview_url");
start = in.readInt("start");
end = in.readInt("end");
//noinspection ResourceType
type = in.readInt("type");
width = in.readInt("width");
height = in.readInt("height");
@ -96,6 +120,7 @@ public class ParcelableMedia implements Parcelable, JSONParcelable, SimpleValueS
public ParcelableMedia(final MediaEntity entity) {
page_url = entity.getMediaURL();
media_url = entity.getMediaURL();
preview_url = entity.getMediaURL();
start = entity.getStart();
end = entity.getEnd();
type = getTypeInt(entity.getType());
@ -108,6 +133,7 @@ public class ParcelableMedia implements Parcelable, JSONParcelable, SimpleValueS
public ParcelableMedia(ParcelableMediaUpdate update) {
media_url = update.uri;
page_url = update.uri;
preview_url = update.uri;
type = update.type;
}
@ -135,8 +161,10 @@ public class ParcelableMedia implements Parcelable, JSONParcelable, SimpleValueS
public ParcelableMedia(final Parcel in) {
page_url = in.readString();
media_url = in.readString();
preview_url = in.readString();
start = in.readInt();
end = in.readInt();
//noinspection ResourceType
type = in.readInt();
width = in.readInt();
height = in.readInt();
@ -144,9 +172,11 @@ public class ParcelableMedia implements Parcelable, JSONParcelable, SimpleValueS
}
private ParcelableMedia(@NonNull final String media_url, @Nullable final String page_url,
final int start, final int end, final int type) {
@Nullable final String preview_url, final int start, final int end,
final int type) {
this.page_url = page_url;
this.media_url = media_url;
this.preview_url = preview_url;
this.start = start;
this.end = end;
this.type = type;
@ -165,6 +195,10 @@ public class ParcelableMedia implements Parcelable, JSONParcelable, SimpleValueS
page_url = reader.nextString();
break;
}
case "preview_url": {
preview_url = reader.nextString();
break;
}
case "start": {
start = reader.nextInt();
break;
@ -174,6 +208,7 @@ public class ParcelableMedia implements Parcelable, JSONParcelable, SimpleValueS
break;
}
case "type": {
//noinspection ResourceType
type = reader.nextInt();
break;
}
@ -240,6 +275,7 @@ public class ParcelableMedia implements Parcelable, JSONParcelable, SimpleValueS
public void write(Writer writer) {
writer.write("media_url", media_url);
writer.write("page_url", page_url);
writer.write("preview_url", preview_url);
writer.write("start", String.valueOf(start));
writer.write("end", String.valueOf(end));
writer.write("type", String.valueOf(type));
@ -251,6 +287,7 @@ public class ParcelableMedia implements Parcelable, JSONParcelable, SimpleValueS
public void writeToParcel(final JSONParcel out) {
out.writeString("media_url", media_url);
out.writeString("page_url", page_url);
out.writeString("preview_url", preview_url);
out.writeInt("start", start);
out.writeInt("end", end);
out.writeInt("type", type);
@ -262,6 +299,7 @@ public class ParcelableMedia implements Parcelable, JSONParcelable, SimpleValueS
public void writeToParcel(final Parcel dest, final int flags) {
dest.writeString(page_url);
dest.writeString(media_url);
dest.writeString(preview_url);
dest.writeInt(start);
dest.writeInt(end);
dest.writeInt(type);
@ -270,7 +308,60 @@ public class ParcelableMedia implements Parcelable, JSONParcelable, SimpleValueS
dest.writeParcelable(video_info, flags);
}
public static ParcelableMedia[] fromEntities(final EntitySupport entities) {
@Nullable
public static ParcelableMedia[] fromStatus(final Status status) {
final ParcelableMedia[] fromEntities = fromEntities(status);
final ParcelableMedia[] fromCard = fromCard(status.getCard(), status.getURLEntities());
if (fromEntities == null) {
return fromCard;
} else if (fromCard == null) {
return fromEntities;
}
final ParcelableMedia[] merged = new ParcelableMedia[fromCard.length + fromEntities.length];
TwidereArrayUtils.mergeArray(merged, fromEntities, fromCard);
return merged;
}
@Nullable
private static ParcelableMedia[] fromCard(@Nullable CardEntity card, @Nullable URLEntity[] entities) {
if (card == null) return null;
if ("animated_gif".equals(card.getName())) {
final BindingValue player_stream_url = card.getBindingValue("player_stream_url");
if (player_stream_url == null || !BindingValue.TYPE_STRING.equals(player_stream_url.getType()))
return null;
final ParcelableMedia media = new ParcelableMedia();
media.type = ParcelableMedia.TYPE_CARD_ANIMATED_GIF;
media.media_url = ((StringValue) player_stream_url).getValue();
media.page_url = card.getUrl();
final BindingValue player_image = card.getBindingValue("player_image");
if (player_image instanceof ImageValue) {
media.preview_url = ((ImageValue) player_image).getUrl();
}
final BindingValue player_width = card.getBindingValue("player_width");
final BindingValue player_height = card.getBindingValue("player_height");
if (player_width instanceof StringValue && player_height instanceof StringValue) {
media.width = ParseUtils.parseInt(((StringValue) player_width).getValue());
media.height = ParseUtils.parseInt(((StringValue) player_height).getValue());
}
if (entities != null) {
for (URLEntity entity : entities) {
if (entity.getURL().equals(media.page_url)) {
media.start = entity.getStart();
media.end = entity.getEnd();
break;
}
}
}
return new ParcelableMedia[]{media};
}
return null;
}
@Nullable
public static ParcelableMedia[] fromEntities(@Nullable final EntitySupport entities) {
if (entities == null) return null;
final List<ParcelableMedia> list = new ArrayList<>();
final MediaEntity[] mediaEntities;
if (entities instanceof ExtendedEntitySupport) {
@ -294,7 +385,14 @@ public class ParcelableMedia implements Parcelable, JSONParcelable, SimpleValueS
final String expanded = url.getExpandedURL();
final String media_url = MediaPreviewUtils.getSupportedLink(expanded);
if (expanded != null && media_url != null) {
list.add(new ParcelableMedia(media_url, expanded, url.getStart(), url.getEnd(), TYPE_IMAGE));
final ParcelableMedia media = new ParcelableMedia();
media.type = TYPE_IMAGE;
media.page_url = expanded;
media.media_url = media_url;
media.preview_url = media_url;
media.start = url.getStart();
media.end = url.getEnd();
list.add(media);
}
}
}
@ -313,6 +411,10 @@ public class ParcelableMedia implements Parcelable, JSONParcelable, SimpleValueS
}
public VideoInfo() {
}
public VideoInfo(MediaEntity.VideoInfo videoInfo) {
variants = Variant.fromMediaEntityVariants(videoInfo.getVariants());
aspect_ratio = videoInfo.getAspectRatio();
@ -431,7 +533,7 @@ public class ParcelableMedia implements Parcelable, JSONParcelable, SimpleValueS
}
public static ParcelableMedia newImage(final String media_url, final String url) {
return new ParcelableMedia(media_url, url, 0, 0, TYPE_IMAGE);
return new ParcelableMedia(media_url, url, media_url, 0, 0, TYPE_IMAGE);
}
public static class MediaSize {

View File

@ -395,7 +395,7 @@ public class ParcelableStatus implements TwidereParcelable, Comparable<Parcelabl
user_is_verified = user.isVerified();
user_is_following = user.isFollowing();
text_html = TwitterContentUtils.formatStatusText(status);
media = ParcelableMedia.fromEntities(status);
media = ParcelableMedia.fromStatus(status);
text_plain = status.getText();
retweet_count = status.getRetweetCount();
favorite_count = status.getFavoriteCount();
@ -732,7 +732,7 @@ public class ParcelableStatus implements TwidereParcelable, Comparable<Parcelabl
public ParcelableCardEntity(CardEntity card, long account_id) {
name = card.getName();
users = ParcelableUser.fromUsersArray(card.gerUsers(), account_id);
users = ParcelableUser.fromUsersArray(card.getUsers(), account_id);
final BindingValue[] bindingValues = card.getBindingValues();
if (bindingValues != null) {
values = new ParcelableValueItem[bindingValues.length];

View File

@ -393,7 +393,7 @@ public final class ContentValuesCreator implements TwidereConstants {
values.put(Statuses.PLACE_FULL_NAME, place.getFullName());
}
values.put(Statuses.IS_FAVORITE, status.isFavorited());
final ParcelableMedia[] media = ParcelableMedia.fromEntities(status);
final ParcelableMedia[] media = ParcelableMedia.fromStatus(status);
if (media != null) {
values.put(Statuses.MEDIA_LIST, SimpleValueSerializer.toSerializedString(media));
}

View File

@ -28,7 +28,9 @@ public interface CardEntity extends Serializable {
String getName();
User[] gerUsers();
String getUrl();
User[] getUsers();
BindingValue getBindingValue(String key);

View File

@ -21,6 +21,7 @@ package twitter4j.internal.json;
import org.json.JSONException;
import org.json.JSONObject;
import org.mariotaku.twidere.library.twitter4j.BuildConfig;
import java.util.Arrays;
import java.util.HashMap;
@ -38,11 +39,19 @@ import twitter4j.internal.util.InternalParseUtil;
public class CardEntityJSONImpl implements CardEntity {
private final String name;
private final String url;
@Override
public String getUrl() {
return url;
}
private final Map<String, BindingValue> bindingValues;
private final User[] users;
public CardEntityJSONImpl(JSONObject json) throws JSONException, TwitterException {
this.name = json.getString("name");
this.url = json.optString("url");
this.bindingValues = BindingValueImpl.valuesOf(json.getJSONObject("binding_values"));
if (!json.isNull("users")) {
final JSONObject usersJSON = json.getJSONObject("users");
@ -63,7 +72,7 @@ public class CardEntityJSONImpl implements CardEntity {
}
@Override
public User[] gerUsers() {
public User[] getUsers() {
return users;
}
@ -107,7 +116,11 @@ public class CardEntityJSONImpl implements CardEntity {
} else if (TYPE_BOOLEAN.equals(type)) {
return new BooleanValueImpl(name, json);
}
throw new TwitterException(String.format("Unsupported type %s", type));
if (BuildConfig.DEBUG) {
throw new RuntimeException(String.format("Unsupported type %s", type));
} else {
throw new TwitterException(String.format("Unsupported type %s", type));
}
}
private static HashMap<String, BindingValue> valuesOf(JSONObject json) throws JSONException, TwitterException {
@ -115,7 +128,7 @@ public class CardEntityJSONImpl implements CardEntity {
final HashMap<String, BindingValue> values = new HashMap<>();
while (keys.hasNext()) {
final String key = keys.next();
if (!json.isNull("name")) {
if (!json.isNull(key)) {
values.put(key, valueOf(key, json.getJSONObject(key)));
}
}

View File

@ -30,50 +30,37 @@ import org.mariotaku.twidere.util.TwidereLinkify;
public class TypeMappingUtil {
public static String getLinkType(int type) {
String linkType = "";
switch (type) {
case TwidereLinkify.LINK_TYPE_MENTION:
linkType = "mention";
break;
return "mention";
case TwidereLinkify.LINK_TYPE_CASHTAG:
linkType = "cashTag";
break;
return "cashTag";
case TwidereLinkify.LINK_TYPE_LINK:
linkType = "urlLink";
break;
return "urlLink";
case TwidereLinkify.LINK_TYPE_LIST:
linkType = "userList";
break;
return "userList";
case TwidereLinkify.LINK_TYPE_STATUS:
linkType = "status";
break;
return "status";
case TwidereLinkify.LINK_TYPE_USER_ID:
linkType = "userID";
break;
return "userID";
case TwidereLinkify.LINK_TYPE_HASHTAG:
linkType = "hashTag";
break;
return "hashTag";
default:
linkType = "unknown";
break;
return "unknown";
}
return linkType;
}
public static String getMediaType(int type) {
String mediaType = "";
switch (type) {
case ParcelableMedia.TYPE_IMAGE:
mediaType = "image";
break;
return "image";
case ParcelableMedia.TYPE_VIDEO:
mediaType = "video";
break;
return "video";
case ParcelableMedia.TYPE_CARD_ANIMATED_GIF:
return "card_animated_gif";
default:
mediaType = "unknown";
break;
return "unknown";
}
return mediaType;
}
}

View File

@ -82,6 +82,8 @@ import pl.droidsonroids.gif.InputSource.FileSource;
public final class MediaViewerActivity extends ThemedActionBarActivity implements Constants, OnPageChangeListener {
private static final String EXTRA_LOOP = "loop";
private ViewPager mViewPager;
private MediaPagerAdapter mAdapter;
private ActionBar mActionBar;
@ -228,6 +230,10 @@ public final class MediaViewerActivity extends ThemedActionBarActivity implement
// mVideoViewProgress.setVisibility(View.GONE);
}
public boolean isLoopEnabled() {
return getArguments().getBoolean(EXTRA_LOOP, false);
}
@Override
public View onCreateView(LayoutInflater inflater, @Nullable ViewGroup container, @Nullable Bundle savedInstanceState) {
return inflater.inflate(R.layout.fragment_media_page_video, container, false);
@ -291,6 +297,7 @@ public final class MediaViewerActivity extends ThemedActionBarActivity implement
} else {
mp.setVolume(0, 0);
}
mp.setLooping(isLoopEnabled());
mp.start();
mVideoViewProgress.setVisibility(View.VISIBLE);
mVideoViewProgress.post(mVideoProgressRunnable);
@ -298,13 +305,25 @@ public final class MediaViewerActivity extends ThemedActionBarActivity implement
}
private String getBestVideoUrl(ParcelableMedia media) {
if (media == null || media.video_info == null) return null;
for (String supportedType : SUPPORTED_VIDEO_TYPES) {
for (Variant variant : media.video_info.variants) {
if (supportedType.equalsIgnoreCase(variant.content_type)) return variant.url;
if (media == null) return null;
switch (media.type) {
case ParcelableMedia.TYPE_VIDEO: {
if (media.video_info == null) return null;
for (String supportedType : SUPPORTED_VIDEO_TYPES) {
for (Variant variant : media.video_info.variants) {
if (supportedType.equalsIgnoreCase(variant.content_type))
return variant.url;
}
}
return null;
}
case ParcelableMedia.TYPE_CARD_ANIMATED_GIF: {
return media.media_url;
}
default: {
return null;
}
}
return null;
}
@Override
@ -637,6 +656,10 @@ public final class MediaViewerActivity extends ThemedActionBarActivity implement
args.putLong(EXTRA_ACCOUNT_ID, mAccountId);
args.putParcelable(EXTRA_MEDIA, media);
switch (media.type) {
case ParcelableMedia.TYPE_CARD_ANIMATED_GIF: {
args.putBoolean(EXTRA_LOOP, true);
return Fragment.instantiate(mActivity, VideoPageFragment.class.getName(), args);
}
case ParcelableMedia.TYPE_VIDEO: {
return Fragment.instantiate(mActivity, VideoPageFragment.class.getName(), args);
}

View File

@ -849,10 +849,10 @@ public class StatusFragment extends BaseSupportFragment implements LoaderCallbac
mediaPreview.setStyle(adapter.getMediaPreviewStyle());
textView.setMovementMethod(StatusContentMovementMethod.getInstance());
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
// if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
quoteTextView.setCustomSelectionActionModeCallback(new StatusActionModeCallback(quoteTextView, fragment, activity));
textView.setCustomSelectionActionModeCallback(new StatusActionModeCallback(textView, fragment, activity));
}
// }
}

View File

@ -92,7 +92,8 @@ public class ImageLoadingHandler implements ImageLoadingListener, ImageLoadingPr
private static boolean isVideoItem(ViewGroup parent) {
final Object tag = parent.getTag();
if (tag instanceof ParcelableMedia) {
return ((ParcelableMedia) tag).type == ParcelableMedia.TYPE_VIDEO;
final int type = ((ParcelableMedia) tag).type;
return type == ParcelableMedia.TYPE_VIDEO || type == ParcelableMedia.TYPE_CARD_ANIMATED_GIF;
}
return false;
}

View File

@ -71,7 +71,7 @@ public class TwitterCardUtils {
public static boolean isCardSupported(ParcelableCardEntity card) {
if (card == null) return false;
return CARD_NAME_PLAYER.equals(card.name) || CARD_NAME_AUDIO.equals(card.name) || CARD_NAME_ANIMATED_GIF.equals(card.name);
return CARD_NAME_PLAYER.equals(card.name) || CARD_NAME_AUDIO.equals(card.name);
}
}

View File

@ -24,6 +24,7 @@ import android.content.res.TypedArray;
import android.support.annotation.IntDef;
import android.support.annotation.NonNull;
import android.support.annotation.Nullable;
import android.text.TextUtils;
import android.util.AttributeSet;
import android.view.View;
import android.view.ViewGroup;
@ -116,7 +117,12 @@ public class CardMediaContainer extends ViewGroup implements Constants {
}
if (i < k) {
final ParcelableMedia media = mediaArray[i];
loader.displayPreviewImage(imageView, media.page_url, loadingHandler);
if (TextUtils.isEmpty(media.preview_url)) {
// For backward compatibility
loader.displayPreviewImage(imageView, media.media_url, loadingHandler);
} else {
loader.displayPreviewImage(imageView, media.preview_url, loadingHandler);
}
child.setTag(media);
child.setVisibility(VISIBLE);
if (i == j - 1) {