split mediaviewer into separate activities, bug fix

This commit is contained in:
nuclearfog 2022-01-21 20:40:21 +01:00
parent 66e6189370
commit c9e20cbc42
No known key found for this signature in database
GPG Key ID: AA0271FBE406DB98
16 changed files with 388 additions and 335 deletions

View File

@ -104,7 +104,12 @@
android:theme="@style/AppTheme" />
<activity
android:name=".activities.MediaViewer"
android:name=".activities.VideoViewer"
android:screenOrientation="portrait"
android:theme="@style/Transparency" />
<activity
android:name=".activities.ImageViewer"
android:screenOrientation="portrait"
android:theme="@style/Transparency" />

View File

@ -7,6 +7,7 @@ import androidx.appcompat.app.AppCompatDelegate;
import org.nuclearfog.twidda.backend.proxy.GlobalProxySelector;
import org.nuclearfog.twidda.backend.proxy.ProxyAuthenticator;
import org.nuclearfog.twidda.backend.utils.TLSSocketFactory;
import org.nuclearfog.twidda.database.GlobalSettings;
import java.net.Authenticator;
@ -26,6 +27,8 @@ public class CompatApplication extends Application {
if (Build.VERSION.SDK_INT < Build.VERSION_CODES.LOLLIPOP) {
AppCompatDelegate.setCompatVectorFromResourcesEnabled(true);
}
// enable TLS 1.2 support
TLSSocketFactory.setSupportTLS();
// setup proxy settings
GlobalSettings settings = GlobalSettings.getInstance(this);
GlobalProxySelector proxyConnection = new GlobalProxySelector(settings);

View File

@ -0,0 +1,175 @@
package org.nuclearfog.twidda.activities;
import static android.os.AsyncTask.Status.RUNNING;
import static android.view.View.INVISIBLE;
import static androidx.recyclerview.widget.LinearLayoutManager.HORIZONTAL;
import android.location.Location;
import android.net.Uri;
import android.os.Bundle;
import android.os.Parcelable;
import android.widget.ProgressBar;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import androidx.recyclerview.widget.LinearLayoutManager;
import androidx.recyclerview.widget.RecyclerView;
import org.nuclearfog.twidda.R;
import org.nuclearfog.twidda.adapter.ImageAdapter;
import org.nuclearfog.twidda.backend.ImageLoader;
import org.nuclearfog.twidda.backend.utils.AppStyles;
import org.nuclearfog.twidda.backend.utils.ErrorHandler;
import org.nuclearfog.twidda.database.GlobalSettings;
import org.nuclearfog.zoomview.ZoomView;
import java.io.File;
/**
* Activity to show online of local images
*
* @author nuclearfog
*/
public class ImageViewer extends MediaActivity implements ImageAdapter.OnImageClickListener {
/**
* key to add URI of the image (online or local)
*/
public static final String IMAGE_URIS = "image-uri";
/**
* key to define where the images are located (online or local)
*/
public static final String IMAGE_DOWNLOAD = "image-download";
/**
* name of the cache folder where online images will be stored
* after the end of this activity this folder will be cleared
*/
private static final String CACHE_FOLDER = "imagecache";
@Nullable
private ImageLoader imageAsync;
private ImageAdapter adapter;
private File cacheFolder;
private ZoomView zoomImage;
private ProgressBar loadingCircle;
@Override
protected void onCreate(@Nullable Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.page_image);
loadingCircle = findViewById(R.id.media_progress);
zoomImage = findViewById(R.id.image_full);
RecyclerView imageList = findViewById(R.id.image_list);
GlobalSettings settings = GlobalSettings.getInstance(this);
AppStyles.setProgressColor(loadingCircle, settings.getHighlightColor());
cacheFolder = new File(getExternalCacheDir(), ImageViewer.CACHE_FOLDER);
cacheFolder.mkdirs();
adapter = new ImageAdapter(getApplicationContext(), this);
imageList.setLayoutManager(new LinearLayoutManager(this, HORIZONTAL, false));
imageList.setAdapter(adapter);
Parcelable[] links = getIntent().getParcelableArrayExtra(IMAGE_URIS);
boolean online = getIntent().getBooleanExtra(IMAGE_DOWNLOAD, true);
Uri[] uris = {null};
if (links != null) {
uris = new Uri[links.length];
for (int i = 0 ; i < uris.length ; i++) {
uris[i] = (Uri) links[i];
}
}
if (online) {
imageAsync = new ImageLoader(this, cacheFolder);
imageAsync.execute(uris);
} else {
adapter.addAll(uris);
adapter.disableSaveButton();
zoomImage.setImageURI(uris[0]);
loadingCircle.setVisibility(INVISIBLE);
}
}
@Override
protected void onDestroy() {
if (imageAsync != null && imageAsync.getStatus() == RUNNING)
imageAsync.cancel(true);
clearCache();
super.onDestroy();
}
@Override
public void onImageClick(Uri uri) {
zoomImage.reset();
zoomImage.setImageURI(uri);
}
@Override
public void onImageSave(Uri uri) {
storeImage(uri);
}
@Override
protected void onAttachLocation(@Nullable Location location) {
}
@Override
protected void onMediaFetched(int resultType, @NonNull Uri uri) {
}
/**
* set downloaded image into preview list
*
* @param imageUri Image Uri
*/
public void setImage(Uri imageUri) {
if (adapter.isEmpty()) {
zoomImage.reset();
zoomImage.setImageURI(imageUri);
loadingCircle.setVisibility(INVISIBLE);
}
adapter.addLast(imageUri);
}
/**
* Called from {@link ImageLoader} when all images are downloaded successfully
*/
public void onSuccess() {
adapter.disableLoading();
}
/**
* Called from {@link ImageLoader} when an error occurs
*
* @param err Exception caught by {@link ImageLoader}
*/
public void onError(ErrorHandler.TwitterError err) {
ErrorHandler.handleFailure(getApplicationContext(), err);
finish();
}
/**
* clear the image cache
*/
private void clearCache() {
try {
File[] files = cacheFolder.listFiles();
if (files != null && files.length > 0) {
for (File file : files) {
file.delete();
}
}
} catch (SecurityException e) {
e.printStackTrace();
}
}
}

