Group settings by account (AND-170)

This commit is contained in:
Grishka 2024-11-05 23:05:33 +03:00
parent c60d06950f
commit 1b17600835
28 changed files with 505 additions and 205 deletions

View file

@ -3,6 +3,7 @@ package org.joinmastodon.android;
import android.content.Context;
import android.content.SharedPreferences;
import org.joinmastodon.android.api.session.AccountSession;
import org.joinmastodon.android.api.session.AccountSessionManager;
import org.joinmastodon.android.model.Account;
@ -12,6 +13,10 @@ public class GlobalUserPreferences{
public static boolean altTextReminders, confirmUnfollow, confirmBoost, confirmDeletePost;
public static ThemePreference theme=ThemePreference.AUTO;
public static boolean useDynamicColors;
public static boolean showInteractionCounts;
public static boolean customEmojiInNames;
public static boolean showCWs;
public static boolean hideSensitiveMedia;
private static SharedPreferences getPrefs(){
return MastodonApp.context.getSharedPreferences("global", Context.MODE_PRIVATE);
@ -31,6 +36,19 @@ public class GlobalUserPreferences{
confirmDeletePost=prefs.getBoolean("confirmDeletePost", true);
theme=ThemePreference.values()[prefs.getInt("theme", 0)];
useDynamicColors=prefs.getBoolean("useDynamicColors", true);
if(!prefs.getBoolean("perAccountMigrationDone", false)){
AccountSession account=AccountSessionManager.getInstance().getLastActiveAccount();
if(account!=null){
SharedPreferences accPrefs=account.getRawLocalPreferences();
showInteractionCounts=accPrefs.getBoolean("interactionCounts", true);
customEmojiInNames=accPrefs.getBoolean("emojiInNames", true);
showCWs=accPrefs.getBoolean("showCWs", true);
hideSensitiveMedia=accPrefs.getBoolean("hideSensitive", true);
save();
}
// Also applies to new app installs
prefs.edit().putBoolean("perAccountMigrationDone", true).apply();
}
}
public static void save(){
@ -43,6 +61,10 @@ public class GlobalUserPreferences{
.putBoolean("confirmBoost", confirmBoost)
.putBoolean("confirmDeletePost", confirmDeletePost)
.putBoolean("useDynamicColors", useDynamicColors)
.putBoolean("interactionCounts", showInteractionCounts)
.putBoolean("emojiInNames", customEmojiInNames)
.putBoolean("showCWs", showCWs)
.putBoolean("hideSensitive", hideSensitiveMedia)
.apply();
}

View file

@ -5,18 +5,10 @@ import android.content.SharedPreferences;
public class AccountLocalPreferences{
private final SharedPreferences prefs;
public boolean showInteractionCounts;
public boolean customEmojiInNames;
public boolean showCWs;
public boolean hideSensitiveMedia;
public boolean serverSideFiltersSupported;
public AccountLocalPreferences(SharedPreferences prefs){
this.prefs=prefs;
showInteractionCounts=prefs.getBoolean("interactionCounts", true);
customEmojiInNames=prefs.getBoolean("emojiInNames", true);
showCWs=prefs.getBoolean("showCWs", true);
hideSensitiveMedia=prefs.getBoolean("hideSensitive", true);
serverSideFiltersSupported=prefs.getBoolean("serverSideFilters", false);
}
@ -30,10 +22,6 @@ public class AccountLocalPreferences{
public void save(){
prefs.edit()
.putBoolean("interactionCounts", showInteractionCounts)
.putBoolean("emojiInNames", customEmojiInNames)
.putBoolean("showCWs", showCWs)
.putBoolean("hideSensitive", hideSensitiveMedia)
.putBoolean("serverSideFilters", serverSideFiltersSupported)
.apply();
}

View file

@ -500,7 +500,7 @@ public abstract class BaseStatusListFragment<T extends DisplayItemsParent> exten
int itemCount=spoilerItem.contentItems.size();
displayItems.addAll(index+1, spoilerItem.contentItems);
if(spoilerItem.spoilerType==Status.SpoilerType.FILTER && spoilerItem.contentItems.get(0) instanceof SpoilerStatusDisplayItem nestedSpoiler
&& nestedSpoiler.spoilerType==Status.SpoilerType.CONTENT_WARNING && !AccountSessionManager.get(accountID).getLocalPreferences().showCWs){
&& nestedSpoiler.spoilerType==Status.SpoilerType.CONTENT_WARNING && !GlobalUserPreferences.showCWs){
status.revealedSpoilers.add(Status.SpoilerType.CONTENT_WARNING);
displayItems.addAll(index+1+itemCount, nestedSpoiler.contentItems);
itemCount+=nestedSpoiler.contentItems.size();

View file

@ -560,7 +560,7 @@ public class ProfileFragment extends LoaderFragment implements ScrollableToTop,
ViewImageLoader.loadWithoutAnimation(avatar, avatar.getDrawable(), new UrlImageLoaderRequest(GlobalUserPreferences.playGifs ? account.avatar : account.avatarStatic, V.dp(100), V.dp(100)));
ViewImageLoader.loadWithoutAnimation(cover, cover.getDrawable(), new UrlImageLoaderRequest(GlobalUserPreferences.playGifs ? account.header : account.headerStatic, 1000, 1000));
SpannableStringBuilder ssb=new SpannableStringBuilder(account.displayName);
if(AccountSessionManager.get(accountID).getLocalPreferences().customEmojiInNames)
if(GlobalUserPreferences.customEmojiInNames)
HtmlParser.parseCustomEmoji(ssb, account.emojis);
name.setText(ssb);
setTitle(ssb);

View file

@ -68,7 +68,7 @@ public class ThreadFragment extends StatusListFragment implements AssistContentP
knownAccounts.put(inReplyToAccount.id, inReplyToAccount);
data.add(mainStatus);
onAppendItems(Collections.singletonList(mainStatus));
if(AccountSessionManager.get(accountID).getLocalPreferences().customEmojiInNames)
if(GlobalUserPreferences.customEmojiInNames)
setTitle(HtmlParser.parseCustomEmoji(getString(R.string.post_from_user, mainStatus.account.displayName), mainStatus.account.emojis));
else
setTitle(getString(R.string.post_from_user, mainStatus.account.displayName));

View file

@ -0,0 +1,239 @@
package org.joinmastodon.android.fragments.settings;
import android.app.Activity;
import android.app.AlertDialog;
import android.content.Context;
import android.content.Intent;
import android.os.Bundle;
import android.view.View;
import android.widget.TextView;
import android.widget.Toast;
import org.joinmastodon.android.BuildConfig;
import org.joinmastodon.android.MainActivity;
import org.joinmastodon.android.R;
import org.joinmastodon.android.api.requests.catalog.GetDonationCampaigns;
import org.joinmastodon.android.api.session.AccountSession;
import org.joinmastodon.android.api.session.AccountSessionManager;
import org.joinmastodon.android.model.Preferences;
import org.joinmastodon.android.model.donations.DonationCampaign;
import org.joinmastodon.android.model.viewmodel.ListItem;
import org.joinmastodon.android.model.viewmodel.SectionHeaderListItem;
import org.joinmastodon.android.ui.M3AlertDialogBuilder;
import org.joinmastodon.android.ui.sheets.DonationSheet;
import org.joinmastodon.android.ui.sheets.DonationSuccessfulSheet;
import org.joinmastodon.android.ui.utils.UiUtils;
import org.joinmastodon.android.ui.viewcontrollers.ComposeLanguageAlertViewController;
import java.util.ArrayList;
import java.util.Locale;
import androidx.annotation.NonNull;
import androidx.recyclerview.widget.RecyclerView;
import me.grishka.appkit.Nav;
import me.grishka.appkit.api.Callback;
import me.grishka.appkit.api.ErrorResponse;
import me.grishka.appkit.utils.CubicBezierInterpolator;
import me.grishka.appkit.utils.MergeRecyclerAdapter;
import me.grishka.appkit.utils.SingleViewRecyclerAdapter;
import me.grishka.appkit.utils.V;
public class SettingsAccountFragment extends BaseSettingsFragment<Void>{
private static final int DONATION_RESULT=433;
private DonationSheet donationSheet;
private boolean loggedOut;
private ListItem<Void> languageItem;
private Locale postLanguage;
private ComposeLanguageAlertViewController.SelectedOption newPostLanguage;
@Override
public void onCreate(Bundle savedInstanceState){
super.onCreate(savedInstanceState);
AccountSession account=AccountSessionManager.get(accountID);
setTitle(account.getFullUsername());
if(account.preferences!=null && account.preferences.postingDefaultLanguage!=null){
postLanguage=Locale.forLanguageTag(account.preferences.postingDefaultLanguage);
}
ArrayList<ListItem<Void>> items=new ArrayList<>();
items.add(new SectionHeaderListItem(R.string.account_settings));
items.add(new ListItem<>(R.string.settings_privacy, 0, R.drawable.ic_privacy_tip_24px, this::onPrivacyClick));
items.add(new ListItem<>(R.string.settings_filters, 0, R.drawable.ic_filter_alt_24px, this::onFiltersClick));
items.add(new ListItem<>(R.string.settings_notifications, 0, R.drawable.ic_notifications_24px, this::onNotificationsClick));
items.add(languageItem=new ListItem<>(getString(R.string.default_post_language), postLanguage!=null ? postLanguage.getDisplayName(Locale.getDefault()) : null, R.drawable.ic_language_24px, this::onDefaultLanguageClick));
items.add(new SectionHeaderListItem(account.domain));
items.add(new ListItem<>(getString(R.string.settings_about_this_server), getString(R.string.settings_server_explanation), R.drawable.ic_dns_24px, this::onServerClick));
if(account.isEligibleForDonations()){
items.add(new ListItem<>(R.string.settings_donate, 0, R.drawable.ic_volunteer_activism_24px, this::onDonateClick));
}
items.add(new SectionHeaderListItem(R.string.manage_account));
items.add(new ListItem<>(R.string.switch_to_this_account, 0, R.drawable.ic_switch_account_24px, AccountSessionManager.getInstance().getLastActiveAccountID().equals(accountID) ? null : this::onSwitchAccountClick));
items.add(new ListItem<>(R.string.delete_account, 0, R.drawable.ic_delete_forever_24px, this::onDeleteAccountClick, R.attr.colorM3Error, false));
items.add(new ListItem<>(R.string.log_out, 0, R.drawable.ic_logout_24px, this::onLogOutClick, R.attr.colorM3Error, false));
onDataLoaded(items);
}
@Override
protected void doLoadData(int offset, int count){}
@Override
public void onActivityResult(int requestCode, int resultCode, Intent data){
if(requestCode==DONATION_RESULT){
if(donationSheet!=null)
donationSheet.dismissWithoutAnimation();
if(resultCode==Activity.RESULT_OK){
new DonationSuccessfulSheet(getActivity(), accountID, data.getStringExtra("postText")).showWithoutAnimation();
}
}
}
@Override
protected void onHidden(){
super.onHidden();
if(!loggedOut){
if(newPostLanguage!=null){
AccountSession s=AccountSessionManager.get(accountID);
if(s.preferences==null)
s.preferences=new Preferences();
s.preferences.postingDefaultLanguage=newPostLanguage.locale.toLanguageTag();
s.savePreferencesLater();
}
AccountSessionManager.get(accountID).savePreferencesIfPending();
}
}
@Override
protected void onUpdateToolbar(){
super.onUpdateToolbar();
toolbarTitleView.setAlpha(list.getChildCount()==0 || list.getChildAdapterPosition(list.getChildAt(0))==0 ? 0 : 1);
}
@Override
protected RecyclerView.Adapter<?> getAdapter(){
TextView largeTitle=(TextView) getToolbarLayoutInflater().inflate(R.layout.large_title, list, false);
largeTitle.setText(getTitle());
largeTitle.setPadding(largeTitle.getPaddingLeft(), largeTitle.getPaddingTop(), largeTitle.getPaddingRight(), 0);
MergeRecyclerAdapter adapter=new MergeRecyclerAdapter();
adapter.addAdapter(new SingleViewRecyclerAdapter(largeTitle));
adapter.addAdapter(super.getAdapter());
return adapter;
}
@Override
public void onViewCreated(View view, Bundle savedInstanceState){
super.onViewCreated(view, savedInstanceState);
list.addOnScrollListener(new RecyclerView.OnScrollListener(){
private boolean titleVisible=true;
@Override
public void onScrolled(@NonNull RecyclerView recyclerView, int dx, int dy){
boolean newTitleVisible=list.getChildAdapterPosition(list.getChildAt(0))==0;
if(newTitleVisible!=titleVisible){
titleVisible=newTitleVisible;
toolbarTitleView.animate().alpha(titleVisible ? 0 : 1).setDuration(250).setInterpolator(CubicBezierInterpolator.DEFAULT).start();
}
}
});
}
private Bundle makeFragmentArgs(){
Bundle args=new Bundle();
args.putString("account", accountID);
return args;
}
private void onPrivacyClick(ListItem<?> item_){
Nav.go(getActivity(), SettingsPrivacyFragment.class, makeFragmentArgs());
}
private void onFiltersClick(ListItem<?> item_){
Nav.go(getActivity(), SettingsFiltersFragment.class, makeFragmentArgs());
}
private void onNotificationsClick(ListItem<?> item_){
Nav.go(getActivity(), SettingsNotificationsFragment.class, makeFragmentArgs());
}
private void onDefaultLanguageClick(ListItem<?> item){
ComposeLanguageAlertViewController vc=new ComposeLanguageAlertViewController(getActivity(), null, newPostLanguage==null ? new ComposeLanguageAlertViewController.SelectedOption(-1, postLanguage, null) : newPostLanguage, null);
AlertDialog dlg=new M3AlertDialogBuilder(getActivity())
.setTitle(R.string.default_post_language)
.setView(vc.getView())
.setPositiveButton(R.string.cancel, null)
.show();
vc.setSelectionListener(opt->{
if(!opt.locale.equals(postLanguage)){
newPostLanguage=opt;
languageItem.subtitle=newPostLanguage.locale.getDisplayLanguage(Locale.getDefault());
rebindItem(languageItem);
}
dlg.dismiss();
});
}
private void onServerClick(ListItem<?> item_){
Nav.go(getActivity(), SettingsServerFragment.class, makeFragmentArgs());
}
private boolean useStagingEnvironmentForDonations(){
return (BuildConfig.DEBUG || BuildConfig.BUILD_TYPE.equals("appcenterPrivateBeta")) && getActivity().getSharedPreferences("debug", Context.MODE_PRIVATE).getBoolean("donationsStaging", false);
}
private void onDonateClick(ListItem<?> item){
GetDonationCampaigns req=new GetDonationCampaigns(Locale.getDefault().toLanguageTag().replace('-', '_'), String.valueOf(AccountSessionManager.get(accountID).getDonationSeed()), "menu");
if(useStagingEnvironmentForDonations()){
req.setStaging(true);
}
req.setCallback(new Callback<>(){
@Override
public void onSuccess(DonationCampaign result){
Activity activity=getActivity();
if(activity==null)
return;
if(result==null){
Toast.makeText(activity, "No campaign available (server misconfiguration?)", Toast.LENGTH_SHORT).show();
return;
}
donationSheet=new DonationSheet(getActivity(), result, accountID, intent->startActivityForResult(intent, DONATION_RESULT));
donationSheet.setOnDismissListener(dialog->donationSheet=null);
donationSheet.show();
}
@Override
public void onError(ErrorResponse error){
error.showToast(getActivity());
}
})
.wrapProgress(getActivity(), R.string.loading, true)
.execNoAuth("");
}
private void onSwitchAccountClick(ListItem<?> item){
if(AccountSessionManager.getInstance().tryGetAccount(accountID)!=null){
AccountSessionManager.getInstance().setLastActiveAccountID(accountID);
((MainActivity)getActivity()).restartHomeFragment();
}
}
private void onDeleteAccountClick(ListItem<?> item){
AccountSession session=AccountSessionManager.getInstance().getAccount(accountID);
UiUtils.launchWebBrowser(getActivity(), "https://"+session.domain+"/settings/delete");
}
private void onLogOutClick(ListItem<?> item_){
AccountSession session=AccountSessionManager.getInstance().getAccount(accountID);
new M3AlertDialogBuilder(getActivity())
.setMessage(getString(R.string.confirm_log_out, session.getFullUsername()))
.setPositiveButton(R.string.log_out, (dialog, which)->AccountSessionManager.get(accountID).logOut(getActivity(), ()->{
loggedOut=true;
((MainActivity)getActivity()).restartHomeFragment();
}))
.setNegativeButton(R.string.cancel, null)
.show();
}
}

View file

@ -1,6 +1,5 @@
package org.joinmastodon.android.fragments.settings;
import android.app.AlertDialog;
import android.content.Intent;
import android.content.pm.PackageManager;
import android.content.pm.ResolveInfo;
@ -13,38 +12,25 @@ import android.widget.TextView;
import org.joinmastodon.android.GlobalUserPreferences;
import org.joinmastodon.android.R;
import org.joinmastodon.android.api.session.AccountSession;
import org.joinmastodon.android.api.session.AccountSessionManager;
import org.joinmastodon.android.model.Preferences;
import org.joinmastodon.android.model.viewmodel.CheckableListItem;
import org.joinmastodon.android.model.viewmodel.ListItem;
import org.joinmastodon.android.ui.M3AlertDialogBuilder;
import org.joinmastodon.android.ui.viewcontrollers.ComposeLanguageAlertViewController;
import java.util.List;
import java.util.Locale;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
public class SettingsBehaviorFragment extends BaseSettingsFragment<Void>{
private ListItem<Void> languageItem, customTabsItem;
private ListItem<Void> customTabsItem;
private CheckableListItem<Void> altTextItem, playGifsItem, confirmUnfollowItem, confirmBoostItem, confirmDeleteItem;
private Locale postLanguage;
private ComposeLanguageAlertViewController.SelectedOption newPostLanguage;
@Override
public void onCreate(Bundle savedInstanceState){
super.onCreate(savedInstanceState);
setTitle(R.string.settings_behavior);
AccountSession s=AccountSessionManager.get(accountID);
if(s.preferences!=null && s.preferences.postingDefaultLanguage!=null){
postLanguage=Locale.forLanguageTag(s.preferences.postingDefaultLanguage);
}
onDataLoaded(List.of(
languageItem=new ListItem<>(getString(R.string.default_post_language), postLanguage!=null ? postLanguage.getDisplayName(Locale.getDefault()) : null, R.drawable.ic_language_24px, this::onDefaultLanguageClick),
customTabsItem=new ListItem<>(R.string.settings_custom_tabs, GlobalUserPreferences.useCustomTabs ? R.string.in_app_browser : R.string.system_browser, R.drawable.ic_open_in_browser_24px, this::onCustomTabsClick),
altTextItem=new CheckableListItem<>(R.string.settings_alt_text_reminders, 0, CheckableListItem.Style.SWITCH, GlobalUserPreferences.altTextReminders, R.drawable.ic_alt_24px, this::toggleCheckableItem),
playGifsItem=new CheckableListItem<>(R.string.settings_gif, 0, CheckableListItem.Style.SWITCH, GlobalUserPreferences.playGifs, R.drawable.ic_animation_24px, this::toggleCheckableItem),
@ -57,25 +43,7 @@ public class SettingsBehaviorFragment extends BaseSettingsFragment<Void>{
@Override
protected void doLoadData(int offset, int count){}
private void onDefaultLanguageClick(ListItem<?> item){
ComposeLanguageAlertViewController vc=new ComposeLanguageAlertViewController(getActivity(), null, newPostLanguage==null ? new ComposeLanguageAlertViewController.SelectedOption(-1, postLanguage, null) : newPostLanguage, null);
AlertDialog dlg=new M3AlertDialogBuilder(getActivity())
.setTitle(R.string.default_post_language)
.setView(vc.getView())
.setPositiveButton(R.string.cancel, null)
.show();
vc.setSelectionListener(opt->{
if(!opt.locale.equals(postLanguage)){
newPostLanguage=opt;
languageItem.subtitle=newPostLanguage.locale.getDisplayLanguage(Locale.getDefault());
rebindItem(languageItem);
}
dlg.dismiss();
});
}
private void onCustomTabsClick(ListItem<?> item){
// GlobalUserPreferences.useCustomTabs=customTabsItem.checked;
Intent intent=new Intent(Intent.ACTION_VIEW, Uri.parse("http://example.com"));
ResolveInfo info=getActivity().getPackageManager().resolveActivity(intent, PackageManager.MATCH_DEFAULT_ONLY);
final String browserName;
@ -125,12 +93,5 @@ public class SettingsBehaviorFragment extends BaseSettingsFragment<Void>{
GlobalUserPreferences.confirmBoost=confirmBoostItem.checked;
GlobalUserPreferences.confirmDeletePost=confirmDeleteItem.checked;
GlobalUserPreferences.save();
if(newPostLanguage!=null){
AccountSession s=AccountSessionManager.get(accountID);
if(s.preferences==null)
s.preferences=new Preferences();
s.preferences.postingDefaultLanguage=newPostLanguage.locale.toLanguageTag();
s.savePreferencesLater();
}
}
}

View file

@ -13,9 +13,6 @@ import org.joinmastodon.android.E;
import org.joinmastodon.android.GlobalUserPreferences;
import org.joinmastodon.android.MastodonApp;
import org.joinmastodon.android.R;
import org.joinmastodon.android.api.session.AccountLocalPreferences;
import org.joinmastodon.android.api.session.AccountSession;
import org.joinmastodon.android.api.session.AccountSessionManager;
import org.joinmastodon.android.events.StatusDisplaySettingsChangedEvent;
import org.joinmastodon.android.model.viewmodel.CheckableListItem;
import org.joinmastodon.android.model.viewmodel.ListItem;
@ -36,8 +33,6 @@ public class SettingsDisplayFragment extends BaseSettingsFragment<Void>{
public void onCreate(Bundle savedInstanceState){
super.onCreate(savedInstanceState);
setTitle(R.string.settings_display);
AccountSession s=AccountSessionManager.get(accountID);
AccountLocalPreferences lp=s.getLocalPreferences();
List<ListItem<Void>> items=new ArrayList<>();
items.add(themeItem=new ListItem<>(R.string.settings_theme, getAppearanceValue(), R.drawable.ic_dark_mode_24px, this::onAppearanceClick));
if(Build.VERSION.SDK_INT>=Build.VERSION_CODES.S){
@ -47,10 +42,10 @@ public class SettingsDisplayFragment extends BaseSettingsFragment<Void>{
}));
dynamicColorsItem.checkedChangeListener=this::setUseDynamicColors;
}
items.add(showCWsItem=new CheckableListItem<>(R.string.settings_show_cws, 0, CheckableListItem.Style.SWITCH, lp.showCWs, R.drawable.ic_warning_24px, this::toggleCheckableItem));
items.add(hideSensitiveMediaItem=new CheckableListItem<>(R.string.settings_hide_sensitive_media, 0, CheckableListItem.Style.SWITCH, lp.hideSensitiveMedia, R.drawable.ic_no_adult_content_24px, this::toggleCheckableItem));
items.add(interactionCountsItem=new CheckableListItem<>(R.string.settings_show_interaction_counts, 0, CheckableListItem.Style.SWITCH, lp.showInteractionCounts, R.drawable.ic_social_leaderboard_24px, this::toggleCheckableItem));
items.add(emojiInNamesItem=new CheckableListItem<>(R.string.settings_show_emoji_in_names, 0, CheckableListItem.Style.SWITCH, lp.customEmojiInNames, R.drawable.ic_emoticon_24px, this::toggleCheckableItem));
items.add(showCWsItem=new CheckableListItem<>(R.string.settings_show_cws, 0, CheckableListItem.Style.SWITCH, GlobalUserPreferences.showCWs, R.drawable.ic_warning_24px, this::toggleCheckableItem));
items.add(hideSensitiveMediaItem=new CheckableListItem<>(R.string.settings_hide_sensitive_media, 0, CheckableListItem.Style.SWITCH, GlobalUserPreferences.hideSensitiveMedia, R.drawable.ic_no_adult_content_24px, this::toggleCheckableItem));
items.add(interactionCountsItem=new CheckableListItem<>(R.string.settings_show_interaction_counts, 0, CheckableListItem.Style.SWITCH, GlobalUserPreferences.showInteractionCounts, R.drawable.ic_social_leaderboard_24px, this::toggleCheckableItem));
items.add(emojiInNamesItem=new CheckableListItem<>(R.string.settings_show_emoji_in_names, 0, CheckableListItem.Style.SWITCH, GlobalUserPreferences.customEmojiInNames, R.drawable.ic_emoticon_24px, this::toggleCheckableItem));
onDataLoaded(items);
}
@ -70,13 +65,11 @@ public class SettingsDisplayFragment extends BaseSettingsFragment<Void>{
@Override
protected void onHidden(){
super.onHidden();
AccountSession s=AccountSessionManager.get(accountID);
AccountLocalPreferences lp=s.getLocalPreferences();
lp.showCWs=showCWsItem.checked;
lp.hideSensitiveMedia=hideSensitiveMediaItem.checked;
lp.showInteractionCounts=interactionCountsItem.checked;
lp.customEmojiInNames=emojiInNamesItem.checked;
lp.save();
GlobalUserPreferences.showCWs=showCWsItem.checked;
GlobalUserPreferences.hideSensitiveMedia=hideSensitiveMediaItem.checked;
GlobalUserPreferences.showInteractionCounts=interactionCountsItem.checked;
GlobalUserPreferences.customEmojiInNames=emojiInNamesItem.checked;
GlobalUserPreferences.save();
E.post(new StatusDisplaySettingsChangedEvent(accountID));
}

View file

@ -1,53 +1,44 @@
package org.joinmastodon.android.fragments.settings;
import android.app.Activity;
import android.content.Context;
import android.content.Intent;
import android.os.Bundle;
import android.view.View;
import android.widget.Button;
import android.widget.ImageView;
import android.widget.TextView;
import android.widget.Toast;
import com.squareup.otto.Subscribe;
import org.joinmastodon.android.BuildConfig;
import org.joinmastodon.android.E;
import org.joinmastodon.android.MainActivity;
import org.joinmastodon.android.GlobalUserPreferences;
import org.joinmastodon.android.R;
import org.joinmastodon.android.api.requests.catalog.GetDonationCampaigns;
import org.joinmastodon.android.api.session.AccountSession;
import org.joinmastodon.android.api.session.AccountSessionManager;
import org.joinmastodon.android.events.SelfUpdateStateChangedEvent;
import org.joinmastodon.android.model.donations.DonationCampaign;
import org.joinmastodon.android.fragments.SplashFragment;
import org.joinmastodon.android.model.viewmodel.ListItem;
import org.joinmastodon.android.ui.M3AlertDialogBuilder;
import org.joinmastodon.android.ui.sheets.AccountSwitcherSheet;
import org.joinmastodon.android.ui.sheets.DonationSheet;
import org.joinmastodon.android.ui.sheets.DonationSuccessfulSheet;
import org.joinmastodon.android.model.viewmodel.SectionHeaderListItem;
import org.joinmastodon.android.model.viewmodel.SettingsAccountListItem;
import org.joinmastodon.android.ui.utils.HideableSingleViewRecyclerAdapter;
import org.joinmastodon.android.ui.utils.UiUtils;
import org.joinmastodon.android.updater.GithubSelfUpdater;
import java.util.ArrayList;
import java.util.List;
import java.util.Locale;
import androidx.recyclerview.widget.RecyclerView;
import me.grishka.appkit.Nav;
import me.grishka.appkit.api.Callback;
import me.grishka.appkit.api.ErrorResponse;
import me.grishka.appkit.imageloader.requests.ImageLoaderRequest;
import me.grishka.appkit.imageloader.requests.UrlImageLoaderRequest;
import me.grishka.appkit.utils.MergeRecyclerAdapter;
import me.grishka.appkit.utils.V;
public class SettingsMainFragment extends BaseSettingsFragment<Void>{
private static final int DONATION_RESULT=433;
public class SettingsMainFragment extends BaseSettingsFragment<Object>{
private boolean loggedOut;
private HideableSingleViewRecyclerAdapter bannerAdapter;
private Button updateButton1, updateButton2;
private TextView updateText;
private DonationSheet donationSheet;
private Runnable updateDownloadProgressUpdater=new Runnable(){
@Override
public void run(){
@ -63,27 +54,35 @@ public class SettingsMainFragment extends BaseSettingsFragment<Void>{
public void onCreate(Bundle savedInstanceState){
super.onCreate(savedInstanceState);
setTitle(R.string.settings);
setSubtitle(AccountSessionManager.get(accountID).getFullUsername());
ArrayList<ListItem<Void>> items=new ArrayList<>();
if(BuildConfig.DEBUG || BuildConfig.BUILD_TYPE.equals("appcenterPrivateBeta")){
items.add(new ListItem<>("Debug settings", null, R.drawable.ic_settings_24px, i->Nav.go(getActivity(), SettingsDebugFragment.class, makeFragmentArgs()), null, 0, true));
ArrayList<ListItem<?>> items=new ArrayList<>();
items.add(new SectionHeaderListItem(R.string.settings_accounts));
for(AccountSession session:AccountSessionManager.getInstance().getLoggedInAccounts()){
ImageLoaderRequest req;
if(session.self.avatar!=null)
req=new UrlImageLoaderRequest(GlobalUserPreferences.playGifs ? session.self.avatar : session.self.avatarStatic, V.dp(50), V.dp(50));
else
req=null;
items.add(new SettingsAccountListItem<>(session.getFullUsername(), null, req, this::onAccountClick, session, false));
}
items.addAll(List.of(
new ListItem<>(R.string.settings_behavior, 0, R.drawable.ic_settings_24px, this::onBehaviorClick),
new ListItem<>(R.string.settings_display, 0, R.drawable.ic_style_24px, this::onDisplayClick),
new ListItem<>(R.string.settings_privacy, 0, R.drawable.ic_privacy_tip_24px, this::onPrivacyClick),
new ListItem<>(R.string.settings_filters, 0, R.drawable.ic_filter_alt_24px, this::onFiltersClick),
new ListItem<>(R.string.settings_notifications, 0, R.drawable.ic_notifications_24px, this::onNotificationsClick),
new ListItem<>(AccountSessionManager.get(accountID).domain, getString(R.string.settings_server_explanation), R.drawable.ic_dns_24px, this::onServerClick),
new ListItem<>(getString(R.string.about_app, getString(R.string.app_name)), null, R.drawable.ic_info_24px, this::onAboutClick, null, 0, true)
new ListItem<>(R.string.settings_add_account, 0, R.drawable.ic_add_24px, this::onAddAccountClick),
new SectionHeaderListItem(R.string.settings_app_settings),
new ListItem<>(R.string.settings_behavior, 0, R.drawable.ic_tune_24px, this::onBehaviorClick),
new ListItem<>(R.string.settings_display, 0, R.drawable.ic_style_24px, this::onDisplayClick)
));
if(AccountSessionManager.get(accountID).isEligibleForDonations()){
items.add(new ListItem<>(R.string.settings_donate, 0, R.drawable.ic_volunteer_activism_24px, this::onDonateClick));
items.add(new ListItem<>(R.string.settings_manage_donations, 0, R.drawable.ic_settings_heart_24px, this::onManageDonationClick, 0, true));
items.add(new ListItem<>(R.string.settings_manage_donations, 0, R.drawable.ic_settings_heart_24px, this::onManageDonationClick));
}
items.add(new ListItem<>(R.string.manage_accounts, 0, R.drawable.ic_switch_account_24px, this::onManageAccountsClick));
items.add(new ListItem<>(R.string.log_out, 0, R.drawable.ic_logout_24px, this::onLogOutClick, R.attr.colorM3Error, false));
onDataLoaded(items);
items.add(new ListItem<>(getString(R.string.about_app, getString(R.string.app_name)), null, R.drawable.ic_info_24px, this::onAboutClick, null));
if(BuildConfig.DEBUG || BuildConfig.BUILD_TYPE.equals("appcenterPrivateBeta")){
items.add(new ListItem<>("Debug settings", null, R.drawable.ic_bug_report_24px, i->Nav.go(getActivity(), SettingsDebugFragment.class, makeFragmentArgs()), null));
}
//noinspection unchecked
onDataLoaded((List<ListItem<Object>>)(Object)items);
AccountSession session=AccountSessionManager.get(accountID);
session.reloadPreferences(null);
@ -100,13 +99,6 @@ public class SettingsMainFragment extends BaseSettingsFragment<Void>{
@Override
protected void doLoadData(int offset, int count){}
@Override
protected void onHidden(){
super.onHidden();
if(!loggedOut)
AccountSessionManager.get(accountID).savePreferencesIfPending();
}
@Override
protected RecyclerView.Adapter<?> getAdapter(){
View banner=getActivity().getLayoutInflater().inflate(R.layout.item_settings_banner, list, false);
@ -137,23 +129,22 @@ public class SettingsMainFragment extends BaseSettingsFragment<Void>{
}
}
@Override
public void onActivityResult(int requestCode, int resultCode, Intent data){
if(requestCode==DONATION_RESULT){
if(donationSheet!=null)
donationSheet.dismissWithoutAnimation();
if(resultCode==Activity.RESULT_OK){
new DonationSuccessfulSheet(getActivity(), accountID, data.getStringExtra("postText")).showWithoutAnimation();
}
}
}
private Bundle makeFragmentArgs(){
Bundle args=new Bundle();
args.putString("account", accountID);
return args;
}
private void onAccountClick(SettingsAccountListItem<AccountSession> item){
Bundle args=new Bundle();
args.putString("account", item.parentObject.getID());
Nav.go(getActivity(), SettingsAccountFragment.class, args);
}
private void onAddAccountClick(ListItem<?> item_){
Nav.go(getActivity(), SplashFragment.class, null);
}
private void onBehaviorClick(ListItem<?> item_){
Nav.go(getActivity(), SettingsBehaviorFragment.class, makeFragmentArgs());
}
@ -162,73 +153,16 @@ public class SettingsMainFragment extends BaseSettingsFragment<Void>{
Nav.go(getActivity(), SettingsDisplayFragment.class, makeFragmentArgs());
}
private void onPrivacyClick(ListItem<?> item_){
Nav.go(getActivity(), SettingsPrivacyFragment.class, makeFragmentArgs());
}
private void onFiltersClick(ListItem<?> item_){
Nav.go(getActivity(), SettingsFiltersFragment.class, makeFragmentArgs());
}
private void onNotificationsClick(ListItem<?> item_){
Nav.go(getActivity(), SettingsNotificationsFragment.class, makeFragmentArgs());
}
private void onServerClick(ListItem<?> item_){
Nav.go(getActivity(), SettingsServerFragment.class, makeFragmentArgs());
}
private void onAboutClick(ListItem<?> item_){
Nav.go(getActivity(), SettingsAboutAppFragment.class, makeFragmentArgs());
}
private void onManageAccountsClick(ListItem<?> item){
new AccountSwitcherSheet(getActivity(), null).setOnLoggedOutCallback(()->loggedOut=true).show();
}
private void onLogOutClick(ListItem<?> item_){
AccountSession session=AccountSessionManager.getInstance().getAccount(accountID);
new M3AlertDialogBuilder(getActivity())
.setMessage(getString(R.string.confirm_log_out, session.getFullUsername()))
.setPositiveButton(R.string.log_out, (dialog, which)->AccountSessionManager.get(accountID).logOut(getActivity(), ()->{
loggedOut=true;
((MainActivity)getActivity()).restartHomeFragment();
}))
.setNegativeButton(R.string.cancel, null)
.show();
}
private void onDonateClick(ListItem<?> item){
GetDonationCampaigns req=new GetDonationCampaigns(Locale.getDefault().toLanguageTag().replace('-', '_'), String.valueOf(AccountSessionManager.get(accountID).getDonationSeed()), "menu");
if(getActivity().getSharedPreferences("debug", Context.MODE_PRIVATE).getBoolean("donationsStaging", false)){
req.setStaging(true);
}
req.setCallback(new Callback<>(){
@Override
public void onSuccess(DonationCampaign result){
Activity activity=getActivity();
if(activity==null)
return;
if(result==null){
Toast.makeText(activity, "No campaign available (server misconfiguration?)", Toast.LENGTH_SHORT).show();
return;
}
donationSheet=new DonationSheet(getActivity(), result, accountID, intent->startActivityForResult(intent, DONATION_RESULT));
donationSheet.setOnDismissListener(dialog->donationSheet=null);
donationSheet.show();
}
@Override
public void onError(ErrorResponse error){
error.showToast(getActivity());
}
})
.wrapProgress(getActivity(), R.string.loading, true)
.execNoAuth("");
private boolean useStagingEnvironmentForDonations(){
return (BuildConfig.DEBUG || BuildConfig.BUILD_TYPE.equals("appcenterPrivateBeta")) && getActivity().getSharedPreferences("debug", Context.MODE_PRIVATE).getBoolean("donationsStaging", false);
}
private void onManageDonationClick(ListItem<?> item){
UiUtils.launchWebBrowser(getActivity(), "https://sponsor.staging.joinmastodon.org/donate/manage");
UiUtils.launchWebBrowser(getActivity(), useStagingEnvironmentForDonations() ? "https://sponsor.staging.joinmastodon.org/donate/manage" : "https://sponsor.joinmastodon.org/donate/manage");
}
@Subscribe

View file

@ -5,7 +5,6 @@ import android.text.Spannable;
import android.text.SpannableStringBuilder;
import org.joinmastodon.android.GlobalUserPreferences;
import org.joinmastodon.android.api.session.AccountSessionManager;
import org.joinmastodon.android.model.Account;
import org.joinmastodon.android.model.AccountField;
import org.joinmastodon.android.ui.text.HtmlParser;
@ -33,7 +32,7 @@ public class AccountViewModel{
this.account=account;
avaRequest=new UrlImageLoaderRequest(GlobalUserPreferences.playGifs ? account.avatar : account.avatarStatic, V.dp(50), V.dp(50));
emojiHelper=new CustomEmojiHelper();
if(AccountSessionManager.get(accountID).getLocalPreferences().customEmojiInNames)
if(GlobalUserPreferences.customEmojiInNames)
parsedName=HtmlParser.parseCustomEmoji(account.displayName, account.emojis);
else
parsedName=account.displayName;

View file

@ -4,7 +4,6 @@ import android.text.SpannableStringBuilder;
import android.text.TextUtils;
import org.joinmastodon.android.GlobalUserPreferences;
import org.joinmastodon.android.api.session.AccountSessionManager;
import org.joinmastodon.android.model.Account;
import org.joinmastodon.android.model.Card;
import org.joinmastodon.android.ui.text.HtmlParser;
@ -31,7 +30,7 @@ public class CardViewModel{
if(authorAccount!=null){
parsedAuthorName=new SpannableStringBuilder(authorAccount.displayName);
if(AccountSessionManager.get(accountID).getLocalPreferences().customEmojiInNames)
if(GlobalUserPreferences.customEmojiInNames)
HtmlParser.parseCustomEmoji(parsedAuthorName, authorAccount.emojis);
authorNameEmojiHelper.setText(parsedAuthorName);
authorAvaRequest=new UrlImageLoaderRequest(GlobalUserPreferences.playGifs ? authorAccount.avatar : authorAccount.avatarStatic, V.dp(50), V.dp(50));

View file

@ -0,0 +1,20 @@
package org.joinmastodon.android.model.viewmodel;
import org.joinmastodon.android.R;
import androidx.annotation.StringRes;
public class SectionHeaderListItem extends ListItem<Void>{
public SectionHeaderListItem(String title){
super(title, null, null);
}
public SectionHeaderListItem(@StringRes int title){
super(title, 0, null);
}
@Override
public int getItemViewType(){
return R.id.list_item_section_header;
}
}

View file

@ -0,0 +1,21 @@
package org.joinmastodon.android.model.viewmodel;
import org.joinmastodon.android.R;
import java.util.function.Consumer;
import me.grishka.appkit.imageloader.requests.ImageLoaderRequest;
public class SettingsAccountListItem<T> extends ListItem<T>{
public ImageLoaderRequest avatar;
public SettingsAccountListItem(String title, String subtitle, ImageLoaderRequest avatar, Consumer<SettingsAccountListItem<T>> onClick, T parentObject, boolean dividerAfter){
super(title, subtitle, 0, (Consumer<ListItem<T>>)(Object)onClick, parentObject, 0, dividerAfter);
this.avatar=avatar;
}
@Override
public int getItemViewType(){
return R.id.list_item_settings_account;
}
}

View file

@ -5,17 +5,19 @@ import android.view.ViewGroup;
import org.joinmastodon.android.R;
import org.joinmastodon.android.model.viewmodel.AvatarPileListItem;
import org.joinmastodon.android.model.viewmodel.ListItem;
import org.joinmastodon.android.model.viewmodel.SettingsAccountListItem;
import org.joinmastodon.android.ui.viewholders.AvatarPileListItemViewHolder;
import org.joinmastodon.android.ui.viewholders.CheckboxOrRadioListItemViewHolder;
import org.joinmastodon.android.ui.viewholders.ListItemViewHolder;
import org.joinmastodon.android.ui.viewholders.OptionsListItemViewHolder;
import org.joinmastodon.android.ui.viewholders.SectionHeaderListItemViewHolder;
import org.joinmastodon.android.ui.viewholders.SettingsAccountListItemViewHolder;
import org.joinmastodon.android.ui.viewholders.SimpleListItemViewHolder;
import org.joinmastodon.android.ui.viewholders.SwitchListItemViewHolder;
import java.util.List;
import androidx.annotation.NonNull;
import androidx.recyclerview.widget.RecyclerView;
import me.grishka.appkit.imageloader.ImageLoaderRecyclerAdapter;
import me.grishka.appkit.imageloader.ListImageLoaderWrapper;
import me.grishka.appkit.imageloader.requests.ImageLoaderRequest;
@ -49,6 +51,10 @@ public class GenericListItemsAdapter<T> extends UsableRecyclerView.Adapter<ListI
return new OptionsListItemViewHolder(parent.getContext(), parent);
if(viewType==R.id.list_item_avatar_pile)
return new AvatarPileListItemViewHolder(parent.getContext(), parent);
if(viewType==R.id.list_item_settings_account)
return new SettingsAccountListItemViewHolder(parent.getContext(), parent);
if(viewType==R.id.list_item_section_header)
return new SectionHeaderListItemViewHolder(parent.getContext(), parent);
throw new IllegalArgumentException("Unexpected view type "+viewType);
}
@ -74,6 +80,8 @@ public class GenericListItemsAdapter<T> extends UsableRecyclerView.Adapter<ListI
ListItem<?> item=items.get(position);
if(item instanceof AvatarPileListItem<?> avatarPileListItem)
return avatarPileListItem.avatars.size();
if(item instanceof SettingsAccountListItem<?>)
return 1;
return 0;
}
@ -82,6 +90,8 @@ public class GenericListItemsAdapter<T> extends UsableRecyclerView.Adapter<ListI
ListItem<?> item=items.get(position);
if(item instanceof AvatarPileListItem<?> avatarPileListItem)
return avatarPileListItem.avatars.get(image);
if(item instanceof SettingsAccountListItem<?> accountItem)
return accountItem.avatar;
return null;
}
}

View file

@ -75,7 +75,7 @@ public class HeaderStatusDisplayItem extends StatusDisplayItem{
this.accountID=accountID;
parsedName=new SpannableStringBuilder(user.displayName);
this.status=status;
if(AccountSessionManager.get(accountID).getLocalPreferences().customEmojiInNames)
if(GlobalUserPreferences.customEmojiInNames)
HtmlParser.parseCustomEmoji(parsedName, user.emojis);
emojiHelper.setText(parsedName);
if(status!=null){

View file

@ -9,7 +9,6 @@ import android.widget.TextView;
import org.joinmastodon.android.GlobalUserPreferences;
import org.joinmastodon.android.R;
import org.joinmastodon.android.api.session.AccountSessionManager;
import org.joinmastodon.android.fragments.BaseStatusListFragment;
import org.joinmastodon.android.model.Status;
import org.joinmastodon.android.ui.OutlineProviders;
@ -36,7 +35,7 @@ public class InlineStatusStatusDisplayItem extends StatusDisplayItem{
this.status=status;
parsedName=new SpannableStringBuilder(status.account.displayName);
if(AccountSessionManager.get(parentFragment.getAccountID()).getLocalPreferences().customEmojiInNames)
if(GlobalUserPreferences.customEmojiInNames)
HtmlParser.parseCustomEmoji(parsedName, status.account.emojis);
parsedPostText=HtmlParser.parse(status.content, status.emojis, status.mentions, status.tags, parentFragment.getAccountID(), status.getContentStatus(), parentFragment.getActivity());

View file

@ -7,8 +7,8 @@ import android.text.SpannableStringBuilder;
import android.view.ViewGroup;
import android.widget.TextView;
import org.joinmastodon.android.GlobalUserPreferences;
import org.joinmastodon.android.R;
import org.joinmastodon.android.api.session.AccountSessionManager;
import org.joinmastodon.android.fragments.BaseStatusListFragment;
import org.joinmastodon.android.model.Emoji;
import org.joinmastodon.android.ui.text.HtmlParser;
@ -31,7 +31,7 @@ public class ReblogOrReplyLineStatusDisplayItem extends StatusDisplayItem{
public ReblogOrReplyLineStatusDisplayItem(String parentID, BaseStatusListFragment parentFragment, CharSequence text, List<Emoji> emojis, @DrawableRes int icon){
super(parentID, parentFragment);
SpannableStringBuilder ssb=new SpannableStringBuilder(text);
if(AccountSessionManager.get(parentFragment.getAccountID()).getLocalPreferences().customEmojiInNames)
if(GlobalUserPreferences.customEmojiInNames)
HtmlParser.parseCustomEmoji(ssb, emojis);
this.text=ssb;
emojiHelper.setText(ssb);

View file

@ -8,8 +8,8 @@ import android.text.TextUtils;
import android.view.View;
import android.view.ViewGroup;
import org.joinmastodon.android.GlobalUserPreferences;
import org.joinmastodon.android.R;
import org.joinmastodon.android.api.session.AccountSessionManager;
import org.joinmastodon.android.fragments.BaseStatusListFragment;
import org.joinmastodon.android.fragments.ThreadFragment;
import org.joinmastodon.android.model.Account;
@ -97,7 +97,7 @@ public abstract class StatusDisplayItem{
ArrayList<StatusDisplayItem> items=new ArrayList<>();
Status statusForContent=status.getContentStatus();
HeaderStatusDisplayItem header=null;
boolean hideCounts=!AccountSessionManager.get(accountID).getLocalPreferences().showInteractionCounts;
boolean hideCounts=!GlobalUserPreferences.showInteractionCounts;
if((flags & FLAG_NO_HEADER)==0){
if(status.reblog!=null){
items.add(new ReblogOrReplyLineStatusDisplayItem(parentID, fragment, fragment.getString(R.string.user_boosted, status.account.displayName), status.account.emojis, R.drawable.ic_repeat_wght700_20px));
@ -134,7 +134,7 @@ public abstract class StatusDisplayItem{
SpoilerStatusDisplayItem spoilerItem=new SpoilerStatusDisplayItem(parentID, fragment, null, status, statusForContent, Type.SPOILER, Status.SpoilerType.CONTENT_WARNING);
contentItems.add(spoilerItem);
contentItems=spoilerItem.contentItems;
if(!AccountSessionManager.get(accountID).getLocalPreferences().showCWs && !filtered){
if(!GlobalUserPreferences.showCWs && !filtered){
status.revealedSpoilers.add(Status.SpoilerType.CONTENT_WARNING);
needAddCWItems=true;
}
@ -158,7 +158,7 @@ public abstract class StatusDisplayItem{
if((flags & FLAG_MEDIA_FORCE_HIDDEN)!=0){
mediaGrid.sensitiveTitle=fragment.getString(R.string.media_hidden);
mediaGrid.sensitiveRevealed=false;
}else if(statusForContent.sensitive && !AccountSessionManager.get(accountID).getLocalPreferences().hideSensitiveMedia){
}else if(statusForContent.sensitive && !GlobalUserPreferences.hideSensitiveMedia){
mediaGrid.sensitiveRevealed=true;
}
contentItems.add(mediaGrid);

View file

@ -11,8 +11,8 @@ import android.widget.ImageView;
import android.widget.LinearLayout;
import android.widget.TextView;
import org.joinmastodon.android.GlobalUserPreferences;
import org.joinmastodon.android.R;
import org.joinmastodon.android.api.session.AccountSessionManager;
import org.joinmastodon.android.model.Account;
import org.joinmastodon.android.ui.OutlineProviders;
import org.joinmastodon.android.ui.text.HtmlParser;
@ -57,7 +57,7 @@ public class NonMutualPreReplySheet extends PreReplySheet{
name.setEllipsize(TextUtils.TruncateAt.END);
name.setTextAppearance(R.style.m3_title_medium);
name.setTextColor(UiUtils.getThemeColor(context, R.attr.colorM3OnSurface));
if(AccountSessionManager.get(accountID).getLocalPreferences().customEmojiInNames){
if(GlobalUserPreferences.customEmojiInNames){
name.setText(HtmlParser.parseCustomEmoji(account.displayName, account.emojis));
UiUtils.loadCustomEmojiInTextView(name);
}else{

View file

@ -28,7 +28,7 @@ public abstract class ListItemViewHolder<T extends ListItem<?>> extends Bindable
title=findViewById(R.id.title);
subtitle=findViewById(R.id.subtitle);
icon=findViewById(R.id.icon);
view=(LinearLayout) itemView;
view=itemView instanceof LinearLayout ll ? ll : null;
}
@Override
@ -52,12 +52,7 @@ public abstract class ListItemViewHolder<T extends ListItem<?>> extends Bindable
subtitle.setText(item.subtitle);
}
if(item.iconRes!=0){
icon.setVisibility(View.VISIBLE);
icon.setImageResource(item.iconRes);
}else{
icon.setVisibility(View.GONE);
}
bindIcon(item);
if(item.colorOverrideAttr!=0){
int color=UiUtils.getThemeColor(view.getContext(), item.colorOverrideAttr);
@ -68,6 +63,15 @@ public abstract class ListItemViewHolder<T extends ListItem<?>> extends Bindable
view.setAlpha(item.isEnabled ? 1 : .4f);
}
protected void bindIcon(T item){
if(item.iconRes!=0){
icon.setVisibility(View.VISIBLE);
icon.setImageResource(item.iconRes);
}else{
icon.setVisibility(View.GONE);
}
}
@Override
public boolean isEnabled(){
return item.isEnabled;

View file

@ -0,0 +1,22 @@
package org.joinmastodon.android.ui.viewholders;
import android.content.Context;
import android.text.TextUtils;
import android.view.ViewGroup;
import org.joinmastodon.android.R;
import org.joinmastodon.android.model.viewmodel.SectionHeaderListItem;
public class SectionHeaderListItemViewHolder extends ListItemViewHolder<SectionHeaderListItem>{
public SectionHeaderListItemViewHolder(Context context, ViewGroup parent){
super(context, R.layout.item_generic_list_header, parent);
}
@Override
public void onBind(SectionHeaderListItem item){
if(TextUtils.isEmpty(item.title))
title.setText(item.titleRes);
else
title.setText(item.title);
}
}

View file

@ -0,0 +1,34 @@
package org.joinmastodon.android.ui.viewholders;
import android.content.Context;
import android.graphics.drawable.Drawable;
import android.view.ViewGroup;
import org.joinmastodon.android.R;
import org.joinmastodon.android.model.viewmodel.SettingsAccountListItem;
import org.joinmastodon.android.ui.OutlineProviders;
import me.grishka.appkit.imageloader.ImageLoaderViewHolder;
public class SettingsAccountListItemViewHolder extends ListItemViewHolder<SettingsAccountListItem<?>> implements ImageLoaderViewHolder{
public SettingsAccountListItemViewHolder(Context context, ViewGroup parent){
super(context, R.layout.item_generic_list, parent);
icon.setOutlineProvider(OutlineProviders.OVAL);
icon.setClipToOutline(true);
icon.setImageTintList(null);
}
@Override
protected void bindIcon(SettingsAccountListItem<?> item){}
@Override
public void setImage(int index, Drawable image){
icon.setImageDrawable(image);
}
@Override
public void clearImage(int index){
icon.setImageResource(R.drawable.image_placeholder);
}
}

View file

@ -0,0 +1,9 @@
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="24dp"
android:height="24dp"
android:viewportWidth="960"
android:viewportHeight="960">
<path
android:fillColor="@android:color/white"
android:pathData="M480,760Q546,760 593,713Q640,666 640,600L640,440Q640,374 593,327Q546,280 480,280Q414,280 367,327Q320,374 320,440L320,600Q320,666 367,713Q414,760 480,760ZM400,640L560,640L560,560L400,560L400,640ZM400,480L560,480L560,400L400,400L400,480ZM480,520Q480,520 480,520Q480,520 480,520L480,520Q480,520 480,520Q480,520 480,520Q480,520 480,520Q480,520 480,520L480,520Q480,520 480,520Q480,520 480,520ZM480,840Q415,840 359.5,808Q304,776 272,720L160,720L160,640L244,640Q241,620 240.5,600Q240,580 240,560L160,560L160,480L240,480Q240,460 240.5,440Q241,420 244,400L160,400L160,320L272,320Q286,297 303.5,277Q321,257 344,242L280,176L336,120L422,206Q450,197 479,197Q508,197 536,206L624,120L680,176L614,242Q637,257 655.5,276.5Q674,296 688,320L800,320L800,400L716,400Q719,420 719.5,440Q720,460 720,480L800,480L800,560L720,560Q720,580 719.5,600Q719,620 716,640L800,640L800,720L688,720Q656,776 600.5,808Q545,840 480,840Z"/>
</vector>

View file

@ -0,0 +1,9 @@
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="24dp"
android:height="24dp"
android:viewportWidth="960"
android:viewportHeight="960">
<path
android:fillColor="@android:color/white"
android:pathData="M376,660L480,556L584,660L640,604L536,500L640,396L584,340L480,444L376,340L320,396L424,500L320,604L376,660ZM280,840Q247,840 223.5,816.5Q200,793 200,760L200,240L160,240L160,160L360,160L360,120L600,120L600,160L800,160L800,240L760,240L760,760Q760,793 736.5,816.5Q713,840 680,840L280,840ZM680,240L280,240L280,760Q280,760 280,760Q280,760 280,760L680,760Q680,760 680,760Q680,760 680,760L680,240ZM280,240L280,240L280,760Q280,760 280,760Q280,760 280,760L280,760Q280,760 280,760Q280,760 280,760L280,240Z"/>
</vector>

View file

@ -0,0 +1,15 @@
<?xml version="1.0" encoding="utf-8"?>
<TextView xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:id="@+id/title"
android:layout_width="match_parent"
android:layout_height="36dp"
android:layout_marginTop="24dp"
android:paddingStart="16dp"
android:paddingEnd="24dp"
android:gravity="center_vertical"
android:singleLine="true"
android:ellipsize="end"
android:textAppearance="@style/m3_title_small"
android:textColor="?colorM3OnSurfaceVariant"
tools:text="Section header"/>

View file

@ -0,0 +1,12 @@
<?xml version="1.0" encoding="utf-8"?>
<TextView xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:id="@+id/real_title"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:paddingHorizontal="16dp"
android:paddingTop="4dp"
android:paddingBottom="24dp"
android:textAppearance="@style/m3_headline_small"
android:textColor="?colorM3OnSurface"
tools:text="mastodon.social" />

View file

@ -27,6 +27,8 @@
<item name="list_item_account" type="id"/>
<item name="list_item_options" type="id"/>
<item name="list_item_avatar_pile" type="id"/>
<item name="list_item_settings_account" type="id"/>
<item name="list_item_section_header" type="id"/>
<item name="server_about" type="id"/>
<item name="server_rules" type="id"/>

View file

@ -814,6 +814,14 @@
<string name="avatar_move_and_scale">Move and scale</string>
<string name="confirm_avatar_crop">Choose</string>
<string name="settings_use_dynamic_colors">Use system dynamic color</string>
<string name="settings_accounts">Accounts</string>
<string name="settings_add_account">Add account...</string>
<string name="settings_app_settings">App settings</string>
<string name="account_settings">Account settings</string>
<string name="settings_about_this_server">About this server</string>
<string name="manage_account">Manage account</string>
<string name="switch_to_this_account">Switch to this account</string>
<string name="delete_account">Delete account</string>
<plurals name="user_and_x_more_followed">
<item quantity="one">%1$s and %2$,d other followed you</item>
<item quantity="other">%1$s and %2$,d others followed you</item>