Change profile activity to manage featured hashtags

This commit is contained in:
Thomas 2025-06-06 09:50:06 +02:00
parent d514c7b8d6
commit c0b6bbd4ea
4 changed files with 226 additions and 20 deletions

View file

@ -27,6 +27,7 @@ import android.text.Html;
import android.view.Menu;
import android.view.MenuItem;
import android.view.View;
import android.view.ViewGroup;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
@ -45,10 +46,13 @@ import java.util.Objects;
import app.fedilab.android.BaseMainActivity;
import app.fedilab.android.BuildConfig;
import app.fedilab.android.R;
import app.fedilab.android.databinding.AccountFeaturedHashtagItemBinding;
import app.fedilab.android.databinding.AccountFieldItemBinding;
import app.fedilab.android.databinding.ActivityEditProfileBinding;
import app.fedilab.android.mastodon.client.entities.api.Account;
import app.fedilab.android.mastodon.client.entities.api.FeaturedTag;
import app.fedilab.android.mastodon.client.entities.api.Field;
import app.fedilab.android.mastodon.client.entities.api.Tag;
import app.fedilab.android.mastodon.client.entities.app.CachedBundle;
import app.fedilab.android.mastodon.exception.DBException;
import app.fedilab.android.mastodon.helper.Helper;
@ -62,6 +66,8 @@ public class EditProfileActivity extends BaseBarActivity {
public static final int PICK_MEDIA_HEADER = 5706;
private ActivityEditProfileBinding binding;
private AccountsVM accountsVM;
private static final int MAX_FIELDS = 4;
private static final int MAX_FEATURED_HASHTAGS = 10;
@SuppressLint("ClickableViewAccessibility")
@Override
@ -87,8 +93,9 @@ public class EditProfileActivity extends BaseBarActivity {
return false;
});
accountsVM = new ViewModelProvider(EditProfileActivity.this).get(AccountsVM.class);
new ViewModelProvider(EditProfileActivity.this).get(AccountsVM.class).getConnectedAccount(BaseMainActivity.currentInstance, BaseMainActivity.currentToken)
accountsVM.getConnectedAccount(BaseMainActivity.currentInstance, BaseMainActivity.currentToken)
.observe(EditProfileActivity.this, account -> {
if (account != null) {
Helper.setCurrentAccountMastodonAccount(EditProfileActivity.this, account);
@ -97,9 +104,10 @@ public class EditProfileActivity extends BaseBarActivity {
Helper.sendToastMessage(getApplication(), Helper.RECEIVE_TOAST_TYPE_ERROR, getString(R.string.toast_error));
}
});
}
}
@SuppressWarnings("deprecation")
private void initializeView() {
//Hydrate values
@ -113,6 +121,68 @@ public class EditProfileActivity extends BaseBarActivity {
else
bio = Html.fromHtml(Helper.getCurrentAccount(EditProfileActivity.this).mastodon_account.note).toString();
binding.bio.setText(bio);
accountsVM.getFeaturedTagsSuggestions(BaseMainActivity.currentInstance, BaseMainActivity.currentToken).observe(this, featuredTags -> {
StringBuilder text = new StringBuilder(getString(R.string.no_feature_hashtag_suggestion));
if(featuredTags != null && !featuredTags.isEmpty()) {
text = new StringBuilder();
for (Tag tag : featuredTags) {
text.append(String.format("#%s ", tag.name));
}
}
binding.featuredHashtagsSuggestions.setText(text);
});
accountsVM.getAccountFeaturedTags(BaseMainActivity.currentInstance, BaseMainActivity.currentToken, Helper.getCurrentAccount(EditProfileActivity.this).mastodon_account.id).observe(this, featuredTags -> {
if(featuredTags != null && !featuredTags.isEmpty()) {
for (FeaturedTag featuredTag : featuredTags) {
AccountFeaturedHashtagItemBinding featuredHashtagItemBinding = AccountFeaturedHashtagItemBinding.inflate(getLayoutInflater());
featuredHashtagItemBinding.name.setText(featuredTag.name);
featuredHashtagItemBinding.remove.setOnClickListener(v -> {
AlertDialog.Builder deleteConfirm = new MaterialAlertDialogBuilder(EditProfileActivity.this);
deleteConfirm.setTitle(getString(R.string.delete_featured_hashtag));
deleteConfirm.setMessage(getString(R.string.delete_featured_hashtag_confirm));
deleteConfirm.setNegativeButton(R.string.cancel, (dialog, which) -> dialog.dismiss());
deleteConfirm.setPositiveButton(R.string.delete, (dialog, which) -> {
((ViewGroup)featuredHashtagItemBinding.getRoot().getParent()).removeView(featuredHashtagItemBinding.getRoot());
if (binding.featuredHashtagsContainer.getChildCount() >= MAX_FEATURED_HASHTAGS) {
binding.addFeaturedHashtags.setVisibility(View.GONE);
} else {
binding.addFeaturedHashtags.setVisibility(View.VISIBLE);
}
dialog.dismiss();
});
deleteConfirm.create().show();
});
binding.featuredHashtagsContainer.addView(featuredHashtagItemBinding.getRoot());
}
}
binding.addFeaturedHashtags.setOnClickListener(view -> {
AccountFeaturedHashtagItemBinding featuredHashtagItemBinding = AccountFeaturedHashtagItemBinding.inflate(getLayoutInflater());
featuredHashtagItemBinding.remove.setOnClickListener(v -> {
AlertDialog.Builder deleteConfirm = new MaterialAlertDialogBuilder(EditProfileActivity.this);
deleteConfirm.setTitle(getString(R.string.delete_featured_hashtag));
deleteConfirm.setMessage(getString(R.string.delete_featured_hashtag_confirm));
deleteConfirm.setNegativeButton(R.string.cancel, (dialog, which) -> dialog.dismiss());
deleteConfirm.setPositiveButton(R.string.delete, (dialog, which) -> {
((ViewGroup)featuredHashtagItemBinding.getRoot().getParent()).removeView(featuredHashtagItemBinding.getRoot());
if (binding.featuredHashtagsContainer.getChildCount() >= MAX_FEATURED_HASHTAGS) {
binding.addFeaturedHashtags.setVisibility(View.GONE);
} else {
binding.addFeaturedHashtags.setVisibility(View.VISIBLE);
}
dialog.dismiss();
});
deleteConfirm.create().show();
});
binding.featuredHashtagsContainer.addView(featuredHashtagItemBinding.getRoot());
if (binding.featuredHashtagsContainer.getChildCount() >= MAX_FEATURED_HASHTAGS) {
binding.addFeaturedHashtags.setVisibility(View.GONE);
} else {
binding.addFeaturedHashtags.setVisibility(View.VISIBLE);
}
});
});
if (Helper.getCurrentAccount(EditProfileActivity.this).mastodon_account.source != null) {
binding.sensitive.setChecked(Helper.getCurrentAccount(EditProfileActivity.this).mastodon_account.source.sensitive);
switch (Helper.getCurrentAccount(EditProfileActivity.this).mastodon_account.source.privacy) {
@ -135,7 +205,7 @@ public class EditProfileActivity extends BaseBarActivity {
binding.unlocked.setChecked(true);
}
List<Field> fields = Helper.getCurrentAccount(EditProfileActivity.this).mastodon_account.fields;
if (fields != null && fields.size() > 0) {
if (fields != null && !fields.isEmpty()) {
for (Field field : fields) {
AccountFieldItemBinding fieldItemBinding = AccountFieldItemBinding.inflate(getLayoutInflater());
fieldItemBinding.name.setText(field.name);
@ -151,11 +221,11 @@ public class EditProfileActivity extends BaseBarActivity {
deleteConfirm.setMessage(getString(R.string.delete_field_confirm));
deleteConfirm.setNegativeButton(R.string.cancel, (dialog, which) -> dialog.dismiss());
deleteConfirm.setPositiveButton(R.string.delete, (dialog, which) -> {
binding.fieldsContainer.removeView(fieldItemBinding.getRoot());
if (binding.fieldsContainer.getChildCount() < 4) {
binding.fieldsContainer.setVisibility(View.VISIBLE);
((ViewGroup)fieldItemBinding.getRoot().getParent()).removeView(fieldItemBinding.getRoot());
if (binding.fieldsContainer.getChildCount() >= MAX_FIELDS) {
binding.addField.setVisibility(View.GONE);
} else {
binding.fieldsContainer.setVisibility(View.GONE);
binding.addField.setVisibility(View.VISIBLE);
}
dialog.dismiss();
});
@ -163,7 +233,11 @@ public class EditProfileActivity extends BaseBarActivity {
});
binding.fieldsContainer.addView(fieldItemBinding.getRoot());
}
if (binding.fieldsContainer.getChildCount() >= MAX_FIELDS) {
binding.addField.setVisibility(View.GONE);
} else {
binding.addField.setVisibility(View.VISIBLE);
}
}
binding.addField.setOnClickListener(view -> {
AccountFieldItemBinding fieldItemBinding = AccountFieldItemBinding.inflate(getLayoutInflater());
@ -173,25 +247,24 @@ public class EditProfileActivity extends BaseBarActivity {
deleteConfirm.setMessage(getString(R.string.delete_field_confirm));
deleteConfirm.setNegativeButton(R.string.cancel, (dialog, which) -> dialog.dismiss());
deleteConfirm.setPositiveButton(R.string.delete, (dialog, which) -> {
binding.fieldsContainer.removeView(fieldItemBinding.getRoot());
if (binding.fieldsContainer.getChildCount() < 4) {
binding.fieldsContainer.setVisibility(View.VISIBLE);
((ViewGroup)fieldItemBinding.getRoot().getParent()).removeView(fieldItemBinding.getRoot());
if (binding.fieldsContainer.getChildCount() >= MAX_FIELDS) {
binding.addField.setVisibility(View.GONE);
} else {
binding.fieldsContainer.setVisibility(View.GONE);
binding.addField.setVisibility(View.VISIBLE);
}
dialog.dismiss();
});
deleteConfirm.create().show();
});
binding.fieldsContainer.addView(fieldItemBinding.getRoot());
if (binding.fieldsContainer.getChildCount() >= 4) {
if (binding.fieldsContainer.getChildCount() >= MAX_FIELDS) {
binding.addField.setVisibility(View.GONE);
} else {
binding.addField.setVisibility(View.VISIBLE);
}
});
//Actions with the activity
accountsVM = new ViewModelProvider(EditProfileActivity.this).get(AccountsVM.class);
binding.headerSelect.setOnClickListener(view -> startActivityForResult(prepareIntent(), EditProfileActivity.PICK_MEDIA_HEADER));
binding.avatarSelect.setOnClickListener(view -> startActivityForResult(prepareIntent(), EditProfileActivity.PICK_MEDIA_AVATAR));
@ -280,7 +353,7 @@ public class EditProfileActivity extends BaseBarActivity {
intent.setType("*/*");
String[] mimetypes;
long max_size = -1;
if (instanceInfo.getMimeTypeImage().size() > 0) {
if (!instanceInfo.getMimeTypeImage().isEmpty()) {
mimetypes = instanceInfo.getMimeTypeImage().toArray(new String[0]);
max_size = instanceInfo.configuration.media_attachments.image_size_limit;
} else {

View file

@ -0,0 +1,63 @@
<?xml version="1.0" encoding="utf-8"?><!--
Copyright 2025 Thomas Schneider
This file is a part of Fedilab
This program is free software; you can redistribute it and/or modify it under the terms of the
GNU General Public License as published by the Free Software Foundation; either version 3 of the
License, or (at your option) any later version.
Fedilab is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even
the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General
Public License for more details.
You should have received a copy of the GNU General Public License along with Fedilab; if not,
see <http://www.gnu.org/licenses>
-->
<com.google.android.material.card.MaterialCardView xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:clipChildren="false"
android:clipToPadding="false"
android:layout_marginTop="6dp"
app:cardElevation="0dp"
app:strokeWidth="0dp">
<androidx.appcompat.widget.LinearLayoutCompat
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_margin="5dp"
android:gravity="center_vertical"
android:orientation="horizontal">
<com.google.android.material.textfield.TextInputEditText
android:id="@+id/name"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_weight="1"
android:gravity="center_vertical"
tools:text="@tools:sample/first_names" />
<com.google.android.material.button.MaterialButton
android:id="@+id/remove"
style="@style/Widget.Material3.Button.OutlinedButton.Icon"
android:layout_width="36dp"
android:layout_height="36dp"
android:contentDescription="@string/delete_field"
android:insetTop="0dp"
android:insetBottom="0dp"
android:padding="0dp"
android:textColor="?colorError"
app:icon="@drawable/ic_compose_attachment_remove"
app:iconGravity="textStart"
app:iconPadding="0dp"
app:iconTint="?colorError"
app:layout_constraintStart_toStartOf="@id/banner_pp"
app:layout_constraintTop_toTopOf="@id/banner_pp"
app:strokeColor="?colorError" />
</androidx.appcompat.widget.LinearLayoutCompat>
</com.google.android.material.card.MaterialCardView>

View file

@ -161,6 +161,16 @@
app:layout_constraintTop_toBottomOf="@id/acct"
tools:text="@tools:sample/lorem/random" />
<com.google.android.material.textview.MaterialTextView
android:id="@+id/fields_label"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginHorizontal="12dp"
android:layout_marginTop="12dp"
android:text="@string/fields_title"
android:textAppearance="@style/TextAppearance.Material3.BodyMedium"
app:layout_constraintTop_toBottomOf="@id/bio" />
<androidx.appcompat.widget.LinearLayoutCompat
android:id="@+id/fields"
android:layout_width="match_parent"
@ -168,7 +178,7 @@
android:layout_marginHorizontal="12dp"
android:layout_marginTop="12dp"
android:orientation="vertical"
app:layout_constraintTop_toBottomOf="@id/bio">
app:layout_constraintTop_toBottomOf="@id/fields_label">
<androidx.appcompat.widget.LinearLayoutCompat
android:id="@+id/fields_container"
@ -188,6 +198,60 @@
</androidx.appcompat.widget.LinearLayoutCompat>
<com.google.android.material.textview.MaterialTextView
android:id="@+id/featured_hashtags_label"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginHorizontal="12dp"
android:layout_marginTop="12dp"
android:text="@string/featured_hashtags_title"
android:textAppearance="@style/TextAppearance.Material3.BodyMedium"
app:layout_constraintTop_toBottomOf="@id/fields" />
<androidx.appcompat.widget.LinearLayoutCompat
android:id="@+id/featured_hashtags"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginHorizontal="12dp"
android:layout_marginTop="12dp"
android:orientation="vertical"
app:layout_constraintTop_toBottomOf="@id/featured_hashtags_label">
<androidx.appcompat.widget.LinearLayoutCompat
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="horizontal">
<com.google.android.material.textview.MaterialTextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="@string/Suggestions"/>
<com.google.android.material.textview.MaterialTextView
android:id="@+id/featured_hashtags_suggestions"
android:layout_marginStart="10dp"
android:layout_width="0dp"
android:layout_weight="1"
android:layout_height="wrap_content"
android:text="@string/Suggestions"/>
</androidx.appcompat.widget.LinearLayoutCompat>
<androidx.appcompat.widget.LinearLayoutCompat
android:layout_marginTop="5dp"
android:id="@+id/featured_hashtags_container"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="vertical" />
<com.google.android.material.button.MaterialButton
android:id="@+id/add_featured_hashtags"
style="@style/Widget.Material3.Button.OutlinedButton"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="end"
android:layout_marginVertical="6dp"
android:text="@string/add_featured_hashtag"
app:icon="@drawable/ic_baseline_add_24" />
</androidx.appcompat.widget.LinearLayoutCompat>
<com.google.android.material.textview.MaterialTextView
android:id="@+id/visibility_label"
android:layout_width="match_parent"
@ -196,7 +260,7 @@
android:layout_marginTop="12dp"
android:text="@string/toots_visibility_title"
android:textAppearance="@style/TextAppearance.Material3.BodyMedium"
app:layout_constraintTop_toBottomOf="@id/fields" />
app:layout_constraintTop_toBottomOf="@id/featured_hashtags" />
<com.google.android.material.button.MaterialButtonToggleGroup
android:id="@+id/visibility_group"

View file

@ -732,13 +732,19 @@
<string name="interactions">Interactions</string>
<string name="add_filter">Add filter</string>
<string name="add_field">Add Field</string>
<string name="add_featured_hashtag">Add Featured Hashtag</string>
<string name="no_feature_hashtag_suggestion">No suggestions for featured hashtags!</string>
<string name="unlocked">Unlocked</string>
<string name="locked">Locked</string>
<string name="save_changes">Save changes</string>
<string name="set_bot_content">Bot account</string>
<string name="fields_title">Add or remove fields</string>
<string name="featured_hashtags_title">Add or remove featured hashtags</string>
<string name="set_discoverable_content">Account discoverable</string>
<string name="delete_field">Delete field</string>
<string name="delete_featured_hashtag">Delete featured hashtag</string>
<string name="delete_field_confirm">Are you sure you want to delete that field?</string>
<string name="delete_featured_hashtag_confirm">Are you sure you want to delete that featured hashtag?</string>
<string name="profiled_updated">Profile has been updated!</string>
<string name="not_valid_list_name">List name is not valid!</string>
<string name="no_account_in_list">No accounts found for this list!</string>