From c9e20cbc42382f1b87818183bc54dd4bdd0f7dbb Mon Sep 17 00:00:00 2001 From: nuclearfog Date: Fri, 21 Jan 2022 20:40:21 +0100 Subject: [PATCH] split mediaviewer into separate activities, bug fix --- app/src/main/AndroidManifest.xml | 7 +- .../nuclearfog/twidda/CompatApplication.java | 3 + .../twidda/activities/ImageViewer.java | 175 +++++++++++++ .../twidda/activities/MessageEditor.java | 13 +- .../twidda/activities/TweetActivity.java | 64 ++--- .../twidda/activities/TweetEditor.java | 44 ++-- .../twidda/activities/UserProfile.java | 48 ++-- .../{MediaViewer.java => VideoViewer.java} | 240 +++++------------- .../twidda/adapter/ImageAdapter.java | 8 + .../twidda/backend/ImageLoader.java | 12 +- .../twidda/backend/SeekbarUpdater.java | 12 +- .../twidda/backend/api/Twitter.java | 1 - .../twidda/fragments/MessageFragment.java | 23 +- .../layout/{page_media.xml => page_image.xml} | 26 +- app/src/main/res/layout/page_video.xml | 41 +++ gradle.properties | 6 +- 16 files changed, 388 insertions(+), 335 deletions(-) create mode 100644 app/src/main/java/org/nuclearfog/twidda/activities/ImageViewer.java rename app/src/main/java/org/nuclearfog/twidda/activities/{MediaViewer.java => VideoViewer.java} (61%) rename app/src/main/res/layout/{page_media.xml => page_image.xml} (65%) create mode 100644 app/src/main/res/layout/page_video.xml diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml index ba794a8b..1515391b 100644 --- a/app/src/main/AndroidManifest.xml +++ b/app/src/main/AndroidManifest.xml @@ -104,7 +104,12 @@ android:theme="@style/AppTheme" /> + + diff --git a/app/src/main/java/org/nuclearfog/twidda/CompatApplication.java b/app/src/main/java/org/nuclearfog/twidda/CompatApplication.java index 0d6b1e10..cd94455c 100644 --- a/app/src/main/java/org/nuclearfog/twidda/CompatApplication.java +++ b/app/src/main/java/org/nuclearfog/twidda/CompatApplication.java @@ -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); diff --git a/app/src/main/java/org/nuclearfog/twidda/activities/ImageViewer.java b/app/src/main/java/org/nuclearfog/twidda/activities/ImageViewer.java new file mode 100644 index 00000000..241162c2 --- /dev/null +++ b/app/src/main/java/org/nuclearfog/twidda/activities/ImageViewer.java @@ -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(); + } + } +} \ No newline at end of file diff --git a/app/src/main/java/org/nuclearfog/twidda/activities/MessageEditor.java b/app/src/main/java/org/nuclearfog/twidda/activities/MessageEditor.java index 2634f8a3..f03edad9 100644 --- a/app/src/main/java/org/nuclearfog/twidda/activities/MessageEditor.java +++ b/app/src/main/java/org/nuclearfog/twidda/activities/MessageEditor.java @@ -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); } } diff --git a/app/src/main/java/org/nuclearfog/twidda/activities/TweetActivity.java b/app/src/main/java/org/nuclearfog/twidda/activities/TweetActivity.java index e8ae3f34..d685293c 100644 --- a/app/src/main/java/org/nuclearfog/twidda/activities/TweetActivity.java +++ b/app/src/main/java/org/nuclearfog/twidda/activities/TweetActivity.java @@ -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) { diff --git a/app/src/main/java/org/nuclearfog/twidda/activities/TweetEditor.java b/app/src/main/java/org/nuclearfog/twidda/activities/TweetEditor.java index 644edadc..51b7992c 100644 --- a/app/src/main/java/org/nuclearfog/twidda/activities/TweetEditor.java +++ b/app/src/main/java/org/nuclearfog/twidda/activities/TweetEditor.java @@ -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) { diff --git a/app/src/main/java/org/nuclearfog/twidda/activities/UserProfile.java b/app/src/main/java/org/nuclearfog/twidda/activities/UserProfile.java index e1cee466..66a8fe0e 100644 --- a/app/src/main/java/org/nuclearfog/twidda/activities/UserProfile.java +++ b/app/src/main/java/org/nuclearfog/twidda/activities/UserProfile.java @@ -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); } } } diff --git a/app/src/main/java/org/nuclearfog/twidda/activities/MediaViewer.java b/app/src/main/java/org/nuclearfog/twidda/activities/VideoViewer.java similarity index 61% rename from app/src/main/java/org/nuclearfog/twidda/activities/MediaViewer.java rename to app/src/main/java/org/nuclearfog/twidda/activities/VideoViewer.java index 3007099a..c2e0eceb 100644 --- a/app/src/main/java/org/nuclearfog/twidda/activities/MediaViewer.java +++ b/app/src/main/java/org/nuclearfog/twidda/activities/VideoViewer.java @@ -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(); - } - } } \ No newline at end of file diff --git a/app/src/main/java/org/nuclearfog/twidda/adapter/ImageAdapter.java b/app/src/main/java/org/nuclearfog/twidda/adapter/ImageAdapter.java index 871479f3..34ca7199 100644 --- a/app/src/main/java/org/nuclearfog/twidda/adapter/ImageAdapter.java +++ b/app/src/main/java/org/nuclearfog/twidda/adapter/ImageAdapter.java @@ -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 { 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 * diff --git a/app/src/main/java/org/nuclearfog/twidda/backend/ImageLoader.java b/app/src/main/java/org/nuclearfog/twidda/backend/ImageLoader.java index a6abcd66..779eacd6 100644 --- a/app/src/main/java/org/nuclearfog/twidda/backend/ImageLoader.java +++ b/app/src/main/java/org/nuclearfog/twidda/backend/ImageLoader.java @@ -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 { @Nullable private ErrorHandler.TwitterError err; private Twitter twitter; - private WeakReference callback; + private WeakReference callback; private File cache; /** @@ -39,13 +38,12 @@ public class ImageLoader extends AsyncTask { * * @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; } diff --git a/app/src/main/java/org/nuclearfog/twidda/backend/SeekbarUpdater.java b/app/src/main/java/org/nuclearfog/twidda/backend/SeekbarUpdater.java index d684990d..d60698d9 100644 --- a/app/src/main/java/org/nuclearfog/twidda/backend/SeekbarUpdater.java +++ b/app/src/main/java/org/nuclearfog/twidda/backend/SeekbarUpdater.java @@ -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 callback; + private WeakReference 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); } diff --git a/app/src/main/java/org/nuclearfog/twidda/backend/api/Twitter.java b/app/src/main/java/org/nuclearfog/twidda/backend/api/Twitter.java index b2857b39..c840c96b 100644 --- a/app/src/main/java/org/nuclearfog/twidda/backend/api/Twitter.java +++ b/app/src/main/java/org/nuclearfog/twidda/backend/api/Twitter.java @@ -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); diff --git a/app/src/main/java/org/nuclearfog/twidda/fragments/MessageFragment.java b/app/src/main/java/org/nuclearfog/twidda/fragments/MessageFragment.java index 0431e78f..d5d24c8d 100644 --- a/app/src/main/java/org/nuclearfog/twidda/fragments/MessageFragment.java +++ b/app/src/main/java/org/nuclearfog/twidda/fragments/MessageFragment.java @@ -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; } diff --git a/app/src/main/res/layout/page_media.xml b/app/src/main/res/layout/page_image.xml similarity index 65% rename from app/src/main/res/layout/page_media.xml rename to app/src/main/res/layout/page_image.xml index ba1db257..4339e518 100644 --- a/app/src/main/res/layout/page_media.xml +++ b/app/src/main/res/layout/page_image.xml @@ -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"> - - - - \ No newline at end of file diff --git a/app/src/main/res/layout/page_video.xml b/app/src/main/res/layout/page_video.xml new file mode 100644 index 00000000..39db8412 --- /dev/null +++ b/app/src/main/res/layout/page_video.xml @@ -0,0 +1,41 @@ + + + + + + + + + + \ No newline at end of file diff --git a/gradle.properties b/gradle.properties index b2a07664..36beae3f 100644 --- a/gradle.properties +++ b/gradle.properties @@ -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 \ No newline at end of file +android.useAndroidX=true