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
|
@ -30,6 +30,7 @@
|
|||
<activity
|
||||
android:name=".ComposeActivity"
|
||||
android:windowSoftInputMode="stateVisible|adjustResize" />
|
||||
<activity android:name=".ViewVideoActivity" />
|
||||
</application>
|
||||
|
||||
</manifest>
|
|
@ -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();
|
||||
}
|
||||
}
|
|
@ -1,5 +1,5 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
xmlns:tools="http://schemas.android.com/tools"
|
||||
xmlns:app="http://schemas.android.com/apk/res-auto"
|
||||
android:id="@+id/activity_main"
|
||||
|
@ -9,7 +9,11 @@
|
|||
android:paddingLeft="@dimen/activity_horizontal_margin"
|
||||
android:paddingRight="@dimen/activity_horizontal_margin"
|
||||
android:paddingTop="@dimen/activity_vertical_margin"
|
||||
tools:context="com.keylesspalace.tusky.MainActivity"
|
||||
tools:context="com.keylesspalace.tusky.MainActivity">
|
||||
|
||||
<LinearLayout
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent"
|
||||
android:orientation="vertical">
|
||||
|
||||
<android.support.v7.widget.Toolbar
|
||||
|
@ -50,3 +54,12 @@
|
|||
</android.support.v4.view.ViewPager>
|
||||
|
||||
</LinearLayout>
|
||||
|
||||
<FrameLayout
|
||||
android:id="@+id/overlay_fragment_container"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent">
|
||||
|
||||
</FrameLayout>
|
||||
|
||||
</RelativeLayout>
|
14
app/src/main/res/layout/activity_view_video.xml
Normal file
14
app/src/main/res/layout/activity_view_video.xml
Normal file
|
@ -0,0 +1,14 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:orientation="vertical"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent"
|
||||
android:background="@color/view_video_background">
|
||||
|
||||
<VideoView
|
||||
android:id="@+id/video_player"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent"
|
||||
android:layout_centerInParent="true" />
|
||||
|
||||
</RelativeLayout>
|
14
app/src/main/res/layout/fragment_view_media.xml
Normal file
14
app/src/main/res/layout/fragment_view_media.xml
Normal file
|
@ -0,0 +1,14 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent"
|
||||
android:background="#60000000">
|
||||
|
||||
<com.android.volley.toolbox.NetworkImageView
|
||||
android:id="@+id/view_media_image"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent"
|
||||
android:layout_centerInParent="true"
|
||||
android:scaleType="fitCenter" />
|
||||
|
||||
</RelativeLayout>
|
|
@ -76,11 +76,62 @@
|
|||
android:layout_toEndOf="@+id/status_avatar"
|
||||
android:layout_below="@+id/status_name_bar" />
|
||||
|
||||
<LinearLayout
|
||||
android:id="@+id/status_media_preview_container"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:orientation="vertical"
|
||||
android:layout_below="@+id/status_content"
|
||||
android:layout_toRightOf="@+id/status_avatar">
|
||||
|
||||
<LinearLayout
|
||||
android:orientation="horizontal"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content">
|
||||
|
||||
<com.android.volley.toolbox.NetworkImageView
|
||||
android:id="@+id/status_media_preview_0"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_weight="1"
|
||||
android:scaleType="centerCrop"
|
||||
android:layout_marginTop="@dimen/status_media_preview_top_margin" />
|
||||
|
||||
<com.android.volley.toolbox.NetworkImageView
|
||||
android:id="@+id/status_media_preview_1"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_weight="1"
|
||||
android:scaleType="centerCrop"
|
||||
android:layout_marginTop="@dimen/status_media_preview_top_margin" />
|
||||
</LinearLayout>
|
||||
|
||||
<LinearLayout
|
||||
android:orientation="horizontal"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content">
|
||||
|
||||
<com.android.volley.toolbox.NetworkImageView
|
||||
android:id="@+id/status_media_preview_2"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_weight="1" />
|
||||
|
||||
<com.android.volley.toolbox.NetworkImageView
|
||||
android:id="@+id/status_media_preview_3"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_weight="1"
|
||||
android:scaleType="centerCrop" />
|
||||
</LinearLayout>
|
||||
|
||||
</LinearLayout>
|
||||
|
||||
<LinearLayout
|
||||
android:orientation="horizontal"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_below="@+id/status_content"
|
||||
android:layout_below="@id/status_media_preview_container"
|
||||
android:layout_toRightOf="@+id/status_avatar"
|
||||
android:paddingBottom="8dp"
|
||||
android:paddingTop="8dp">
|
||||
|
|
|
@ -4,4 +4,5 @@
|
|||
<color name="colorPrimaryDark">#303F9F</color>
|
||||
<color name="colorAccent">#FF4081</color>
|
||||
<color name="gray">#4F4F4F</color>
|
||||
<color name="view_video_background">#000000</color>
|
||||
</resources>
|
||||
|
|
|
@ -5,4 +5,5 @@
|
|||
<dimen name="status_since_created_left_margin">4dp</dimen>
|
||||
<dimen name="status_avatar_padding">8dp</dimen>
|
||||
<dimen name="status_boost_icon_vertical_padding">5dp</dimen>
|
||||
<dimen name="status_media_preview_top_margin">4dp</dimen>
|
||||
</resources>
|
||||
|
|
Loading…
Reference in a new issue