View File

@ -1,13 +1,8 @@
package org.nuclearfog.twidda.activities;
import static android.os.AsyncTask.Status.RUNNING;
import static android.view.View.GONE;
import static android.view.View.OnClickListener;
import static android.view.View.VISIBLE;
import static android.view.View.*;
import static android.widget.Toast.LENGTH_SHORT;
import static org.nuclearfog.twidda.activities.MediaViewer.KEY_MEDIA_TYPE;
import static org.nuclearfog.twidda.activities.MediaViewer.KEY_MEDIA_URI;
import static org.nuclearfog.twidda.activities.MediaViewer.MEDIAVIEWER_IMAGE;
import android.app.Dialog;
import android.content.Context;
@ -143,9 +138,9 @@ public class MessageEditor extends MediaActivity implements OnClickListener, OnC
}
// open media
else if (v.getId() == R.id.dm_preview) {
Intent image = new Intent(this, MediaViewer.class);
image.putExtra(KEY_MEDIA_URI, new Uri[]{mediaUri});
image.putExtra(KEY_MEDIA_TYPE, MEDIAVIEWER_IMAGE);
Intent image = new Intent(this, ImageViewer.class);
image.putExtra(ImageViewer.IMAGE_URIS, new Uri[]{mediaUri});
image.putExtra(ImageViewer.IMAGE_DOWNLOAD, false);
startActivity(image);
}
}

View File

