diff --git a/mastodon/src/main/java/org/joinmastodon/android/GlobalUserPreferences.java b/mastodon/src/main/java/org/joinmastodon/android/GlobalUserPreferences.java index ecbea0fa9..9274af267 100644 --- a/mastodon/src/main/java/org/joinmastodon/android/GlobalUserPreferences.java +++ b/mastodon/src/main/java/org/joinmastodon/android/GlobalUserPreferences.java @@ -2,6 +2,14 @@ package org.joinmastodon.android; import android.content.Context; import android.content.SharedPreferences; +import android.content.res.Resources; +import android.os.Build; +import android.os.LocaleList; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.List; +import java.util.Locale; +import java.util.stream.Collectors; public class GlobalUserPreferences{ public static boolean playGifs; @@ -17,6 +25,21 @@ public class GlobalUserPreferences{ public static boolean voteButtonForSingleChoice; public static ThemePreference theme; public static ColorPreference color; + public static List recentLanguages; + + private static String defaultRecentLanguages; + + static { + List systemLocales = new ArrayList<>();; + if (Build.VERSION.SDK_INT < Build.VERSION_CODES.N) { + systemLocales.add(Resources.getSystem().getConfiguration().locale); + } else { + LocaleList localeList = Resources.getSystem().getConfiguration().getLocales(); + for (int i = 0; i < localeList.size(); i++) systemLocales.add(localeList.get(i)); + } + + defaultRecentLanguages = systemLocales.stream().map(Locale::getLanguage).collect(Collectors.joining(",")); + } private static SharedPreferences getPrefs(){ return MastodonApp.context.getSharedPreferences("global", Context.MODE_PRIVATE); @@ -37,6 +60,7 @@ public class GlobalUserPreferences{ voteButtonForSingleChoice=prefs.getBoolean("voteButtonForSingleChoice", true); theme=ThemePreference.values()[prefs.getInt("theme", 0)]; color=ColorPreference.values()[prefs.getInt("color", 0)]; + recentLanguages=Arrays.stream(prefs.getString("recentLanguages", defaultRecentLanguages).split(",")).filter(s->!s.isEmpty()).collect(Collectors.toList()); } public static void save(){ @@ -53,6 +77,7 @@ public class GlobalUserPreferences{ .putBoolean("disableMarquee", disableMarquee) .putInt("theme", theme.ordinal()) .putInt("color", color.ordinal()) + .putString("recentLanguages", String.join(",", recentLanguages)) .apply(); } diff --git a/mastodon/src/main/java/org/joinmastodon/android/fragments/ComposeFragment.java b/mastodon/src/main/java/org/joinmastodon/android/fragments/ComposeFragment.java index 10c625484..5260c0b6a 100644 --- a/mastodon/src/main/java/org/joinmastodon/android/fragments/ComposeFragment.java +++ b/mastodon/src/main/java/org/joinmastodon/android/fragments/ComposeFragment.java @@ -29,11 +29,13 @@ import android.text.Spanned; import android.text.TextUtils; import android.text.TextWatcher; import android.util.Log; +import android.util.TypedValue; import android.view.Gravity; import android.view.LayoutInflater; import android.view.Menu; import android.view.MenuInflater; import android.view.MenuItem; +import android.view.SubMenu; import android.view.View; import android.view.ViewGroup; import android.view.ViewOutlineProvider; @@ -55,6 +57,7 @@ import android.widget.Toast; import com.twitter.twittertext.TwitterTextEmojiRegex; 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.MastodonAPIController; @@ -103,6 +106,8 @@ import java.net.SocketException; import java.net.UnknownHostException; import java.time.temporal.ChronoUnit; import java.util.ArrayList; +import java.util.Collections; +import java.util.Comparator; import java.util.List; import java.util.Locale; import java.util.UUID; @@ -130,6 +135,7 @@ public class ComposeFragment extends MastodonToolbarFragment implements OnBackPr // from https://github.com/mastodon/mastodon-ios/blob/main/Mastodon/Helper/MastodonRegex.swift private static final Pattern AUTO_COMPLETE_PATTERN=Pattern.compile("(? allIsoLanguages; @SuppressLint("NewApi") // this class actually exists on 6.0 private final BreakIterator breakIterator=BreakIterator.getCharacterInstance(); @@ -145,7 +151,8 @@ public class ComposeFragment extends MastodonToolbarFragment implements OnBackPr private String accountID; private int charCount, charLimit, trimmedCharCount; - private Button publishButton; + private Button publishButton, languageButton; + private PopupMenu languagePopup; private ImageButton mediaBtn, pollBtn, emojiBtn, spoilerBtn, visibilityBtn; private ImageView sensitiveIcon; private ComposeMediaLayout attachmentsView; @@ -190,6 +197,24 @@ public class ComposeFragment extends MastodonToolbarFragment implements OnBackPr private boolean ignoreSelectionChanges=false; private Runnable updateUploadEtaRunnable; + private String language; + + static { + Locale[] locales = Locale.getAvailableLocales(); + List allLocales = new ArrayList<>(); + List addedLanguages = new ArrayList<>(); + for (Locale locale : locales) { + String lang = locale.getLanguage(); + if (!addedLanguages.contains(lang)) { + allLocales.add(locale); + addedLanguages.add(lang); + } + } + + Collections.sort(allLocales, Comparator.comparing(l -> l.getDisplayLanguage(Locale.getDefault()))); + allIsoLanguages = allLocales; + } + @Override public void onCreate(Bundle savedInstanceState){ super.onCreate(savedInstanceState); @@ -403,6 +428,7 @@ public class ComposeFragment extends MastodonToolbarFragment implements OnBackPr } outState.putBoolean("sensitive", sensitive); outState.putBoolean("hasSpoiler", hasSpoiler); + outState.putString("language", language); if(!attachments.isEmpty()){ ArrayList serializedAttachments=new ArrayList<>(attachments.size()); for(DraftMediaAttachment att:attachments){ @@ -542,6 +568,7 @@ public class ComposeFragment extends MastodonToolbarFragment implements OnBackPr spoilerEdit.setText(replyTo.spoilerText); spoilerBtn.setSelected(true); } + if (replyTo.language != null && !replyTo.language.isEmpty()) updateLanguage(replyTo.language); } }else if (editingStatus==null || editingStatus.inReplyToId==null){ // TODO: remove workaround after https://github.com/mastodon/mastodon-android/issues/341 gets fixed @@ -554,6 +581,7 @@ public class ComposeFragment extends MastodonToolbarFragment implements OnBackPr ignoreSelectionChanges=true; mainEditText.setSelection(mainEditText.length()); ignoreSelectionChanges=false; + updateLanguage(editingStatus.language); if(!editingStatus.mediaAttachments.isEmpty()){ attachmentsView.setVisibility(View.VISIBLE); for(Attachment att:editingStatus.mediaAttachments){ @@ -616,6 +644,10 @@ public class ComposeFragment extends MastodonToolbarFragment implements OnBackPr sendError.setVisibility(View.GONE); sendProgress.setVisibility(View.GONE); + LinearLayout.LayoutParams langParams=new LinearLayout.LayoutParams(ViewGroup.LayoutParams.WRAP_CONTENT, ViewGroup.LayoutParams.WRAP_CONTENT); + langParams.setMarginEnd(V.dp(8)); + wrap.addView(buildLanguageSelector(), langParams); + wrap.addView(publishButton, new LinearLayout.LayoutParams(ViewGroup.LayoutParams.WRAP_CONTENT, ViewGroup.LayoutParams.WRAP_CONTENT)); wrap.setPadding(V.dp(16), V.dp(4), V.dp(16), V.dp(8)); wrap.setClipToPadding(false); @@ -625,6 +657,55 @@ public class ComposeFragment extends MastodonToolbarFragment implements OnBackPr updatePublishButtonState(); } + private void updateLanguage(String lang) { + updateLanguage(new Locale(lang)); + } + + private void updateLanguage(Locale loc) { + language = loc.getLanguage(); + languageButton.setText(loc.getDisplayLanguage(loc)); + languageButton.setContentDescription(getActivity().getString(R.string.sk_post_language, loc.getDisplayLanguage(Locale.getDefault()))); + } + + @SuppressLint("ClickableViewAccessibility") + private Button buildLanguageSelector() { + languageButton=new Button(getActivity()); + TypedValue typedValue = new TypedValue(); + getActivity().getTheme().resolveAttribute(android.R.attr.textColorSecondary, typedValue, true); + languageButton.setTextColor(typedValue.data); + languageButton.setBackground(getActivity().getDrawable(R.drawable.bg_text_button)); + languageButton.setPadding(V.dp(8), 0, V.dp(8), 0); + languageButton.setCompoundDrawablesRelativeWithIntrinsicBounds(getActivity().getDrawable(R.drawable.ic_fluent_local_language_16_regular), null, null, null); + languageButton.setCompoundDrawableTintList(languageButton.getTextColors()); + languageButton.setCompoundDrawablePadding(V.dp(6)); + + updateLanguage(Locale.getDefault()); + languagePopup=new PopupMenu(getActivity(), languageButton); + languageButton.setOnTouchListener(languagePopup.getDragToOpenListener()); + languageButton.setOnClickListener(v->languagePopup.show()); + + Menu languageMenu = languagePopup.getMenu(); + + for (String recentLanguage : GlobalUserPreferences.recentLanguages) { + Locale loc = new Locale(recentLanguage); + languageMenu.add(0, allIsoLanguages.indexOf(loc), Menu.NONE, loc.getDisplayLanguage(Locale.getDefault())); + } + + SubMenu allLanguagesMenu = languageMenu.addSubMenu(R.string.sk_all_languages); + for (int i = 0; i < allIsoLanguages.size(); i++) { + Locale loc = allIsoLanguages.get(i); + allLanguagesMenu.add(0, i, Menu.NONE, loc.getDisplayLanguage(Locale.getDefault())); + } + + languagePopup.setOnMenuItemClickListener(i->{ + if (i.hasSubMenu()) return false; + updateLanguage(allIsoLanguages.get(i.getItemId())); + return true; + }); + + return languageButton; + } + @Override public boolean onOptionsItemSelected(MenuItem item){ return true; @@ -698,6 +779,7 @@ public class ComposeFragment extends MastodonToolbarFragment implements OnBackPr req.status=text; req.visibility=statusVisibility; req.sensitive=sensitive; + req.language=language; if(!attachments.isEmpty()){ req.mediaIds=attachments.stream().map(a->a.serverAttachment.id).collect(Collectors.toList()); } @@ -775,6 +857,12 @@ public class ComposeFragment extends MastodonToolbarFragment implements OnBackPr .setCallback(resCallback) .exec(accountID); } + + List recentLanguages = new ArrayList<>(GlobalUserPreferences.recentLanguages); + recentLanguages.remove(language); + recentLanguages.add(0, language); + GlobalUserPreferences.recentLanguages = recentLanguages.stream().limit(4).collect(Collectors.toList()); + GlobalUserPreferences.save(); } private boolean hasDraft(){ diff --git a/mastodon/src/main/java/org/joinmastodon/android/fragments/SettingsFragment.java b/mastodon/src/main/java/org/joinmastodon/android/fragments/SettingsFragment.java index 898b45100..f25afd82d 100644 --- a/mastodon/src/main/java/org/joinmastodon/android/fragments/SettingsFragment.java +++ b/mastodon/src/main/java/org/joinmastodon/android/fragments/SettingsFragment.java @@ -159,6 +159,10 @@ public class SettingsFragment extends MastodonToolbarFragment{ } items.add(new TextItem(R.string.sk_settings_contribute, ()->UiUtils.launchWebBrowser(getActivity(), "https://github.com/sk22/megalodon"))); items.add(new TextItem(R.string.settings_clear_cache, this::clearImageCache)); + items.add(new TextItem(R.string.sk_clear_recent_languages, ()->UiUtils.showConfirmationAlert(getActivity(), R.string.sk_clear_recent_languages, R.string.sk_confirm_clear_recent_languages, R.string.clear, ()->{ + GlobalUserPreferences.recentLanguages.clear(); + GlobalUserPreferences.save(); + }))); items.add(new TextItem(R.string.log_out, this::confirmLogOut)); items.add(new FooterItem(getString(R.string.sk_settings_app_version, BuildConfig.VERSION_NAME, BuildConfig.VERSION_CODE))); diff --git a/mastodon/src/main/res/values/strings_sk.xml b/mastodon/src/main/res/values/strings_sk.xml index ed4fa2699..7dbe7a06f 100644 --- a/mastodon/src/main/res/values/strings_sk.xml +++ b/mastodon/src/main/res/values/strings_sk.xml @@ -51,4 +51,8 @@ Translate Show original Translated using %s + Language: %s + All languages + Clear recent languages + Are you sure you want to clear your recently used languages?