Custom emojis in drawer (#737)
* upgrade MaterialDrawer * improve CustomEmojiHelper so now any parent view can be used for invalidation * cleanup MainActivity a bit * add emojiList to account database and show compatEmojis and custom emojis in drawer * improve perf of drawer profile update * fix account switching * reuse gson, break after profile item was found
This commit is contained in:
parent
b5a8915845
commit
9b422a97fe
|
@ -65,7 +65,7 @@ ext.daggerVersion = '2.16'
|
|||
|
||||
// if libraries are changed here, they should also be changed in LicenseActivity
|
||||
dependencies {
|
||||
implementation('com.mikepenz:materialdrawer:6.0.7@aar') {
|
||||
implementation('com.mikepenz:materialdrawer:6.0.9@aar') {
|
||||
transitive = true
|
||||
}
|
||||
implementation "com.android.support:appcompat-v7:$supportLibraryVersion"
|
||||
|
|
|
@ -26,6 +26,7 @@ import android.support.annotation.Nullable;
|
|||
import android.support.design.widget.FloatingActionButton;
|
||||
import android.support.design.widget.TabLayout;
|
||||
import android.support.graphics.drawable.VectorDrawableCompat;
|
||||
import android.support.text.emoji.EmojiCompat;
|
||||
import android.support.v4.app.Fragment;
|
||||
import android.support.v4.content.ContextCompat;
|
||||
import android.support.v4.view.ViewPager;
|
||||
|
@ -39,6 +40,7 @@ import com.keylesspalace.tusky.db.AccountEntity;
|
|||
import com.keylesspalace.tusky.entity.Account;
|
||||
import com.keylesspalace.tusky.interfaces.ActionButtonActivity;
|
||||
import com.keylesspalace.tusky.pager.TimelinePagerAdapter;
|
||||
import com.keylesspalace.tusky.util.CustomEmojiHelper;
|
||||
import com.keylesspalace.tusky.util.NotificationHelper;
|
||||
import com.keylesspalace.tusky.util.ThemeUtils;
|
||||
import com.mikepenz.google_material_typeface_library.GoogleMaterial;
|
||||
|
@ -71,6 +73,7 @@ import retrofit2.Response;
|
|||
|
||||
public final class MainActivity extends BottomSheetActivity implements ActionButtonActivity,
|
||||
HasSupportFragmentInjector {
|
||||
|
||||
private static final String TAG = "MainActivity"; // logging tag
|
||||
private static final long DRAWER_ITEM_ADD_ACCOUNT = -13;
|
||||
private static final long DRAWER_ITEM_EDIT_PROFILE = 0;
|
||||
|
@ -88,8 +91,6 @@ public final class MainActivity extends BottomSheetActivity implements ActionBut
|
|||
@Inject
|
||||
public DispatchingAndroidInjector<Fragment> fragmentInjector;
|
||||
|
||||
private static int COMPOSE_RESULT = 1;
|
||||
|
||||
private FloatingActionButton composeButton;
|
||||
private AccountHeader headerResult;
|
||||
private Drawer drawer;
|
||||
|
@ -125,7 +126,7 @@ public final class MainActivity extends BottomSheetActivity implements ActionBut
|
|||
|
||||
floatingBtn.setOnClickListener(v -> {
|
||||
Intent composeIntent = new Intent(getApplicationContext(), ComposeActivity.class);
|
||||
startActivityForResult(composeIntent, COMPOSE_RESULT);
|
||||
startActivity(composeIntent);
|
||||
});
|
||||
|
||||
setupDrawer();
|
||||
|
@ -305,29 +306,25 @@ public final class MainActivity extends BottomSheetActivity implements ActionBut
|
|||
R.drawable.ic_mute_24dp, getTheme());
|
||||
ThemeUtils.setDrawableTint(this, muteDrawable, R.attr.toolbar_icon_tint);
|
||||
|
||||
List<IDrawerItem> listItem = new ArrayList<>();
|
||||
listItem.add(new PrimaryDrawerItem().withIdentifier(DRAWER_ITEM_EDIT_PROFILE).withName(getString(R.string.action_edit_profile)).withSelectable(false).withIcon(GoogleMaterial.Icon.gmd_person));
|
||||
listItem.add(new PrimaryDrawerItem().withIdentifier(DRAWER_ITEM_FAVOURITES).withName(getString(R.string.action_view_favourites)).withSelectable(false).withIcon(GoogleMaterial.Icon.gmd_star));
|
||||
listItem.add(new PrimaryDrawerItem().withIdentifier(DRAWER_ITEM_LISTS).withName(R.string.action_lists).withSelectable(false).withIcon(GoogleMaterial.Icon.gmd_list));
|
||||
listItem.add(new PrimaryDrawerItem().withIdentifier(DRAWER_ITEM_MUTED_USERS).withName(getString(R.string.action_view_mutes)).withSelectable(false).withIcon(muteDrawable));
|
||||
listItem.add(new PrimaryDrawerItem().withIdentifier(DRAWER_ITEM_BLOCKED_USERS).withName(getString(R.string.action_view_blocks)).withSelectable(false).withIcon(GoogleMaterial.Icon.gmd_block));
|
||||
listItem.add(new PrimaryDrawerItem().withIdentifier(DRAWER_ITEM_SEARCH).withName(getString(R.string.action_search)).withSelectable(false).withIcon(GoogleMaterial.Icon.gmd_search));
|
||||
listItem.add(new PrimaryDrawerItem().withIdentifier(DRAWER_ITEM_SAVED_TOOT).withName(getString(R.string.action_access_saved_toot)).withSelectable(false).withIcon(GoogleMaterial.Icon.gmd_save));
|
||||
listItem.add(new DividerDrawerItem());
|
||||
listItem.add(new SecondaryDrawerItem().withIdentifier(DRAWER_ITEM_PREFERENCES).withName(getString(R.string.action_view_preferences)).withSelectable(false).withIcon(GoogleMaterial.Icon.gmd_settings));
|
||||
listItem.add(new SecondaryDrawerItem().withIdentifier(DRAWER_ITEM_ABOUT).withName(getString(R.string.about_title_activity)).withSelectable(false).withIcon(GoogleMaterial.Icon.gmd_info));
|
||||
listItem.add(new SecondaryDrawerItem().withIdentifier(DRAWER_ITEM_LOG_OUT).withName(getString(R.string.action_logout)).withSelectable(false).withIcon(GoogleMaterial.Icon.gmd_exit_to_app));
|
||||
|
||||
IDrawerItem[] array = new IDrawerItem[listItem.size()];
|
||||
listItem.toArray(array); // fill the array
|
||||
List<IDrawerItem> listItems = new ArrayList<>(11);
|
||||
listItems.add(new PrimaryDrawerItem().withIdentifier(DRAWER_ITEM_EDIT_PROFILE).withName(R.string.action_edit_profile).withSelectable(false).withIcon(GoogleMaterial.Icon.gmd_person));
|
||||
listItems.add(new PrimaryDrawerItem().withIdentifier(DRAWER_ITEM_FAVOURITES).withName(R.string.action_view_favourites).withSelectable(false).withIcon(GoogleMaterial.Icon.gmd_star));
|
||||
listItems.add(new PrimaryDrawerItem().withIdentifier(DRAWER_ITEM_LISTS).withName(R.string.action_lists).withSelectable(false).withIcon(GoogleMaterial.Icon.gmd_list));
|
||||
listItems.add(new PrimaryDrawerItem().withIdentifier(DRAWER_ITEM_MUTED_USERS).withName(R.string.action_view_mutes).withSelectable(false).withIcon(muteDrawable));
|
||||
listItems.add(new PrimaryDrawerItem().withIdentifier(DRAWER_ITEM_BLOCKED_USERS).withName(R.string.action_view_blocks).withSelectable(false).withIcon(GoogleMaterial.Icon.gmd_block));
|
||||
listItems.add(new PrimaryDrawerItem().withIdentifier(DRAWER_ITEM_SEARCH).withName(R.string.action_search).withSelectable(false).withIcon(GoogleMaterial.Icon.gmd_search));
|
||||
listItems.add(new PrimaryDrawerItem().withIdentifier(DRAWER_ITEM_SAVED_TOOT).withName(R.string.action_access_saved_toot).withSelectable(false).withIcon(GoogleMaterial.Icon.gmd_save));
|
||||
listItems.add(new DividerDrawerItem());
|
||||
listItems.add(new SecondaryDrawerItem().withIdentifier(DRAWER_ITEM_PREFERENCES).withName(R.string.action_view_preferences).withSelectable(false).withIcon(GoogleMaterial.Icon.gmd_settings));
|
||||
listItems.add(new SecondaryDrawerItem().withIdentifier(DRAWER_ITEM_ABOUT).withName(R.string.about_title_activity).withSelectable(false).withIcon(GoogleMaterial.Icon.gmd_info));
|
||||
listItems.add(new SecondaryDrawerItem().withIdentifier(DRAWER_ITEM_LOG_OUT).withName(R.string.action_logout).withSelectable(false).withIcon(GoogleMaterial.Icon.gmd_exit_to_app));
|
||||
|
||||
drawer = new DrawerBuilder()
|
||||
.withActivity(this)
|
||||
//.withToolbar(toolbar)
|
||||
.withAccountHeader(headerResult)
|
||||
.withHasStableIds(true)
|
||||
.withSelectedItem(-1)
|
||||
.addDrawerItems(array)
|
||||
.withDrawerItems(listItems)
|
||||
.withOnDrawerItemClickListener((view, position, drawerItem) -> {
|
||||
if (drawerItem != null) {
|
||||
long drawerItemIdentifier = drawerItem.getIdentifier();
|
||||
|
@ -490,7 +487,7 @@ public final class MainActivity extends BottomSheetActivity implements ActionBut
|
|||
.withSelectable(false)
|
||||
.withIcon(GoogleMaterial.Icon.gmd_person_add);
|
||||
drawer.addItemAtPosition(followRequestsItem, 3);
|
||||
} else {
|
||||
} else if(!me.getLocked()){
|
||||
drawer.removeItem(DRAWER_ITEM_FOLLOW_REQUESTS);
|
||||
}
|
||||
|
||||
|
@ -502,24 +499,29 @@ public final class MainActivity extends BottomSheetActivity implements ActionBut
|
|||
|
||||
List<AccountEntity> allAccounts = accountManager.getAllAccountsOrderedByActive();
|
||||
|
||||
//remove profiles before adding them again to avoid duplicates
|
||||
List<IProfile> profiles = new ArrayList<>(headerResult.getProfiles());
|
||||
for (IProfile profile : profiles) {
|
||||
if (profile.getIdentifier() != DRAWER_ITEM_ADD_ACCOUNT) {
|
||||
headerResult.removeProfile(profile);
|
||||
// reuse the already existing "add account" item
|
||||
List<IProfile> profiles = new ArrayList<>(allAccounts.size()+1);
|
||||
for (IProfile profile: headerResult.getProfiles()) {
|
||||
if (profile.getIdentifier() == DRAWER_ITEM_ADD_ACCOUNT) {
|
||||
profiles.add(profile);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
for (AccountEntity acc : allAccounts) {
|
||||
headerResult.addProfiles(
|
||||
CharSequence emojifiedName = CustomEmojiHelper.emojifyString(acc.getDisplayName(), acc.getEmojis(), headerResult.getView());
|
||||
emojifiedName = EmojiCompat.get().process(emojifiedName);
|
||||
|
||||
profiles.add(0,
|
||||
new ProfileDrawerItem()
|
||||
.withName(acc.getDisplayName())
|
||||
.withName(emojifiedName)
|
||||
.withIcon(acc.getProfilePictureUrl())
|
||||
.withNameShown(true)
|
||||
.withIdentifier(acc.getId())
|
||||
.withEmail(acc.getFullName()));
|
||||
|
||||
}
|
||||
headerResult.setProfiles(profiles);
|
||||
|
||||
}
|
||||
|
||||
|
|
|
@ -70,7 +70,8 @@ public class TuskyApplication extends Application implements HasActivityInjector
|
|||
|
||||
appDatabase = Room.databaseBuilder(getApplicationContext(), AppDatabase.class, "tuskyDB")
|
||||
.allowMainThreadQueries()
|
||||
.addMigrations(AppDatabase.MIGRATION_2_3, AppDatabase.MIGRATION_3_4, AppDatabase.MIGRATION_4_5, AppDatabase.MIGRATION_5_6, AppDatabase.MIGRATION_6_7)
|
||||
.addMigrations(AppDatabase.MIGRATION_2_3, AppDatabase.MIGRATION_3_4, AppDatabase.MIGRATION_4_5,
|
||||
AppDatabase.MIGRATION_5_6, AppDatabase.MIGRATION_6_7, AppDatabase.MIGRATION_7_8)
|
||||
.build();
|
||||
accountManager = new AccountManager(appDatabase);
|
||||
serviceLocator = new ServiceLocator() {
|
||||
|
|
|
@ -18,9 +18,13 @@ package com.keylesspalace.tusky.db
|
|||
import android.arch.persistence.room.Entity
|
||||
import android.arch.persistence.room.Index
|
||||
import android.arch.persistence.room.PrimaryKey
|
||||
import android.arch.persistence.room.TypeConverters
|
||||
|
||||
import com.keylesspalace.tusky.entity.Emoji
|
||||
|
||||
@Entity(indices = [Index(value = ["domain", "accountId"],
|
||||
unique = true)])
|
||||
@TypeConverters(Converters::class)
|
||||
data class AccountEntity(@field:PrimaryKey(autoGenerate = true) var id: Long,
|
||||
val domain: String,
|
||||
var accessToken: String,
|
||||
|
@ -38,7 +42,8 @@ data class AccountEntity(@field:PrimaryKey(autoGenerate = true) var id: Long,
|
|||
var notificationVibration: Boolean = true,
|
||||
var notificationLight: Boolean = true,
|
||||
var lastNotificationId: String = "0",
|
||||
var activeNotifications: String = "[]") {
|
||||
var activeNotifications: String = "[]",
|
||||
var emojis: List<Emoji> = emptyList()) {
|
||||
|
||||
val identifier: String
|
||||
get() = "$domain:$accountId"
|
||||
|
|
|
@ -111,6 +111,7 @@ class AccountManager(db: AppDatabase) {
|
|||
it.username = account.username
|
||||
it.displayName = account.name
|
||||
it.profilePictureUrl = account.avatar
|
||||
it.emojis = account.emojis ?: emptyList()
|
||||
|
||||
Log.d(TAG, "updateActiveAccount: saving account with id " + it.id)
|
||||
it.id = accountDao.insertOrReplace(it)
|
||||
|
|
|
@ -25,7 +25,7 @@ import android.support.annotation.NonNull;
|
|||
* DB version & declare DAO
|
||||
*/
|
||||
|
||||
@Database(entities = {TootEntity.class, AccountEntity.class, InstanceEntity.class}, version = 7, exportSchema = false)
|
||||
@Database(entities = {TootEntity.class, AccountEntity.class, InstanceEntity.class}, version = 8, exportSchema = false)
|
||||
public abstract class AppDatabase extends RoomDatabase {
|
||||
|
||||
public abstract TootDao tootDao();
|
||||
|
@ -87,8 +87,15 @@ public abstract class AppDatabase extends RoomDatabase {
|
|||
@Override
|
||||
public void migrate(@NonNull SupportSQLiteDatabase database) {
|
||||
database.execSQL("CREATE TABLE IF NOT EXISTS `InstanceEntity` (`instance` TEXT NOT NULL, `emojiList` TEXT, `maximumTootCharacters` INTEGER, PRIMARY KEY(`instance`))");
|
||||
database.execSQL("INSERT OR REPLACE INTO `InstanceEntity` SELECT `instance`,`emojiList`,NULL FROM `EmojiListEntity`;");
|
||||
database.execSQL("INSERT OR REPLACE INTO `InstanceEntity` SELECT `instance`,`emojiList`, NULL FROM `EmojiListEntity`;");
|
||||
database.execSQL("DROP TABLE `EmojiListEntity`;");
|
||||
}
|
||||
};
|
||||
|
||||
public static final Migration MIGRATION_7_8 = new Migration(7, 8) {
|
||||
@Override
|
||||
public void migrate(@NonNull SupportSQLiteDatabase database) {
|
||||
database.execSQL("ALTER TABLE `AccountEntity` ADD COLUMN `emojis` TEXT NOT NULL DEFAULT '[]'");
|
||||
}
|
||||
};
|
||||
}
|
|
@ -0,0 +1,36 @@
|
|||
/* Copyright 2018 Conny Duck
|
||||
*
|
||||
* This file is a part of Tusky.
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify it under the terms of the
|
||||
* GNU General Public License as published by the Free Software Foundation; either version 3 of the
|
||||
* License, or (at your option) any later version.
|
||||
*
|
||||
* Tusky is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even
|
||||
* the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General
|
||||
* Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License along with Tusky; if not,
|
||||
* see <http://www.gnu.org/licenses>. */
|
||||
|
||||
package com.keylesspalace.tusky.db
|
||||
|
||||
import android.arch.persistence.room.TypeConverter
|
||||
import com.google.gson.Gson
|
||||
import com.google.gson.reflect.TypeToken
|
||||
import com.keylesspalace.tusky.entity.Emoji
|
||||
|
||||
class Converters {
|
||||
|
||||
private val gson = Gson()
|
||||
|
||||
@TypeConverter
|
||||
fun jsonToEmojiList(emojiListJson: String?): List<Emoji>? {
|
||||
return gson.fromJson(emojiListJson, object : TypeToken<List<Emoji>>() {}.type)
|
||||
}
|
||||
|
||||
@TypeConverter
|
||||
fun emojiListToJson(emojiList: List<Emoji>?): String {
|
||||
return gson.toJson(emojiList)
|
||||
}
|
||||
}
|
|
@ -17,10 +17,7 @@ package com.keylesspalace.tusky.db
|
|||
|
||||
import android.arch.persistence.room.Entity
|
||||
import android.arch.persistence.room.PrimaryKey
|
||||
import android.arch.persistence.room.TypeConverter
|
||||
import android.arch.persistence.room.TypeConverters
|
||||
import com.google.gson.Gson
|
||||
import com.google.gson.reflect.TypeToken
|
||||
import com.keylesspalace.tusky.entity.Emoji
|
||||
|
||||
@Entity
|
||||
|
@ -29,17 +26,3 @@ data class InstanceEntity(
|
|||
@field:PrimaryKey var instance: String,
|
||||
val emojiList: List<Emoji>?,
|
||||
val maximumTootCharacters: Int?)
|
||||
|
||||
|
||||
class Converters {
|
||||
|
||||
@TypeConverter
|
||||
fun jsonToList(emojiListJson: String?): List<Emoji>? {
|
||||
return Gson().fromJson(emojiListJson, object : TypeToken<List<Emoji>>() {}.type)
|
||||
}
|
||||
|
||||
@TypeConverter
|
||||
fun listToJson(emojiList: List<Emoji>?): String {
|
||||
return Gson().toJson(emojiList)
|
||||
}
|
||||
}
|
|
@ -26,7 +26,7 @@ import android.text.SpannableStringBuilder;
|
|||
import android.text.Spanned;
|
||||
import android.text.SpannedString;
|
||||
import android.text.style.ReplacementSpan;
|
||||
import android.widget.TextView;
|
||||
import android.view.View;
|
||||
|
||||
import com.keylesspalace.tusky.entity.Emoji;
|
||||
import com.squareup.picasso.Picasso;
|
||||
|
@ -43,10 +43,10 @@ public class CustomEmojiHelper {
|
|||
* replaces emoji shortcodes in a text with EmojiSpans
|
||||
* @param text the text containing custom emojis
|
||||
* @param emojis a list of the custom emojis (nullable for backward compatibility with old mastodon instances)
|
||||
* @param textView a reference to the textView the emojis will be shown in
|
||||
* @param view a reference to the a view the emojis will be shown in (should be the TextView, but parents of the TextView are also acceptable)
|
||||
* @return the text with the shortcodes replaced by EmojiSpans
|
||||
*/
|
||||
public static Spanned emojifyText(@NonNull Spanned text, @Nullable List<Emoji> emojis, @NonNull final TextView textView) {
|
||||
public static Spanned emojifyText(@NonNull Spanned text, @Nullable List<Emoji> emojis, @NonNull final View view) {
|
||||
|
||||
if (emojis != null && !emojis.isEmpty()) {
|
||||
|
||||
|
@ -57,9 +57,9 @@ public class CustomEmojiHelper {
|
|||
while (matcher.find()) {
|
||||
// We keep a span as a Picasso target, because Picasso keeps weak reference to
|
||||
// the target so an anonymous class would likely be garbage collected.
|
||||
EmojiSpan span = new EmojiSpan(textView);
|
||||
EmojiSpan span = new EmojiSpan(view);
|
||||
builder.setSpan(span, matcher.start(), matcher.end(), 0);
|
||||
Picasso.with(textView.getContext())
|
||||
Picasso.with(view.getContext())
|
||||
.load(emoji.getUrl())
|
||||
.into(span);
|
||||
}
|
||||
|
@ -71,18 +71,18 @@ public class CustomEmojiHelper {
|
|||
return text;
|
||||
}
|
||||
|
||||
public static Spanned emojifyString(@NonNull String string, @Nullable List<Emoji> emojis, @NonNull final TextView textView) {
|
||||
return emojifyText(new SpannedString(string), emojis, textView);
|
||||
public static Spanned emojifyString(@NonNull String string, @Nullable List<Emoji> emojis, @NonNull final View ciew) {
|
||||
return emojifyText(new SpannedString(string), emojis, ciew);
|
||||
}
|
||||
|
||||
|
||||
public static class EmojiSpan extends ReplacementSpan implements Target {
|
||||
|
||||
private @Nullable Drawable imageDrawable;
|
||||
private WeakReference<TextView> textViewWeakReference;
|
||||
private WeakReference<View> viewWeakReference;
|
||||
|
||||
EmojiSpan(TextView textView) {
|
||||
this.textViewWeakReference = new WeakReference<>(textView);
|
||||
EmojiSpan(View view) {
|
||||
this.viewWeakReference = new WeakReference<>(view);
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@ -120,10 +120,10 @@ public class CustomEmojiHelper {
|
|||
|
||||
@Override
|
||||
public void onBitmapLoaded(Bitmap bitmap, Picasso.LoadedFrom from) {
|
||||
TextView textView = textViewWeakReference.get();
|
||||
if(textView != null) {
|
||||
imageDrawable = new BitmapDrawable(textView.getContext().getResources(), bitmap);
|
||||
textView.invalidate();
|
||||
View view = viewWeakReference.get();
|
||||
if(view != null) {
|
||||
imageDrawable = new BitmapDrawable(view.getContext().getResources(), bitmap);
|
||||
view.invalidate();
|
||||
}
|
||||
}
|
||||
|
||||
|
|
Loading…
Reference in New Issue