diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml index 728d9cba..c7ef41bd 100644 --- a/app/src/main/AndroidManifest.xml +++ b/app/src/main/AndroidManifest.xml @@ -7,6 +7,7 @@ + diff --git a/app/src/main/java/org/nuclearfog/twidda/adapter/IconAdapter.java b/app/src/main/java/org/nuclearfog/twidda/adapter/IconAdapter.java index 3348c12f..04d55c55 100644 --- a/app/src/main/java/org/nuclearfog/twidda/adapter/IconAdapter.java +++ b/app/src/main/java/org/nuclearfog/twidda/adapter/IconAdapter.java @@ -104,6 +104,30 @@ public class IconAdapter extends Adapter implements OnHolderClickLis notifyDataSetChanged(); } + /** + * add a single image icon + */ + public void addImageItem() { + items.add(IconHolder.TYPE_IMAGE); + notifyItemInserted(items.size() - 1); + } + + /** + * add a single gif item + */ + public void addGifItem() { + items.add(IconHolder.TYPE_GIF); + notifyItemInserted(items.size() - 1); + } + + /** + * add a single video item + */ + public void addVideoItem() { + items.add(IconHolder.TYPE_VIDEO); + notifyItemInserted(items.size() - 1); + } + /** * add media iconsdepending on type */ diff --git a/app/src/main/java/org/nuclearfog/twidda/adapter/StatusAdapter.java b/app/src/main/java/org/nuclearfog/twidda/adapter/StatusAdapter.java index 873b6fd3..b4ba86a7 100644 --- a/app/src/main/java/org/nuclearfog/twidda/adapter/StatusAdapter.java +++ b/app/src/main/java/org/nuclearfog/twidda/adapter/StatusAdapter.java @@ -126,14 +126,16 @@ public class StatusAdapter extends Adapter implements OnHolderClickL long sinceId = 0; long maxId = 0; if (position == 0) { - Status status = items.get(position + 1); - if (status != null) { - sinceId = status.getId(); + if (items.size() > 1) { + Status status = items.get(1); + if (status != null) { + sinceId = status.getId(); + } } } else if (position == items.size() - 1) { Status status = items.get(position - 1); if (status != null) { - maxId = status.getId() - 1; + maxId = status.getId() - 1L; } } else { Status status = items.get(position + 1); @@ -142,7 +144,7 @@ public class StatusAdapter extends Adapter implements OnHolderClickL } status = items.get(position - 1); if (status != null) { - maxId = status.getId() - 1; + maxId = status.getId() - 1L; } } boolean success = listener.onPlaceholderClick(sinceId, maxId, position); diff --git a/app/src/main/java/org/nuclearfog/twidda/adapter/holder/IconHolder.java b/app/src/main/java/org/nuclearfog/twidda/adapter/holder/IconHolder.java index 591473ad..d117d04f 100644 --- a/app/src/main/java/org/nuclearfog/twidda/adapter/holder/IconHolder.java +++ b/app/src/main/java/org/nuclearfog/twidda/adapter/holder/IconHolder.java @@ -57,7 +57,9 @@ public class IconHolder extends ViewHolder implements OnClickListener { */ public IconHolder(ViewGroup parent, GlobalSettings settings) { super(LayoutInflater.from(parent.getContext()).inflate(R.layout.item_attachment, parent, false)); - button = (ImageButton) itemView; + button = itemView.findViewById(R.id.item_status_media); + itemView.getLayoutParams().width = parent.getMeasuredHeight(); + itemView.getLayoutParams().height = parent.getMeasuredHeight(); button.setOnClickListener(this); this.settings = settings; } diff --git a/app/src/main/java/org/nuclearfog/twidda/backend/api/twitter/Twitter.java b/app/src/main/java/org/nuclearfog/twidda/backend/api/twitter/Twitter.java index f8f35dfb..5deca826 100644 --- a/app/src/main/java/org/nuclearfog/twidda/backend/api/twitter/Twitter.java +++ b/app/src/main/java/org/nuclearfog/twidda/backend/api/twitter/Twitter.java @@ -1250,7 +1250,7 @@ public class Twitter implements Connection { params.add(LocationV2.FIELDS_PLACE); // add metrics information if the author is the current user and the tweet is not older than 28 days and not a retweet/quote if (statusCompat.getAuthor().isCurrentUser() && System.currentTimeMillis() - statusCompat.getTimestamp() < 2419200000L - && (statusCompat.getEmbeddedStatus() == null || statusCompat.getEmbeddedStatus().getRepostId() <= 0)) { + && (statusCompat.getEmbeddedStatus() == null || statusCompat.getEmbeddedStatus().getRepostId() <= 0L)) { params.add(TweetV2.FIELDS_TWEET_PRIVATE); } else { params.add(TweetV2.FIELDS_TWEET); diff --git a/app/src/main/java/org/nuclearfog/twidda/backend/api/twitter/impl/v1/UserV1.java b/app/src/main/java/org/nuclearfog/twidda/backend/api/twitter/impl/v1/UserV1.java index 3c7ce3a0..4297235c 100644 --- a/app/src/main/java/org/nuclearfog/twidda/backend/api/twitter/impl/v1/UserV1.java +++ b/app/src/main/java/org/nuclearfog/twidda/backend/api/twitter/impl/v1/UserV1.java @@ -25,7 +25,6 @@ public class UserV1 implements User { public static final String PARAM_INCLUDE_ENTITIES = "include_entities=true"; - private long id; private long createdAt; private String username; diff --git a/app/src/main/java/org/nuclearfog/twidda/backend/update/StatusUpdate.java b/app/src/main/java/org/nuclearfog/twidda/backend/update/StatusUpdate.java index aa975903..c5dd795c 100644 --- a/app/src/main/java/org/nuclearfog/twidda/backend/update/StatusUpdate.java +++ b/app/src/main/java/org/nuclearfog/twidda/backend/update/StatusUpdate.java @@ -19,14 +19,60 @@ import java.util.List; */ public class StatusUpdate { + /** + * returned if attaching media failed + */ + public static final int MEDIA_ERROR = -1; + + /** + * indicates that there is no media attached + */ + public static final int MEDIA_NONE = 0; + + /** + * returned if image is attached + */ + public static final int MEDIA_IMAGE = 1; + + /** + * returned if video is attached + */ + public static final int MEDIA_VIDEO = 2; + + /** + * returned if an animated image is attached + */ + public static final int MEDIA_GIF = 3; + + /** + * image limit of a status + */ + private static final int MAX_IMAGES = 4; + + /** + * video limit of a status + */ + private static final int MAX_VIDEOS = 1; + + /** + * gif limit of a status + */ + private static final int MAX_GIF = 1; + + private static final String MIME_GIF = "image/gif"; + private static final String MIME_IMAGE_ALL = "image/"; + private static final String MIME_VIDEO_ALL = "video/"; + private String text; private long replyId; private double longitude; private double latitude; + private int mediaType = MEDIA_NONE; private List mediaUris = new ArrayList<>(5); private MediaStatus[] mediaUpdates = {}; private boolean hasLocation = false; + private boolean mediaLimitReached = false; /** * set ID of the replied status @@ -51,12 +97,66 @@ public class StatusUpdate { * @return number of media attached to this holder or -1 if file is invalid */ public int addMedia(Context context, Uri mediaUri) { - DocumentFile file = DocumentFile.fromSingleUri(context, mediaUri); - if (file != null && file.length() > 0) { - mediaUris.add(mediaUri); - return mediaUris.size(); + String mime = context.getContentResolver().getType(mediaUri); + if (mime == null) { + return MEDIA_ERROR; } - return -1; + // check if file is a 'gif' image + else if (mime.equals(MIME_GIF)) { + switch (mediaType) { + case MEDIA_NONE: + mediaType = MEDIA_GIF; + + case MEDIA_GIF: + DocumentFile file = DocumentFile.fromSingleUri(context, mediaUri); + if (file != null && file.length() > 0) { + mediaUris.add(mediaUri); + if (mediaUris.size() == MAX_GIF) { + mediaLimitReached = true; + } + return MEDIA_GIF; + } + break; + } + + } + // check if file is an image + else if (mime.startsWith(MIME_IMAGE_ALL)) { + switch (mediaType) { + case MEDIA_NONE: + mediaType = MEDIA_IMAGE; + + case MEDIA_IMAGE: + DocumentFile file = DocumentFile.fromSingleUri(context, mediaUri); + if (file != null && file.length() > 0) { + mediaUris.add(mediaUri); + if (mediaUris.size() == MAX_IMAGES) { + mediaLimitReached = true; + } + return MEDIA_IMAGE; + } + break; + } + } + // check if file is a video + else if (mime.startsWith(MIME_VIDEO_ALL)) { + switch (mediaType) { + case MEDIA_NONE: + mediaType = MEDIA_VIDEO; + + case MAX_VIDEOS: + DocumentFile file = DocumentFile.fromSingleUri(context, mediaUri); + if (file != null && file.length() > 0) { + mediaUris.add(mediaUri); + if (mediaUris.size() == MAX_VIDEOS) { + mediaLimitReached = true; + } + return MEDIA_VIDEO; + } + break; + } + } + return MEDIA_ERROR; } /** @@ -70,6 +170,25 @@ public class StatusUpdate { hasLocation = true; } + /** + * get type of attached media + * currently there is only one type of media used at once + * + * @return media type {@link #MEDIA_NONE,#MEDIA_VIDEO,#MEDIA_IMAGE,#MEDIA_GIF} + */ + public int getMediaType() { + return mediaType; + } + + /** + * check if media limit is reached + * + * @return true if media limit is reached + */ + public boolean mediaLimitReached() { + return mediaLimitReached; + } + /** * get status text * diff --git a/app/src/main/java/org/nuclearfog/twidda/ui/activities/MediaActivity.java b/app/src/main/java/org/nuclearfog/twidda/ui/activities/MediaActivity.java index 999f338a..5393cb3c 100644 --- a/app/src/main/java/org/nuclearfog/twidda/ui/activities/MediaActivity.java +++ b/app/src/main/java/org/nuclearfog/twidda/ui/activities/MediaActivity.java @@ -152,7 +152,7 @@ public abstract class MediaActivity extends AppCompatActivity implements Locatio super.onRequestPermissionsResult(requestCode, permissions, grantResults); if (grantResults.length > 0 && permissions.length > 0 && grantResults[0] == PERMISSION_GRANTED) { // read storage permission granted - switch(permissions[0]) { + switch (permissions[0]) { case ACCESS_FINE_LOCATION: startLocating(); break; diff --git a/app/src/main/java/org/nuclearfog/twidda/ui/activities/ProfileActivity.java b/app/src/main/java/org/nuclearfog/twidda/ui/activities/ProfileActivity.java index adc1060b..597237fb 100644 --- a/app/src/main/java/org/nuclearfog/twidda/ui/activities/ProfileActivity.java +++ b/app/src/main/java/org/nuclearfog/twidda/ui/activities/ProfileActivity.java @@ -491,7 +491,7 @@ public class ProfileActivity extends AppCompatActivity implements OnClickListene if (v.getId() == R.id.following) { if (relation != null) { if ((settings.getLogin().getApiType() != Account.API_TWITTER || !user.isProtected()) - || user.isCurrentUser() || relation.isFollowing()) { + || user.isCurrentUser() || relation.isFollowing()) { Intent usersIntent = new Intent(this, UsersActivity.class); usersIntent.putExtra(KEY_USERS_ID, user.getId()); usersIntent.putExtra(KEY_USERS_MODE, USERS_FRIENDS); @@ -503,7 +503,7 @@ public class ProfileActivity extends AppCompatActivity implements OnClickListene else if (v.getId() == R.id.follower) { if (relation != null) { if ((settings.getLogin().getApiType() != Account.API_TWITTER || !user.isProtected()) - || user.isCurrentUser() || relation.isFollowing()) { + || user.isCurrentUser() || relation.isFollowing()) { Intent usersIntent = new Intent(this, UsersActivity.class); usersIntent.putExtra(KEY_USERS_ID, user.getId()); usersIntent.putExtra(KEY_USERS_MODE, USERS_FOLLOWER); diff --git a/app/src/main/java/org/nuclearfog/twidda/ui/activities/StatusEditor.java b/app/src/main/java/org/nuclearfog/twidda/ui/activities/StatusEditor.java index 690f1c14..50daa01f 100644 --- a/app/src/main/java/org/nuclearfog/twidda/ui/activities/StatusEditor.java +++ b/app/src/main/java/org/nuclearfog/twidda/ui/activities/StatusEditor.java @@ -20,11 +20,14 @@ import android.widget.ImageButton; import android.widget.ImageView; import android.widget.Toast; -import androidx.annotation.DrawableRes; 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.IconAdapter; +import org.nuclearfog.twidda.adapter.IconAdapter.OnMediaClickListener; import org.nuclearfog.twidda.backend.api.ConnectionException; import org.nuclearfog.twidda.backend.async.StatusUpdater; import org.nuclearfog.twidda.backend.update.StatusUpdate; @@ -42,7 +45,7 @@ import org.nuclearfog.twidda.ui.dialogs.ProgressDialog.OnProgressStopListener; * * @author nuclearfog */ -public class StatusEditor extends MediaActivity implements OnClickListener, OnProgressStopListener, OnConfirmListener { +public class StatusEditor extends MediaActivity implements OnClickListener, OnProgressStopListener, OnConfirmListener, OnMediaClickListener { /** * key to add a statusd ID to reply @@ -56,47 +59,23 @@ public class StatusEditor extends MediaActivity implements OnClickListener, OnPr */ public static final String KEY_STATUS_EDITOR_TEXT = "status_text"; - private static final String MIME_GIF = "image/gif"; - private static final String MIME_IMAGE_ALL = "image/"; - private static final String MIME_VIDEO_ALL = "video/"; - - /** - * image limit of a status - */ - private static final int MAX_IMAGES = 4; - - /** - * video limit of a status - */ - private static final int MAX_VIDEOS = 1; - - /** - * gif limit of a status - */ - private static final int MAX_GIF = 1; - /** * mention limit of a status */ private static final int MAX_MENTIONS = 10; - private static final int MEDIA_NONE = 0; - private static final int MEDIA_IMAGE = 1; - private static final int MEDIA_VIDEO = 2; - private static final int MEDIA_GIF = 3; private StatusUpdater uploaderAsync; - private GlobalSettings settings; private ConfirmDialog confirmDialog; private ProgressDialog loadingCircle; + private IconAdapter adapter; - private ImageButton mediaBtn, previewBtn, locationBtn; + private ImageButton mediaBtn, locationBtn; private EditText statusText; private View locationPending; private StatusUpdate statusUpdate = new StatusUpdate(); - private int selectedFormat = MEDIA_NONE; @Override @@ -113,13 +92,13 @@ public class StatusEditor extends MediaActivity implements OnClickListener, OnPr ImageView background = findViewById(R.id.popup_status_background); ImageButton statusButton = findViewById(R.id.popup_status_send); ImageButton closeButton = findViewById(R.id.popup_status_close); + RecyclerView iconList = findViewById(R.id.popup_status_media_icons); locationBtn = findViewById(R.id.popup_status_add_location); mediaBtn = findViewById(R.id.popup_status_add_media); - previewBtn = findViewById(R.id.popup_status_prev_media); statusText = findViewById(R.id.popup_status_input); locationPending = findViewById(R.id.popup_status_location_loading); - settings = GlobalSettings.getInstance(this); + GlobalSettings settings = GlobalSettings.getInstance(this); loadingCircle = new ProgressDialog(this); confirmDialog = new ConfirmDialog(this); AppStyles.setEditorTheme(root, background); @@ -131,14 +110,18 @@ public class StatusEditor extends MediaActivity implements OnClickListener, OnPr if (prefix != null) { statusText.append(prefix); } + adapter = new IconAdapter(settings); + adapter.addOnMediaClickListener(this); + iconList.setLayoutManager(new LinearLayoutManager(this, LinearLayoutManager.HORIZONTAL, true)); + iconList.setAdapter(adapter); closeButton.setOnClickListener(this); statusButton.setOnClickListener(this); mediaBtn.setOnClickListener(this); - previewBtn.setOnClickListener(this); locationBtn.setOnClickListener(this); confirmDialog.setConfirmListener(this); loadingCircle.addOnProgressStopListener(this); + adapter.addOnMediaClickListener(this); } @@ -198,7 +181,7 @@ public class StatusEditor extends MediaActivity implements OnClickListener, OnPr } // Add media to the status else if (v.getId() == R.id.popup_status_add_media) { - if (selectedFormat == MEDIA_NONE) { + if (statusUpdate.getMediaType() == StatusUpdate.MEDIA_NONE) { // request images/videos getMedia(REQUEST_IMG_VID); } else { @@ -206,32 +189,6 @@ public class StatusEditor extends MediaActivity implements OnClickListener, OnPr getMedia(REQUEST_IMAGE); } } - // open media preview - else if (v.getId() == R.id.popup_status_prev_media) { - Uri[] uris = statusUpdate.getMediaUris(); - // - if (selectedFormat == MEDIA_VIDEO) { - 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); - } - } // add location to the status else if (v.getId() == R.id.popup_status_add_location) { locationPending.setVisibility(VISIBLE); @@ -256,41 +213,26 @@ public class StatusEditor extends MediaActivity implements OnClickListener, OnPr @Override protected void onMediaFetched(int resultType, @NonNull Uri uri) { - int mediaCount = 0; - String mime = getContentResolver().getType(uri); - if (mime == null) { - Toast.makeText(this, R.string.error_file_format, LENGTH_SHORT).show(); + int mediaType = statusUpdate.addMedia(this, uri); + switch (mediaType) { + case StatusUpdate.MEDIA_IMAGE: + adapter.addImageItem(); + break; + + case StatusUpdate.MEDIA_GIF: + adapter.addGifItem(); + break; + + case StatusUpdate.MEDIA_VIDEO: + adapter.addVideoItem(); + break; + + case StatusUpdate.MEDIA_ERROR: + Toast.makeText(this, R.string.error_adding_media, LENGTH_SHORT).show(); + break; } - // check if file is a 'gif' image - else if (mime.equals(MIME_GIF)) { - if (selectedFormat == MEDIA_NONE || selectedFormat == MEDIA_GIF) { - mediaCount = addStatusMedia(uri, R.drawable.gif, MAX_GIF); - if (mediaCount > 0) { - selectedFormat = MEDIA_GIF; - } - } - } - // check if file is an image - else if (mime.startsWith(MIME_IMAGE_ALL)) { - if (selectedFormat == MEDIA_NONE || selectedFormat == MEDIA_IMAGE) { - mediaCount = addStatusMedia(uri, R.drawable.image, MAX_IMAGES); - if (mediaCount > 0) { - selectedFormat = MEDIA_IMAGE; - } - } - } - // check if file is a video - else if (mime.startsWith(MIME_VIDEO_ALL)) { - if (selectedFormat == MEDIA_NONE || selectedFormat == MEDIA_VIDEO) { - mediaCount = addStatusMedia(uri, R.drawable.video, MAX_VIDEOS); - if (mediaCount > 0) { - selectedFormat = MEDIA_VIDEO; - } - } - } - // check if media was successfully added - if (mediaCount <= 0) { - Toast.makeText(this, R.string.error_adding_media, LENGTH_SHORT).show(); + if (statusUpdate.mediaLimitReached()) { + mediaBtn.setVisibility(GONE); } } @@ -315,6 +257,28 @@ public class StatusEditor extends MediaActivity implements OnClickListener, OnPr } } + + @Override + public void onMediaClick(int index) { + Uri[] uris = statusUpdate.getMediaUris(); + switch (statusUpdate.getMediaType()) { + case StatusUpdate.MEDIA_IMAGE: + case StatusUpdate.MEDIA_GIF: + Intent mediaViewer = new Intent(this, ImageViewer.class); + mediaViewer.putExtra(ImageViewer.IMAGE_URIS, uris); + mediaViewer.putExtra(ImageViewer.IMAGE_DOWNLOAD, false); + startActivity(mediaViewer); + break; + + case StatusUpdate.MEDIA_VIDEO: + mediaViewer = new Intent(this, VideoViewer.class); + mediaViewer.putExtra(VideoViewer.VIDEO_URI, uris[0]); + mediaViewer.putExtra(VideoViewer.ENABLE_VIDEO_CONTROLS, true); + startActivity(mediaViewer); + break; + } + } + /** * called if status was updated successfully */ @@ -343,27 +307,6 @@ public class StatusEditor extends MediaActivity implements OnClickListener, OnPr } } - /** - * attach media to the status - * - * @param uri Uri link of the media - * @param icon icon of the preview button - * @param limit limit of the media count - * @return media count or -1 if adding failed - */ - private int addStatusMedia(Uri uri, @DrawableRes int icon, int limit) { - previewBtn.setImageResource(icon); - AppStyles.setDrawableColor(previewBtn, settings.getIconColor()); - int mediaCount = statusUpdate.addMedia(this, uri); - if (mediaCount > 0) - previewBtn.setVisibility(VISIBLE); - // if limit reached, remove mediaselect button - if (mediaCount == limit) { - mediaBtn.setVisibility(GONE); - } - return mediaCount; - } - /** * start uploading status and media files */ diff --git a/app/src/main/res/drawable/poll.xml b/app/src/main/res/drawable/poll.xml index c454d470..d125a96c 100644 --- a/app/src/main/res/drawable/poll.xml +++ b/app/src/main/res/drawable/poll.xml @@ -1,9 +1,9 @@ - + android:width="16sp" + android:height="16sp" + android:viewportWidth="20" + android:viewportHeight="20"> + diff --git a/app/src/main/res/layout/item_attachment.xml b/app/src/main/res/layout/item_attachment.xml index d3c2ee8c..75ab5568 100644 --- a/app/src/main/res/layout/item_attachment.xml +++ b/app/src/main/res/layout/item_attachment.xml @@ -1,10 +1,16 @@ - \ No newline at end of file + + + + + \ No newline at end of file diff --git a/app/src/main/res/layout/item_poll.xml b/app/src/main/res/layout/item_poll.xml index 98dd22f1..30fbbabb 100644 --- a/app/src/main/res/layout/item_poll.xml +++ b/app/src/main/res/layout/item_poll.xml @@ -26,7 +26,7 @@ android:layout_width="match_parent" android:layout_height="wrap_content" android:textSize="@dimen/tiem_poll_text_size" - android:lines="1"/> + android:lines="1" /> diff --git a/app/src/main/res/layout/item_status.xml b/app/src/main/res/layout/item_status.xml index 5b17ff72..3b602ce9 100644 --- a/app/src/main/res/layout/item_status.xml +++ b/app/src/main/res/layout/item_status.xml @@ -174,7 +174,7 @@ + app:constraint_referenced_ids="popup_status_media_icons,popup_status_add_media,popup_status_add_location,popup_status_send,popup_status_close" /> - + app:layout_constraintTop_toTopOf="@id/popup_status_send" + app:layout_constraintBottom_toBottomOf="@id/popup_status_send" + app:layout_constraintEnd_toStartOf="@id/popup_status_add_media" /> 12sp 12sp 14sp - 3dp 6sp + 30sp 5dp @@ -156,6 +156,7 @@ 10dp 7dp + 7dp 7dp @@ -259,7 +260,7 @@ 11sp - 28sp + 2dp 12sp