feat(profile): add note (#918)
* feat(profile): add note * simplify note code * adjust spacing * size and hitbox adjustments, progress * profile menu item to add note --------- Co-authored-by: sk <sk22@mailbox.org>
This commit is contained in:
parent
c85af5502d
commit
8a5b36db96
|
@ -0,0 +1,19 @@
|
|||
package org.joinmastodon.android.api.requests.accounts;
|
||||
|
||||
import org.joinmastodon.android.api.MastodonAPIRequest;
|
||||
import org.joinmastodon.android.model.Relationship;
|
||||
|
||||
public class SetPrivateNote extends MastodonAPIRequest<Relationship>{
|
||||
public SetPrivateNote(String id, String comment){
|
||||
super(MastodonAPIRequest.HttpMethod.POST, "/accounts/"+id+"/note", Relationship.class);
|
||||
Request req = new Request(comment);
|
||||
setRequestBody(req);
|
||||
}
|
||||
|
||||
private static class Request{
|
||||
public String comment;
|
||||
public Request(String comment){
|
||||
this.comment=comment;
|
||||
}
|
||||
}
|
||||
}
|
|
@ -21,8 +21,11 @@ import android.graphics.drawable.LayerDrawable;
|
|||
import android.net.Uri;
|
||||
import android.os.Build;
|
||||
import android.os.Bundle;
|
||||
import android.text.Editable;
|
||||
import android.text.InputType;
|
||||
import android.text.SpannableStringBuilder;
|
||||
import android.text.TextUtils;
|
||||
import android.text.TextWatcher;
|
||||
import android.transition.ChangeBounds;
|
||||
import android.transition.Fade;
|
||||
import android.transition.TransitionManager;
|
||||
|
@ -56,6 +59,7 @@ import org.joinmastodon.android.api.requests.accounts.GetAccountRelationships;
|
|||
import org.joinmastodon.android.api.requests.accounts.GetAccountStatuses;
|
||||
import org.joinmastodon.android.api.requests.accounts.GetOwnAccount;
|
||||
import org.joinmastodon.android.api.requests.accounts.SetAccountFollowed;
|
||||
import org.joinmastodon.android.api.requests.accounts.SetPrivateNote;
|
||||
import org.joinmastodon.android.api.requests.accounts.UpdateAccountCredentials;
|
||||
import org.joinmastodon.android.api.requests.instance.GetInstance;
|
||||
import org.joinmastodon.android.api.session.AccountSessionManager;
|
||||
|
@ -145,7 +149,7 @@ public class ProfileFragment extends LoaderFragment implements OnBackPressedList
|
|||
private SwipeRefreshLayout refreshLayout;
|
||||
private View followersBtn, followingBtn;
|
||||
private EditText nameEdit, bioEdit;
|
||||
private ProgressBar actionProgress, notifyProgress;
|
||||
private ProgressBar actionProgress, notifyProgress, noteSaveProgress;
|
||||
private FrameLayout[] tabViews;
|
||||
private TabLayoutMediator tabLayoutMediator;
|
||||
private TextView followsYouView;
|
||||
|
@ -186,6 +190,11 @@ public class ProfileFragment extends LoaderFragment implements OnBackPressedList
|
|||
private ItemTouchHelper dragHelper=new ItemTouchHelper(new ReorderCallback());
|
||||
private ListImageLoaderWrapper imgLoader;
|
||||
|
||||
// profile note
|
||||
private FrameLayout noteWrap;
|
||||
private ImageButton noteSaveBtn;
|
||||
private EditText noteEdit;
|
||||
|
||||
@Override
|
||||
public void onCreate(Bundle savedInstanceState){
|
||||
super.onCreate(savedInstanceState);
|
||||
|
@ -257,6 +266,7 @@ public class ProfileFragment extends LoaderFragment implements OnBackPressedList
|
|||
bioEditWrap=content.findViewById(R.id.bio_edit_wrap);
|
||||
actionProgress=content.findViewById(R.id.action_progress);
|
||||
notifyProgress=content.findViewById(R.id.notify_progress);
|
||||
noteSaveProgress=content.findViewById(R.id.note_save_progress);
|
||||
fab=content.findViewById(R.id.fab);
|
||||
followsYouView=content.findViewById(R.id.follows_you);
|
||||
countersLayout=content.findViewById(R.id.profile_counters);
|
||||
|
@ -271,6 +281,51 @@ public class ProfileFragment extends LoaderFragment implements OnBackPressedList
|
|||
avatar.setOutlineProvider(OutlineProviders.roundedRect(24));
|
||||
avatar.setClipToOutline(true);
|
||||
|
||||
noteEdit=content.findViewById(R.id.note_edit);
|
||||
noteWrap=content.findViewById(R.id.note_edit_wrap);
|
||||
noteSaveBtn=content.findViewById(R.id.note_save_btn);
|
||||
|
||||
noteSaveBtn.setOnClickListener((v->{
|
||||
savePrivateNote(noteEdit.getText().toString());
|
||||
InputMethodManager imm=(InputMethodManager) getContext().getSystemService(Activity.INPUT_METHOD_SERVICE);
|
||||
imm.hideSoftInputFromWindow(this.getView().getRootView().getWindowToken(), 0);
|
||||
noteEdit.clearFocus();
|
||||
noteSaveBtn.clearFocus();
|
||||
}));
|
||||
|
||||
|
||||
noteEdit.setOnFocusChangeListener((v, hasFocus)->{
|
||||
if(hasFocus){
|
||||
hideFab();
|
||||
V.setVisibilityAnimated(noteSaveBtn, View.VISIBLE);
|
||||
noteEdit.setInputType(InputType.TYPE_CLASS_TEXT | InputType.TYPE_TEXT_FLAG_MULTI_LINE | InputType.TYPE_TEXT_FLAG_CAP_SENTENCES);
|
||||
}else if(!noteSaveBtn.hasFocus()){
|
||||
showFab();
|
||||
hideNoteSaveBtnIfNotDirty();
|
||||
}
|
||||
});
|
||||
|
||||
noteEdit.addTextChangedListener(new TextWatcher(){
|
||||
@Override
|
||||
public void beforeTextChanged(CharSequence s, int start, int count, int after){}
|
||||
|
||||
@Override
|
||||
public void onTextChanged(CharSequence s, int start, int before, int count){
|
||||
if(relationship!=null && noteSaveBtn.getVisibility()!=View.VISIBLE && !s.toString().equals(relationship.note))
|
||||
V.setVisibilityAnimated(noteSaveBtn, View.VISIBLE);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void afterTextChanged(Editable s){}
|
||||
});
|
||||
|
||||
noteSaveBtn.setOnFocusChangeListener((v, hasFocus)->{
|
||||
if(!hasFocus && !noteEdit.hasFocus()){
|
||||
showFab();
|
||||
hideNoteSaveBtnIfNotDirty();
|
||||
}
|
||||
});
|
||||
|
||||
FrameLayout sizeWrapper=new FrameLayout(getActivity()){
|
||||
@Override
|
||||
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec){
|
||||
|
@ -435,6 +490,46 @@ public class ProfileFragment extends LoaderFragment implements OnBackPressedList
|
|||
return sizeWrapper;
|
||||
}
|
||||
|
||||
private void hideNoteSaveBtnIfNotDirty(){
|
||||
if(noteEdit.getText().toString().equals(relationship.note)){
|
||||
V.setVisibilityAnimated(noteSaveBtn, View.INVISIBLE);
|
||||
}
|
||||
}
|
||||
|
||||
private void showPrivateNote(){
|
||||
noteWrap.setVisibility(View.VISIBLE);
|
||||
noteEdit.setText(relationship.note);
|
||||
}
|
||||
|
||||
private void hidePrivateNote(){
|
||||
noteWrap.setVisibility(View.GONE);
|
||||
noteEdit.setText(null);
|
||||
}
|
||||
|
||||
private void savePrivateNote(String note){
|
||||
if(note!=null && note.equals(relationship.note)){
|
||||
updateRelationship();
|
||||
invalidateOptionsMenu();
|
||||
return;
|
||||
}
|
||||
V.setVisibilityAnimated(noteSaveProgress, View.VISIBLE);
|
||||
V.setVisibilityAnimated(noteSaveBtn, View.INVISIBLE);
|
||||
new SetPrivateNote(profileAccountID, note).setCallback(new Callback<>() {
|
||||
@Override
|
||||
public void onSuccess(Relationship result) {
|
||||
updateRelationship(result);
|
||||
invalidateOptionsMenu();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onError(ErrorResponse error) {
|
||||
error.showToast(getContext());
|
||||
V.setVisibilityAnimated(noteSaveProgress, View.GONE);
|
||||
V.setVisibilityAnimated(noteSaveBtn, View.VISIBLE);
|
||||
}
|
||||
}).exec(accountID);
|
||||
}
|
||||
|
||||
private void onAccountLoaded(Account result) {
|
||||
account=result;
|
||||
isOwnProfile=AccountSessionManager.getInstance().isSelf(accountID, account);
|
||||
|
@ -793,6 +888,8 @@ public class ProfileFragment extends LoaderFragment implements OnBackPressedList
|
|||
}else{
|
||||
blockDomain.setVisible(false);
|
||||
}
|
||||
menu.findItem(R.id.edit_note).setTitle(noteWrap.getVisibility()==View.GONE && (relationship.note==null || relationship.note.isEmpty())
|
||||
? R.string.sk_add_note : R.string.sk_delete_note);
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@ -874,6 +971,26 @@ public class ProfileFragment extends LoaderFragment implements OnBackPressedList
|
|||
}else if(id==R.id.save){
|
||||
if(isInEditMode)
|
||||
saveAndExitEditMode();
|
||||
}else if(id==R.id.edit_note){
|
||||
if(noteWrap.getVisibility()==View.GONE){
|
||||
showPrivateNote();
|
||||
UiUtils.beginLayoutTransition(scrollableContent);
|
||||
noteEdit.requestFocus();
|
||||
noteEdit.postDelayed(()->{
|
||||
InputMethodManager imm=getActivity().getSystemService(InputMethodManager.class);
|
||||
imm.showSoftInput(noteEdit, 0);
|
||||
}, 100);
|
||||
}else if(relationship.note.isEmpty()){
|
||||
hidePrivateNote();
|
||||
UiUtils.beginLayoutTransition(scrollableContent);
|
||||
}else{
|
||||
new M3AlertDialogBuilder(getActivity())
|
||||
.setMessage(getContext().getString(R.string.sk_private_note_confirm_delete, account.getDisplayUsername()))
|
||||
.setPositiveButton(R.string.delete, (dlg, btn)->savePrivateNote(null))
|
||||
.setNegativeButton(R.string.cancel, null)
|
||||
.show();
|
||||
}
|
||||
invalidateOptionsMenu();
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
@ -899,15 +1016,22 @@ public class ProfileFragment extends LoaderFragment implements OnBackPressedList
|
|||
|
||||
private void updateRelationship(){
|
||||
if(getActivity()==null) return;
|
||||
if(relationship.note!=null && !relationship.note.isEmpty()) showPrivateNote();
|
||||
else hidePrivateNote();
|
||||
invalidateOptionsMenu();
|
||||
actionButton.setVisibility(View.VISIBLE);
|
||||
notifyButton.setVisibility(relationship.following ? View.VISIBLE : View.GONE);
|
||||
UiUtils.setRelationshipToActionButtonM3(relationship, actionButton);
|
||||
actionProgress.setIndeterminateTintList(actionButton.getTextColors());
|
||||
notifyProgress.setIndeterminateTintList(notifyButton.getTextColors());
|
||||
noteSaveProgress.setIndeterminateTintList(noteEdit.getTextColors());
|
||||
followsYouView.setVisibility(relationship.followedBy ? View.VISIBLE : View.GONE);
|
||||
notifyButton.setSelected(relationship.notifying);
|
||||
notifyButton.setContentDescription(getString(relationship.notifying ? R.string.sk_user_post_notifications_on : R.string.sk_user_post_notifications_off, '@'+account.username));
|
||||
noteEdit.setInputType(InputType.TYPE_CLASS_TEXT | InputType.TYPE_TEXT_FLAG_MULTI_LINE | InputType.TYPE_TEXT_FLAG_CAP_SENTENCES | InputType.TYPE_TEXT_FLAG_NO_SUGGESTIONS);
|
||||
V.setVisibilityAnimated(noteSaveProgress, View.GONE);
|
||||
V.setVisibilityAnimated(noteSaveBtn, View.INVISIBLE);
|
||||
UiUtils.beginLayoutTransition(scrollableContent);
|
||||
}
|
||||
|
||||
public ImageButton getFab() {
|
||||
|
|
|
@ -0,0 +1,7 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<shape xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:tint="@color/m3_primary_alpha11"
|
||||
android:tintMode="src_over">
|
||||
<solid android:color="?colorM3Surface" />
|
||||
<corners android:radius="26dp" />
|
||||
</shape>
|
|
@ -0,0 +1,3 @@
|
|||
<vector xmlns:android="http://schemas.android.com/apk/res/android" android:width="24dp" android:height="24dp" android:viewportWidth="24" android:viewportHeight="24">
|
||||
<path android:pathData="M11 15c0-0.35 0.06-0.687 0.171-1H4.253c-1.242 0-2.25 1.007-2.25 2.25v0.577c0 0.892 0.32 1.756 0.9 2.435 1.565 1.834 3.951 2.74 7.097 2.74 0.398 0 0.783-0.015 1.157-0.044C11.055 21.658 11 21.335 11 21v-0.534c-0.322 0.023-0.655 0.035-1 0.035-2.739 0-4.705-0.745-5.958-2.213-0.348-0.407-0.54-0.926-0.54-1.461v-0.578c0-0.413 0.336-0.749 0.75-0.749H11V15zM10 2.005c2.762 0 5 2.239 5 5s-2.238 5-5 5c-2.761 0-5-2.239-5-5s2.239-5 5-5zm0 1.5c-1.933 0-3.5 1.567-3.5 3.5s1.567 3.5 3.5 3.5 3.5-1.567 3.5-3.5-1.567-3.5-3.5-3.5zM12 15c0-1.104 0.896-2 2-2h7c1.105 0 2 0.896 2 2v6c0 1.105-0.895 2-2 2h-7c-1.104 0-2-0.895-2-2v-6zm2.5 1c-0.276 0-0.5 0.224-0.5 0.5s0.224 0.5 0.5 0.5h6c0.277 0 0.5-0.224 0.5-0.5S20.777 16 20.5 16h-6zm0 3c-0.276 0-0.5 0.224-0.5 0.5s0.224 0.5 0.5 0.5h6c0.277 0 0.5-0.224 0.5-0.5S20.777 19 20.5 19h-6z" android:fillColor="@color/fluent_default_icon_tint"/>
|
||||
</vector>
|
|
@ -50,7 +50,7 @@
|
|||
android:text="@string/follows_you"
|
||||
android:textAllCaps="true"
|
||||
android:textColor="#fff"
|
||||
android:textSize="14dp"
|
||||
android:textSize="14sp"
|
||||
android:visibility="gone"
|
||||
tools:visibility="visible" />
|
||||
|
||||
|
@ -80,11 +80,13 @@
|
|||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_below="@id/cover"
|
||||
android:layout_alignParentEnd="true">
|
||||
android:layout_alignParentEnd="true"
|
||||
android:clipChildren="false">
|
||||
|
||||
<FrameLayout
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:minHeight="48dp"
|
||||
android:layout_marginTop="16dp"
|
||||
android:layout_marginEnd="4dp">
|
||||
|
||||
|
@ -95,7 +97,8 @@
|
|||
style="@style/Widget.Mastodon.M3.Button.Tonal"
|
||||
android:background="@drawable/bg_button_m3_tonal_circle_selector"
|
||||
android:paddingStart="12dp"
|
||||
android:drawableStart="@drawable/ic_fluent_alert_24_selector" />
|
||||
android:drawableStart="@drawable/ic_fluent_alert_24_selector"
|
||||
tools:ignore="RtlSymmetry" />
|
||||
|
||||
<ProgressBar
|
||||
android:id="@+id/notify_progress"
|
||||
|
@ -112,6 +115,7 @@
|
|||
<FrameLayout
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:minHeight="48dp"
|
||||
android:layout_marginTop="16dp"
|
||||
android:layout_marginStart="4dp"
|
||||
android:layout_marginEnd="16dp">
|
||||
|
@ -213,6 +217,60 @@
|
|||
|
||||
</LinearLayout>
|
||||
|
||||
<FrameLayout
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_below="@id/username"
|
||||
android:id="@+id/note_edit_wrap"
|
||||
android:layout_marginTop="4dp"
|
||||
android:layout_marginBottom="12dp"
|
||||
android:layout_marginHorizontal="16dp"
|
||||
android:visibility="gone">
|
||||
|
||||
<EditText
|
||||
android:id="@+id/note_edit"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:minHeight="52dp"
|
||||
android:paddingVertical="15dp"
|
||||
android:textColor="?colorM3OnSurface"
|
||||
android:inputType="text|textMultiLine|textCapSentences"
|
||||
android:singleLine="false"
|
||||
android:background="@drawable/bg_note_edit"
|
||||
android:paddingEnd="52dp"
|
||||
android:paddingStart="20dp"
|
||||
android:elevation="0dp"
|
||||
android:hint="@string/sk_private_note_hint"
|
||||
tools:ignore="RtlSymmetry" />
|
||||
|
||||
<FrameLayout
|
||||
android:layout_width="52dp"
|
||||
android:layout_height="52dp"
|
||||
android:layout_gravity="end">
|
||||
|
||||
<ImageButton
|
||||
android:id="@+id/note_save_btn"
|
||||
android:layout_width="52dp"
|
||||
android:layout_height="52dp"
|
||||
android:visibility="invisible"
|
||||
android:background="@drawable/bg_button_m3_text_circle"
|
||||
android:tooltipText="@string/sk_private_note_confirm"
|
||||
android:contentDescription="@string/sk_private_note_confirm"
|
||||
android:src="@drawable/ic_fluent_checkmark_24_regular"/>
|
||||
|
||||
<ProgressBar
|
||||
android:id="@+id/note_save_progress"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_gravity="center"
|
||||
style="?android:progressBarStyleSmall"
|
||||
android:indeterminate="true"
|
||||
android:visibility="gone" />
|
||||
|
||||
</FrameLayout>
|
||||
|
||||
</FrameLayout>
|
||||
|
||||
<org.joinmastodon.android.ui.views.LinkedTextView
|
||||
android:id="@+id/bio"
|
||||
android:layout_width="match_parent"
|
||||
|
|
|
@ -1,6 +1,9 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<menu xmlns:android="http://schemas.android.com/apk/res/android">
|
||||
<group android:id="@+id/menu_group1">
|
||||
<item android:id="@+id/edit_note" android:title="@string/sk_add_note" android:icon="@drawable/ic_fluent_person_note_24_regular" />
|
||||
</group>
|
||||
<group android:id="@+id/menu_group2">
|
||||
<item android:id="@+id/manage_user_lists" android:title="@string/sk_lists_with_user" android:icon="@drawable/ic_fluent_people_24_regular"/>
|
||||
<item android:id="@+id/mute" android:title="@string/mute_user" android:icon="@drawable/ic_fluent_speaker_off_24_regular"/>
|
||||
<item android:id="@+id/hide_boosts" android:title="@string/hide_boosts_from_user" android:icon="@drawable/ic_fluent_arrow_repeat_all_off_24_regular"/>
|
||||
|
@ -9,7 +12,7 @@
|
|||
<item android:id="@+id/report" android:title="@string/report_user" android:icon="@drawable/ic_fluent_warning_24_regular"/>
|
||||
<item android:id="@+id/block_domain" android:title="@string/block_domain" android:icon="@drawable/ic_fluent_shield_prohibited_24_regular"/>
|
||||
</group>
|
||||
<group android:id="@+id/menu_group2">
|
||||
<group android:id="@+id/menu_group3">
|
||||
<item android:id="@+id/open_in_browser" android:title="@string/open_in_browser" android:icon="@drawable/ic_fluent_globe_24_regular"/>
|
||||
<item android:id="@+id/share" android:title="@string/share_user" android:icon="@drawable/ic_fluent_share_24_regular"/>
|
||||
<item android:id="@+id/open_with_account" android:title="@string/sk_open_with_account" android:visible="false" android:icon="@drawable/ic_fluent_person_swap_24_regular">
|
||||
|
|
|
@ -417,4 +417,10 @@
|
|||
<string name="sk_settings_lock_account">Manually approve new followers</string>
|
||||
<string name="sk_settings_default_visibility">Default posting visibility</string>
|
||||
<string name="sk_button_mutuals">Mutuals</string>
|
||||
<string name="sk_private_note_hint">Add a personal note about this profile</string>
|
||||
<string name="sk_private_note_confirm">Confirm changes to note</string>
|
||||
<string name="sk_private_note_update_failed">Failed to save note</string>
|
||||
<string name="sk_private_note_confirm_delete">Delete personal note about %s?</string>
|
||||
<string name="sk_delete_note">Delete personal note</string>
|
||||
<string name="sk_add_note">Add personal note</string>
|
||||
</resources>
|
Loading…
Reference in New Issue