@ -1,29 +1,12 @@
package org.nuclearfog.twidda.activities;
import static android.os.AsyncTask.Status.RUNNING;
import static android.view.View.GONE;
import static android.view.View.VISIBLE;
import static android.view.View.*;
import static android.widget.Toast.LENGTH_SHORT;
import static org.nuclearfog.twidda.activities.MediaViewer.KEY_MEDIA_TYPE;
import static org.nuclearfog.twidda.activities.MediaViewer.KEY_MEDIA_URI;
import static org.nuclearfog.twidda.activities.MediaViewer.MEDIAVIEWER_ANGIF;
import static org.nuclearfog.twidda.activities.MediaViewer.MEDIAVIEWER_IMAGE;
import static org.nuclearfog.twidda.activities.MediaViewer.MEDIAVIEWER_VIDEO;
import static org.nuclearfog.twidda.activities.SearchPage.KEY_SEARCH_QUERY;
import static org.nuclearfog.twidda.activities.TweetEditor.KEY_TWEETPOPUP_REPLYID;
import static org.nuclearfog.twidda.activities.TweetEditor.KEY_TWEETPOPUP_TEXT;
import static org.nuclearfog.twidda.activities.UserDetail.KEY_USERDETAIL_ID;
import static org.nuclearfog.twidda.activities.UserDetail.KEY_USERDETAIL_MODE;
import static org.nuclearfog.twidda.activities.UserDetail.USERLIST_FAVORIT;
import static org.nuclearfog.twidda.activities.UserDetail.USERLIST_RETWEETS;
import static org.nuclearfog.twidda.fragments.TweetFragment.INTENT_TWEET_REMOVED_ID;
import static org.nuclearfog.twidda.fragments.TweetFragment.INTENT_TWEET_UPDATE_DATA;
import static org.nuclearfog.twidda.fragments.TweetFragment.KEY_FRAG_TWEET_ID;
import static org.nuclearfog.twidda.fragments.TweetFragment.KEY_FRAG_TWEET_MODE;
import static org.nuclearfog.twidda.fragments.TweetFragment.KEY_FRAG_TWEET_SEARCH;
import static org.nuclearfog.twidda.fragments.TweetFragment.RETURN_TWEET_NOT_FOUND;
import static org.nuclearfog.twidda.fragments.TweetFragment.RETURN_TWEET_UPDATE;
import static org.nuclearfog.twidda.fragments.TweetFragment.TWEET_FRAG_ANSWER;
import static org.nuclearfog.twidda.activities.SearchPage.*;
import static org.nuclearfog.twidda.activities.TweetEditor.*;
import static org.nuclearfog.twidda.activities.UserDetail.*;
import static org.nuclearfog.twidda.fragments.TweetFragment.*;
import android.app.Dialog;
import android.content.ActivityNotFoundException;
@ -368,23 +351,28 @@ public class TweetActivity extends AppCompatActivity implements OnClickListener,
}
// open tweet media
else if (v.getId() == R.id.tweet_media_attach) {
// convert links to uri
Intent mediaIntent = new Intent(this, MediaViewer.class);
mediaIntent.putExtra(KEY_MEDIA_URI, clickedTweet.getMediaLinks());
switch (clickedTweet.getMediaType()) {
case Tweet.MEDIA_PHOTO:
mediaIntent.putExtra(KEY_MEDIA_TYPE, MEDIAVIEWER_IMAGE);
break;
case Tweet.MEDIA_VIDEO:
mediaIntent.putExtra(KEY_MEDIA_TYPE, MEDIAVIEWER_VIDEO);
break;
case Tweet.MEDIA_GIF:
mediaIntent.putExtra(KEY_MEDIA_TYPE, MEDIAVIEWER_ANGIF);
break;
if (clickedTweet.getMediaType().equals(Tweet.MEDIA_PHOTO)) {
Intent mediaIntent = new Intent(this, ImageViewer.class);
mediaIntent.putExtra(ImageViewer.IMAGE_URIS, clickedTweet.getMediaLinks());
mediaIntent.putExtra(ImageViewer.IMAGE_DOWNLOAD, true);
startActivity(mediaIntent);
}
//
else if (clickedTweet.getMediaType().equals(Tweet.MEDIA_VIDEO)) {
Uri link = clickedTweet.getMediaLinks()[0];
Intent mediaIntent = new Intent(this, VideoViewer.class);
mediaIntent.putExtra(VideoViewer.VIDEO_URI, link);
mediaIntent.putExtra(VideoViewer.ENABLE_VIDEO_CONTROLS, true);
startActivity(mediaIntent);
}
//
else if (clickedTweet.getMediaType().equals(Tweet.MEDIA_GIF)) {
Uri link = clickedTweet.getMediaLinks()[0];
Intent mediaIntent = new Intent(this, VideoViewer.class);
mediaIntent.putExtra(VideoViewer.VIDEO_URI, link);
mediaIntent.putExtra(VideoViewer.ENABLE_VIDEO_CONTROLS, false);
startActivity(mediaIntent);
}
startActivity(mediaIntent);
}
// go to user retweeting this tweet
else if (v.getId() == R.id.tweet_retweeter_reference) {

View File

@ -1,15 +1,8 @@
package org.nuclearfog.twidda.activities;
import static android.os.AsyncTask.Status.RUNNING;
import static android.view.View.GONE;
import static android.view.View.INVISIBLE;
import static android.view.View.VISIBLE;
import static android.widget.Toast.LENGTH_LONG;
import static android.widget.Toast.LENGTH_SHORT;
import static org.nuclearfog.twidda.activities.MediaViewer.KEY_MEDIA_TYPE;
import static org.nuclearfog.twidda.activities.MediaViewer.KEY_MEDIA_URI;
import static org.nuclearfog.twidda.activities.MediaViewer.MEDIAVIEWER_IMAGE;
import static org.nuclearfog.twidda.activities.MediaViewer.MEDIAVIEWER_VIDEO;
import static android.os.AsyncTask.Status.*;
import static android.view.View.*;
import static android.widget.Toast.*;
import android.app.Dialog;
import android.content.Context;
@ -215,20 +208,29 @@ public class TweetEditor extends MediaActivity implements OnClickListener, OnPro
}
// open media preview
else if (v.getId() == R.id.tweet_prev_media) {
Intent mediaViewer = new Intent(this, MediaViewer.class);
Uri[] uris = tweetUpdate.getMediaUris();
mediaViewer.putExtra(KEY_MEDIA_URI, uris);
//
if (selectedFormat == MEDIA_VIDEO) {
mediaViewer.putExtra(KEY_MEDIA_TYPE, MEDIAVIEWER_VIDEO);
} else if (selectedFormat == MEDIA_IMAGE) {
mediaViewer.putExtra(KEY_MEDIA_TYPE, MEDIAVIEWER_IMAGE);
} else if (selectedFormat == MEDIA_GIF) {
// todo add support for local gif animation
mediaViewer.putExtra(KEY_MEDIA_TYPE, MEDIAVIEWER_IMAGE);
} else {
return;
Intent mediaViewer = new Intent(this, VideoViewer.class);
mediaViewer.putExtra(VideoViewer.VIDEO_URI, uris[0]);
mediaViewer.putExtra(VideoViewer.ENABLE_VIDEO_CONTROLS, true);
startActivity(mediaViewer);
}
//
else if (selectedFormat == MEDIA_IMAGE) {
Intent mediaViewer = new Intent(this, ImageViewer.class);
mediaViewer.putExtra(ImageViewer.IMAGE_URIS, uris);
mediaViewer.putExtra(ImageViewer.IMAGE_DOWNLOAD, false);
startActivity(mediaViewer);
}
//
else if (selectedFormat == MEDIA_GIF) {
// todo add support for local gif animation
Intent mediaViewer = new Intent(this, ImageViewer.class);
mediaViewer.putExtra(ImageViewer.IMAGE_URIS, uris);
mediaViewer.putExtra(ImageViewer.IMAGE_DOWNLOAD, false);
startActivity(mediaViewer);
}
startActivity(mediaViewer);
}
// add location to the tweet
else if (v.getId() == R.id.tweet_add_location) {

View File

@ -2,36 +2,18 @@ package org.nuclearfog.twidda.activities;
import static android.content.Intent.ACTION_VIEW;
import static android.os.AsyncTask.Status.RUNNING;
import static android.view.View.GONE;
import static android.view.View.OnClickListener;
import static android.view.View.VISIBLE;
import static android.view.View.*;
import static android.widget.Toast.LENGTH_SHORT;
import static org.nuclearfog.twidda.activities.MediaViewer.KEY_MEDIA_TYPE;
import static org.nuclearfog.twidda.activities.MediaViewer.KEY_MEDIA_URI;
import static org.nuclearfog.twidda.activities.MediaViewer.MEDIAVIEWER_IMAGE;
import static org.nuclearfog.twidda.activities.MessageEditor.KEY_DM_PREFIX;
import static org.nuclearfog.twidda.activities.ProfileEditor.KEY_USER_DATA;
import static org.nuclearfog.twidda.activities.SearchPage.KEY_SEARCH_QUERY;
import static org.nuclearfog.twidda.activities.TweetActivity.KEY_TWEET_ID;
import static org.nuclearfog.twidda.activities.TweetActivity.KEY_TWEET_NAME;
import static org.nuclearfog.twidda.activities.TweetActivity.LINK_PATTERN;
import static org.nuclearfog.twidda.activities.TweetActivity.*;
import static org.nuclearfog.twidda.activities.TweetEditor.KEY_TWEETPOPUP_TEXT;
import static org.nuclearfog.twidda.activities.UserDetail.KEY_USERDETAIL_ID;
import static org.nuclearfog.twidda.activities.UserDetail.KEY_USERDETAIL_MODE;
import static org.nuclearfog.twidda.activities.UserDetail.USERLIST_FOLLOWER;
import static org.nuclearfog.twidda.activities.UserDetail.USERLIST_FRIENDS;
import static org.nuclearfog.twidda.activities.UserDetail.*;
import static org.nuclearfog.twidda.activities.UserLists.KEY_USERLIST_OWNER_ID;
import static org.nuclearfog.twidda.backend.UserAction.Action.ACTION_BLOCK;
import static org.nuclearfog.twidda.backend.UserAction.Action.ACTION_FOLLOW;
import static org.nuclearfog.twidda.backend.UserAction.Action.ACTION_MUTE;
import static org.nuclearfog.twidda.backend.UserAction.Action.ACTION_UNBLOCK;
import static org.nuclearfog.twidda.backend.UserAction.Action.ACTION_UNFOLLOW;
import static org.nuclearfog.twidda.backend.UserAction.Action.ACTION_UNMUTE;
import static org.nuclearfog.twidda.backend.UserAction.Action.PROFILE_DB;
import static org.nuclearfog.twidda.backend.UserAction.Action.PROFILE_lOAD;
import static org.nuclearfog.twidda.backend.UserAction.Action.*;
import static org.nuclearfog.twidda.database.GlobalSettings.PROFILE_IMG_HIGH_RES;
import static org.nuclearfog.twidda.fragments.UserFragment.KEY_USER_UPDATE;
import static org.nuclearfog.twidda.fragments.UserFragment.RETURN_USER_UPDATED;
import static org.nuclearfog.twidda.fragments.UserFragment.*;
import android.app.Dialog;
import android.content.ActivityNotFoundException;
@ -522,21 +504,21 @@ public class UserProfile extends AppCompatActivity implements OnClickListener, O
// open profile image
else if (v.getId() == R.id.profile_img) {
if (user != null && !user.getImageUrl().isEmpty()) {
Uri[] link = {Uri.parse(user.getImageUrl())};
Intent mediaImage = new Intent(this, MediaViewer.class);
mediaImage.putExtra(KEY_MEDIA_URI, link);
mediaImage.putExtra(KEY_MEDIA_TYPE, MEDIAVIEWER_IMAGE);
startActivity(mediaImage);
Uri[] uris = {Uri.parse(user.getImageUrl())};
Intent imageIntent = new Intent(this, ImageViewer.class);
imageIntent.putExtra(ImageViewer.IMAGE_URIS, uris);
imageIntent.putExtra(ImageViewer.IMAGE_DOWNLOAD, true);
startActivity(imageIntent);
}
}
// open banner image
else if (v.getId() == R.id.profile_banner) {
if (user != null && !user.getBannerUrl().isEmpty()) {
Uri[] link = {Uri.parse(user.getBannerUrl())};
Intent mediaBanner = new Intent(this, MediaViewer.class);
mediaBanner.putExtra(KEY_MEDIA_URI, link);
mediaBanner.putExtra(KEY_MEDIA_TYPE, MEDIAVIEWER_IMAGE);
startActivity(mediaBanner);
Uri[] uris = {Uri.parse(user.getBannerUrl())};
Intent imageIntent = new Intent(this, ImageViewer.class);
imageIntent.putExtra(ImageViewer.IMAGE_URIS, uris);
imageIntent.putExtra(ImageViewer.IMAGE_DOWNLOAD, true);
startActivity(imageIntent);
}
}
}

View File

@ -8,7 +8,6 @@ import static android.media.MediaPlayer.OnCompletionListener;
import static android.media.MediaPlayer.OnErrorListener;
import static android.media.MediaPlayer.OnInfoListener;
import static android.media.MediaPlayer.OnPreparedListener;
import static android.os.AsyncTask.Status.RUNNING;
import static android.view.MotionEvent.ACTION_DOWN;
import static android.view.MotionEvent.ACTION_UP;
import static android.view.View.GONE;
@ -17,7 +16,6 @@ import static android.view.View.OnClickListener;
import static android.view.View.OnTouchListener;
import static android.view.View.VISIBLE;
import static android.widget.Toast.LENGTH_SHORT;
import static androidx.recyclerview.widget.LinearLayoutManager.HORIZONTAL;
import android.content.ActivityNotFoundException;
import android.content.Context;
@ -27,7 +25,6 @@ import android.location.Location;
import android.media.MediaPlayer;
import android.net.Uri;
import android.os.Bundle;
import android.os.Parcelable;
import android.view.MotionEvent;
import android.view.View;
import android.view.ViewGroup;
@ -41,60 +38,55 @@ import android.widget.VideoView;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import androidx.recyclerview.widget.LinearLayoutManager;
import androidx.recyclerview.widget.RecyclerView;
import org.nuclearfog.twidda.R;
import org.nuclearfog.twidda.adapter.ImageAdapter;
import org.nuclearfog.twidda.adapter.ImageAdapter.OnImageClickListener;
import org.nuclearfog.twidda.backend.ImageLoader;
import org.nuclearfog.twidda.backend.SeekbarUpdater;
import org.nuclearfog.twidda.backend.utils.AppStyles;
import org.nuclearfog.twidda.backend.utils.ErrorHandler;
import org.nuclearfog.twidda.backend.utils.StringTools;
import org.nuclearfog.twidda.database.GlobalSettings;
import org.nuclearfog.zoomview.ZoomView;
import java.io.File;
/**
* Media viewer activity for images and videos
*
* @author nuclearfog
*/
public class MediaViewer extends MediaActivity implements OnImageClickListener, OnSeekBarChangeListener, OnCompletionListener,
public class VideoViewer extends MediaActivity implements OnSeekBarChangeListener, OnCompletionListener,
OnPreparedListener, OnInfoListener, OnErrorListener, OnClickListener, OnTouchListener {
/**
* key for an Uri array with local links
*/
public static final String KEY_MEDIA_URI = "media_uri";
public static final String VIDEO_URI = "media_uri";
/**
* Key for the media type, required
* {@link #MEDIAVIEWER_IMAGE}, {@link #MEDIAVIEWER_VIDEO} or {@link #MEDIAVIEWER_ANGIF}
* Key to enable extra layouts for a video
*/
public static final String KEY_MEDIA_TYPE = "media_type";
public static final String ENABLE_VIDEO_CONTROLS = "enable_controls";
/**
* cache folder name
* playback status marks that the player isn't initialized yet
*/
public static final String CACHE_FOLDER = "imagecache";
private static final int IDLE = -1;
/**
* value for {@link #KEY_MEDIA_TYPE} to show images
* playback status marks that the player currently plays a video
*/
public static final int MEDIAVIEWER_IMAGE = 0x997BCDCE;
private static final int PLAY = 1;
/**
* value for {@link #KEY_MEDIA_TYPE} to show a video
* playback status marks that the player has been paused
*/
public static final int MEDIAVIEWER_VIDEO = 0x500C9A42;
private static final int PAUSE = 2;
/**
* value for {@link #KEY_MEDIA_TYPE} to show an animated image
* playback status marks that the player is fast forwarding
*/
public static final int MEDIAVIEWER_ANGIF = 0x6500EDB0;
private static final int FORWARD = 3;
/**
* playback status marks that the player is fast backwarding
*/
private static final int BACKWARD = 4;
/**
* refresh time for video progress updatein milliseconds
@ -106,34 +98,18 @@ public class MediaViewer extends MediaActivity implements OnImageClickListener,
*/
private static final int SPEED_FACTOR = 6;
/**
* video play status
*/
private enum PlayStat {
PLAY,
PAUSE,
FORWARD,
BACKWARD,
IDLE
}
@Nullable
private ImageLoader imageAsync;
private SeekbarUpdater seekUpdate;
private TextView duration, position;
private ProgressBar loadingCircle;
private SeekBar video_progress;
private ImageButton play, pause;
private ImageAdapter adapter;
private VideoView videoView;
private ZoomView zoomImage;
private ViewGroup controlPanel;
private Uri[] mediaLinks;
private int type;
private PlayStat playStat = PlayStat.IDLE;
private boolean enableVideoExtras;
private int playstatus = IDLE;
@Override
@ -145,12 +121,9 @@ public class MediaViewer extends MediaActivity implements OnImageClickListener,
@Override
protected void onCreate(@Nullable Bundle b) {
super.onCreate(b);
setContentView(R.layout.page_media);
RecyclerView imageList = findViewById(R.id.image_list);
View imageListContainer = findViewById(R.id.image_preview_list);
setContentView(R.layout.page_video);
controlPanel = findViewById(R.id.media_controlpanel);
loadingCircle = findViewById(R.id.media_progress);
zoomImage = findViewById(R.id.image_full);
videoView = findViewById(R.id.video_view);
video_progress = controlPanel.findViewById(R.id.controller_progress);
play = controlPanel.findViewById(R.id.controller_play);
@ -170,50 +143,27 @@ public class MediaViewer extends MediaActivity implements OnImageClickListener,
GlobalSettings settings = GlobalSettings.getInstance(this);
AppStyles.setProgressColor(loadingCircle, settings.getHighlightColor());
AppStyles.setTheme(controlPanel, settings.getBackgroundColor());
adapter = new ImageAdapter(getApplicationContext(), this);
videoView.setZOrderMediaOverlay(true); // disable black background
videoView.getHolder().setFormat(PixelFormat.TRANSPARENT);
// get extras
type = getIntent().getIntExtra(KEY_MEDIA_TYPE, 0);
Parcelable[] links = getIntent().getParcelableArrayExtra(KEY_MEDIA_URI);
enableVideoExtras = getIntent().getBooleanExtra(ENABLE_VIDEO_CONTROLS, false);
Uri link = getIntent().getParcelableExtra(VIDEO_URI);
// init media view
if (links != null) {
mediaLinks = new Uri[links.length];
for (int i = 0; i < mediaLinks.length; i++) {
mediaLinks[i] = (Uri) links[i];
}
switch (type) {
case MEDIAVIEWER_IMAGE:
zoomImage.setVisibility(VISIBLE);
imageListContainer.setVisibility(VISIBLE);
if (!mediaLinks[0].getScheme().startsWith("http")) {
adapter.disableSaveButton();
for (Uri uri : mediaLinks)
setImage(uri);
adapter.disableLoading();
} else {
imageAsync = new ImageLoader(this);
imageAsync.execute(mediaLinks);
}
imageList.setLayoutManager(new LinearLayoutManager(this, HORIZONTAL, false));
imageList.setAdapter(adapter);
break;
case MEDIAVIEWER_VIDEO:
controlPanel.setVisibility(VISIBLE);
if (!mediaLinks[0].getScheme().startsWith("http"))
share.setVisibility(GONE); // local image
seekUpdate = new SeekbarUpdater(this, PROGRESS_UPDATE);
// fall through
case MEDIAVIEWER_ANGIF:
videoView.setVisibility(VISIBLE);
videoView.setZOrderMediaOverlay(true); // disable black background
videoView.getHolder().setFormat(PixelFormat.TRANSPARENT);
videoView.setVideoURI(mediaLinks[0]);
break;
if (link != null) {
// enable control bar if set
if (enableVideoExtras) {
controlPanel.setVisibility(VISIBLE);
if (link.getScheme().startsWith("http")) {
// attach link to share button
share.setTag(link);
} else {
share.setVisibility(GONE);
}
seekUpdate = new SeekbarUpdater(this, PROGRESS_UPDATE);
}
videoView.setVideoURI(link);
}
share.setOnClickListener(this);
play.setOnClickListener(this);
pause.setOnClickListener(this);
@ -230,8 +180,8 @@ public class MediaViewer extends MediaActivity implements OnImageClickListener,
@Override
protected void onStop() {
super.onStop();
if (type == MEDIAVIEWER_VIDEO) {
playStat = PlayStat.PAUSE;
if (enableVideoExtras) {
playstatus = PAUSE;
setPlayPauseButton();
videoView.pause();
}
@ -240,11 +190,8 @@ public class MediaViewer extends MediaActivity implements OnImageClickListener,
@Override
protected void onDestroy() {
if (imageAsync != null && imageAsync.getStatus() == RUNNING)
imageAsync.cancel(true);
if (seekUpdate != null)
seekUpdate.shutdown();
clearCache();
super.onDestroy();
}
@ -255,20 +202,20 @@ public class MediaViewer extends MediaActivity implements OnImageClickListener,
if (v.getId() == R.id.controller_play) {
if (!videoView.isPlaying())
videoView.start();
playStat = PlayStat.PLAY;
playstatus = PLAY;
setPlayPauseButton();
}
// pause video
if (v.getId() == R.id.controller_pause) {
if (videoView.isPlaying())
videoView.pause();
playStat = PlayStat.PAUSE;
playstatus = PAUSE;
setPlayPauseButton();
}
// open link with another app
else if (v.getId() == R.id.controller_share) {
if (mediaLinks != null && mediaLinks.length > 0) {
Intent intent = new Intent(Intent.ACTION_VIEW, mediaLinks[0]);
if (v.getTag() instanceof Uri) {
Intent intent = new Intent(Intent.ACTION_VIEW, (Uri) v.getTag());
try {
startActivity(intent);
} catch (ActivityNotFoundException err) {
@ -283,30 +230,30 @@ public class MediaViewer extends MediaActivity implements OnImageClickListener,
public boolean onTouch(View v, MotionEvent event) {
// fast backward
if (v.getId() == R.id.controller_backward) {
if (playStat == PlayStat.PAUSE)
if (playstatus == PAUSE)
return false;
if (event.getAction() == ACTION_DOWN) {
playStat = PlayStat.BACKWARD;
playstatus = BACKWARD;
videoView.pause();
return true;
}
if (event.getAction() == ACTION_UP) {
playStat = PlayStat.PLAY;
playstatus = PLAY;
videoView.start();
return true;
}
}
// fast forward
else if (v.getId() == R.id.controller_forward) {
if (playStat == PlayStat.PAUSE)
if (playstatus == PAUSE)
return false;
if (event.getAction() == ACTION_DOWN) {
playStat = PlayStat.FORWARD;
playstatus = FORWARD;
videoView.pause();
return true;
}
if (event.getAction() == ACTION_UP) {
playStat = PlayStat.PLAY;
playstatus = PLAY;
videoView.start();
return true;
}
@ -314,7 +261,7 @@ public class MediaViewer extends MediaActivity implements OnImageClickListener,
// show/hide control panel
else if (v.getId() == R.id.video_view) {
if (event.getAction() == ACTION_DOWN) {
if (type == MEDIAVIEWER_VIDEO) {
if (enableVideoExtras) {
if (controlPanel.getVisibility() == VISIBLE) {
controlPanel.setVisibility(INVISIBLE);
} else {
@ -338,41 +285,28 @@ public class MediaViewer extends MediaActivity implements OnImageClickListener,
}
@Override
public void onImageClick(Uri uri) {
zoomImage.reset();
zoomImage.setImageURI(uri);
}
@Override
public void onImageSave(Uri uri) {
storeImage(uri);
}
@Override
public void onPrepared(MediaPlayer mp) {
// configure to play GIF
if (type == MEDIAVIEWER_ANGIF) {
loadingCircle.setVisibility(INVISIBLE);
mp.setLooping(true);
mp.start();
}
// configure to play video
else if (type == MEDIAVIEWER_VIDEO) {
if (playStat == PlayStat.IDLE) {
playStat = PlayStat.PLAY;
// enable controls for video
if (enableVideoExtras) {
if (playstatus == IDLE) {
playstatus = PLAY;
video_progress.setMax(mp.getDuration());
duration.setText(StringTools.formatMediaTime(mp.getDuration()));
mp.setOnInfoListener(this);
}
if (playStat == PlayStat.PLAY) {
if (playstatus == PLAY) {
int videoPos = video_progress.getProgress();
mp.seekTo(videoPos);
mp.start();
}
}
// setup video looping for gif
else {
loadingCircle.setVisibility(INVISIBLE);
mp.setLooping(true);
mp.start();
}
}
@ -405,7 +339,7 @@ public class MediaViewer extends MediaActivity implements OnImageClickListener,
@Override
public void onCompletion(MediaPlayer mp) {
playStat = PlayStat.PAUSE;
playstatus = PAUSE;
setPlayPauseButton();
video_progress.setProgress(0);
}
@ -429,42 +363,11 @@ public class MediaViewer extends MediaActivity implements OnImageClickListener,
videoView.start();
}
/**
* Called from {@link ImageLoader} when all images are downloaded successfully
*/
public void onSuccess() {
adapter.disableLoading();
}
/**
* Called from {@link ImageLoader} when an error occurs
*
* @param err Exception caught by {@link ImageLoader}
*/
public void onError(ErrorHandler.TwitterError err) {
ErrorHandler.handleFailure(getApplicationContext(), err);
finish();
}
/**
* set downloaded image into preview list
*
* @param imageUri Image Uri
*/
public void setImage(Uri imageUri) {
if (adapter.isEmpty()) {
zoomImage.reset();
zoomImage.setImageURI(imageUri);
loadingCircle.setVisibility(INVISIBLE);
}
adapter.addLast(imageUri);
}
/**
* set play pause button
*/
private void setPlayPauseButton() {
if (playStat == PlayStat.PAUSE || playStat == PlayStat.IDLE) {
if (playstatus == PAUSE || playstatus == IDLE) {
play.setVisibility(VISIBLE);
pause.setVisibility(INVISIBLE);
} else {
@ -478,7 +381,7 @@ public class MediaViewer extends MediaActivity implements OnImageClickListener,
*/
public void updateSeekBar() {
int videoPos = video_progress.getProgress();
switch (playStat) {
switch (playstatus) {
case PLAY:
video_progress.setProgress(videoView.getCurrentPosition());
break;
@ -500,21 +403,4 @@ public class MediaViewer extends MediaActivity implements OnImageClickListener,
break;
}
}
/**
* clear the image cache
*/
private void clearCache() {
try {
File cacheFolder = new File(getExternalCacheDir(), CACHE_FOLDER);
File[] files = cacheFolder.listFiles();
if (files != null && files.length > 0) {
for (File file : files) {
file.delete();
}
}
} catch (SecurityException e) {
e.printStackTrace();
}
}
}

View File

@ -19,6 +19,7 @@ import org.nuclearfog.twidda.adapter.holder.ImageHolder;
import org.nuclearfog.twidda.database.GlobalSettings;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
/**
@ -54,6 +55,13 @@ public class ImageAdapter extends Adapter<ViewHolder> {
this.settings = GlobalSettings.getInstance(context);
}
public void addAll(Uri[] uris) {
imageUri.clear();
imageUri.addAll(Arrays.asList(uris));
notifyDataSetChanged();
}
/**
* add new image at the last position
*

View File

@ -3,10 +3,9 @@ package org.nuclearfog.twidda.backend;
import android.net.Uri;
import android.os.AsyncTask;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import org.nuclearfog.twidda.activities.MediaViewer;
import org.nuclearfog.twidda.activities.ImageViewer;
import org.nuclearfog.twidda.backend.api.Twitter;
import org.nuclearfog.twidda.backend.api.TwitterException;
import org.nuclearfog.twidda.backend.api.holder.MediaStream;
@ -24,14 +23,14 @@ import java.lang.ref.WeakReference;
* and creates Uri of the images.
*
* @author nuclearfog
* @see MediaViewer
* @see ImageViewer
*/
public class ImageLoader extends AsyncTask<Uri, Uri, Boolean> {
@Nullable
private ErrorHandler.TwitterError err;
private Twitter twitter;
private WeakReference<MediaViewer> callback;
private WeakReference<ImageViewer> callback;
private File cache;
/**
@ -39,13 +38,12 @@ public class ImageLoader extends AsyncTask<Uri, Uri, Boolean> {
*
* @param activity Activity context
*/
public ImageLoader(@NonNull MediaViewer activity) {
public ImageLoader(ImageViewer activity, File cache) {
super();
callback = new WeakReference<>(activity);
twitter = Twitter.get(activity);
// create cache folder if not exists
cache = new File(activity.getExternalCacheDir(), MediaViewer.CACHE_FOLDER);
cache.mkdirs();
this.cache = cache;
}

View File

@ -1,6 +1,6 @@
package org.nuclearfog.twidda.backend;
import org.nuclearfog.twidda.activities.MediaViewer;
import org.nuclearfog.twidda.activities.VideoViewer;
import java.lang.ref.WeakReference;
import java.util.concurrent.Executors;
@ -8,18 +8,18 @@ import java.util.concurrent.ScheduledExecutorService;
import java.util.concurrent.TimeUnit;
/**
* This class updates {@link MediaViewer}'s Seekbar while playing a video
* This class updates {@link VideoViewer}'s Seekbar while playing a video
*
* @author nuclearfog
*/
public class SeekbarUpdater implements Runnable {
private ScheduledExecutorService updater;
private WeakReference<MediaViewer> callback;
private WeakReference<VideoViewer> callback;
private Runnable seekUpdate = new Runnable() {
public void run() {
MediaViewer mediaViewer = callback.get();
VideoViewer mediaViewer = callback.get();
if (mediaViewer != null) {
mediaViewer.updateSeekBar();
}
@ -27,7 +27,7 @@ public class SeekbarUpdater implements Runnable {
};
public SeekbarUpdater(MediaViewer callback, int milliseconds) {
public SeekbarUpdater(VideoViewer callback, int milliseconds) {
this.callback = new WeakReference<>(callback);
updater = Executors.newScheduledThreadPool(1);
updater.scheduleWithFixedDelay(this, milliseconds, milliseconds, TimeUnit.MILLISECONDS);
@ -36,7 +36,7 @@ public class SeekbarUpdater implements Runnable {
@Override
public void run() {
MediaViewer mediaViewer = callback.get();
VideoViewer mediaViewer = callback.get();
if (mediaViewer != null) {
mediaViewer.runOnUiThread(seekUpdate);
}

View File

@ -143,7 +143,6 @@ public class Twitter implements GlobalSettings.SettingsListener {
builder.proxyAuthenticator(new ProxyAuthenticator(settings));
// enable experimental TLS 1.2 support for old android versions
if (Build.VERSION.SDK_INT < Build.VERSION_CODES.LOLLIPOP) {
TLSSocketFactory.setSupportTLS();
try {
TrustManagerFactory factory = TrustManagerFactory.getInstance(TrustManagerFactory.getDefaultAlgorithm());
factory.init((KeyStore) null);

View File

@ -2,15 +2,10 @@ package org.nuclearfog.twidda.fragments;
import static android.os.AsyncTask.Status.RUNNING;
import static android.widget.Toast.LENGTH_SHORT;
import static org.nuclearfog.twidda.activities.MediaViewer.KEY_MEDIA_TYPE;
import static org.nuclearfog.twidda.activities.MediaViewer.KEY_MEDIA_URI;
import static org.nuclearfog.twidda.activities.MediaViewer.MEDIAVIEWER_IMAGE;
import static org.nuclearfog.twidda.activities.MessageEditor.KEY_DM_PREFIX;
import static org.nuclearfog.twidda.activities.SearchPage.KEY_SEARCH_QUERY;
import static org.nuclearfog.twidda.activities.TweetActivity.KEY_TWEET_ID;
import static org.nuclearfog.twidda.activities.TweetActivity.KEY_TWEET_NAME;
import static org.nuclearfog.twidda.activities.TweetActivity.LINK_PATTERN;
import static org.nuclearfog.twidda.activities.UserProfile.KEY_PROFILE_DATA;
import static org.nuclearfog.twidda.activities.MessageEditor.*;
import static org.nuclearfog.twidda.activities.SearchPage.*;
import static org.nuclearfog.twidda.activities.TweetActivity.*;
import static org.nuclearfog.twidda.activities.UserProfile.*;
import android.app.Dialog;
import android.content.ActivityNotFoundException;
@ -24,7 +19,7 @@ import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import org.nuclearfog.twidda.R;
import org.nuclearfog.twidda.activities.MediaViewer;
import org.nuclearfog.twidda.activities.ImageViewer;
import org.nuclearfog.twidda.activities.MessageEditor;
import org.nuclearfog.twidda.activities.SearchPage;
import org.nuclearfog.twidda.activities.TweetActivity;
@ -156,10 +151,10 @@ public class MessageFragment extends ListFragment implements OnItemSelected, OnC
case MEDIA:
if (message.getMedia() != null) {
Intent mediaIntent = new Intent(requireContext(), MediaViewer.class);
mediaIntent.putExtra(KEY_MEDIA_URI, new Uri[]{message.getMedia()});
mediaIntent.putExtra(KEY_MEDIA_TYPE, MEDIAVIEWER_IMAGE);
startActivity(mediaIntent);
Intent imageIntent = new Intent(requireContext(), ImageViewer.class);
imageIntent.putExtra(ImageViewer.IMAGE_URIS, message.getMedia());
imageIntent.putExtra(ImageViewer.IMAGE_DOWNLOAD, true);
startActivity(imageIntent);
}
break;
}

View File

@ -5,13 +5,12 @@
android:layout_width="match_parent"
android:layout_height="match_parent"
android:background="@color/half_transparent"
tools:context=".activities.MediaViewer">
tools:context=".activities.ImageViewer">
<org.nuclearfog.zoomview.ZoomView
android:id="@+id/image_full"
android:layout_width="0dp"
android:layout_height="0dp"
android:visibility="invisible"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent"
app:layout_constraintBottom_toBottomOf="parent"
@ -25,7 +24,6 @@
android:orientation="horizontal"
android:layout_width="0dp"
android:layout_height="0dp"
android:visibility="invisible"
android:layout_margin="@dimen/mediapage_preview_margin"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintBottom_toBottomOf="parent"
@ -40,17 +38,6 @@
</LinearLayout>
<VideoView
android:id="@+id/video_view"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:visibility="invisible"
android:background="@android:color/transparent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent" />
<ProgressBar
android:id="@+id/media_progress"
android:layout_width="@dimen/mediapage_circle_size"
@ -61,15 +48,4 @@
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent" />
<include
android:id="@+id/media_controlpanel"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:visibility="invisible"
android:layout_marginBottom="@dimen/mediapage_controller_bottom_margin"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent"
layout="@layout/popup_controlpanel" />
</androidx.constraintlayout.widget.ConstraintLayout>

View File

@ -0,0 +1,41 @@
<?xml version="1.0" encoding="utf-8"?>
<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:background="@color/half_transparent"
tools:context=".activities.VideoViewer">
<VideoView
android:id="@+id/video_view"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:background="@android:color/transparent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent" />
<ProgressBar
android:id="@+id/media_progress"
android:layout_width="@dimen/mediapage_circle_size"
android:layout_height="@dimen/mediapage_circle_size"
android:layout_marginTop="@dimen/mediapage_preview_margin"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent" />
<include
android:id="@+id/media_controlpanel"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:visibility="invisible"
android:layout_marginBottom="@dimen/mediapage_controller_bottom_margin"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent"
layout="@layout/popup_controlpanel" />
</androidx.constraintlayout.widget.ConstraintLayout>

View File

@ -10,8 +10,8 @@
# This option should only be used with decoupled projects. More details, visit
# http://www.gradle.org/docs/current/userguide/multi_project_builds.html#sec:decoupled_projects
# org.gradle.parallel=true
#Fri Dec 03 17:30:19 CET 2021
#Fri Jan 21 20:21:43 CET 2022
org.gradle.configureondemand=false;
org.gradle.jvmargs=-Xmx1536M -Dkotlin.daemon.jvm.options\="-Xmx1536M"
org.gradle.jvmargs=-Xmx1024M -Dkotlin.daemon.jvm.options\="-Xmx1024M"
android.enableJetifier=true
android.useAndroidX=true
android.useAndroidX=true