package com.keylesspalace.tusky; import android.app.AlarmManager; import android.app.PendingIntent; import android.content.Context; import android.content.Intent; import android.os.Build; import android.preference.DialogPreference; import android.preference.PreferenceManager; import android.support.v7.app.AlertDialog; import android.util.AttributeSet; import android.util.Log; import android.view.View; import android.widget.ImageButton; import android.widget.ImageView; import android.widget.ProgressBar; import android.widget.RadioButton; import android.widget.TextView; import android.widget.Toast; import com.keylesspalace.tusky.util.EmojiCompatFont; import java.util.ArrayList; /** * This Preference lets the user select their preferred emoji font */ public class EmojiPreference extends DialogPreference { private static final String TAG = "EmojiPreference"; private final Context context; private EmojiCompatFont selected, original; static final String FONT_PREFERENCE = "selected_emoji_font"; private static final EmojiCompatFont[] FONTS = EmojiCompatFont.FONTS; // Please note that this array should be sorted in the same way as their fonts. private static final int[] viewIds = { R.id.item_nomoji, R.id.item_blobmoji, R.id.item_twemoji}; private ArrayList radioButtons = new ArrayList<>(); public EmojiPreference(Context context, AttributeSet attrs) { super(context, attrs); this.context = context; setDialogLayoutResource(R.layout.dialog_emojicompat); setPositiveButtonText(android.R.string.ok); setNegativeButtonText(android.R.string.cancel); setDialogIcon(null); // Find out which font is currently active this.selected = EmojiCompatFont.byId(PreferenceManager .getDefaultSharedPreferences(context) .getInt(FONT_PREFERENCE, 0)); // We'll use this later to determine if anything has changed this.original = this.selected; setSummary(selected.getDisplay(context)); } @Override protected void onBindDialogView(View view) { super.onBindDialogView(view); for(int i = 0; i < viewIds.length; i++) { setupItem(view.findViewById(viewIds[i]), FONTS[i]); } } private void setupItem(View container, EmojiCompatFont font) { Context context = container.getContext(); TextView title = container.findViewById(R.id.emojicompat_name); TextView caption = container.findViewById(R.id.emojicompat_caption); ImageView thumb = container.findViewById(R.id.emojicompat_thumb); ImageButton download = container.findViewById(R.id.emojicompat_download); ImageButton cancel = container.findViewById(R.id.emojicompat_download_cancel); RadioButton radio = container.findViewById(R.id.emojicompat_radio); // Initialize all the views title.setText(font.getDisplay(context)); caption.setText(font.getCaption(context)); thumb.setImageDrawable(font.getThumb(context)); // There needs to be a list of all the radio buttons in order to uncheck them when one is selected radioButtons.add(radio); updateItem(font, container); // Set actions download.setOnClickListener((downloadButton) -> startDownload(font, container)); cancel.setOnClickListener((cancelButton) -> cancelDownload(font, container)); radio.setOnClickListener((radioButton) -> select(font, (RadioButton) radioButton)); container.setOnClickListener((containterView) -> select(font, containterView.findViewById(R.id.emojicompat_radio ))); } private void startDownload(EmojiCompatFont font, View container) { ImageButton download = container.findViewById(R.id.emojicompat_download); TextView caption = container.findViewById(R.id.emojicompat_caption); ProgressBar progressBar = container.findViewById(R.id.emojicompat_progress); ImageButton cancel = container.findViewById(R.id.emojicompat_download_cancel); // Switch to downloading style download.setVisibility(View.GONE); caption.setVisibility(View.GONE); progressBar.setVisibility(View.VISIBLE); cancel.setVisibility(View.VISIBLE); font.downloadFont(context, new EmojiCompatFont.Downloader.EmojiDownloadListener() { @Override public void onDownloaded(EmojiCompatFont font) { finishDownload(font, container); } @Override public void onProgress(float progress) { // The progress is returned as a float between 0 and 1 progress *= progressBar.getMax(); if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) { progressBar.setProgress((int) progress, true); } else { progressBar.setProgress((int) progress); } } @Override public void onFailed() { Toast.makeText(getContext(), R.string.download_failed, Toast.LENGTH_SHORT).show(); updateItem(font, container); } }); } private void cancelDownload(EmojiCompatFont font, View container) { font.cancelDownload(); updateItem(font, container); } private void finishDownload(EmojiCompatFont font, View container) { select(font, container.findViewById(R.id.emojicompat_radio)); updateItem(font, container); } /** * Select a font both visually and logically * @param font The font to be selected * @param radio The radio button associated with it's visual item */ private void select(EmojiCompatFont font, RadioButton radio) { selected = font; // Uncheck all the other buttons for(RadioButton other : radioButtons) { if(other != radio) { other.setChecked(false); } } radio.setChecked(true); } /** * Called when a "consistent" state is reached, i.e. it's not downloading the font * @param font The font to be displayed * @param container The ConstraintLayout containing the item */ private void updateItem(EmojiCompatFont font, View container) { // Assignments ImageButton download = container.findViewById(R.id.emojicompat_download); TextView caption = container.findViewById(R.id.emojicompat_caption); ProgressBar progress = container.findViewById(R.id.emojicompat_progress); ImageButton cancel = container.findViewById(R.id.emojicompat_download_cancel); RadioButton radio = container.findViewById(R.id.emojicompat_radio); // There's no download going on progress.setVisibility(View.GONE); cancel.setVisibility(View.GONE); caption.setVisibility(View.VISIBLE); if(font.isDownloaded(context)) { // Make it selectable download.setVisibility(View.GONE); radio.setVisibility(View.VISIBLE); container.setClickable(true); } else { // Make it downloadable download.setVisibility(View.VISIBLE); radio.setVisibility(View.GONE); container.setClickable(false); } // Select it if necessary if(font == selected) { radio.setChecked(true); } else { radio.setChecked(false); } } /** * In order to be able to use this font later on, it needs to be saved first. */ private void saveSelectedFont() { int index = selected.getId(); Log.i(TAG, "saveSelectedFont: Font ID: " + index); // It's saved using the key FONT_PREFERENCE PreferenceManager .getDefaultSharedPreferences(context) .edit() .putInt(FONT_PREFERENCE, index) .apply(); setSummary(selected.getDisplay(getContext())); } /** * That's it. The user doesn't want to switch between these amazing radio buttons anymore! * That means, the selected font can be saved (if the user hit OK) * @param positiveResult if OK has been selected. */ @Override public void onDialogClosed(boolean positiveResult) { if(positiveResult) { saveSelectedFont(); if(selected != original) { new AlertDialog.Builder(context) .setTitle(R.string.restart_required) .setMessage(R.string.restart_emoji) .setNegativeButton(R.string.later, null) .setPositiveButton(R.string.restart, ((dialog, which) -> { // Restart the app // TODO: I'm not sure if this is a good solution but it seems to work // From https://stackoverflow.com/a/17166729/5070653 Intent launchIntent = new Intent(context, MainActivity.class); PendingIntent mPendingIntent = PendingIntent.getActivity( context, // This is the codepoint of the party face emoji :D 0x1f973, launchIntent, PendingIntent.FLAG_CANCEL_CURRENT); AlarmManager mgr = (AlarmManager) context.getSystemService(Context.ALARM_SERVICE); if (mgr != null) { mgr.set( AlarmManager.RTC, System.currentTimeMillis() + 100, mPendingIntent); } System.exit(0); })).show(); } } else { // This line is needed in order to reset the radio buttons later selected = original; } } }