added media previews to timeline and media viewers
This commit is contained in:
parent
acbd5acb20
commit
e551de7521
13 changed files with 328 additions and 37 deletions
|
@ -1,9 +1,13 @@
|
|||
package com.keylesspalace.tusky;
|
||||
|
||||
import android.graphics.drawable.Drawable;
|
||||
import android.os.Build;
|
||||
import android.provider.MediaStore;
|
||||
import android.text.Html;
|
||||
import android.text.Spanned;
|
||||
|
||||
import com.android.volley.toolbox.NetworkImageView;
|
||||
|
||||
import org.json.JSONArray;
|
||||
import org.json.JSONException;
|
||||
import org.json.JSONObject;
|
||||
|
@ -38,6 +42,9 @@ public class Status {
|
|||
/** whether the authenticated user has favourited this status */
|
||||
private boolean favourited;
|
||||
private Visibility visibility;
|
||||
private MediaAttachment[] attachments = null;
|
||||
|
||||
public static final int MAX_MEDIA_ATTACHMENTS = 4;
|
||||
|
||||
public Status(String id, String accountId, String displayName, String username, Spanned content,
|
||||
String avatar, Date createdAt, boolean reblogged, boolean favourited,
|
||||
|
@ -52,6 +59,7 @@ public class Status {
|
|||
this.reblogged = reblogged;
|
||||
this.favourited = favourited;
|
||||
this.visibility = Visibility.valueOf(visibility.toUpperCase());
|
||||
this.attachments = new MediaAttachment[0];
|
||||
}
|
||||
|
||||
public String getId() {
|
||||
|
@ -98,6 +106,10 @@ public class Status {
|
|||
return visibility;
|
||||
}
|
||||
|
||||
public MediaAttachment[] getAttachments() {
|
||||
return attachments;
|
||||
}
|
||||
|
||||
public void setRebloggedByUsername(String name) {
|
||||
rebloggedByUsername = name;
|
||||
}
|
||||
|
@ -110,6 +122,10 @@ public class Status {
|
|||
this.favourited = favourited;
|
||||
}
|
||||
|
||||
public void setAttachments(MediaAttachment[] attachments) {
|
||||
this.attachments = attachments;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int hashCode() {
|
||||
return id.hashCode();
|
||||
|
@ -173,6 +189,21 @@ public class Status {
|
|||
String username = account.getString("acct");
|
||||
String avatar = account.getString("avatar");
|
||||
|
||||
JSONArray mediaAttachments = object.getJSONArray("media_attachments");
|
||||
MediaAttachment[] attachments = null;
|
||||
if (mediaAttachments != null) {
|
||||
int n = mediaAttachments.length();
|
||||
attachments = new MediaAttachment[n];
|
||||
for (int i = 0; i < n; i++) {
|
||||
JSONObject attachment = mediaAttachments.getJSONObject(i);
|
||||
String url = attachment.getString("url");
|
||||
String previewUrl = attachment.getString("preview_url");
|
||||
String type = attachment.getString("type");
|
||||
attachments[i] = new MediaAttachment(url, previewUrl,
|
||||
MediaAttachment.Type.valueOf(type.toUpperCase()));
|
||||
}
|
||||
}
|
||||
|
||||
Status reblog = null;
|
||||
/* This case shouldn't be hit after the first recursion at all. But if this method is
|
||||
* passed unusual data this check will prevent extra recursion */
|
||||
|
@ -193,6 +224,9 @@ public class Status {
|
|||
id, accountId, displayName, username, contentPlus, avatar, createdAt,
|
||||
reblogged, favourited, visibility);
|
||||
}
|
||||
if (attachments != null) {
|
||||
status.setAttachments(attachments);
|
||||
}
|
||||
return status;
|
||||
}
|
||||
|
||||
|
@ -204,4 +238,33 @@ public class Status {
|
|||
}
|
||||
return statuses;
|
||||
}
|
||||
|
||||
public static class MediaAttachment {
|
||||
enum Type {
|
||||
IMAGE,
|
||||
VIDEO,
|
||||
}
|
||||
|
||||
private String url;
|
||||
private String previewUrl;
|
||||
private Type type;
|
||||
|
||||
public MediaAttachment(String url, String previewUrl, Type type) {
|
||||
this.url = url;
|
||||
this.previewUrl = previewUrl;
|
||||
this.type = type;
|
||||
}
|
||||
|
||||
public String getUrl() {
|
||||
return url;
|
||||
}
|
||||
|
||||
public String getPreviewUrl() {
|
||||
return previewUrl;
|
||||
}
|
||||
|
||||
public Type getType() {
|
||||
return type;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -6,4 +6,5 @@ public interface StatusActionListener {
|
|||
void onReblog(final boolean reblog, final int position);
|
||||
void onFavourite(final boolean favourite, final int position);
|
||||
void onMore(View view, final int position);
|
||||
void onViewMedia(String url, Status.MediaAttachment.Type type);
|
||||
}
|
||||
|
|
|
@ -2,8 +2,10 @@ package com.keylesspalace.tusky;
|
|||
|
||||
import android.content.Context;
|
||||
import android.support.annotation.Nullable;
|
||||
import android.support.v7.widget.PagerSnapHelper;
|
||||
import android.support.v7.widget.RecyclerView;
|
||||
import android.text.Spanned;
|
||||
import android.text.style.ImageSpan;
|
||||
import android.view.LayoutInflater;
|
||||
import android.view.View;
|
||||
import android.view.ViewGroup;
|
||||
|
@ -53,6 +55,7 @@ public class TimelineAdapter extends RecyclerView.Adapter {
|
|||
} else {
|
||||
holder.setRebloggedByUsername(rebloggedByUsername);
|
||||
}
|
||||
holder.setMediaPreviews(status.getAttachments(), listener);
|
||||
holder.setupButtons(listener, position);
|
||||
if (status.getVisibility() == Status.Visibility.PRIVATE) {
|
||||
holder.disableReblogging();
|
||||
|
@ -112,6 +115,11 @@ public class TimelineAdapter extends RecyclerView.Adapter {
|
|||
private ImageButton moreButton;
|
||||
private boolean favourited;
|
||||
private boolean reblogged;
|
||||
private NetworkImageView mediaPreview0;
|
||||
private NetworkImageView mediaPreview1;
|
||||
private NetworkImageView mediaPreview2;
|
||||
private NetworkImageView mediaPreview3;
|
||||
private String[] mediaAttachmentUrls;
|
||||
|
||||
public ViewHolder(View itemView) {
|
||||
super(itemView);
|
||||
|
@ -128,6 +136,10 @@ public class TimelineAdapter extends RecyclerView.Adapter {
|
|||
moreButton = (ImageButton) itemView.findViewById(R.id.status_more);
|
||||
reblogged = false;
|
||||
favourited = false;
|
||||
mediaPreview0 = (NetworkImageView) itemView.findViewById(R.id.status_media_preview_0);
|
||||
mediaPreview1 = (NetworkImageView) itemView.findViewById(R.id.status_media_preview_1);
|
||||
mediaPreview2 = (NetworkImageView) itemView.findViewById(R.id.status_media_preview_2);
|
||||
mediaPreview3 = (NetworkImageView) itemView.findViewById(R.id.status_media_preview_3);
|
||||
}
|
||||
|
||||
public void setDisplayName(String name) {
|
||||
|
@ -234,6 +246,37 @@ public class TimelineAdapter extends RecyclerView.Adapter {
|
|||
}
|
||||
}
|
||||
|
||||
public void setMediaPreviews(final Status.MediaAttachment[] attachments,
|
||||
final StatusActionListener listener) {
|
||||
final NetworkImageView[] previews = {
|
||||
mediaPreview0,
|
||||
mediaPreview1,
|
||||
mediaPreview2,
|
||||
mediaPreview3
|
||||
};
|
||||
Context context = mediaPreview0.getContext();
|
||||
ImageLoader imageLoader = VolleySingleton.getInstance(context).getImageLoader();
|
||||
int n = Math.min(attachments.length, Status.MAX_MEDIA_ATTACHMENTS);
|
||||
for (int i = 0; i < n; i++) {
|
||||
String previewUrl = attachments[i].getPreviewUrl();
|
||||
previews[i].setImageUrl(previewUrl, imageLoader);
|
||||
previews[i].setVisibility(View.VISIBLE);
|
||||
final String url = attachments[i].getUrl();
|
||||
final Status.MediaAttachment.Type type = attachments[i].getType();
|
||||
previews[i].setOnClickListener(new View.OnClickListener() {
|
||||
@Override
|
||||
public void onClick(View v) {
|
||||
listener.onViewMedia(url, type);
|
||||
}
|
||||
});
|
||||
}
|
||||
// Hide any of the placeholder previews beyond the ones set.
|
||||
for (int i = n; i < Status.MAX_MEDIA_ATTACHMENTS; i++) {
|
||||
previews[i].setImageUrl(null, imageLoader);
|
||||
previews[i].setVisibility(View.GONE);
|
||||
}
|
||||
}
|
||||
|
||||
public void setupButtons(final StatusActionListener listener, final int position) {
|
||||
reblogButton.setOnClickListener(new View.OnClickListener() {
|
||||
@Override
|
||||
|
|
|
@ -1,11 +1,13 @@
|
|||
package com.keylesspalace.tusky;
|
||||
|
||||
import android.content.Context;
|
||||
import android.content.Intent;
|
||||
import android.content.SharedPreferences;
|
||||
import android.graphics.drawable.Drawable;
|
||||
import android.os.Bundle;
|
||||
import android.support.annotation.Nullable;
|
||||
import android.support.v4.app.Fragment;
|
||||
import android.support.v4.app.FragmentManager;
|
||||
import android.support.v4.content.ContextCompat;
|
||||
import android.support.v4.widget.SwipeRefreshLayout;
|
||||
import android.support.v7.widget.DividerItemDecoration;
|
||||
|
@ -184,7 +186,8 @@ public class TimelineFragment extends Fragment implements
|
|||
}
|
||||
|
||||
public void onFetchTimelineFailure(Exception exception) {
|
||||
Toast.makeText(getContext(), R.string.error_fetching_timeline, Toast.LENGTH_SHORT).show();
|
||||
Toast.makeText(getContext(), R.string.error_fetching_timeline, Toast.LENGTH_SHORT)
|
||||
.show();
|
||||
swipeRefreshLayout.setRefreshing(false);
|
||||
}
|
||||
|
||||
|
@ -312,4 +315,24 @@ public class TimelineFragment extends Fragment implements
|
|||
});
|
||||
popup.show();
|
||||
}
|
||||
|
||||
public void onViewMedia(String url, Status.MediaAttachment.Type type) {
|
||||
switch (type) {
|
||||
case IMAGE: {
|
||||
Fragment newFragment = ViewMediaFragment.newInstance(url);
|
||||
FragmentManager manager = getFragmentManager();
|
||||
manager.beginTransaction()
|
||||
.add(R.id.overlay_fragment_container, newFragment)
|
||||
.addToBackStack(null)
|
||||
.commit();
|
||||
break;
|
||||
}
|
||||
case VIDEO: {
|
||||
Intent intent = new Intent(getContext(), ViewVideoActivity.class);
|
||||
intent.putExtra("url", url);
|
||||
startActivity(intent);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -0,0 +1,45 @@
|
|||
package com.keylesspalace.tusky;
|
||||
|
||||
import android.os.Bundle;
|
||||
import android.support.v4.app.Fragment;
|
||||
import android.view.LayoutInflater;
|
||||
import android.view.View;
|
||||
import android.view.ViewGroup;
|
||||
|
||||
import com.android.volley.toolbox.ImageLoader;
|
||||
import com.android.volley.toolbox.NetworkImageView;
|
||||
|
||||
public class ViewMediaFragment extends Fragment {
|
||||
public static ViewMediaFragment newInstance(String url) {
|
||||
Bundle arguments = new Bundle();
|
||||
ViewMediaFragment fragment = new ViewMediaFragment();
|
||||
arguments.putString("url", url);
|
||||
fragment.setArguments(arguments);
|
||||
return fragment;
|
||||
}
|
||||
|
||||
@Override
|
||||
public View onCreateView(LayoutInflater inflater, final ViewGroup container,
|
||||
Bundle savedInstanceState) {
|
||||
View rootView = inflater.inflate(R.layout.fragment_view_media, container, false);
|
||||
|
||||
Bundle arguments = getArguments();
|
||||
String url = arguments.getString("url");
|
||||
NetworkImageView image = (NetworkImageView) rootView.findViewById(R.id.view_media_image);
|
||||
ImageLoader imageLoader = VolleySingleton.getInstance(getContext()).getImageLoader();
|
||||
image.setImageUrl(url, imageLoader);
|
||||
|
||||
rootView.setOnClickListener(new View.OnClickListener() {
|
||||
@Override
|
||||
public void onClick(View v) {
|
||||
dismiss();
|
||||
}
|
||||
});
|
||||
|
||||
return rootView;
|
||||
}
|
||||
|
||||
private void dismiss() {
|
||||
getFragmentManager().popBackStack();
|
||||
}
|
||||
}
|
|
@ -0,0 +1,21 @@
|
|||
package com.keylesspalace.tusky;
|
||||
|
||||
import android.os.Bundle;
|
||||
import android.support.v7.app.AppCompatActivity;
|
||||
import android.widget.MediaController;
|
||||
import android.widget.VideoView;
|
||||
|
||||
public class ViewVideoActivity extends AppCompatActivity {
|
||||
@Override
|
||||
public void onCreate(Bundle savedInstanceState) {
|
||||
super.onCreate(savedInstanceState);
|
||||
setContentView(R.layout.activity_view_video);
|
||||
String url = getIntent().getStringExtra("url");
|
||||
VideoView videoView = (VideoView) findViewById(R.id.video_player);
|
||||
videoView.setVideoPath(url);
|
||||
MediaController controller = new MediaController(this);
|
||||
videoView.setMediaController(controller);
|
||||
controller.show();
|
||||
videoView.start();
|
||||
}
|
||||
}
|
Loading…
Add table
Add a link
Reference in a new issue