diff --git a/app/build.gradle b/app/build.gradle index 064abf9c..9a77c3f9 100644 --- a/app/build.gradle +++ b/app/build.gradle @@ -12,8 +12,8 @@ android { applicationId 'org.nuclearfog.twidda' minSdkVersion 21 targetSdkVersion 34 - versionCode 100 - versionName '3.4.4' + versionCode 101 + versionName '3.4.5' resConfigs 'en', 'es', 'de-rDE', 'zh-rCN' } @@ -48,7 +48,7 @@ android { dependencies { implementation 'androidx.appcompat:appcompat:1.6.1' - implementation 'androidx.recyclerview:recyclerview:1.3.1' + implementation 'androidx.recyclerview:recyclerview:1.3.2' implementation 'androidx.constraintlayout:constraintlayout:2.1.4' implementation 'androidx.cardview:cardview:1.0.0' implementation 'androidx.swiperefreshlayout:swiperefreshlayout:1.1.0' diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml index 05b23e58..9749555e 100644 --- a/app/src/main/AndroidManifest.xml +++ b/app/src/main/AndroidManifest.xml @@ -130,6 +130,11 @@ android:screenOrientation="portrait" android:theme="@style/AppTheme" /> + + ()); ResponseBody body = response.body(); if (response.code() == 200 && body != null) { diff --git a/app/src/main/java/org/nuclearfog/twidda/backend/api/mastodon/impl/EditedMastodonStatus.java b/app/src/main/java/org/nuclearfog/twidda/backend/api/mastodon/impl/EditedMastodonStatus.java index f6fc32f5..273d8eda 100644 --- a/app/src/main/java/org/nuclearfog/twidda/backend/api/mastodon/impl/EditedMastodonStatus.java +++ b/app/src/main/java/org/nuclearfog/twidda/backend/api/mastodon/impl/EditedMastodonStatus.java @@ -8,6 +8,7 @@ import org.json.JSONObject; import org.nuclearfog.twidda.backend.utils.StringUtils; import org.nuclearfog.twidda.model.EditedStatus; import org.nuclearfog.twidda.model.Emoji; +import org.nuclearfog.twidda.model.Location; import org.nuclearfog.twidda.model.Media; import org.nuclearfog.twidda.model.Poll; import org.nuclearfog.twidda.model.User; @@ -38,9 +39,8 @@ public class EditedMastodonStatus implements EditedStatus { JSONArray mediaArray = json.optJSONArray("media_attachments"); JSONArray emojiArray = json.optJSONArray("emojis"); String content = json.optString("content", ""); - String spoilerText = json.optString("spoiler_text", ""); text = StringUtils.extractText(content); - spoiler = !content.equals(spoilerText); + spoiler = json.optBoolean("spoiler_text", false); sensitive = json.optBoolean("sensitive", false); timestamp = StringUtils.getIsoTime(json.optString("created_at")); author = new MastodonUser(json.getJSONObject("account"), currentUserId); @@ -112,4 +112,11 @@ public class EditedMastodonStatus implements EditedStatus { public Emoji[] getEmojis() { return emojis; } + + + @Nullable + @Override + public Location getLocation() { + return null; + } } \ No newline at end of file diff --git a/app/src/main/java/org/nuclearfog/twidda/backend/async/EditHistoryLoader.java b/app/src/main/java/org/nuclearfog/twidda/backend/async/EditHistoryLoader.java new file mode 100644 index 00000000..aa437dc1 --- /dev/null +++ b/app/src/main/java/org/nuclearfog/twidda/backend/async/EditHistoryLoader.java @@ -0,0 +1,55 @@ +package org.nuclearfog.twidda.backend.async; + +import android.content.Context; + +import androidx.annotation.NonNull; +import androidx.annotation.Nullable; + +import org.nuclearfog.twidda.backend.api.Connection; +import org.nuclearfog.twidda.backend.api.ConnectionException; +import org.nuclearfog.twidda.backend.api.ConnectionManager; +import org.nuclearfog.twidda.model.lists.StatusEditHistory; + +/** + * Async loader for {@link org.nuclearfog.twidda.ui.fragments.EditHistoryFragment} + * + * @author nuclearfog + */ +public class EditHistoryLoader extends AsyncExecutor { + + private Connection connection; + + /** + * + */ + public EditHistoryLoader(Context context) { + connection = ConnectionManager.getDefaultConnection(context); + } + + + @Override + protected Result doInBackground(@NonNull Long param) { + try { + StatusEditHistory history = connection.getStatusEditHistory(param); + return new Result(history, null); + } catch (ConnectionException exception) { + return new Result(null, exception); + } + } + + /** + * + */ + public static class Result { + + @Nullable + public final StatusEditHistory history; + @Nullable + public final ConnectionException exception; + + Result(@Nullable StatusEditHistory history, @Nullable ConnectionException exception) { + this.history = history; + this.exception = exception; + } + } +} \ No newline at end of file diff --git a/app/src/main/java/org/nuclearfog/twidda/model/EditedStatus.java b/app/src/main/java/org/nuclearfog/twidda/model/EditedStatus.java index f0502360..16c655ce 100644 --- a/app/src/main/java/org/nuclearfog/twidda/model/EditedStatus.java +++ b/app/src/main/java/org/nuclearfog/twidda/model/EditedStatus.java @@ -9,7 +9,7 @@ import java.io.Serializable; * * @author nuclearfog */ -public interface EditedStatus extends Serializable { +public interface EditedStatus extends Serializable, Comparable { /** * @return timestamp of this revision @@ -51,4 +51,18 @@ public interface EditedStatus extends Serializable { * @return array of emojis used in the text */ Emoji[] getEmojis(); + + /** + * @return location associated with this status + */ + @Nullable + Location getLocation(); + + /** + * + */ + @Override + default int compareTo(EditedStatus status) { + return Long.compare(status.getTimestamp(), getTimestamp()); + } } \ No newline at end of file diff --git a/app/src/main/java/org/nuclearfog/twidda/model/ScheduledStatus.java b/app/src/main/java/org/nuclearfog/twidda/model/ScheduledStatus.java index 43e1007e..bee96d69 100644 --- a/app/src/main/java/org/nuclearfog/twidda/model/ScheduledStatus.java +++ b/app/src/main/java/org/nuclearfog/twidda/model/ScheduledStatus.java @@ -9,7 +9,7 @@ import java.io.Serializable; * * @author nuclearfog */ -public interface ScheduledStatus extends Serializable { +public interface ScheduledStatus extends Serializable, Comparable { /** * @return ID of the scheduled status @@ -56,4 +56,11 @@ public interface ScheduledStatus extends Serializable { * @return true if status contains spoiler information */ boolean isSpoiler(); + + /** + * + */ + default int compareTo(ScheduledStatus status) { + return Long.compare(status.getId(), getId()); + } } \ No newline at end of file diff --git a/app/src/main/java/org/nuclearfog/twidda/model/lists/Accounts.java b/app/src/main/java/org/nuclearfog/twidda/model/lists/Accounts.java index ee6ac2f8..f2022cbb 100644 --- a/app/src/main/java/org/nuclearfog/twidda/model/lists/Accounts.java +++ b/app/src/main/java/org/nuclearfog/twidda/model/lists/Accounts.java @@ -1,5 +1,7 @@ package org.nuclearfog.twidda.model.lists; +import androidx.annotation.NonNull; + import org.nuclearfog.twidda.model.Account; import java.util.LinkedList; @@ -24,4 +26,11 @@ public class Accounts extends LinkedList { public Accounts(Accounts accounts) { super(accounts); } + + + @NonNull + @Override + public String toString() { + return "item_count=" + size(); + } } \ No newline at end of file diff --git a/app/src/main/java/org/nuclearfog/twidda/model/lists/Domains.java b/app/src/main/java/org/nuclearfog/twidda/model/lists/Domains.java index a1c79004..eaea1cc9 100644 --- a/app/src/main/java/org/nuclearfog/twidda/model/lists/Domains.java +++ b/app/src/main/java/org/nuclearfog/twidda/model/lists/Domains.java @@ -1,5 +1,7 @@ package org.nuclearfog.twidda.model.lists; +import androidx.annotation.NonNull; + import java.util.LinkedList; /** @@ -69,12 +71,35 @@ public class Domains extends LinkedList { super.addAll(index, list); } + /** - * get cursor for next items + * get previous cursor of this list + * + * @return cursor or 0L if not set + */ + public long getPreviousCursor() { + return prevCursor; + } + + /** + * get next cursor of this list * * @return cursor or 0L if not set */ public long getNextCursor() { return nextCursor; } + + + @Override + @NonNull + public String toString() { + int itemCount = 0; + for (String item : this) { + if (item != null) { + itemCount++; + } + } + return "item_count=" + itemCount + " previous=" + getPreviousCursor() + " next=" + getNextCursor(); + } } \ No newline at end of file diff --git a/app/src/main/java/org/nuclearfog/twidda/model/lists/Fields.java b/app/src/main/java/org/nuclearfog/twidda/model/lists/Fields.java index 63b66158..08efdb57 100644 --- a/app/src/main/java/org/nuclearfog/twidda/model/lists/Fields.java +++ b/app/src/main/java/org/nuclearfog/twidda/model/lists/Fields.java @@ -33,6 +33,6 @@ public class Fields extends LinkedList { @NonNull @Override public String toString() { - return "size=" + size(); + return "item_count=" + size(); } } \ No newline at end of file diff --git a/app/src/main/java/org/nuclearfog/twidda/model/lists/Filters.java b/app/src/main/java/org/nuclearfog/twidda/model/lists/Filters.java index d61c2088..164e6799 100644 --- a/app/src/main/java/org/nuclearfog/twidda/model/lists/Filters.java +++ b/app/src/main/java/org/nuclearfog/twidda/model/lists/Filters.java @@ -1,5 +1,7 @@ package org.nuclearfog.twidda.model.lists; +import androidx.annotation.NonNull; + import org.nuclearfog.twidda.model.Filter; import java.util.LinkedList; @@ -15,4 +17,11 @@ public class Filters extends LinkedList { public Filters() { super(); } + + + @NonNull + @Override + public String toString() { + return "item_count=" + size(); + } } \ No newline at end of file diff --git a/app/src/main/java/org/nuclearfog/twidda/model/lists/Hashtags.java b/app/src/main/java/org/nuclearfog/twidda/model/lists/Hashtags.java index 4c568226..87eacfd9 100644 --- a/app/src/main/java/org/nuclearfog/twidda/model/lists/Hashtags.java +++ b/app/src/main/java/org/nuclearfog/twidda/model/lists/Hashtags.java @@ -101,6 +101,12 @@ public class Hashtags extends LinkedList { @Override @NonNull public String toString() { - return "size=" + size() + " min_id=" + prevCursor + " max_id=" + nextCursor; + int itemCount = 0; + for (Hashtag item : this) { + if (item != null) { + itemCount++; + } + } + return "item_count=" + itemCount + " previous=" + getPreviousCursor() + " next=" + getNextCursor(); } } \ No newline at end of file diff --git a/app/src/main/java/org/nuclearfog/twidda/model/lists/Notifications.java b/app/src/main/java/org/nuclearfog/twidda/model/lists/Notifications.java index a16250a8..75981dac 100644 --- a/app/src/main/java/org/nuclearfog/twidda/model/lists/Notifications.java +++ b/app/src/main/java/org/nuclearfog/twidda/model/lists/Notifications.java @@ -1,5 +1,7 @@ package org.nuclearfog.twidda.model.lists; +import androidx.annotation.NonNull; + import org.nuclearfog.twidda.model.Notification; import java.util.LinkedList; @@ -24,4 +26,17 @@ public class Notifications extends LinkedList { public Notifications(Notifications notifications) { super(notifications); } + + + @NonNull + @Override + public String toString() { + int itemCount = 0; + for (Notification item : this) { + if (item != null) { + itemCount++; + } + } + return "item_count=" + itemCount; + } } \ No newline at end of file diff --git a/app/src/main/java/org/nuclearfog/twidda/model/lists/Rules.java b/app/src/main/java/org/nuclearfog/twidda/model/lists/Rules.java index 60025035..cd50be8f 100644 --- a/app/src/main/java/org/nuclearfog/twidda/model/lists/Rules.java +++ b/app/src/main/java/org/nuclearfog/twidda/model/lists/Rules.java @@ -1,5 +1,7 @@ package org.nuclearfog.twidda.model.lists; +import androidx.annotation.NonNull; + import org.nuclearfog.twidda.model.Rule; import java.util.ArrayList; @@ -23,4 +25,11 @@ public class Rules extends ArrayList { public Rules(int cap) { super(cap); } + + + @NonNull + @Override + public String toString() { + return "item_count=" + size(); + } } \ No newline at end of file diff --git a/app/src/main/java/org/nuclearfog/twidda/model/lists/ScheduledStatuses.java b/app/src/main/java/org/nuclearfog/twidda/model/lists/ScheduledStatuses.java index 50dc04f9..f12699b6 100644 --- a/app/src/main/java/org/nuclearfog/twidda/model/lists/ScheduledStatuses.java +++ b/app/src/main/java/org/nuclearfog/twidda/model/lists/ScheduledStatuses.java @@ -1,5 +1,6 @@ package org.nuclearfog.twidda.model.lists; +import androidx.annotation.NonNull; import androidx.annotation.Nullable; import org.nuclearfog.twidda.model.ScheduledStatus; @@ -28,4 +29,17 @@ public class ScheduledStatuses extends LinkedList { public ScheduledStatus get(int index) { return super.get(index); } + + + @NonNull + @Override + public String toString() { + int itemCount = 0; + for (ScheduledStatus item : this) { + if (item != null) { + itemCount++; + } + } + return "item_count=" + itemCount; + } } \ No newline at end of file diff --git a/app/src/main/java/org/nuclearfog/twidda/model/lists/StatusEditHistory.java b/app/src/main/java/org/nuclearfog/twidda/model/lists/StatusEditHistory.java new file mode 100644 index 00000000..a2b7d4d0 --- /dev/null +++ b/app/src/main/java/org/nuclearfog/twidda/model/lists/StatusEditHistory.java @@ -0,0 +1,35 @@ +package org.nuclearfog.twidda.model.lists; + +import androidx.annotation.NonNull; + +import org.nuclearfog.twidda.model.EditedStatus; + +import java.util.LinkedList; + +/** + * @author nuclearfog + */ +public class StatusEditHistory extends LinkedList { + + private static final long serialVersionUID = 6241896565923670373L; + + /** + * + */ + public StatusEditHistory() { + } + + /** + * + */ + public StatusEditHistory(StatusEditHistory items) { + super(items); + } + + + @NonNull + @Override + public String toString() { + return "item_count=" + size(); + } +} diff --git a/app/src/main/java/org/nuclearfog/twidda/model/lists/StatusEditHistoy.java b/app/src/main/java/org/nuclearfog/twidda/model/lists/StatusEditHistoy.java deleted file mode 100644 index 90b177df..00000000 --- a/app/src/main/java/org/nuclearfog/twidda/model/lists/StatusEditHistoy.java +++ /dev/null @@ -1,13 +0,0 @@ -package org.nuclearfog.twidda.model.lists; - -import org.nuclearfog.twidda.model.EditedStatus; - -import java.util.LinkedList; - -/** - * @author nuclearfog - */ -public class StatusEditHistoy extends LinkedList { - - private static final long serialVersionUID = 6241896565923670373L; -} diff --git a/app/src/main/java/org/nuclearfog/twidda/model/lists/Statuses.java b/app/src/main/java/org/nuclearfog/twidda/model/lists/Statuses.java index 892f5b14..b099ebdf 100644 --- a/app/src/main/java/org/nuclearfog/twidda/model/lists/Statuses.java +++ b/app/src/main/java/org/nuclearfog/twidda/model/lists/Statuses.java @@ -1,5 +1,6 @@ package org.nuclearfog.twidda.model.lists; +import androidx.annotation.NonNull; import androidx.annotation.Nullable; import org.nuclearfog.twidda.model.Status; @@ -134,4 +135,17 @@ public class Statuses extends LinkedList { prevCursor = statuses.getPreviousCursor(); nextCursor = statuses.getNextCursor(); } + + + @NonNull + @Override + public String toString() { + int itemCount = 0; + for (Status item : this) { + if (item != null) { + itemCount++; + } + } + return "item_count=" + itemCount + " prev=" + getPreviousCursor() + " next=" + getNextCursor(); + } } \ No newline at end of file diff --git a/app/src/main/java/org/nuclearfog/twidda/model/lists/UserLists.java b/app/src/main/java/org/nuclearfog/twidda/model/lists/UserLists.java index 87156da0..5f8827c5 100644 --- a/app/src/main/java/org/nuclearfog/twidda/model/lists/UserLists.java +++ b/app/src/main/java/org/nuclearfog/twidda/model/lists/UserLists.java @@ -52,11 +52,20 @@ public class UserLists extends LinkedList { } /** - * get next link to a list + * get previous cursor of this list * * @return cursor */ - public long getNext() { + public long getPreviousCursor() { + return prevCursor; + } + + /** + * get next cursor of this list + * + * @return cursor + */ + public long getNextCursor() { return nextCursor; } @@ -111,6 +120,12 @@ public class UserLists extends LinkedList { @Override @NonNull public String toString() { - return "size=" + size() + " previous=" + prevCursor + " next=" + nextCursor; + int itemCount = 0; + for (UserList item : this) { + if (item != null) { + itemCount++; + } + } + return "item_count=" + itemCount + " previous=" + getPreviousCursor() + " next=" + getNextCursor(); } } \ No newline at end of file diff --git a/app/src/main/java/org/nuclearfog/twidda/model/lists/Users.java b/app/src/main/java/org/nuclearfog/twidda/model/lists/Users.java index cb231e91..bd0cfdb3 100644 --- a/app/src/main/java/org/nuclearfog/twidda/model/lists/Users.java +++ b/app/src/main/java/org/nuclearfog/twidda/model/lists/Users.java @@ -54,7 +54,16 @@ public class Users extends LinkedList { } /** - * get next link to a list + * get previous cursor of this list + * + * @return cursor + */ + public long getPreviousCursor() { + return prevCursor; + } + + /** + * get next cursor of this list * * @return cursor */ @@ -96,6 +105,12 @@ public class Users extends LinkedList { @Override @NonNull public String toString() { - return "size=" + size() + " previous=" + prevCursor + " next=" + nextCursor; + int itemCount = 0; + for (User item : this) { + if (item != null) { + itemCount++; + } + } + return "item_count=" + itemCount + " previous=" + getPreviousCursor() + " next=" + getNextCursor(); } } \ No newline at end of file diff --git a/app/src/main/java/org/nuclearfog/twidda/ui/activities/EditHistoryActivity.java b/app/src/main/java/org/nuclearfog/twidda/ui/activities/EditHistoryActivity.java new file mode 100644 index 00000000..3de3f055 --- /dev/null +++ b/app/src/main/java/org/nuclearfog/twidda/ui/activities/EditHistoryActivity.java @@ -0,0 +1,38 @@ +package org.nuclearfog.twidda.ui.activities; + +import android.os.Bundle; +import android.view.ViewGroup; + +import androidx.annotation.Nullable; +import androidx.appcompat.app.AppCompatActivity; +import androidx.appcompat.widget.Toolbar; +import androidx.fragment.app.FragmentTransaction; + +import org.nuclearfog.twidda.R; +import org.nuclearfog.twidda.backend.utils.AppStyles; +import org.nuclearfog.twidda.ui.fragments.EditHistoryFragment; + +/** + * @author nuclearfog + */ +public class EditHistoryActivity extends AppCompatActivity { + + public static final String KEY_ID = EditHistoryFragment.KEY_ID; + + + @Override + protected void onCreate(@Nullable Bundle savedInstanceState) { + super.onCreate(savedInstanceState); + setContentView(R.layout.page_fragment); + ViewGroup root = findViewById(R.id.page_fragment_root); + Toolbar toolbar = findViewById(R.id.page_fragment_toolbar); + + FragmentTransaction fragmentTransaction = getSupportFragmentManager().beginTransaction(); + fragmentTransaction.replace(R.id.page_fragment_container, EditHistoryFragment.class, getIntent().getExtras()); + fragmentTransaction.commit(); + + toolbar.setTitle(R.string.toolbar_status_history); + setSupportActionBar(toolbar); + AppStyles.setTheme(root); + } +} \ No newline at end of file diff --git a/app/src/main/java/org/nuclearfog/twidda/ui/activities/StatusActivity.java b/app/src/main/java/org/nuclearfog/twidda/ui/activities/StatusActivity.java index e1048279..55d3b2c8 100644 --- a/app/src/main/java/org/nuclearfog/twidda/ui/activities/StatusActivity.java +++ b/app/src/main/java/org/nuclearfog/twidda/ui/activities/StatusActivity.java @@ -163,11 +163,12 @@ public class StatusActivity extends AppCompatActivity implements OnClickListener private AudioPlayerDialog audioDialog; private ReportDialog reportDialog; - private TextView status_source, created_at, status_text, screen_name, username, location_name, sensitive, visibility, spoiler, spoiler_hint, translate_text; + private TextView status_source, created_at, status_text, screen_name, username, edited; + private TextView location_name, sensitive, visibility, spoiler, spoiler_hint, translate_text; private Button reply_button, repost_button, like_button, reply_name, repost_name_button; private ImageView profile_image; private Toolbar toolbar; - private View card_container; + private RecyclerView card_list; @Nullable private Status status; @@ -187,7 +188,7 @@ public class StatusActivity extends AppCompatActivity implements OnClickListener super.onCreate(savedInstanceState); setContentView(R.layout.page_status); ViewGroup root = findViewById(R.id.page_status_root); - RecyclerView card_list = findViewById(R.id.page_status_cards); + card_list = findViewById(R.id.page_status_cards); toolbar = findViewById(R.id.page_status_toolbar); reply_button = findViewById(R.id.page_status_reply); repost_button = findViewById(R.id.page_status_repost); @@ -203,10 +204,11 @@ public class StatusActivity extends AppCompatActivity implements OnClickListener sensitive = findViewById(R.id.page_status_sensitive); spoiler = findViewById(R.id.page_status_spoiler); visibility = findViewById(R.id.page_status_visibility); + edited = findViewById(R.id.page_status_edited); repost_name_button = findViewById(R.id.page_status_reposter_reference); translate_text = findViewById(R.id.page_status_text_translate); spoiler_hint = findViewById(R.id.page_status_text_sensitive_hint); - card_container = findViewById(R.id.page_status_cards_container); + //card_container = findViewById(R.id.page_status_cards_container); clip = (ClipboardManager) getSystemService(CLIPBOARD_SERVICE); statusLoader = new StatusAction(this); @@ -229,6 +231,7 @@ public class StatusActivity extends AppCompatActivity implements OnClickListener visibility.setCompoundDrawablesWithIntrinsicBounds(R.drawable.global, 0, 0, 0); reply_name.setCompoundDrawablesWithIntrinsicBounds(R.drawable.back, 0, 0, 0); repost_name_button.setCompoundDrawablesWithIntrinsicBounds(R.drawable.repost, 0, 0, 0); + edited.setCompoundDrawablesWithIntrinsicBounds(R.drawable.edit, 0, 0, 0); status_text.setMovementMethod(LinkAndScrollMovement.getInstance()); status_text.setLinkTextColor(settings.getHighlightColor()); card_list.setLayoutManager(new LinearLayoutManager(this, RecyclerView.HORIZONTAL, false)); @@ -498,7 +501,9 @@ public class StatusActivity extends AppCompatActivity implements OnClickListener } // get edit history else if (item.getItemId() == R.id.menu_status_history) { - // todo implement history viewer + Intent intent = new Intent(this, EditHistoryActivity.class); + intent.putExtra(EditHistoryActivity.KEY_ID, status.getId()); + startActivity(intent); return true; } } @@ -844,6 +849,11 @@ public class StatusActivity extends AppCompatActivity implements OnClickListener } else { sensitive.setVisibility(View.GONE); } + if (status.editedAt() != 0L) { + edited.setVisibility(View.VISIBLE); + } else { + edited.setVisibility(View.GONE); + } // set status spoiler warning if (status.isSpoiler()) { spoiler.setVisibility(View.VISIBLE); @@ -895,11 +905,11 @@ public class StatusActivity extends AppCompatActivity implements OnClickListener } // set status attachment preview if ((status.getCards().length > 0 || status.getMedia().length > 0) || status.getPoll() != null) { - card_container.setVisibility(View.VISIBLE); + card_list.setVisibility(View.VISIBLE); adapter.replaceAll(status, settings.hideSensitiveEnabled()); status_text.setMaxLines(5); } else { - card_container.setVisibility(View.GONE); + card_list.setVisibility(View.GONE); status_text.setMaxLines(10); } } diff --git a/app/src/main/java/org/nuclearfog/twidda/ui/adapter/recyclerview/EditHistoryAdapter.java b/app/src/main/java/org/nuclearfog/twidda/ui/adapter/recyclerview/EditHistoryAdapter.java new file mode 100644 index 00000000..be816da2 --- /dev/null +++ b/app/src/main/java/org/nuclearfog/twidda/ui/adapter/recyclerview/EditHistoryAdapter.java @@ -0,0 +1,62 @@ +package org.nuclearfog.twidda.ui.adapter.recyclerview; + +import android.view.ViewGroup; + +import androidx.annotation.NonNull; +import androidx.recyclerview.widget.RecyclerView; + +import org.nuclearfog.twidda.model.lists.StatusEditHistory; +import org.nuclearfog.twidda.ui.adapter.recyclerview.holder.EditHistoryHolder; + +/** + * RecyclerView adapter for {@link org.nuclearfog.twidda.ui.fragments.EditHistoryFragment} + * + * @author nuclearfog + */ +public class EditHistoryAdapter extends RecyclerView.Adapter { + + private StatusEditHistory items = new StatusEditHistory(); + + + @NonNull + @Override + public EditHistoryHolder onCreateViewHolder(@NonNull ViewGroup parent, int viewType) { + return new EditHistoryHolder(parent); + } + + + @Override + public void onBindViewHolder(@NonNull EditHistoryHolder holder, int position) { + holder.setContent(items.get(position)); + } + + + @Override + public int getItemCount() { + return items.size(); + } + + /** + * + */ + public void setItems(StatusEditHistory items) { + this.items.clear(); + this.items.addAll(items); + notifyDataSetChanged(); + } + + /** + * + */ + public StatusEditHistory getItems() { + return new StatusEditHistory(items); + } + + /** + * + */ + public void clear() { + items.clear(); + notifyDataSetChanged(); + } +} diff --git a/app/src/main/java/org/nuclearfog/twidda/ui/adapter/recyclerview/IconAdapter.java b/app/src/main/java/org/nuclearfog/twidda/ui/adapter/recyclerview/IconAdapter.java index 55616449..dc58cf11 100644 --- a/app/src/main/java/org/nuclearfog/twidda/ui/adapter/recyclerview/IconAdapter.java +++ b/app/src/main/java/org/nuclearfog/twidda/ui/adapter/recyclerview/IconAdapter.java @@ -6,6 +6,7 @@ import androidx.annotation.NonNull; import androidx.annotation.Nullable; import androidx.recyclerview.widget.RecyclerView.Adapter; +import org.nuclearfog.twidda.model.EditedStatus; import org.nuclearfog.twidda.model.Media; import org.nuclearfog.twidda.model.Status; import org.nuclearfog.twidda.ui.adapter.recyclerview.holder.IconHolder; @@ -99,6 +100,23 @@ public class IconAdapter extends Adapter implements OnHolderClickLis notifyDataSetChanged(); } + /** + * add icons using edited status information + */ + public void setItems(EditedStatus status) { + items.clear(); + if (status.getMedia().length > 0) { + addMediaIcons(status.getMedia()); + } + if (status.getLocation() != null) { + items.add(IconHolder.TYPE_LOCATION); + } + if (status.getPoll() != null) { + items.add(IconHolder.TYPE_POLL); + } + notifyDataSetChanged(); + } + /** * set media icons */ diff --git a/app/src/main/java/org/nuclearfog/twidda/ui/adapter/recyclerview/UserlistAdapter.java b/app/src/main/java/org/nuclearfog/twidda/ui/adapter/recyclerview/UserlistAdapter.java index cd54d3f2..9545430a 100644 --- a/app/src/main/java/org/nuclearfog/twidda/ui/adapter/recyclerview/UserlistAdapter.java +++ b/app/src/main/java/org/nuclearfog/twidda/ui/adapter/recyclerview/UserlistAdapter.java @@ -108,7 +108,7 @@ public class UserlistAdapter extends Adapter implements OnHolderClic @Override public boolean onPlaceholderClick(int index) { - boolean actionPerformed = listener.onPlaceholderClick(userlists.getNext(), index); + boolean actionPerformed = listener.onPlaceholderClick(userlists.getNextCursor(), index); if (actionPerformed) loadingIndex = index; return actionPerformed; @@ -133,17 +133,17 @@ public class UserlistAdapter extends Adapter implements OnHolderClic disableLoading(); if (index < 0) { userlists.replaceAll(newUserlists); - if (userlists.getNext() != 0L) { + if (userlists.getNextCursor() != 0L) { // Add placeholder userlists.add(null); } notifyDataSetChanged(); } else { userlists.addAll(index, newUserlists); - if (userlists.getNext() != 0L && userlists.peekLast() != null) { + if (userlists.getNextCursor() != 0L && userlists.peekLast() != null) { userlists.add(null); notifyItemRangeInserted(index, newUserlists.size() + 1); - } else if (userlists.getNext() == 0L && userlists.peekLast() == null) { + } else if (userlists.getNextCursor() == 0L && userlists.peekLast() == null) { userlists.pollLast(); notifyItemRangeInserted(index, newUserlists.size() - 1); } else { @@ -159,7 +159,7 @@ public class UserlistAdapter extends Adapter implements OnHolderClic */ public void replaceItems(UserLists newUserlists) { userlists.replaceAll(newUserlists); - if (userlists.getNext() != 0L && userlists.peekLast() != null) { + if (userlists.getNextCursor() != 0L && userlists.peekLast() != null) { // Add placeholder userlists.add(null); } diff --git a/app/src/main/java/org/nuclearfog/twidda/ui/adapter/recyclerview/holder/EditHistoryHolder.java b/app/src/main/java/org/nuclearfog/twidda/ui/adapter/recyclerview/holder/EditHistoryHolder.java new file mode 100644 index 00000000..cd3e20b1 --- /dev/null +++ b/app/src/main/java/org/nuclearfog/twidda/ui/adapter/recyclerview/holder/EditHistoryHolder.java @@ -0,0 +1,191 @@ +package org.nuclearfog.twidda.ui.adapter.recyclerview.holder; + +import android.graphics.Color; +import android.graphics.drawable.ColorDrawable; +import android.graphics.drawable.Drawable; +import android.text.Spannable; +import android.text.SpannableString; +import android.view.LayoutInflater; +import android.view.View; +import android.view.ViewGroup; +import android.widget.ImageView; +import android.widget.TextView; + +import androidx.annotation.NonNull; +import androidx.cardview.widget.CardView; +import androidx.recyclerview.widget.LinearLayoutManager; +import androidx.recyclerview.widget.RecyclerView; +import androidx.recyclerview.widget.RecyclerView.ViewHolder; + +import com.squareup.picasso.Picasso; +import com.squareup.picasso.Transformation; + +import org.nuclearfog.tag.Tagger; +import org.nuclearfog.textviewtool.LinkAndScrollMovement; +import org.nuclearfog.twidda.R; +import org.nuclearfog.twidda.backend.async.AsyncExecutor; +import org.nuclearfog.twidda.backend.async.TextEmojiLoader; +import org.nuclearfog.twidda.backend.image.PicassoBuilder; +import org.nuclearfog.twidda.backend.utils.AppStyles; +import org.nuclearfog.twidda.backend.utils.EmojiUtils; +import org.nuclearfog.twidda.backend.utils.StringUtils; +import org.nuclearfog.twidda.config.GlobalSettings; +import org.nuclearfog.twidda.model.EditedStatus; +import org.nuclearfog.twidda.model.User; +import org.nuclearfog.twidda.ui.adapter.recyclerview.IconAdapter; + +import java.util.Random; + +import jp.wasabeef.picasso.transformations.RoundedCornersTransformation; + +/** + * View holder for {@link org.nuclearfog.twidda.ui.adapter.recyclerview.EditHistoryAdapter} + * + * @author nuclearfog + */ +public class EditHistoryHolder extends ViewHolder { + + private static final int EMPTY_COLOR = 0x2F000000; + + private static final int IMG_SIZE = 150; + + private static final Random RND = new Random(); + + private ImageView profile, icon_lock; + private RecyclerView icon_list; + private TextView username, screen_name, text, spoiler, sensitive, timestamp; + + private GlobalSettings settings; + private Picasso picasso; + private TextEmojiLoader emojiLoader; + private Drawable placeholder; + private IconAdapter adapter; + + private long tagId = RND.nextLong(); + private AsyncExecutor.AsyncCallback textResult = this::setTextEmojis; + private AsyncExecutor.AsyncCallback usernameResult = this::setUsernameEmojis; + + /** + * + */ + public EditHistoryHolder(ViewGroup parent) { + super(LayoutInflater.from(parent.getContext()).inflate(R.layout.item_edit_status, parent, false)); + settings = GlobalSettings.get(parent.getContext()); + picasso = PicassoBuilder.get(parent.getContext()); + placeholder = new ColorDrawable(EMPTY_COLOR); + emojiLoader = new TextEmojiLoader(parent.getContext()); + adapter = new IconAdapter(null, false); + + CardView card = (CardView) itemView; + ViewGroup container = itemView.findViewById(R.id.item_edit_status_container); + profile = itemView.findViewById(R.id.item_edit_status_profile); + username = itemView.findViewById(R.id.item_edit_status_username); + screen_name = itemView.findViewById(R.id.item_edit_status_screenname); + text = itemView.findViewById(R.id.item_edit_status_text); + spoiler = itemView.findViewById(R.id.item_edit_status_spoiler); + sensitive = itemView.findViewById(R.id.item_edit_status_sensitive); + icon_list = itemView.findViewById(R.id.item_edit_status_attachments); + icon_lock = itemView.findViewById(R.id.item_edit_status_locked_icon); + timestamp = itemView.findViewById(R.id.item_edit_status_created_at); + + spoiler.setCompoundDrawablesWithIntrinsicBounds(R.drawable.exclamation, 0, 0, 0); + sensitive.setCompoundDrawablesWithIntrinsicBounds(R.drawable.sensitive, 0, 0, 0); + icon_list.setLayoutManager(new LinearLayoutManager(parent.getContext(), LinearLayoutManager.HORIZONTAL, false)); + icon_list.setAdapter(adapter); + text.setMovementMethod(LinkAndScrollMovement.getInstance()); + AppStyles.setTheme(container, Color.TRANSPARENT); + card.setCardBackgroundColor(settings.getCardColor()); + } + + /** + * set view content + * + * @param editedStatus edited status content + */ + public void setContent(EditedStatus editedStatus) { + User author = editedStatus.getAuthor(); + // set profile image + if (settings.imagesEnabled() && !author.getProfileImageThumbnailUrl().isEmpty()) { + Transformation roundCorner = new RoundedCornersTransformation(2, 0); + picasso.load(author.getProfileImageThumbnailUrl()).transform(roundCorner).resize(IMG_SIZE, IMG_SIZE) + .placeholder(placeholder).centerCrop().error(R.drawable.no_image).into(profile); + } else { + profile.setImageDrawable(placeholder); + } + // set status text and emojis + if (!editedStatus.getText().trim().isEmpty()) { + Spannable textSpan = Tagger.makeTextWithLinks(editedStatus.getText(), settings.getHighlightColor()); + if (editedStatus.getEmojis().length > 0 && settings.imagesEnabled()) { + TextEmojiLoader.Param param = new TextEmojiLoader.Param(tagId, editedStatus.getEmojis(), textSpan, text.getResources().getDimensionPixelSize(R.dimen.item_status_icon_size)); + emojiLoader.execute(param, textResult); + textSpan = EmojiUtils.removeTags(textSpan); + } + text.setText(textSpan); + text.setVisibility(View.VISIBLE); + } else { + text.setVisibility(View.GONE); + } + // set username and emojis + if (author.getEmojis().length > 0 && !author.getUsername().trim().isEmpty() && settings.imagesEnabled()) { + SpannableString usernameSpan = new SpannableString(author.getUsername()); + TextEmojiLoader.Param param = new TextEmojiLoader.Param(tagId, author.getEmojis(), usernameSpan, username.getResources().getDimensionPixelSize(R.dimen.item_status_icon_size)); + emojiLoader.execute(param, usernameResult); + username.setText(EmojiUtils.removeTags(usernameSpan)); + } else { + username.setText(author.getUsername()); + } + if (author.isProtected()) { + icon_lock.setVisibility(View.VISIBLE); + } else { + icon_lock.setVisibility(View.GONE); + } + if (editedStatus.isSpoiler()) { + spoiler.setVisibility(View.VISIBLE); + } else { + spoiler.setVisibility(View.GONE); + } + if (editedStatus.isSensitive()) { + sensitive.setVisibility(View.VISIBLE); + } else { + sensitive.setVisibility(View.GONE); + } + // setup attachment indicators + if (settings.statusIndicatorsEnabled()) { + icon_list.setVisibility(View.VISIBLE); + adapter.setItems(editedStatus); + if (adapter.isEmpty()) { + icon_list.setVisibility(View.GONE); + } else { + icon_list.setVisibility(View.VISIBLE); + } + } else { + icon_list.setVisibility(View.GONE); + } + screen_name.setText(author.getScreenname()); + timestamp.setText(StringUtils.formatCreationTime(timestamp.getResources(), editedStatus.getTimestamp())); + } + + /** + * update username + * + * @param result username with emojis + */ + private void setUsernameEmojis(@NonNull TextEmojiLoader.Result result) { + if (result.id == tagId && result.images != null) { + Spannable spannable = EmojiUtils.addEmojis(username.getContext(), result.spannable, result.images); + username.setText(spannable); + } + } + + /** + * update status text + * + * @param result status text with emojis + */ + private void setTextEmojis(@NonNull TextEmojiLoader.Result result) { + if (result.id == tagId && result.images != null) { + Spannable spannable = EmojiUtils.addEmojis(text.getContext(), result.spannable, result.images); + text.setText(spannable); + } + } +} \ No newline at end of file diff --git a/app/src/main/java/org/nuclearfog/twidda/ui/fragments/EditHistoryFragment.java b/app/src/main/java/org/nuclearfog/twidda/ui/fragments/EditHistoryFragment.java new file mode 100644 index 00000000..b3938be4 --- /dev/null +++ b/app/src/main/java/org/nuclearfog/twidda/ui/fragments/EditHistoryFragment.java @@ -0,0 +1,90 @@ +package org.nuclearfog.twidda.ui.fragments; + +import android.os.Bundle; +import android.view.View; + +import androidx.annotation.NonNull; +import androidx.annotation.Nullable; + +import org.nuclearfog.twidda.backend.async.AsyncExecutor.AsyncCallback; +import org.nuclearfog.twidda.backend.async.EditHistoryLoader; +import org.nuclearfog.twidda.backend.utils.ErrorUtils; +import org.nuclearfog.twidda.model.lists.StatusEditHistory; +import org.nuclearfog.twidda.ui.adapter.recyclerview.EditHistoryAdapter; + +/** + * Status edit history fragment + * + * @author nuclearfog + * @see org.nuclearfog.twidda.ui.activities.EditHistoryActivity + */ +public class EditHistoryFragment extends ListFragment implements AsyncCallback { + + public static final String KEY_ID = "status-id"; + + private static final String KEY_DATA = "history-save"; + + private EditHistoryLoader historyLoader; + private EditHistoryAdapter adapter; + + private long id; + + + @Override + public void onViewCreated(@NonNull View view, @Nullable Bundle savedInstanceState) { + historyLoader = new EditHistoryLoader(requireContext()); + adapter = new EditHistoryAdapter(); + setAdapter(adapter); + + if (getArguments() != null) { + id = getArguments().getLong(KEY_ID); + } + if (savedInstanceState != null) { + Object data = savedInstanceState.getSerializable(KEY_DATA); + if (data instanceof StatusEditHistory) { + adapter.setItems((StatusEditHistory) data); + } + } else { + historyLoader.execute(id, this); + setRefresh(true); + } + } + + + @Override + public void onSaveInstanceState(@NonNull Bundle outState) { + outState.putSerializable(KEY_DATA, adapter.getItems()); + super.onSaveInstanceState(outState); + } + + + @Override + public void onDestroy() { + historyLoader.cancel(); + super.onDestroy(); + } + + + @Override + protected void onReload() { + historyLoader.execute(id, this); + } + + + @Override + protected void onReset() { + adapter.clear(); + historyLoader.execute(id, this); + } + + + @Override + public void onResult(@NonNull EditHistoryLoader.Result result) { + if (result.history != null) { + adapter.setItems(result.history); + } else { + ErrorUtils.showErrorMessage(requireContext(), result.exception); + } + setRefresh(false); + } +} diff --git a/app/src/main/res/drawable/edit.xml b/app/src/main/res/drawable/edit.xml new file mode 100644 index 00000000..0e5050c5 --- /dev/null +++ b/app/src/main/res/drawable/edit.xml @@ -0,0 +1,9 @@ + + + diff --git a/app/src/main/res/layout/item_edit_status.xml b/app/src/main/res/layout/item_edit_status.xml new file mode 100644 index 00000000..da1804df --- /dev/null +++ b/app/src/main/res/layout/item_edit_status.xml @@ -0,0 +1,143 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/app/src/main/res/layout/page_status.xml b/app/src/main/res/layout/page_status.xml index 29ddaa9a..bc151379 100644 --- a/app/src/main/res/layout/page_status.xml +++ b/app/src/main/res/layout/page_status.xml @@ -174,39 +174,43 @@ app:layout_constraintStart_toStartOf="@id/page_status_text" app:layout_constraintTop_toBottomOf="@id/page_status_text" /> - - - - - + app:layout_constraintEnd_toEndOf="parent" /> + app:layout_constraintTop_toBottomOf="@id/page_status_cards" + app:layout_constraintEnd_toStartOf="@id/page_status_edited" /> + + + app:layout_constraintTop_toBottomOf="@id/page_status_cards" /> Liste entfolgen? \u0020Posts Verbindung fehlgeschlagen! + bearbeitet Nutzernamen eingeben Profilbanner Banner einfügen @@ -157,6 +158,7 @@ Filter verwalten Einstellungen erstelle Nutzerliste + Bearbeitungsverlauf jetzt folgen Nutzerlisten diff --git a/app/src/main/res/values/dimens.xml b/app/src/main/res/values/dimens.xml index 18a7b57b..2d540626 100644 --- a/app/src/main/res/values/dimens.xml +++ b/app/src/main/res/values/dimens.xml @@ -39,7 +39,7 @@ 12sp 18sp 12sp - 5dp + 5dp 130sp 17sp 5 @@ -266,12 +266,22 @@ 5dp 13sp - + 3dp 24dp 2dp 5dp + + 36sp + 5dp + 24dp + 5dp + 5dp + 14sp + 12sp + 15sp + 20sp 11sp diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml index 4bf9f54a..6fca59a9 100644 --- a/app/src/main/res/values/strings.xml +++ b/app/src/main/res/values/strings.xml @@ -267,6 +267,7 @@ delete userlist? unfollow list? \u0020Posts + edited enter username Banner Image add banner @@ -322,6 +323,7 @@ User favoriting this status User liking this status scheduled Posts + Edit history now Reply Media preview