This repository has been archived by the owner on Oct 28, 2024. It is now read-only.
forked from mastodon/mastodon-android
-
Notifications
You must be signed in to change notification settings - Fork 32
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
Samuel Kaiser
committed
Jan 13, 2024
1 parent
ea2b19b
commit e095c46
Showing
3 changed files
with
392 additions
and
0 deletions.
There are no files selected for viewing
176 changes: 176 additions & 0 deletions
176
mastodon/src/main/java/org/joinmastodon/android/fragments/BaseEditListFragment.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,176 @@ | ||
package org.joinmastodon.android.fragments; | ||
|
||
import android.app.Activity; | ||
import android.os.Bundle; | ||
import android.view.ViewGroup; | ||
import android.widget.ArrayAdapter; | ||
import android.widget.EditText; | ||
import android.widget.LinearLayout; | ||
import android.widget.Spinner; | ||
|
||
import org.joinmastodon.android.E; | ||
import org.joinmastodon.android.R; | ||
import org.joinmastodon.android.api.requests.lists.DeleteList; | ||
import org.joinmastodon.android.api.requests.lists.GetListAccounts; | ||
import org.joinmastodon.android.api.session.AccountSessionManager; | ||
import org.joinmastodon.android.events.ListDeletedEvent; | ||
import org.joinmastodon.android.fragments.settings.BaseSettingsFragment; | ||
import org.joinmastodon.android.model.Account; | ||
import org.joinmastodon.android.model.FollowList; | ||
import org.joinmastodon.android.model.HeaderPaginationList; | ||
import org.joinmastodon.android.model.viewmodel.AvatarPileListItem; | ||
import org.joinmastodon.android.model.viewmodel.CheckableListItem; | ||
import org.joinmastodon.android.model.viewmodel.ListItem; | ||
import org.joinmastodon.android.ui.views.FloatingHintEditTextLayout; | ||
import org.parceler.Parcels; | ||
|
||
import java.util.ArrayList; | ||
import java.util.List; | ||
|
||
import androidx.recyclerview.widget.RecyclerView; | ||
import me.grishka.appkit.Nav; | ||
import me.grishka.appkit.api.APIRequest; | ||
import me.grishka.appkit.api.Callback; | ||
import me.grishka.appkit.api.ErrorResponse; | ||
import me.grishka.appkit.imageloader.requests.UrlImageLoaderRequest; | ||
import me.grishka.appkit.utils.MergeRecyclerAdapter; | ||
import me.grishka.appkit.utils.SingleViewRecyclerAdapter; | ||
import me.grishka.appkit.utils.V; | ||
|
||
public abstract class BaseEditListFragment extends BaseSettingsFragment<Void>{ | ||
protected FollowList followList; | ||
protected AvatarPileListItem<Void> membersItem; | ||
protected CheckableListItem<Void> exclusiveItem; | ||
protected FloatingHintEditTextLayout titleEditLayout; | ||
protected EditText titleEdit; | ||
protected Spinner showRepliesSpinner; | ||
private APIRequest<?> getMembersRequest; | ||
|
||
@Override | ||
public void onCreate(Bundle savedInstanceState){ | ||
super.onCreate(savedInstanceState); | ||
followList=Parcels.unwrap(getArguments().getParcelable("list")); | ||
|
||
membersItem=new AvatarPileListItem<>(getString(R.string.list_members), null, List.of(), 0, i->onMembersClick(), null, false); | ||
List<ListItem<Void>> items=new ArrayList<>(); | ||
if(followList!=null){ | ||
items.add(membersItem); | ||
} | ||
exclusiveItem=new CheckableListItem<>(R.string.list_exclusive, R.string.list_exclusive_subtitle, CheckableListItem.Style.SWITCH, followList!=null && followList.exclusive, this::toggleCheckableItem); | ||
items.add(exclusiveItem); | ||
onDataLoaded(items); | ||
} | ||
|
||
@Override | ||
public void onDestroy(){ | ||
super.onDestroy(); | ||
if(getMembersRequest!=null) | ||
getMembersRequest.cancel(); | ||
} | ||
|
||
@Override | ||
protected void doLoadData(int offset, int count){} | ||
|
||
@Override | ||
protected RecyclerView.Adapter<?> getAdapter(){ | ||
LinearLayout topView=new LinearLayout(getActivity()); | ||
topView.setOrientation(LinearLayout.VERTICAL); | ||
|
||
titleEditLayout=(FloatingHintEditTextLayout) getActivity().getLayoutInflater().inflate(R.layout.floating_hint_edit_text, topView, false); | ||
titleEdit=titleEditLayout.findViewById(R.id.edit); | ||
titleEdit.setHint(R.string.list_name); | ||
titleEditLayout.updateHint(); | ||
if(followList!=null) | ||
titleEdit.setText(followList.title); | ||
topView.addView(titleEditLayout); | ||
|
||
FloatingHintEditTextLayout showRepliesLayout=(FloatingHintEditTextLayout) getActivity().getLayoutInflater().inflate(R.layout.floating_hint_spinner, topView, false); | ||
showRepliesSpinner=showRepliesLayout.findViewById(R.id.spinner); | ||
showRepliesLayout.setHint(R.string.list_show_replies_to); | ||
topView.addView(showRepliesLayout); | ||
ArrayAdapter<String> spinnerAdapter=new ArrayAdapter<>(getActivity(), R.layout.item_spinner, List.of( | ||
getString(R.string.list_replies_no_one), | ||
getString(R.string.list_replies_members), | ||
getString(R.string.list_replies_anyone) | ||
)); | ||
showRepliesSpinner.setAdapter(spinnerAdapter); | ||
showRepliesSpinner.setSelection(switch(followList!=null ? followList.repliesPolicy : FollowList.RepliesPolicy.LIST){ | ||
case FOLLOWED -> 2; | ||
case LIST -> 1; | ||
case NONE -> 0; | ||
}); | ||
ViewGroup.MarginLayoutParams llp=(ViewGroup.MarginLayoutParams)showRepliesLayout.getLabel().getLayoutParams(); | ||
llp.setMarginStart(llp.getMarginStart()+V.dp(16)); | ||
|
||
MergeRecyclerAdapter adapter=new MergeRecyclerAdapter(); | ||
adapter.addAdapter(new SingleViewRecyclerAdapter(topView)); | ||
adapter.addAdapter(super.getAdapter()); | ||
return adapter; | ||
} | ||
|
||
@Override | ||
protected int indexOfItemsAdapter(){ | ||
return 1; | ||
} | ||
|
||
protected void doDeleteList(){ | ||
new DeleteList(followList.id) | ||
.setCallback(new Callback<>(){ | ||
@Override | ||
public void onSuccess(Void result){ | ||
AccountSessionManager.get(accountID).getCacheController().deleteList(followList.id); | ||
E.post(new ListDeletedEvent(accountID, followList.id)); | ||
Nav.finish(BaseEditListFragment.this); | ||
} | ||
|
||
@Override | ||
public void onError(ErrorResponse error){ | ||
Activity activity=getActivity(); | ||
if(activity==null) | ||
return; | ||
error.showToast(activity); | ||
} | ||
}) | ||
.wrapProgress(getActivity(), R.string.loading, true) | ||
.exec(accountID); | ||
} | ||
|
||
private void onMembersClick(){ | ||
Bundle args=new Bundle(); | ||
args.putString("account", accountID); | ||
args.putParcelable("list", Parcels.wrap(followList)); | ||
Nav.go(getActivity(), ListMembersFragment.class, args); | ||
} | ||
|
||
protected void loadMembers(){ | ||
getMembersRequest=new GetListAccounts(followList.id, null, 3) | ||
.setCallback(new Callback<>(){ | ||
@Override | ||
public void onSuccess(HeaderPaginationList<Account> result){ | ||
getMembersRequest=null; | ||
membersItem.avatars=new ArrayList<>(); | ||
for(int i=0;i<Math.min(3, result.size());i++){ | ||
Account acc=result.get(i); | ||
membersItem.avatars.add(new UrlImageLoaderRequest(acc.avatarStatic, V.dp(32), V.dp(32))); | ||
} | ||
rebindItem(membersItem); | ||
imgLoader.updateImages(); | ||
} | ||
|
||
@Override | ||
public void onError(ErrorResponse error){ | ||
getMembersRequest=null; | ||
} | ||
}) | ||
.exec(accountID); | ||
} | ||
|
||
protected FollowList.RepliesPolicy getSelectedRepliesPolicy(){ | ||
return switch(showRepliesSpinner.getSelectedItemPosition()){ | ||
case 0 -> FollowList.RepliesPolicy.NONE; | ||
case 1 -> FollowList.RepliesPolicy.LIST; | ||
case 2 -> FollowList.RepliesPolicy.FOLLOWED; | ||
default -> throw new IllegalStateException("Unexpected value: "+showRepliesSpinner.getSelectedItemPosition()); | ||
}; | ||
} | ||
} |
149 changes: 149 additions & 0 deletions
149
mastodon/src/main/java/org/joinmastodon/android/fragments/CreateListFragment.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,149 @@ | ||
package org.joinmastodon.android.fragments; | ||
|
||
import android.os.Bundle; | ||
import android.text.TextUtils; | ||
import android.view.View; | ||
import android.view.WindowInsets; | ||
import android.view.inputmethod.InputMethodManager; | ||
import android.widget.Button; | ||
|
||
import com.squareup.otto.Subscribe; | ||
|
||
import org.joinmastodon.android.E; | ||
import org.joinmastodon.android.R; | ||
import org.joinmastodon.android.api.requests.lists.CreateList; | ||
import org.joinmastodon.android.api.requests.lists.UpdateList; | ||
import org.joinmastodon.android.api.session.AccountSessionManager; | ||
import org.joinmastodon.android.events.FinishListCreationFragmentEvent; | ||
import org.joinmastodon.android.events.ListCreatedEvent; | ||
import org.joinmastodon.android.events.ListUpdatedEvent; | ||
import org.joinmastodon.android.model.FollowList; | ||
import org.joinmastodon.android.ui.utils.UiUtils; | ||
import org.parceler.Parcels; | ||
|
||
import java.util.List; | ||
|
||
import me.grishka.appkit.Nav; | ||
import me.grishka.appkit.api.Callback; | ||
import me.grishka.appkit.api.ErrorResponse; | ||
|
||
public class CreateListFragment extends BaseEditListFragment{ | ||
private Button nextButton; | ||
private View buttonBar; | ||
private FollowList followList; | ||
|
||
@Override | ||
public void onCreate(Bundle savedInstanceState){ | ||
super.onCreate(savedInstanceState); | ||
setTitle(R.string.create_list); | ||
setSubtitle(getString(R.string.step_x_of_y, 1, 2)); | ||
setLayout(R.layout.fragment_login); | ||
if(savedInstanceState!=null) | ||
followList=Parcels.unwrap(savedInstanceState.getParcelable("list")); | ||
E.register(this); | ||
} | ||
|
||
@Override | ||
public void onDestroy(){ | ||
super.onDestroy(); | ||
E.unregister(this); | ||
} | ||
|
||
@Override | ||
protected int getNavigationIconDrawableResource(){ | ||
return R.drawable.ic_baseline_close_24; | ||
} | ||
|
||
@Override | ||
public boolean wantsCustomNavigationIcon(){ | ||
return true; | ||
} | ||
|
||
@Override | ||
public void onViewCreated(View view, Bundle savedInstanceState){ | ||
nextButton=view.findViewById(R.id.btn_next); | ||
nextButton.setOnClickListener(this::onNextClick); | ||
nextButton.setText(R.string.create); | ||
buttonBar=view.findViewById(R.id.button_bar); | ||
super.onViewCreated(view, savedInstanceState); | ||
} | ||
|
||
@Override | ||
public void onApplyWindowInsets(WindowInsets insets){ | ||
super.onApplyWindowInsets(UiUtils.applyBottomInsetToFixedView(buttonBar, insets)); | ||
} | ||
|
||
@Override | ||
protected List<View> getViewsForElevationEffect(){ | ||
return List.of(getToolbar(), buttonBar); | ||
} | ||
|
||
@Override | ||
public void onSaveInstanceState(Bundle outState){ | ||
super.onSaveInstanceState(outState); | ||
outState.putParcelable("list", Parcels.wrap(followList)); | ||
} | ||
|
||
private void onNextClick(View v){ | ||
String title=titleEdit.getText().toString().trim(); | ||
if(TextUtils.isEmpty(title)){ | ||
titleEditLayout.setErrorState(getString(R.string.required_form_field_blank)); | ||
return; | ||
} | ||
if(followList==null){ | ||
new CreateList(title, getSelectedRepliesPolicy(), exclusiveItem.checked) | ||
.setCallback(new Callback<>(){ | ||
@Override | ||
public void onSuccess(FollowList result){ | ||
followList=result; | ||
proceed(false); | ||
E.post(new ListCreatedEvent(accountID, result)); | ||
AccountSessionManager.get(accountID).getCacheController().addList(result); | ||
} | ||
|
||
@Override | ||
public void onError(ErrorResponse error){ | ||
error.showToast(getActivity()); | ||
} | ||
}) | ||
.wrapProgress(getActivity(), R.string.loading, true) | ||
.exec(accountID); | ||
}else if(!title.equals(followList.title) || getSelectedRepliesPolicy()!=followList.repliesPolicy || exclusiveItem.checked!=followList.exclusive){ | ||
new UpdateList(followList.id, title, getSelectedRepliesPolicy(), exclusiveItem.checked) | ||
.setCallback(new Callback<>(){ | ||
@Override | ||
public void onSuccess(FollowList result){ | ||
followList=result; | ||
proceed(true); | ||
E.post(new ListUpdatedEvent(accountID, result)); | ||
AccountSessionManager.get(accountID).getCacheController().updateList(result); | ||
} | ||
|
||
@Override | ||
public void onError(ErrorResponse error){ | ||
error.showToast(getActivity()); | ||
} | ||
}) | ||
.wrapProgress(getActivity(), R.string.loading, true) | ||
.exec(accountID); | ||
}else{ | ||
proceed(true); | ||
} | ||
} | ||
|
||
private void proceed(boolean needLoadMembers){ | ||
Bundle args=new Bundle(); | ||
args.putString("account", accountID); | ||
args.putParcelable("list", Parcels.wrap(followList)); | ||
args.putBoolean("needLoadMembers", needLoadMembers); | ||
Nav.go(getActivity(), CreateListAddMembersFragment.class, args); | ||
getActivity().getSystemService(InputMethodManager.class).hideSoftInputFromWindow(contentView.getWindowToken(), 0); | ||
} | ||
|
||
@Subscribe | ||
public void onFinishListCreationFragment(FinishListCreationFragmentEvent ev){ | ||
if(ev.accountID.equals(accountID) && followList!=null && ev.listID.equals(followList.id)){ | ||
Nav.finish(this); | ||
} | ||
} | ||
} |
67 changes: 67 additions & 0 deletions
67
mastodon/src/main/java/org/joinmastodon/android/fragments/EditListFragment.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,67 @@ | ||
package org.joinmastodon.android.fragments; | ||
|
||
import android.os.Bundle; | ||
import android.view.Menu; | ||
import android.view.MenuInflater; | ||
import android.view.MenuItem; | ||
|
||
import org.joinmastodon.android.E; | ||
import org.joinmastodon.android.R; | ||
import org.joinmastodon.android.api.requests.lists.UpdateList; | ||
import org.joinmastodon.android.api.session.AccountSessionManager; | ||
import org.joinmastodon.android.events.ListUpdatedEvent; | ||
import org.joinmastodon.android.model.FollowList; | ||
import org.joinmastodon.android.ui.M3AlertDialogBuilder; | ||
|
||
import me.grishka.appkit.api.Callback; | ||
import me.grishka.appkit.api.ErrorResponse; | ||
|
||
public class EditListFragment extends BaseEditListFragment{ | ||
@Override | ||
public void onCreate(Bundle savedInstanceState){ | ||
super.onCreate(savedInstanceState); | ||
setTitle(R.string.edit_list); | ||
loadMembers(); | ||
setHasOptionsMenu(true); | ||
} | ||
|
||
@Override | ||
public void onCreateOptionsMenu(Menu menu, MenuInflater inflater){ | ||
menu.add(R.string.delete_list); | ||
} | ||
|
||
@Override | ||
public boolean onOptionsItemSelected(MenuItem item){ | ||
new M3AlertDialogBuilder(getActivity()) | ||
.setTitle(R.string.delete_list) | ||
.setMessage(getString(R.string.delete_list_confirm, followList.title)) | ||
.setPositiveButton(R.string.delete, (dlg, which)->doDeleteList()) | ||
.setNegativeButton(R.string.cancel, null) | ||
.show(); | ||
return true; | ||
} | ||
|
||
@Override | ||
public void onDestroy(){ | ||
super.onDestroy(); | ||
String newTitle=titleEdit.getText().toString(); | ||
FollowList.RepliesPolicy newRepliesPolicy=getSelectedRepliesPolicy(); | ||
boolean newExclusive=exclusiveItem.checked; | ||
if(!newTitle.equals(followList.title) || newRepliesPolicy!=followList.repliesPolicy || newExclusive!=followList.exclusive){ | ||
new UpdateList(followList.id, newTitle, newRepliesPolicy, newExclusive) | ||
.setCallback(new Callback<>(){ | ||
@Override | ||
public void onSuccess(FollowList result){ | ||
AccountSessionManager.get(accountID).getCacheController().updateList(result); | ||
E.post(new ListUpdatedEvent(accountID, result)); | ||
} | ||
|
||
@Override | ||
public void onError(ErrorResponse error){ | ||
// TODO handle errors somehow | ||
} | ||
}) | ||
.exec(accountID); | ||
} | ||
} | ||
} |