code cleanup, added video media description, fixed crash

This commit is contained in:
nuclearfog 2023-07-06 23:38:35 +02:00
parent 5f9b6292d7
commit 55ac35b70c
No known key found for this signature in database
GPG Key ID: 03488A185C476379
11 changed files with 186 additions and 216 deletions

View File

@ -1074,12 +1074,12 @@ public class Mastodon implements Connection {
params.add("display_name=" + StringUtils.encode(update.getName()));
params.add("note=" + StringUtils.encode(update.getDescription()));
if (update.getProfileImageStream() != null) {
streams.add(update.getProfileImageStream());
if (update.getProfileImageMedia() != null) {
streams.add(update.getProfileImageMedia().getStream());
keys.add("avatar");
}
if (update.getBannerImageStream() != null) {
streams.add(update.getBannerImageStream());
if (update.getBannerImageMedia() != null) {
streams.add(update.getBannerImageMedia().getStream());
keys.add("header");
}
try {

View File

@ -1033,10 +1033,10 @@ public class TwitterV1 implements Connection {
params.add("command=INIT");
params.add("media_type=" + mediaUpdate.getMimeType());
params.add("total_bytes=" + mediaUpdate.available());
if (mediaUpdate.getMimeType().startsWith("video/")) {
if (mediaUpdate.getMimeType() != null && mediaUpdate.getMimeType().startsWith("video/")) {
params.add("media_category=tweet_video");
enableChunk = true;
} else if (mediaUpdate.getMimeType().startsWith("image/gif")) {
} else if (mediaUpdate.getMimeType() != null && mediaUpdate.getMimeType().startsWith("image/gif")) {
params.add("media_category=tweet_gif");
enableChunk = true;
} else {
@ -1158,11 +1158,11 @@ public class TwitterV1 implements Connection {
params.add("url=" + StringUtils.encode(update.getUrl()));
params.add("location=" + StringUtils.encode(update.getLocation()));
params.add("description=" + StringUtils.encode(update.getDescription()));
if (update.getProfileImageStream() != null) {
updateImage(PROFILE_UPDATE_IMAGE, update.getProfileImageStream(), "image");
if (update.getProfileImageMedia() != null) {
updateImage(PROFILE_UPDATE_IMAGE, update.getProfileImageMedia().getStream(), "image");
}
if (update.getBannerImageStream() != null) {
updateImage(PROFILE_UPDATE_BANNER, update.getBannerImageStream(), "banner");
if (update.getBannerImageMedia() != null) {
updateImage(PROFILE_UPDATE_BANNER, update.getBannerImageMedia().getStream(), "banner");
}
return getUser(PROFILE_UPDATE, params);
}

View File

@ -12,6 +12,7 @@ import org.nuclearfog.twidda.BuildConfig;
import org.nuclearfog.twidda.model.Media;
import java.io.Closeable;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.io.InputStream;
import java.io.Serializable;
@ -76,20 +77,18 @@ public class MediaStatus implements Serializable, Closeable {
/**
* create MediaStatus from an offline source
*
* @param uri path to the local file
* @param description description of the media source
* @throws IllegalArgumentException when the file is invalid
* @param uri path to the local file
* @throws FileNotFoundException if the file is invalid
*/
public MediaStatus(Context context, Uri uri, String description) throws IllegalArgumentException {
public MediaStatus(Context context, Uri uri) throws FileNotFoundException {
DocumentFile file = DocumentFile.fromSingleUri(context, uri);
if (file != null && file.length() > 0) {
this.description = description;
mimeType = context.getContentResolver().getType(uri);
type = getType(mimeType);
path = uri.toString();
local = true;
} else {
throw new IllegalArgumentException();
throw new FileNotFoundException("file not supported!");
}
}
@ -246,7 +245,7 @@ public class MediaStatus implements Serializable, Closeable {
* @param mimeType mime type of the media file
* @return media type {@link #GIF,#PHOTO ,#VIDEO,#AUDIO} or {@link #INVALID} if media file is not supported
*/
private int getType(String mimeType) throws IllegalArgumentException {
private int getType(String mimeType) {
if (mimeType.equals("image/gif"))
return GIF;
if (mimeType.startsWith("image/"))

View File

@ -1,20 +1,15 @@
package org.nuclearfog.twidda.backend.helper.update;
import android.content.ContentResolver;
import android.content.Context;
import android.net.Uri;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import androidx.documentfile.provider.DocumentFile;
import org.nuclearfog.twidda.backend.helper.MediaStatus;
import org.nuclearfog.twidda.model.Instance;
import java.io.Closeable;
import java.io.Serializable;
import java.util.Arrays;
import java.util.TreeSet;
/**
* This class is used to upload a message
@ -32,9 +27,6 @@ public class MessageUpdate implements Serializable, Closeable {
private String name = "";
private String text = "";
private TreeSet<String> supportedFormats = new TreeSet<>();
/**
* close inputstream of media file
*/
@ -79,41 +71,24 @@ public class MessageUpdate implements Serializable, Closeable {
/**
* get inputstream of the media file
* get media attachment
*
* @return input stream
* @return media attachment
*/
@Nullable
public MediaStatus getMediaStatus() {
return mediaStatus;
}
/**
* add/update media attachment
*
* @param mediaStatus media attachment
*/
public void setMediaUpdate(MediaStatus mediaStatus) {
this.mediaStatus = mediaStatus;
}
/**
* add media uri and create input stream
*
* @param context context used to create inputstream and mime type
* @param uri uri of a local media file
* @return true if file is valid
*/
public boolean addMedia(Context context, @NonNull Uri uri) {
DocumentFile file = DocumentFile.fromSingleUri(context, uri);
String mime = context.getContentResolver().getType(uri);
// check if file is valid
if (mime == null || file == null || file.length() == 0 || !supportedFormats.contains(mime)) {
return false;
}
try {
mediaStatus = new MediaStatus(context, uri, "");
} catch (IllegalArgumentException exception) {
return false;
}
return true;
}
/**
* initialize inputstream of the file to upload
*
@ -129,7 +104,6 @@ public class MessageUpdate implements Serializable, Closeable {
* @param instance instance imformation
*/
public void setInstanceInformation(Instance instance) {
supportedFormats.addAll(Arrays.asList(instance.getSupportedFormats()));
this.instance = instance;
}

View File

@ -1,19 +1,12 @@
package org.nuclearfog.twidda.backend.helper.update;
import android.annotation.SuppressLint;
import android.content.ContentResolver;
import android.content.Context;
import android.net.Uri;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import androidx.documentfile.provider.DocumentFile;
import org.nuclearfog.twidda.BuildConfig;
import org.nuclearfog.twidda.backend.helper.MediaStatus;
import java.io.Closeable;
import java.io.IOException;
import java.io.InputStream;
/**
* This class is used to upload profile information
@ -22,8 +15,7 @@ import java.io.InputStream;
*/
public class ProfileUpdate implements Closeable {
private Uri[] imageUrls = new Uri[2];
private InputStream[] imageStreams = new InputStream[2];
private MediaStatus profileImage, bannerImage;
private String name = "";
private String url = "";
@ -36,14 +28,11 @@ public class ProfileUpdate implements Closeable {
*/
@Override
public void close() {
try {
for (InputStream imageStream : imageStreams) {
if (imageStream != null) {
imageStream.close();
}
}
} catch (IOException e) {
// ignore
if (profileImage != null) {
profileImage.close();
}
if (bannerImage != null) {
bannerImage.close();
}
}
@ -63,35 +52,15 @@ public class ProfileUpdate implements Closeable {
}
/**
* add profile image Uri
*
* @param context context used to resolve Uri
* @param imageUrl Uri of the local image file
* @return true if file is valid, false otherwise
*/
public boolean setImage(Context context, @NonNull Uri imageUrl) {
DocumentFile file = DocumentFile.fromSingleUri(context, imageUrl);
if (file != null && file.length() > 0) {
imageUrls[0] = imageUrl;
return true;
}
return false;
public void setProfileImage(MediaStatus profileImage) {
this.profileImage = profileImage;
}
/**
* add banner image Uri
*
* @param context context used to resolve Uri
* @param bannerUrl Uri of the local image file
* @return true if file is valid, false otherwise
*/
public boolean setBanner(Context context, @NonNull Uri bannerUrl) {
DocumentFile file = DocumentFile.fromSingleUri(context, bannerUrl);
if (file != null && file.length() > 0) {
imageUrls[1] = bannerUrl;
return true;
}
return false;
public void setBannerImage(MediaStatus bannerImage) {
this.bannerImage = bannerImage;
}
/**
@ -126,23 +95,23 @@ public class ProfileUpdate implements Closeable {
* @return true if any image is added
*/
public boolean imageAdded() {
return imageUrls[0] != null || imageUrls[1] != null;
return profileImage != null || bannerImage != null;
}
/**
* @return filestream of the profile image
* @return profile image media instance or null if not added
*/
@Nullable
public InputStream getProfileImageStream() {
return imageStreams[0];
public MediaStatus getProfileImageMedia() {
return profileImage;
}
/**
* @return filestream of the banner image
* @return banner image media instance or null if not added
*/
@Nullable
public InputStream getBannerImageStream() {
return imageStreams[1];
public MediaStatus getBannerImageMedia() {
return bannerImage;
}
/**
@ -151,26 +120,8 @@ public class ProfileUpdate implements Closeable {
*
* @return true if initialization finished without any error
*/
@SuppressLint("Recycle")
public boolean prepare(ContentResolver resolver) {
try {
for (int i = 0; i < imageUrls.length; i++) {
if (imageUrls[i] != null) {
InputStream profileImgStream = resolver.openInputStream(imageUrls[i]);
if (profileImgStream != null && profileImgStream.available() > 0) {
this.imageStreams[i] = profileImgStream;
} else {
return false;
}
}
}
} catch (IOException exception) {
if (BuildConfig.DEBUG) {
exception.printStackTrace();
}
return false;
}
return true;
return (profileImage == null || profileImage.openStream(resolver)) && (bannerImage == null || bannerImage.openStream(resolver));
}

View File

@ -44,21 +44,32 @@ import java.io.Serializable;
*/
public class ImageViewer extends MediaActivity implements AsyncCallback<ImageLoaderResult>, DescriptionCallback {
public static final int IMAGE_LOCAL = 900;
public static final int GIF_LOCAL = 901;
/**
* mode used to show local image/gif file
* requires {@link #KEY_MEDIA_LOCAL} to be set
*/
public static final int MEDIA_LOCAL = 902;
/**
* mode used to show online image
* requires {@link #KEY_MEDIA_URL} to be set
*/
public static final int IMAGE_ONLINE = 903;
/**
* mode used to show online image
* requires {@link #KEY_MEDIA_ONLINE} to be set
*/
public static final int MEDIA_ONLINE = 904;
/**
* activity result code indicates that {@link MediaStatus} data has been updated
*/
public static final int RETURN_MEDIA_STATUS_UPDATE = 0x5895;
/**
* key to set image format (image or gif)
* value type is Integer {@link #IMAGE_LOCAL,#IMAGE_ONLINE,#GIF_LOCAL,#MEDIA_LOCAL}
* value type is Integer {@link #IMAGE_ONLINE,#GIF_LOCAL,#MEDIA_LOCAL}
*/
public static final String TYPE = "image-type";
@ -130,31 +141,17 @@ public class ImageViewer extends MediaActivity implements AsyncCallback<ImageLoa
cacheFolder = new File(getExternalCacheDir(), ImageViewer.CACHE_FOLDER);
cacheFolder.mkdirs();
mode = getIntent().getIntExtra(TYPE, IMAGE_LOCAL);
mode = getIntent().getIntExtra(TYPE, 0);
switch (mode) {
case IMAGE_LOCAL:
zoomImage.setVisibility(View.VISIBLE);
gifImage.setVisibility(View.INVISIBLE);
Uri data = getIntent().getParcelableExtra(KEY_MEDIA_URL);
zoomImage.setImageURI(data);
break;
case IMAGE_ONLINE:
zoomImage.setVisibility(View.VISIBLE);
gifImage.setVisibility(View.INVISIBLE);
loadingCircle.setVisibility(View.VISIBLE);
data = getIntent().getParcelableExtra(KEY_MEDIA_URL);
Uri data = getIntent().getParcelableExtra(KEY_MEDIA_URL);
ImageLoaderParam request = new ImageLoaderParam(data, cacheFolder);
imageAsync.execute(request, this);
break;
case GIF_LOCAL:
zoomImage.setVisibility(View.INVISIBLE);
gifImage.setVisibility(View.VISIBLE);
data = getIntent().getParcelableExtra(KEY_MEDIA_URL);
gifImage.setImageURI(data);
break;
case MEDIA_LOCAL:
Serializable serializedData = getIntent().getSerializableExtra(KEY_MEDIA_LOCAL);
if (serializedData instanceof MediaStatus) {
@ -178,6 +175,7 @@ public class ImageViewer extends MediaActivity implements AsyncCallback<ImageLoa
case MEDIA_ONLINE:
serializedData = getIntent().getSerializableExtra(KEY_MEDIA_ONLINE);
if (serializedData instanceof Media) {
loadingCircle.setVisibility(View.VISIBLE);
Media media = (Media) serializedData;
if (!media.getBlurHash().isEmpty()) {
Bitmap blur = BlurHashDecoder.INSTANCE.decode(media.getBlurHash(), 16, 16, 1f, true);

View File

@ -21,6 +21,7 @@ import org.nuclearfog.twidda.backend.async.AsyncExecutor.AsyncCallback;
import org.nuclearfog.twidda.backend.async.InstanceLoader;
import org.nuclearfog.twidda.backend.async.MessageUpdater;
import org.nuclearfog.twidda.backend.async.MessageUpdater.MessageUpdateResult;
import org.nuclearfog.twidda.backend.helper.MediaStatus;
import org.nuclearfog.twidda.backend.helper.update.MessageUpdate;
import org.nuclearfog.twidda.backend.utils.AppStyles;
import org.nuclearfog.twidda.backend.utils.ErrorUtils;
@ -30,6 +31,7 @@ import org.nuclearfog.twidda.ui.dialogs.ConfirmDialog.OnConfirmListener;
import org.nuclearfog.twidda.ui.dialogs.ProgressDialog;
import org.nuclearfog.twidda.ui.dialogs.ProgressDialog.OnProgressStopListener;
import java.io.FileNotFoundException;
import java.io.Serializable;
/**
@ -154,10 +156,12 @@ public class MessageEditor extends MediaActivity implements OnClickListener, OnC
@Override
protected void onMediaFetched(int resultType, @NonNull Uri uri) {
if (resultType == REQUEST_IMAGE) {
if (messageUpdate.addMedia(this, uri)) {
try {
MediaStatus mediaStatus = new MediaStatus(getApplicationContext(), uri);
messageUpdate.setMediaUpdate(mediaStatus);
preview.setVisibility(View.VISIBLE);
media.setVisibility(View.GONE);
} else {
} catch (FileNotFoundException exception) {
Toast.makeText(getApplicationContext(), R.string.error_adding_media, Toast.LENGTH_SHORT).show();
}
}

View File

@ -31,6 +31,7 @@ import org.nuclearfog.twidda.R;
import org.nuclearfog.twidda.backend.async.AsyncExecutor.AsyncCallback;
import org.nuclearfog.twidda.backend.async.UserUpdater;
import org.nuclearfog.twidda.backend.async.UserUpdater.UserUpdateResult;
import org.nuclearfog.twidda.backend.helper.MediaStatus;
import org.nuclearfog.twidda.backend.helper.update.ProfileUpdate;
import org.nuclearfog.twidda.backend.image.PicassoBuilder;
import org.nuclearfog.twidda.backend.utils.AppStyles;
@ -42,6 +43,8 @@ import org.nuclearfog.twidda.ui.dialogs.ConfirmDialog;
import org.nuclearfog.twidda.ui.dialogs.ConfirmDialog.OnConfirmListener;
import org.nuclearfog.twidda.ui.dialogs.ProgressDialog;
import java.io.FileNotFoundException;
import jp.wasabeef.picasso.transformations.RoundedCornersTransformation;
/**
@ -75,7 +78,7 @@ public class ProfileEditor extends MediaActivity implements OnClickListener, Asy
@Nullable
private User user;
private ProfileUpdate holder = new ProfileUpdate();
private ProfileUpdate profileUpdate = new ProfileUpdate();
@Override
@ -157,7 +160,7 @@ public class ProfileEditor extends MediaActivity implements OnClickListener, Asy
String userLoc = profileLocation.getText().toString();
String userBio = userDescription.getText().toString();
if (user != null && username.equals(user.getUsername()) && userLink.equals(user.getProfileUrl())
&& userLoc.equals(user.getLocation()) && userBio.equals(user.getDescription()) && !holder.imageAdded()) {
&& userLoc.equals(user.getLocation()) && userBio.equals(user.getDescription()) && !profileUpdate.imageAdded()) {
finish();
} else if (username.isEmpty() && userLink.isEmpty() && userLoc.isEmpty() && userBio.isEmpty()) {
finish();
@ -191,24 +194,23 @@ public class ProfileEditor extends MediaActivity implements OnClickListener, Asy
@Override
protected void onMediaFetched(int resultType, @NonNull Uri uri) {
// Add image as profile image
if (resultType == REQUEST_PROFILE) {
if (holder.setImage(this, uri)) {
try {
MediaStatus mediaStatus = new MediaStatus(getApplicationContext(), uri);
// Add image as profile image
if (resultType == REQUEST_PROFILE) {
profileUpdate.setProfileImage(mediaStatus);
profile_image.setImageURI(uri);
} else {
Toast.makeText(getApplicationContext(), R.string.error_adding_media, Toast.LENGTH_SHORT).show();
}
}
// Add image as banner image
else if (resultType == REQUEST_BANNER) {
if (holder.setBanner(this, uri)) {
// Add image as banner image
else if (resultType == REQUEST_BANNER) {
profileUpdate.setBannerImage(mediaStatus);
int widthPixels = Resources.getSystem().getDisplayMetrics().widthPixels;
picasso.load(uri).resize(widthPixels, widthPixels / 3).centerCrop(Gravity.TOP).into(profile_banner, this);
addBannerBtn.setVisibility(View.INVISIBLE);
changeBannerBtn.setVisibility(View.VISIBLE);
} else {
Toast.makeText(getApplicationContext(), R.string.error_adding_media, Toast.LENGTH_SHORT).show();
}
} catch (FileNotFoundException exception) {
Toast.makeText(getApplicationContext(), R.string.error_adding_media, Toast.LENGTH_SHORT).show();
}
}
@ -290,9 +292,9 @@ public class ProfileEditor extends MediaActivity implements OnClickListener, Asy
String errMsg = getString(R.string.error_invalid_link);
profileUrl.setError(errMsg);
} else {
holder.setProfile(username, userLink, userBio, userLoc);
if (holder.prepare(getContentResolver())) {
editorAsync.execute(holder, this);
profileUpdate.setProfile(username, userLink, userBio, userLoc);
if (profileUpdate.prepare(getContentResolver())) {
editorAsync.execute(profileUpdate, this);
loadingCircle.show();
} else {
Toast.makeText(getApplicationContext(), R.string.error_media_init, Toast.LENGTH_SHORT).show();

View File

@ -751,17 +751,10 @@ public class StatusActivity extends AppCompatActivity implements OnClickListener
audioDialog.show(uri);
break;
case Media.GIF:
case Media.VIDEO:
intent = new Intent(this, VideoViewer.class);
intent.putExtra(VideoViewer.KEY_LINK, uri);
intent.putExtra(VideoViewer.KEY_CONTROLS, true);
startActivity(intent);
break;
case Media.GIF:
intent = new Intent(this, VideoViewer.class);
intent.putExtra(VideoViewer.KEY_LINK, uri);
intent.putExtra(VideoViewer.KEY_CONTROLS, false);
intent.putExtra(VideoViewer.KEY_VIDEO_ONLINE, media);
startActivity(intent);
break;
}

View File

@ -51,6 +51,7 @@ import org.nuclearfog.twidda.ui.dialogs.ProgressDialog;
import org.nuclearfog.twidda.ui.dialogs.ProgressDialog.OnProgressStopListener;
import org.nuclearfog.twidda.ui.dialogs.StatusPreferenceDialog;
import java.io.FileNotFoundException;
import java.io.Serializable;
/**
@ -248,11 +249,21 @@ public class StatusEditor extends MediaActivity implements ActivityResultCallbac
@Override
public void onActivityResult(ActivityResult result) {
if (result.getResultCode() == ImageViewer.RETURN_MEDIA_STATUS_UPDATE && result.getData() != null) {
Serializable data = result.getData().getSerializableExtra(ImageViewer.KEY_MEDIA_LOCAL);
if (data instanceof MediaStatus) {
MediaStatus mediaStatus = (MediaStatus) data;
statusUpdate.updateMediaStatus(mediaStatus);
if (result.getResultCode() == ImageViewer.RETURN_MEDIA_STATUS_UPDATE) {
if (result.getData() != null) {
Serializable data = result.getData().getSerializableExtra(ImageViewer.KEY_MEDIA_LOCAL);
if (data instanceof MediaStatus) {
MediaStatus mediaStatus = (MediaStatus) data;
statusUpdate.updateMediaStatus(mediaStatus);
}
}
} else if (result.getResultCode() == VideoViewer.RETURN_VIDEO_UPDATE) {
if (result.getData() != null) {
Serializable data = result.getData().getSerializableExtra(VideoViewer.KEY_VIDEO_LOCAL);
if (data instanceof MediaStatus) {
MediaStatus mediaStatus = (MediaStatus) data;
statusUpdate.updateMediaStatus(mediaStatus);
}
}
} else {
super.onActivityResult(result);
@ -356,9 +367,13 @@ public class StatusEditor extends MediaActivity implements ActivityResultCallbac
@Override
protected void onMediaFetched(int resultType, @NonNull Uri uri) {
MediaStatus mediaStatus = new MediaStatus(getApplicationContext(), uri, "");
int mediaType = statusUpdate.addMedia(mediaStatus);
addMedia(mediaType);
try {
MediaStatus mediaStatus = new MediaStatus(getApplicationContext(), uri);
int mediaType = statusUpdate.addMedia(mediaStatus);
addMedia(mediaType);
} catch (FileNotFoundException e) {
Toast.makeText(getApplicationContext(), R.string.error_adding_media, Toast.LENGTH_SHORT).show();
}
}
@ -386,8 +401,6 @@ public class StatusEditor extends MediaActivity implements ActivityResultCallbac
if (statusUpdate.getMediaStatuses().isEmpty())
return;
MediaStatus media = statusUpdate.getMediaStatuses().get(index);
if (media.getPath() == null)
return;
switch (media.getMediaType()) {
case MediaStatus.PHOTO:
case MediaStatus.GIF:
@ -399,13 +412,13 @@ public class StatusEditor extends MediaActivity implements ActivityResultCallbac
case MediaStatus.VIDEO:
intent = new Intent(this, VideoViewer.class);
intent.putExtra(VideoViewer.KEY_LINK, Uri.parse(media.getPath()));
intent.putExtra(VideoViewer.KEY_CONTROLS, true);
startActivity(intent);
intent.putExtra(VideoViewer.KEY_VIDEO_LOCAL, media);
activityResultLauncher.launch(intent);
break;
case MediaStatus.AUDIO:
audioDialog.show(Uri.parse(media.getPath()));
if (media.getPath() != null)
audioDialog.show(Uri.parse(media.getPath()));
break;
}
}

View File

@ -1,6 +1,7 @@
package org.nuclearfog.twidda.ui.activities;
import android.content.Context;
import android.content.Intent;
import android.content.res.Configuration;
import android.graphics.Color;
import android.net.Uri;
@ -39,9 +40,15 @@ import com.google.android.exoplayer2.video.MediaCodecVideoRenderer;
import com.google.android.exoplayer2.video.VideoRendererEventListener;
import org.nuclearfog.twidda.R;
import org.nuclearfog.twidda.backend.helper.MediaStatus;
import org.nuclearfog.twidda.backend.utils.AppStyles;
import org.nuclearfog.twidda.backend.utils.ConnectionBuilder;
import org.nuclearfog.twidda.backend.utils.LinkUtils;
import org.nuclearfog.twidda.model.Media;
import org.nuclearfog.twidda.ui.dialogs.DescriptionDialog;
import org.nuclearfog.twidda.ui.dialogs.DescriptionDialog.DescriptionCallback;
import java.io.Serializable;
import okhttp3.Call;
@ -50,19 +57,13 @@ import okhttp3.Call;
*
* @author nuclearfog
*/
public class VideoViewer extends AppCompatActivity implements Player.Listener {
public class VideoViewer extends AppCompatActivity implements Player.Listener, DescriptionCallback {
/**
* key for an Uri array with local links
* value type is {@link Uri}
*/
public static final String KEY_LINK = "media_uri";
public static final String KEY_VIDEO_ONLINE = "media-video";
/**
* Key to enable extra layouts for a video
* value type is Boolean
*/
public static final String KEY_CONTROLS = "enable_controls";
public static final String KEY_VIDEO_LOCAL = "video-media-status";
public static final int RETURN_VIDEO_UPDATE = 0x1528;
/**
* online video cache size
@ -72,9 +73,13 @@ public class VideoViewer extends AppCompatActivity implements Player.Listener {
private Toolbar toolbar;
private StyledPlayerView playerView;
private ExoPlayer player;
private DescriptionDialog descriptionDialog;
private Uri data;
private ExoPlayer player;
@Nullable
private MediaStatus mediaStatus;
@Nullable
private Media media;
@Override
@ -105,39 +110,49 @@ public class VideoViewer extends AppCompatActivity implements Player.Listener {
};
}
};
descriptionDialog = new DescriptionDialog(this, this);
player = new ExoPlayer.Builder(this, renderersFactory).build();
player.addListener(this);
data = getIntent().getParcelableExtra(KEY_LINK);
boolean enableControls = getIntent().getBooleanExtra(KEY_CONTROLS, true);
if (!enableControls) {
playerView.setUseController(false);
player.setRepeatMode(Player.REPEAT_MODE_ONE);
}
MediaItem mediaItem;
DataSource.Factory dataSourceFactory;
MediaItem mediaItem = MediaItem.fromUri(data);
// initialize online source
if (data.getScheme().startsWith("http")) {
// configure with okhttp connection of the app
Serializable serializedData = getIntent().getSerializableExtra(KEY_VIDEO_ONLINE);
// check if video is online
if (serializedData instanceof Media) {
this.media = (Media) serializedData;
mediaItem = MediaItem.fromUri(media.getUrl());
dataSourceFactory = new OkHttpDataSource.Factory((Call.Factory) ConnectionBuilder.create(this, CACHE_SIZE));
if (media.getMediaType() != Media.VIDEO) {
playerView.setUseController(false);
player.setRepeatMode(Player.REPEAT_MODE_ONE);
}
}
// initialize local source
// check if viceo is local
else {
toolbar.setVisibility(View.GONE);
dataSourceFactory = new DataSource.Factory() {
@NonNull
@Override
public DataSource createDataSource() {
return new ContentDataSource(getApplicationContext());
serializedData = getIntent().getSerializableExtra(KEY_VIDEO_LOCAL);
if (serializedData instanceof MediaStatus) {
this.mediaStatus = (MediaStatus) serializedData;
mediaItem = MediaItem.fromUri(mediaStatus.getPath());
dataSourceFactory = new DataSource.Factory() {
@NonNull
@Override
public DataSource createDataSource() {
return new ContentDataSource(getApplicationContext());
}
};
if (mediaStatus.getMediaType() != MediaStatus.VIDEO) {
playerView.setUseController(false);
player.setRepeatMode(Player.REPEAT_MODE_ONE);
}
};
} else {
return;
}
}
// initialize video extractor
MediaSource mediaSource = new ProgressiveMediaSource.Factory(dataSourceFactory, new DefaultExtractorsFactory()).createMediaSource(mediaItem);
player.setMediaSource(mediaSource);
playerView.setPlayer(player);
// prepare playback
player.prepare();
player.setPlayWhenReady(true);
}
@ -154,8 +169,13 @@ public class VideoViewer extends AppCompatActivity implements Player.Listener {
@Override
public void onBackPressed() {
super.onBackPressed();
if (mediaStatus != null) {
Intent intent = new Intent();
intent.putExtra(KEY_VIDEO_LOCAL, mediaStatus);
setResult(RETURN_VIDEO_UPDATE, intent);
}
player.stop();
super.onBackPressed();
}
@ -183,7 +203,11 @@ public class VideoViewer extends AppCompatActivity implements Player.Listener {
@Override
public boolean onCreateOptionsMenu(Menu menu) {
getMenuInflater().inflate(R.menu.video, menu);
MenuItem menuOpenLink = menu.findItem(R.id.menu_video_link);
MenuItem menuDescription = menu.findItem(R.id.menu_video_add_description);
AppStyles.setMenuIconColor(menu, Color.WHITE);
menuOpenLink.setVisible(media != null);
menuDescription.setVisible(mediaStatus != null);
return super.onCreateOptionsMenu(menu);
}
@ -191,14 +215,26 @@ public class VideoViewer extends AppCompatActivity implements Player.Listener {
@Override
public boolean onOptionsItemSelected(@NonNull MenuItem item) {
if (item.getItemId() == R.id.menu_video_link) {
if (data != null) {
LinkUtils.openMediaLink(this, data);
if (media != null) {
LinkUtils.openMediaLink(this, Uri.parse(media.getUrl()));
}
}
//
else if (item.getItemId() == R.id.menu_video_add_description) {
descriptionDialog.show();
}
return super.onOptionsItemSelected(item);
}
@Override
public void onDescriptionSet(String description) {
if (mediaStatus != null) {
mediaStatus.setDescription(description);
}
}
@Override
public void onPlayerError(PlaybackException error) {
Toast.makeText(getApplicationContext(), "ExoPlayer: " + error.getErrorCodeName(), Toast.LENGTH_SHORT).show();