chinwag-android/app/src/main/java/com/keylesspalace/tusky/EmojiPreference.java
Constantin A 1108652823 EmojiCompat support (#600)
* Add EmojiCompat

* EmojiCompat doesn' replace all emojis anymore

* This app should be now capable of loading a EmojiCompat-font located in a file somewhere inside the device's storage

* Should now replace all emojis

* Add EmojiCompat support to EditTextTyped

* Provide EmojiCompat fonts

* The app won't crash anymore when no emoji font is available.
Emoji font should now be located at [Private external app directory]/files/EmojiCompat.ttf

* Removed BundledEmojiCompat dependency

Since this EmojiCompat-implementation does not rely on BundledEmojiCompat, there's no reason to have it enabled.

* Update EditTextTyped.kt

Since connection isn't assigned to (I tried doing so), it can be declared final/val again.

* Update README.md

* Add some non-working emoji preferences

* Add a short font list for testing

* Finished implementation

* Add Twemoji to font list

* Update documentation, more comments

* Delete AssetEmojiCompat which is obsolete now

* Update the font list

* Update the font list

* Fix font list & add Exception handling for malformed JSON files (hopefully)

* More fixes. It should work now...

* Removed AssetEmojiCompat (again)

* Add most of the changes

* Improved the EmojiCompat dialog's style

* The font list is now based on a static layout without external files

* Re-add the real font URL for Twemoji

* Emoji-font captions are now translatable

* Removed one unused String (loading)

* Removed emoji fonts from this repo

* Applied changes from the PR change requests

* The correct emoji font will be selected after cancelling a change

* Add details on the EmojiCompat fonts available (not shown yet)

* Add licensing information on Twemoji and Blobmoji

* Reworked some strings

* Moved FileEmojiCompat to its own library

* Update FileEmojiCompat to the latest version (1.0.3)

* EmojiCompat bug should be fixed

* Better handling of failed downloads

* Removed one TODO

Signed-off-by: Constantin A <10349490+C1710@users.noreply.github.com>

* Update emoji attribution strings

Signed-off-by: Constantin A <10349490+C1710@users.noreply.github.com>

* Fixed some misspelled strings

Signed-off-by: Constantin A <10349490+C1710@users.noreply.github.com>
2018-05-10 11:16:56 +02:00

278 lines
10 KiB
Java

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<RadioButton> 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;
}
}
}