2019 Emoji Update (#1261)
* Updated EmojiCompat support: - Fonts are now updatable - Old fonts will automatically be deleted - Noto Emoji is supported - New library version * It will now (hopefully) only look through the emoji font directory if it exists... * Added a noto emoji icon for Android <=6; Font files and their version relation are only loaded once now. * Small bugfix for the noto emoji icon * Changed the initial size of the existingFontFiles list to a constant value * Bugfixes Old files are now (really) deleted The latest version is used as the actuall font Further optimizations * Emoji font update triggers restart dialog * Resized the Noto icon; (Hopefully) fixed EmojiCompat (again)
This commit is contained in:
parent
794903f496
commit
f7581daa75
8 changed files with 326 additions and 14 deletions
|
@ -119,7 +119,7 @@ dependencies {
|
||||||
// EmojiCompat
|
// EmojiCompat
|
||||||
implementation 'androidx.emoji:emoji:1.0.0'
|
implementation 'androidx.emoji:emoji:1.0.0'
|
||||||
implementation 'androidx.emoji:emoji-appcompat:1.0.0'
|
implementation 'androidx.emoji:emoji-appcompat:1.0.0'
|
||||||
implementation 'de.c1710:filemojicompat:1.0.14'
|
implementation 'de.c1710:filemojicompat:1.0.17'
|
||||||
// architecture components
|
// architecture components
|
||||||
implementation 'androidx.lifecycle:lifecycle-extensions:2.0.0'
|
implementation 'androidx.lifecycle:lifecycle-extensions:2.0.0'
|
||||||
//room
|
//room
|
||||||
|
|
|
@ -36,10 +36,14 @@ public class EmojiPreference extends Preference {
|
||||||
private static final int[] viewIds = {
|
private static final int[] viewIds = {
|
||||||
R.id.item_nomoji,
|
R.id.item_nomoji,
|
||||||
R.id.item_blobmoji,
|
R.id.item_blobmoji,
|
||||||
R.id.item_twemoji};
|
R.id.item_twemoji,
|
||||||
|
R.id.item_notoemoji};
|
||||||
|
|
||||||
private ArrayList<RadioButton> radioButtons = new ArrayList<>();
|
private ArrayList<RadioButton> radioButtons = new ArrayList<>();
|
||||||
|
|
||||||
|
private boolean updated, currentNeedsUpdate;
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
public EmojiPreference(Context context, AttributeSet attrs) {
|
public EmojiPreference(Context context, AttributeSet attrs) {
|
||||||
super(context, attrs);
|
super(context, attrs);
|
||||||
|
@ -154,6 +158,11 @@ public class EmojiPreference extends Preference {
|
||||||
private void finishDownload(EmojiCompatFont font, View container) {
|
private void finishDownload(EmojiCompatFont font, View container) {
|
||||||
select(font, container.findViewById(R.id.emojicompat_radio));
|
select(font, container.findViewById(R.id.emojicompat_radio));
|
||||||
updateItem(font, container);
|
updateItem(font, container);
|
||||||
|
// Set the flag to restart the app (because an update has been downloaded)
|
||||||
|
if (selected == original && currentNeedsUpdate) {
|
||||||
|
updated = true;
|
||||||
|
currentNeedsUpdate = false;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -208,6 +217,10 @@ public class EmojiPreference extends Preference {
|
||||||
// Select it if necessary
|
// Select it if necessary
|
||||||
if(font == selected) {
|
if(font == selected) {
|
||||||
radio.setChecked(true);
|
radio.setChecked(true);
|
||||||
|
// Update available
|
||||||
|
if (!font.isDownloaded(getContext())) {
|
||||||
|
currentNeedsUpdate = true;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
radio.setChecked(false);
|
radio.setChecked(false);
|
||||||
|
@ -236,7 +249,7 @@ public class EmojiPreference extends Preference {
|
||||||
*/
|
*/
|
||||||
private void onDialogOk() {
|
private void onDialogOk() {
|
||||||
saveSelectedFont();
|
saveSelectedFont();
|
||||||
if(selected != original) {
|
if (selected != original || updated) {
|
||||||
new AlertDialog.Builder(getContext())
|
new AlertDialog.Builder(getContext())
|
||||||
.setTitle(R.string.restart_required)
|
.setTitle(R.string.restart_required)
|
||||||
.setMessage(R.string.restart_emoji)
|
.setMessage(R.string.restart_emoji)
|
||||||
|
|
|
@ -3,15 +3,25 @@ package com.keylesspalace.tusky.util;
|
||||||
import android.content.Context;
|
import android.content.Context;
|
||||||
import android.graphics.drawable.Drawable;
|
import android.graphics.drawable.Drawable;
|
||||||
import android.os.AsyncTask;
|
import android.os.AsyncTask;
|
||||||
import androidx.annotation.Nullable;
|
|
||||||
import android.util.Log;
|
import android.util.Log;
|
||||||
|
import android.util.Pair;
|
||||||
|
|
||||||
|
import androidx.annotation.NonNull;
|
||||||
|
import androidx.annotation.Nullable;
|
||||||
|
|
||||||
import com.keylesspalace.tusky.R;
|
import com.keylesspalace.tusky.R;
|
||||||
|
|
||||||
import java.io.EOFException;
|
import java.io.EOFException;
|
||||||
import java.io.File;
|
import java.io.File;
|
||||||
|
import java.io.FilenameFilter;
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
|
import java.util.ArrayList;
|
||||||
import java.util.Arrays;
|
import java.util.Arrays;
|
||||||
|
import java.util.Collections;
|
||||||
|
import java.util.Comparator;
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.regex.Matcher;
|
||||||
|
import java.util.regex.Pattern;
|
||||||
|
|
||||||
import de.c1710.filemojicompat.FileEmojiCompatConfig;
|
import de.c1710.filemojicompat.FileEmojiCompatConfig;
|
||||||
import okhttp3.OkHttpClient;
|
import okhttp3.OkHttpClient;
|
||||||
|
@ -26,6 +36,7 @@ import okio.Source;
|
||||||
* This class bundles information about an emoji font as well as many convenient actions.
|
* This class bundles information about an emoji font as well as many convenient actions.
|
||||||
*/
|
*/
|
||||||
public class EmojiCompatFont {
|
public class EmojiCompatFont {
|
||||||
|
private static final String TAG = "EmojiCompatFont";
|
||||||
/**
|
/**
|
||||||
* This String represents the sub-directory the fonts are stored in.
|
* This String represents the sub-directory the fonts are stored in.
|
||||||
*/
|
*/
|
||||||
|
@ -35,6 +46,9 @@ public class EmojiCompatFont {
|
||||||
private final String name, display, url;
|
private final String name, display, url;
|
||||||
// The thumbnail image and the caption are provided as resource ids
|
// The thumbnail image and the caption are provided as resource ids
|
||||||
private final int img, caption;
|
private final int img, caption;
|
||||||
|
// The version is stored as a String in the x.xx.xx format (to be able to compare versions)
|
||||||
|
private final String version;
|
||||||
|
private final int[] versionCode;
|
||||||
private AsyncTask fontDownloader;
|
private AsyncTask fontDownloader;
|
||||||
// The system font gets some special behavior...
|
// The system font gets some special behavior...
|
||||||
private static final EmojiCompatFont SYSTEM_DEFAULT =
|
private static final EmojiCompatFont SYSTEM_DEFAULT =
|
||||||
|
@ -42,38 +56,55 @@ public class EmojiCompatFont {
|
||||||
"System Default",
|
"System Default",
|
||||||
R.string.caption_systememoji,
|
R.string.caption_systememoji,
|
||||||
R.drawable.ic_emoji_34dp,
|
R.drawable.ic_emoji_34dp,
|
||||||
"");
|
"",
|
||||||
|
"0");
|
||||||
private static final EmojiCompatFont BLOBMOJI =
|
private static final EmojiCompatFont BLOBMOJI =
|
||||||
new EmojiCompatFont("Blobmoji",
|
new EmojiCompatFont("Blobmoji",
|
||||||
"Blobmoji",
|
"Blobmoji",
|
||||||
R.string.caption_blobmoji,
|
R.string.caption_blobmoji,
|
||||||
R.drawable.ic_blobmoji,
|
R.drawable.ic_blobmoji,
|
||||||
"https://tusky.app/hosted/emoji/BlobmojiCompat.ttf"
|
"https://tusky.app/hosted/emoji/BlobmojiCompat.ttf",
|
||||||
|
"12.0.0"
|
||||||
);
|
);
|
||||||
private static final EmojiCompatFont TWEMOJI =
|
private static final EmojiCompatFont TWEMOJI =
|
||||||
new EmojiCompatFont("Twemoji",
|
new EmojiCompatFont("Twemoji",
|
||||||
"Twemoji",
|
"Twemoji",
|
||||||
R.string.caption_twemoji,
|
R.string.caption_twemoji,
|
||||||
R.drawable.ic_twemoji,
|
R.drawable.ic_twemoji,
|
||||||
"https://tusky.app/hosted/emoji/TwemojiCompat.ttf"
|
"https://tusky.app/hosted/emoji/TwemojiCompat.ttf",
|
||||||
|
"12.0.0"
|
||||||
|
);
|
||||||
|
private static final EmojiCompatFont NOTOEMOJI =
|
||||||
|
new EmojiCompatFont("NotoEmoji",
|
||||||
|
"Noto Emoji",
|
||||||
|
R.string.caption_notoemoji,
|
||||||
|
R.drawable.ic_notoemoji,
|
||||||
|
"https://tusky.app/hosted/emoji/NotoEmojiCompat.ttf",
|
||||||
|
"11.0.0"
|
||||||
);
|
);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* This array stores all available EmojiCompat fonts.
|
* This array stores all available EmojiCompat fonts.
|
||||||
* References to them can simply be saved by saving their indices
|
* References to them can simply be saved by saving their indices
|
||||||
*/
|
*/
|
||||||
public static final EmojiCompatFont[] FONTS = {SYSTEM_DEFAULT, BLOBMOJI, TWEMOJI};
|
public static final EmojiCompatFont[] FONTS = {SYSTEM_DEFAULT, BLOBMOJI, TWEMOJI, NOTOEMOJI};
|
||||||
|
// A list of all available font files and whether they are older than the current version or not
|
||||||
|
// They are ordered by there version codes in ascending order
|
||||||
|
private ArrayList<Pair<File, int[]>> existingFontFiles;
|
||||||
|
|
||||||
private EmojiCompatFont(String name,
|
private EmojiCompatFont(String name,
|
||||||
String display,
|
String display,
|
||||||
int caption,
|
int caption,
|
||||||
int img,
|
int img,
|
||||||
String url) {
|
String url,
|
||||||
|
String version) {
|
||||||
this.name = name;
|
this.name = name;
|
||||||
this.display = display;
|
this.display = display;
|
||||||
this.caption = caption;
|
this.caption = caption;
|
||||||
this.img = img;
|
this.img = img;
|
||||||
this.url = url;
|
this.url = url;
|
||||||
|
this.version = version;
|
||||||
|
this.versionCode = getVersionCode(version);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -115,28 +146,55 @@ public class EmojiCompatFont {
|
||||||
return context.getResources().getDrawable(img);
|
return context.getResources().getDrawable(img);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public String getVersion() {
|
||||||
|
return version;
|
||||||
|
}
|
||||||
|
|
||||||
|
public int[] getVersionCode() {
|
||||||
|
return versionCode;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* This method will return the actual font file (regardless of its existence).
|
* This method will return the actual font file (regardless of its existence) for
|
||||||
|
* the current version (not necessarily the latest!).
|
||||||
* @return The font (TTF) file or null if called on SYSTEM_FONT
|
* @return The font (TTF) file or null if called on SYSTEM_FONT
|
||||||
*/
|
*/
|
||||||
@Nullable
|
@Nullable
|
||||||
private File getFont(Context context) {
|
private File getFont(Context context) {
|
||||||
if(this != SYSTEM_DEFAULT) {
|
if(this != SYSTEM_DEFAULT) {
|
||||||
File directory = new File(context.getExternalFilesDir(null), DIRECTORY);
|
File directory = new File(context.getExternalFilesDir(null), DIRECTORY);
|
||||||
return new File(directory, this.getName() + ".ttf");
|
return new File(directory, this.getName() + this.getVersion() + ".ttf");
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
public FileEmojiCompatConfig getConfig(Context context) {
|
public FileEmojiCompatConfig getConfig(Context context) {
|
||||||
return new FileEmojiCompatConfig(context, getFont(context));
|
return new FileEmojiCompatConfig(context, getLatestFontFile(context));
|
||||||
}
|
}
|
||||||
|
|
||||||
public boolean isDownloaded(Context context) {
|
public boolean isDownloaded(Context context) {
|
||||||
return this == SYSTEM_DEFAULT || getFont(context) != null && getFont(context).exists();
|
// The existence of the current version is actually checked twice, although the first method should
|
||||||
|
// be much faster and more common.
|
||||||
|
return this == SYSTEM_DEFAULT || getFont(context) != null
|
||||||
|
&& (getFont(context).exists() || newerFileExists(context));
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Checks whether there is already a font version that satisfies the current version, i.e. it
|
||||||
|
* has a higher or equal version code.
|
||||||
|
* @param context The Context
|
||||||
|
* @return Whether there is a font file with a higher or equal version code to the current
|
||||||
|
*/
|
||||||
|
private boolean newerFileExists(Context context) {
|
||||||
|
loadExistingFontFiles(context);
|
||||||
|
if (!existingFontFiles.isEmpty())
|
||||||
|
// The last file is already the newest one...
|
||||||
|
return compareVersions(existingFontFiles.get(existingFontFiles.size() - 1).second,
|
||||||
|
getVersionCode()) >= 0;
|
||||||
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -145,9 +203,18 @@ public class EmojiCompatFont {
|
||||||
*/
|
*/
|
||||||
public void downloadFont(Context context, Downloader.EmojiDownloadListener... listeners) {
|
public void downloadFont(Context context, Downloader.EmojiDownloadListener... listeners) {
|
||||||
if(this != SYSTEM_DEFAULT) {
|
if(this != SYSTEM_DEFAULT) {
|
||||||
|
// Additionally run a cleanup process after the download has been successful.
|
||||||
|
Downloader.EmojiDownloadListener cleanup = font -> deleteOldVersions(context);
|
||||||
|
|
||||||
|
List<Downloader.EmojiDownloadListener> allListeners
|
||||||
|
= new ArrayList<>(Arrays.asList(listeners));
|
||||||
|
allListeners.add(cleanup);
|
||||||
|
Downloader.EmojiDownloadListener[] allListenersA =
|
||||||
|
new Downloader.EmojiDownloadListener[allListeners.size()];
|
||||||
|
|
||||||
fontDownloader = new Downloader(
|
fontDownloader = new Downloader(
|
||||||
this,
|
this,
|
||||||
listeners)
|
allListeners.toArray(allListenersA))
|
||||||
.execute(getFont(context));
|
.execute(getFont(context));
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
|
@ -158,6 +225,157 @@ public class EmojiCompatFont {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Deletes any older version of a font
|
||||||
|
* @param context The current Context
|
||||||
|
*/
|
||||||
|
private void deleteOldVersions(Context context) {
|
||||||
|
loadExistingFontFiles(context);
|
||||||
|
Log.d(TAG, "deleting old versions...");
|
||||||
|
|
||||||
|
Log.d(TAG, String.format("deleteOldVersions: Found %d other font files", existingFontFiles.size()));
|
||||||
|
for (Pair<File, int[]> fileExists : existingFontFiles) {
|
||||||
|
if (compareVersions(fileExists.second, getVersionCode()) < 0) {
|
||||||
|
File file = fileExists.first;
|
||||||
|
// Uses side effects!
|
||||||
|
Log.d(TAG, String.format("Deleted %s successfully: %s", file.getAbsolutePath(),
|
||||||
|
file.delete()));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private static final Comparator<Pair<File, int[]>> pairComparator = (o1, o2) -> compareVersions(o1.second, o2.second);
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Loads all font files that are inside the files directory into an ArrayList with the information
|
||||||
|
* on whether they are older than the currently available version or not.
|
||||||
|
* @param context The Context
|
||||||
|
*/
|
||||||
|
private void loadExistingFontFiles(Context context) {
|
||||||
|
// Only load it once
|
||||||
|
if (this.existingFontFiles == null) {
|
||||||
|
// If we call this on the system default font, just return nothing...
|
||||||
|
if (this == SYSTEM_DEFAULT) {
|
||||||
|
existingFontFiles = new ArrayList<>(0);
|
||||||
|
}
|
||||||
|
|
||||||
|
File directory = new File(context.getExternalFilesDir(null), DIRECTORY);
|
||||||
|
// It will search for old versions using a regex that matches the font's name plus
|
||||||
|
// (if present) a version code. No version code will be regarded as version 0.
|
||||||
|
Pattern fontRegex = Pattern.compile(getName() + "(\\d+(\\.\\d+)*)?" + "\\.ttf");
|
||||||
|
|
||||||
|
|
||||||
|
FilenameFilter ttfFilter = (dir, name) -> name.endsWith(".ttf");
|
||||||
|
File[] existingFontFiles = directory.isDirectory() ? directory.listFiles(ttfFilter) : new File[0];
|
||||||
|
Log.d(TAG, String.format("loadExistingFontFiles: %d other font files found",
|
||||||
|
existingFontFiles.length));
|
||||||
|
// This is actually the upper bound
|
||||||
|
this.existingFontFiles = new ArrayList<>(existingFontFiles.length);
|
||||||
|
|
||||||
|
|
||||||
|
for(File file : existingFontFiles) {
|
||||||
|
Matcher matcher = fontRegex.matcher(file.getName());
|
||||||
|
if (matcher.matches()) {
|
||||||
|
String version = matcher.group(1);
|
||||||
|
int[] versionCode = getVersionCode(version);
|
||||||
|
Pair<File, int[]> entry = new Pair<>(file, versionCode);
|
||||||
|
// https://stackoverflow.com/a/51893026
|
||||||
|
// Insert it in a sorted way
|
||||||
|
int index = Collections.binarySearch(this.existingFontFiles, entry, pairComparator);
|
||||||
|
if (index < 0) {
|
||||||
|
index = -index - 1;
|
||||||
|
}
|
||||||
|
this.existingFontFiles.add(index, entry);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns the current or latest version of this font file (if there is any)
|
||||||
|
* @param context The Context
|
||||||
|
* @return The file for this font with the current or (if not existent) highest version code or null if there is no file for this font.
|
||||||
|
*/
|
||||||
|
private File getLatestFontFile(@NonNull Context context) {
|
||||||
|
File current = getFont(context);
|
||||||
|
if (current != null && current.exists())
|
||||||
|
return current;
|
||||||
|
loadExistingFontFiles(context);
|
||||||
|
try {
|
||||||
|
return existingFontFiles.get(existingFontFiles.size() - 1).first;
|
||||||
|
} catch (IndexOutOfBoundsException e) {
|
||||||
|
return getFont(context);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private @Nullable
|
||||||
|
int[] getVersionCode(@Nullable String version) {
|
||||||
|
if (version == null)
|
||||||
|
return null;
|
||||||
|
String[] versions = version.split("\\.");
|
||||||
|
int[] versionCodes = new int[versions.length];
|
||||||
|
for (int i = 0; i < versions.length; i++)
|
||||||
|
versionCodes[i] = parseInt(versions[i], 0);
|
||||||
|
return versionCodes;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A small helper method to convert a String to an int with a default value
|
||||||
|
*
|
||||||
|
* @param value The String to be parsed
|
||||||
|
* @param def The default value
|
||||||
|
* @return Either the String parsed to an int or - if this is not possible - the default value
|
||||||
|
*/
|
||||||
|
private int parseInt(@Nullable String value, int def) {
|
||||||
|
try {
|
||||||
|
return Integer.parseInt(value);
|
||||||
|
} catch (NumberFormatException | NullPointerException e) {
|
||||||
|
e.printStackTrace();
|
||||||
|
return def;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Compares two version codes to each other
|
||||||
|
*
|
||||||
|
* @param versionA The first version
|
||||||
|
* @param versionB The second version
|
||||||
|
* @return -1 if versionA < versionB, 1 if versionA > versionB and 0 otherwise
|
||||||
|
*/
|
||||||
|
private static int compareVersions(int[] versionA, int[] versionB) {
|
||||||
|
// This saves us much headache about handling a null version
|
||||||
|
if (versionA == null)
|
||||||
|
versionA = new int[]{0};
|
||||||
|
|
||||||
|
int len = Math.max(versionB.length, versionA.length);
|
||||||
|
|
||||||
|
int vA, vB;
|
||||||
|
// Compare the versions
|
||||||
|
for (int i = 0; i < len; i++) {
|
||||||
|
// Just to make sure there is something specified here
|
||||||
|
if (versionA.length > i) {
|
||||||
|
vA = versionA[i];
|
||||||
|
} else {
|
||||||
|
vA = 0;
|
||||||
|
}
|
||||||
|
if (versionB.length > i) {
|
||||||
|
vB = versionB[i];
|
||||||
|
} else {
|
||||||
|
vB = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
// It needs to be decided on the next level
|
||||||
|
if (vB == vA)
|
||||||
|
continue;
|
||||||
|
// Okay, is version B newer or version A?
|
||||||
|
return Integer.compare(vA, vB);
|
||||||
|
}
|
||||||
|
|
||||||
|
// The versions are equal
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Stops downloading the font. If no one started a font download, nothing happens.
|
* Stops downloading the font. If no one started a font download, nothing happens.
|
||||||
*/
|
*/
|
||||||
|
@ -306,6 +524,7 @@ public class EmojiCompatFont {
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
@NonNull
|
||||||
public String toString() {
|
public String toString() {
|
||||||
return display;
|
return display;
|
||||||
}
|
}
|
||||||
|
|
51
app/src/main/res/drawable-v24/ic_notoemoji.xml
Normal file
51
app/src/main/res/drawable-v24/ic_notoemoji.xml
Normal file
|
@ -0,0 +1,51 @@
|
||||||
|
<vector android:height="34dp"
|
||||||
|
android:viewportHeight="128"
|
||||||
|
android:viewportWidth="128"
|
||||||
|
android:width="34dp"
|
||||||
|
xmlns:aapt="http://schemas.android.com/aapt" xmlns:android="http://schemas.android.com/apk/res/android">
|
||||||
|
<path
|
||||||
|
android:pathData="M63.7,119c-28,0 -58,-17.5 -58,-55.9s30.1,-55.9 58,-55.9c15.5,0 29.9,5.1 40.4,14.5A53.5,53.5 0,0 1,121.8 63c0,16.9 -6.1,31.2 -17.7,41.4A60.6,60.6 0,0 1,63.7 119z"
|
||||||
|
android:strokeAlpha="1" android:strokeColor="#eb8f00" android:strokeWidth="3.6">
|
||||||
|
<aapt:attr name="android:fillColor">
|
||||||
|
<gradient android:endX="63.7" android:endY="106.8"
|
||||||
|
android:startX="63.7" android:startY="18.8" android:type="linear">
|
||||||
|
<item android:color="#FFFEE133" android:offset="0.5"/>
|
||||||
|
<item android:color="#FFFEDE32" android:offset="0.6"/>
|
||||||
|
<item android:color="#FFFCD32F" android:offset="0.7"/>
|
||||||
|
<item android:color="#FFFAC12B" android:offset="0.8"/>
|
||||||
|
<item android:color="#FFF7A924" android:offset="0.9"/>
|
||||||
|
<item android:color="#FFF6A323" android:offset="0.9"/>
|
||||||
|
</gradient>
|
||||||
|
</aapt:attr>
|
||||||
|
</path>
|
||||||
|
<path android:fillColor="#242424" android:pathData="m89.4,43.1c4,0 7.5,3.3 7.5,8.7 0,5.4 -3.6,8.8 -7.5,8.8 -3.9,0 -7.4,-3.3 -7.4,-8.8 0,-5.5 3.6,-8.7 7.4,-8.7zM38.6,43.1c-4,0 -7.5,3.3 -7.5,8.7 0,5.4 3.6,8.8 7.5,8.8 3.9,0 7.4,-3.3 7.4,-8.8 0,-5.5 -3.5,-8.7 -7.4,-8.7z"/>
|
||||||
|
<path android:fillColor="#242424" android:pathData="M64,69.5c-15.7,0 -30.3,-1.4 -42.5,-3.6 -3,-0.6 -5.7,2 -5,5 4,19.9 23.5,36.2 47.5,36.2 26.2,0 44,-16.8 47.6,-36.2 0.6,-3 -2.1,-5.6 -5.1,-5A238.4,238.4 0,0 1,64 69.5z"/>
|
||||||
|
<path android:fillAlpha="1" android:fillColor="#ffffff" android:pathData="M20.395,65.82C17.878,65.957 15.887,68.275 16.5,70.9C16.543,71.113 16.598,71.322 16.645,71.533C31.288,79.1 47.255,83.301 64,83.301C80.805,83.301 96.747,79.069 111.484,71.451C111.521,71.267 111.565,71.085 111.6,70.9C112.2,67.9 109.5,65.3 106.5,65.9A238.4,238.4 0,0 1,64 69.5C48.3,69.5 33.7,68.1 21.5,65.9C21.125,65.825 20.754,65.801 20.395,65.82z"/>
|
||||||
|
<path android:fillAlpha="1" android:fillColor="#eb8f00" android:pathData="M107.3,65.8C107,65.8 106.7,65.8 106.5,65.9A238.4,238.4 0,0 1,78.2 69A237.6,237.6 0,0 1,64 69.6A238.5,238.5 0,0 1,61 69.5C55.1,69.4 49.4,69.1 43.9,68.7A238.5,238.5 0,0 1,36.2 68C32.3,67.6 28.5,67.1 24.9,66.5A238.5,238.5 0,0 1,20.7 65.9A4.3,4.3 0,0 0,17 67.8A4.3,4.3 0,0 0,17 67.8A4.3,4.3 0,0 0,16.7 68.4C16.4,69.2 16.3,70 16.5,70.9C16.5,71.1 16.6,71.3 16.6,71.5C17.5,72 18.3,72.4 19.1,72.8C20,73.2 20.9,73.6 21.8,74A39.6,39.6 0,0 1,20.7 70C33.6,72.3 48.6,73.6 64,73.6C79.4,73.6 94.4,72.3 107.3,69.9L107.3,70L107.5,70.1A39,39 0,0 1,106.6 73.8C106.6,73.8 106.7,73.8 106.7,73.8C107.5,73.4 108.3,73.1 109,72.7C109,72.7 109,72.7 109.1,72.7C109.9,72.3 110.7,71.9 111.5,71.5C111.5,71.3 111.6,71.1 111.6,70.9C111.7,70.5 111.7,70.2 111.7,69.8A4.3,4.3 0,0 0,111.7 69.8A4.3,4.3 0,0 0,107.3 65.9L107.3,65.8z"/>
|
||||||
|
<path android:fillColor="#FF000000" android:pathData="M75.2,37.5c-0.5,-0.4 -1.2,-0.5 -1.7,-0.2 0,0 -1.8,1 -4.1,0.8H69c-2,-0.2 -3.7,-0.3 -5.7,-0.3h-0.1l-5.4,0.3c-2.3,0.1 -3.8,-0.8 -3.8,-0.8 -0.6,-0.3 -1.3,-0.2 -1.8,0.2s-0.6,1.2 -0.4,1.9l3,9.8c0.2,0.6 0.7,1 1.2,1.1 0.6,0.1 1.2,-0.2 1.5,-0.8 0,0 2,-3.2 6.2,-3.2 4.3,0 6.2,3.1 6.3,3.2 0.3,0.5 0.8,0.8 1.3,0.8h0.2c0.6,0 1,-0.5 1.3,-1.1l2.7,-9.9c0.2,-0.6 0,-1.3 -0.4,-1.8z"/>
|
||||||
|
<path android:fillColor="#FF000000" android:pathData="M59,38.4s-8,-9.3 -36.6,-6.6C3.3,33.5 2.3,35.3 2.3,35.3L2,42.6s2.9,0.8 4.7,3c1.9,2.3 2.7,15 5.7,21 4.7,9.2 27.7,6.8 27.7,6.8s9.4,0.8 14,-7.2c4.5,-7.8 6,-19.2 6,-19.2L59,38.4zM52.4,55.3c-1.9,8.4 -4.2,14.1 -19.8,13.3 -10,-0.6 -17,-0.6 -18.6,-15.3 -1,-9 0,-12.5 0.8,-13.6 0.8,-1.1 3.5,-4.3 19,-3.7s17.8,4 18.7,5.6c1,1.6 1.7,5.2 -0.1,13.6zM67.9,47s1.4,11.4 6,19.3c4.5,7.9 14,7.1 14,7.1s23,2.4 27.6,-6.9c3,-6 3.8,-18.6 5.7,-20.9 1.8,-2.2 4.7,-3 4.7,-3l-0.2,-7.3s-1,-1.8 -20.1,-3.5c-28.7,-2.7 -36.7,6.6 -36.7,6.6L68,47zM75.5,41.6c0.9,-1.6 3.1,-5 18.7,-5.6 15.5,-0.6 18.2,2.6 19,3.7 0.7,1 1.8,4.5 0.8,13.6 -1.6,14.7 -8.6,14.7 -18.6,15.3 -15.6,0.8 -18,-5 -19.8,-13.3 -1.8,-8.5 -1,-12.1 -0.1,-13.7z"/>
|
||||||
|
<path android:pathData="M117.5,35.6h0z">
|
||||||
|
<aapt:attr name="android:fillColor">
|
||||||
|
<gradient android:endX="117.600006" android:endY="35.6"
|
||||||
|
android:startX="117.600006" android:startY="35.7" android:type="linear">
|
||||||
|
<item android:color="#FF9E9E9E" android:offset="0"/>
|
||||||
|
<item android:color="#FF616161" android:offset="1"/>
|
||||||
|
</gradient>
|
||||||
|
</aapt:attr>
|
||||||
|
</path>
|
||||||
|
<path android:pathData="M118.2,35.9z">
|
||||||
|
<aapt:attr name="android:fillColor">
|
||||||
|
<gradient android:endX="118.00001" android:endY="35.7"
|
||||||
|
android:startX="118.00001" android:startY="35.9" android:type="linear">
|
||||||
|
<item android:color="#FF9E9E9E" android:offset="0"/>
|
||||||
|
<item android:color="#FF616161" android:offset="1"/>
|
||||||
|
</gradient>
|
||||||
|
</aapt:attr>
|
||||||
|
</path>
|
||||||
|
<path android:fillColor="#ffffff" android:pathData="M7.6,40.3L5.8,40.3a1.55,1.55 0,0 1,0 -3.1h1.8a1.5,1.5 0,0 1,0 3zM122.2,40.3h-1.8a1.55,1.55 0,0 1,0 -3.1h1.8a1.5,1.5 0,0 1,0 3z"/>
|
||||||
|
<path android:fillAlpha="1" android:fillColor="#ed7770"
|
||||||
|
android:pathData="M64,93.301C55.457,93.301 47.764,96.719 42.074,102.205C48.628,105.31 56.065,107.1 64,107.1C72.316,107.1 79.781,105.403 86.219,102.473A31.7,31.7 0,0 0,64 93.301z"
|
||||||
|
android:strokeAlpha="1" android:strokeLineCap="butt"
|
||||||
|
android:strokeLineJoin="miter" android:strokeWidth="1"/>
|
||||||
|
<path android:fillColor="#eb8f00" android:pathData="M45.736,99.158C44.429,100.09 43.197,101.119 42.045,102.232C48.614,105.32 56.063,107.1 64,107.1C72.324,107.1 79.794,105.409 86.236,102.49A31.7,31.7 0,0 0,82.574 99.375C76.899,101.678 70.611,102.9 63.9,102.9A46.7,46.7 0,0 1,45.736 99.158z"/>
|
||||||
|
</vector>
|
23
app/src/main/res/drawable/ic_notoemoji.xml
Normal file
23
app/src/main/res/drawable/ic_notoemoji.xml
Normal file
|
@ -0,0 +1,23 @@
|
||||||
|
<vector android:height="34dp"
|
||||||
|
android:viewportHeight="128"
|
||||||
|
android:viewportWidth="128"
|
||||||
|
android:width="34dp"
|
||||||
|
xmlns:aapt="http://schemas.android.com/aapt" xmlns:android="http://schemas.android.com/apk/res/android">
|
||||||
|
<path
|
||||||
|
android:pathData="M63.7,119c-28,0 -58,-17.5 -58,-55.9s30.1,-55.9 58,-55.9c15.5,0 29.9,5.1 40.4,14.5A53.5,53.5 0,0 1,121.8 63c0,16.9 -6.1,31.2 -17.7,41.4A60.6,60.6 0,0 1,63.7 119z"
|
||||||
|
android:strokeAlpha="1" android:strokeColor="#eb8f00" android:strokeWidth="3.6" android:fillColor="#FFCC32"/>
|
||||||
|
<path android:fillColor="#242424" android:pathData="m89.4,43.1c4,0 7.5,3.3 7.5,8.7 0,5.4 -3.6,8.8 -7.5,8.8 -3.9,0 -7.4,-3.3 -7.4,-8.8 0,-5.5 3.6,-8.7 7.4,-8.7zM38.6,43.1c-4,0 -7.5,3.3 -7.5,8.7 0,5.4 3.6,8.8 7.5,8.8 3.9,0 7.4,-3.3 7.4,-8.8 0,-5.5 -3.5,-8.7 -7.4,-8.7z"/>
|
||||||
|
<path android:fillColor="#242424" android:pathData="M64,69.5c-15.7,0 -30.3,-1.4 -42.5,-3.6 -3,-0.6 -5.7,2 -5,5 4,19.9 23.5,36.2 47.5,36.2 26.2,0 44,-16.8 47.6,-36.2 0.6,-3 -2.1,-5.6 -5.1,-5A238.4,238.4 0,0 1,64 69.5z"/>
|
||||||
|
<path android:fillAlpha="1" android:fillColor="#ffffff" android:pathData="M20.395,65.82C17.878,65.957 15.887,68.275 16.5,70.9C16.543,71.113 16.598,71.322 16.645,71.533C31.288,79.1 47.255,83.301 64,83.301C80.805,83.301 96.747,79.069 111.484,71.451C111.521,71.267 111.565,71.085 111.6,70.9C112.2,67.9 109.5,65.3 106.5,65.9A238.4,238.4 0,0 1,64 69.5C48.3,69.5 33.7,68.1 21.5,65.9C21.125,65.825 20.754,65.801 20.395,65.82z"/>
|
||||||
|
<path android:fillAlpha="1" android:fillColor="#eb8f00" android:pathData="M107.3,65.8C107,65.8 106.7,65.8 106.5,65.9A238.4,238.4 0,0 1,78.2 69A237.6,237.6 0,0 1,64 69.6A238.5,238.5 0,0 1,61 69.5C55.1,69.4 49.4,69.1 43.9,68.7A238.5,238.5 0,0 1,36.2 68C32.3,67.6 28.5,67.1 24.9,66.5A238.5,238.5 0,0 1,20.7 65.9A4.3,4.3 0,0 0,17 67.8A4.3,4.3 0,0 0,17 67.8A4.3,4.3 0,0 0,16.7 68.4C16.4,69.2 16.3,70 16.5,70.9C16.5,71.1 16.6,71.3 16.6,71.5C17.5,72 18.3,72.4 19.1,72.8C20,73.2 20.9,73.6 21.8,74A39.6,39.6 0,0 1,20.7 70C33.6,72.3 48.6,73.6 64,73.6C79.4,73.6 94.4,72.3 107.3,69.9L107.3,70L107.5,70.1A39,39 0,0 1,106.6 73.8C106.6,73.8 106.7,73.8 106.7,73.8C107.5,73.4 108.3,73.1 109,72.7C109,72.7 109,72.7 109.1,72.7C109.9,72.3 110.7,71.9 111.5,71.5C111.5,71.3 111.6,71.1 111.6,70.9C111.7,70.5 111.7,70.2 111.7,69.8A4.3,4.3 0,0 0,111.7 69.8A4.3,4.3 0,0 0,107.3 65.9L107.3,65.8z"/>
|
||||||
|
<path android:fillColor="#FF000000" android:pathData="M75.2,37.5c-0.5,-0.4 -1.2,-0.5 -1.7,-0.2 0,0 -1.8,1 -4.1,0.8H69c-2,-0.2 -3.7,-0.3 -5.7,-0.3h-0.1l-5.4,0.3c-2.3,0.1 -3.8,-0.8 -3.8,-0.8 -0.6,-0.3 -1.3,-0.2 -1.8,0.2s-0.6,1.2 -0.4,1.9l3,9.8c0.2,0.6 0.7,1 1.2,1.1 0.6,0.1 1.2,-0.2 1.5,-0.8 0,0 2,-3.2 6.2,-3.2 4.3,0 6.2,3.1 6.3,3.2 0.3,0.5 0.8,0.8 1.3,0.8h0.2c0.6,0 1,-0.5 1.3,-1.1l2.7,-9.9c0.2,-0.6 0,-1.3 -0.4,-1.8z"/>
|
||||||
|
<path android:fillColor="#FF000000" android:pathData="M59,38.4s-8,-9.3 -36.6,-6.6C3.3,33.5 2.3,35.3 2.3,35.3L2,42.6s2.9,0.8 4.7,3c1.9,2.3 2.7,15 5.7,21 4.7,9.2 27.7,6.8 27.7,6.8s9.4,0.8 14,-7.2c4.5,-7.8 6,-19.2 6,-19.2L59,38.4zM52.4,55.3c-1.9,8.4 -4.2,14.1 -19.8,13.3 -10,-0.6 -17,-0.6 -18.6,-15.3 -1,-9 0,-12.5 0.8,-13.6 0.8,-1.1 3.5,-4.3 19,-3.7s17.8,4 18.7,5.6c1,1.6 1.7,5.2 -0.1,13.6zM67.9,47s1.4,11.4 6,19.3c4.5,7.9 14,7.1 14,7.1s23,2.4 27.6,-6.9c3,-6 3.8,-18.6 5.7,-20.9 1.8,-2.2 4.7,-3 4.7,-3l-0.2,-7.3s-1,-1.8 -20.1,-3.5c-28.7,-2.7 -36.7,6.6 -36.7,6.6L68,47zM75.5,41.6c0.9,-1.6 3.1,-5 18.7,-5.6 15.5,-0.6 18.2,2.6 19,3.7 0.7,1 1.8,4.5 0.8,13.6 -1.6,14.7 -8.6,14.7 -18.6,15.3 -15.6,0.8 -18,-5 -19.8,-13.3 -1.8,-8.5 -1,-12.1 -0.1,-13.7z"/>
|
||||||
|
<path android:pathData="M117.5,35.6h0z" android:fillColor="#FF97F7"/>
|
||||||
|
<path android:pathData="M118.2,35.9z" android:fillColor="#FF97F7"/>
|
||||||
|
<path android:fillColor="#ffffff" android:pathData="M7.6,40.3L5.8,40.3a1.55,1.55 0,0 1,0 -3.1h1.8a1.5,1.5 0,0 1,0 3zM122.2,40.3h-1.8a1.55,1.55 0,0 1,0 -3.1h1.8a1.5,1.5 0,0 1,0 3z"/>
|
||||||
|
<path android:fillAlpha="1" android:fillColor="#ed7770"
|
||||||
|
android:pathData="M64,93.301C55.457,93.301 47.764,96.719 42.074,102.205C48.628,105.31 56.065,107.1 64,107.1C72.316,107.1 79.781,105.403 86.219,102.473A31.7,31.7 0,0 0,64 93.301z"
|
||||||
|
android:strokeAlpha="1" android:strokeLineCap="butt"
|
||||||
|
android:strokeLineJoin="miter" android:strokeWidth="1"/>
|
||||||
|
<path android:fillColor="#eb8f00" android:pathData="M45.736,99.158C44.429,100.09 43.197,101.119 42.045,102.232C48.614,105.32 56.063,107.1 64,107.1C72.324,107.1 79.794,105.409 86.236,102.49A31.7,31.7 0,0 0,82.574 99.375C76.899,101.678 70.611,102.9 63.9,102.9A46.7,46.7 0,0 1,45.736 99.158z"/>
|
||||||
|
</vector>
|
|
@ -20,6 +20,10 @@
|
||||||
android:id="@+id/item_twemoji"
|
android:id="@+id/item_twemoji"
|
||||||
layout="@layout/item_emoji_pref" />
|
layout="@layout/item_emoji_pref" />
|
||||||
|
|
||||||
|
<include
|
||||||
|
android:id="@+id/item_notoemoji"
|
||||||
|
layout="@layout/item_emoji_pref" />
|
||||||
|
|
||||||
<include
|
<include
|
||||||
android:id="@+id/item_nomoji"
|
android:id="@+id/item_nomoji"
|
||||||
layout="@layout/item_emoji_pref" />
|
layout="@layout/item_emoji_pref" />
|
||||||
|
|
|
@ -294,6 +294,7 @@
|
||||||
<string name="caption_systememoji">Die Standard-Emojis deines Geräts</string>
|
<string name="caption_systememoji">Die Standard-Emojis deines Geräts</string>
|
||||||
<string name="caption_blobmoji">Die Blob–Emojis aus Android 4.4–7.1</string>
|
<string name="caption_blobmoji">Die Blob–Emojis aus Android 4.4–7.1</string>
|
||||||
<string name="caption_twemoji">Die Standard-Emojis von Mastodon</string>
|
<string name="caption_twemoji">Die Standard-Emojis von Mastodon</string>
|
||||||
|
<string name="caption_notoemoji">Die aktuellen Emojis von Google</string>
|
||||||
<string name="download_failed">Download fehlgeschlagen.</string>
|
<string name="download_failed">Download fehlgeschlagen.</string>
|
||||||
<string name="profile_badge_bot_text">Bot</string>
|
<string name="profile_badge_bot_text">Bot</string>
|
||||||
<string name="account_moved_description">%1$s ist umgezogen auf:</string>
|
<string name="account_moved_description">%1$s ist umgezogen auf:</string>
|
||||||
|
|
|
@ -367,6 +367,7 @@
|
||||||
<string name="caption_systememoji">Your device\'s default emoji set</string>
|
<string name="caption_systememoji">Your device\'s default emoji set</string>
|
||||||
<string name="caption_blobmoji">The Blob emojis known from Android 4.4–7.1</string>
|
<string name="caption_blobmoji">The Blob emojis known from Android 4.4–7.1</string>
|
||||||
<string name="caption_twemoji">Mastodon\'s standard emoji set</string>
|
<string name="caption_twemoji">Mastodon\'s standard emoji set</string>
|
||||||
|
<string name="caption_notoemoji">Google\'s current emoji set</string>
|
||||||
<string name="emoji_shortcode_format" translatable="false">:%s:</string>
|
<string name="emoji_shortcode_format" translatable="false">:%s:</string>
|
||||||
|
|
||||||
<string name="download_failed">Download failed</string>
|
<string name="download_failed">Download failed</string>
|
||||||
|
|
Loading…
Reference in a new issue