From c59fc5143481e9402aac0ed3ad6f2396eabb1c57 Mon Sep 17 00:00:00 2001 From: Mariotaku Lee Date: Sat, 13 May 2017 14:19:23 +0800 Subject: [PATCH] improved emoji support --- .gitattributes | 1 + .../preference/ComponentPickerPreference.java | 80 --------- .../preference/EmojiSupportPreference.java | 49 ------ .../preference/ThemedListPreference.java | 37 ---- .../ktextension/TextViewExtensions.kt | 16 +- .../twidere/activity/ComposeActivity.kt | 24 +-- .../activity/QuickSearchBarActivity.kt | 11 +- .../twidere/adapter/AccountsSpinnerAdapter.kt | 9 +- .../adapter/ComposeAutoCompleteAdapter.kt | 7 +- .../adapter/UserAutoCompleteAdapter.kt | 22 ++- .../twidere/extension/EmojiExtensions.kt | 155 +++++++++++++++++ .../twidere/extension/ViewExtensions.kt | 10 ++ .../AbsUserMuteBlockDialogFragment.kt | 3 +- .../fragment/AccountsDashboardFragment.kt | 9 +- .../twidere/fragment/BaseFragment.kt | 2 + .../twidere/fragment/CustomTabsFragment.kt | 8 +- .../twidere/fragment/StatusFragment.kt | 36 ++-- .../twidere/fragment/UserFragment.kt | 30 ++-- .../fragment/filter/FilteredUsersFragment.kt | 7 +- .../MessageConversationInfoFragment.kt | 9 +- .../message/MessagesConversationFragment.kt | 4 +- .../twidere/fragment/search/SearchFragment.kt | 3 +- .../preference/ActivityPickerPreference.kt} | 35 ++-- .../preference/ComponentPickerPreference.kt | 74 ++++++++ .../preference/EmojiSupportPreference.kt | 39 +++++ .../preference/MediaUploaderPreference.kt} | 32 ++-- .../preference/ServicePickerPreference.kt} | 35 ++-- .../preference/StatusShortenerPreference.kt} | 36 ++-- .../preference/ThemedListPreference.kt | 22 +++ .../twidere/task/twitter/UpdateStatusTask.kt | 6 +- .../twidere/text/util/EmojiEditableFactory.kt | 9 +- .../text/util/EmojiSpannableFactory.kt | 6 +- .../twidere/util/EmojiSupportUtils.kt | 159 ------------------ .../twidere/util/ExternalThemeManager.kt | 7 +- .../twidere/util/dagger/GeneralComponent.kt | 3 +- .../mariotaku/twidere/view/ComposeEditText.kt | 4 +- .../mariotaku/twidere/view/FixedEditText.kt | 6 +- .../mariotaku/twidere/view/FixedTextView.kt | 7 +- .../org/mariotaku/twidere/view/NameView.kt | 4 +- .../twidere/view/TimelineContentTextView.kt | 4 +- .../twitter/card/CardPollViewController.kt | 5 +- .../twidere/view/holder/AccountViewHolder.kt | 8 +- .../twidere/view/holder/DraftViewHolder.kt | 5 +- .../twidere/view/holder/GroupViewHolder.kt | 9 +- .../view/holder/MediaStatusViewHolder.kt | 5 +- .../view/holder/SimpleUserListViewHolder.kt | 5 +- .../view/holder/SimpleUserViewHolder.kt | 5 +- .../twidere/view/holder/StatusViewHolder.kt | 50 +++--- .../twidere/view/holder/UserListViewHolder.kt | 11 +- .../twidere/view/holder/UserViewHolder.kt | 7 +- .../holder/message/MessageEntryViewHolder.kt | 5 +- .../view/holder/message/MessageViewHolder.kt | 3 +- .../message/NoticeSummaryEventViewHolder.kt | 3 +- .../res-localized/values-zh-rCN/strings.xml | 2 +- .../src/main/res/layout/list_item_status.xml | 2 +- 55 files changed, 540 insertions(+), 605 deletions(-) create mode 100644 .gitattributes delete mode 100644 twidere/src/main/java/org/mariotaku/twidere/preference/ComponentPickerPreference.java delete mode 100644 twidere/src/main/java/org/mariotaku/twidere/preference/EmojiSupportPreference.java delete mode 100644 twidere/src/main/java/org/mariotaku/twidere/preference/ThemedListPreference.java create mode 100644 twidere/src/main/kotlin/org/mariotaku/twidere/extension/EmojiExtensions.kt rename twidere/src/main/{java/org/mariotaku/twidere/preference/ActivityPickerPreference.java => kotlin/org/mariotaku/twidere/preference/ActivityPickerPreference.kt} (52%) create mode 100644 twidere/src/main/kotlin/org/mariotaku/twidere/preference/ComponentPickerPreference.kt create mode 100644 twidere/src/main/kotlin/org/mariotaku/twidere/preference/EmojiSupportPreference.kt rename twidere/src/main/{java/org/mariotaku/twidere/preference/StatusShortenerPreference.java => kotlin/org/mariotaku/twidere/preference/MediaUploaderPreference.kt} (51%) rename twidere/src/main/{java/org/mariotaku/twidere/preference/ServicePickerPreference.java => kotlin/org/mariotaku/twidere/preference/ServicePickerPreference.kt} (52%) rename twidere/src/main/{java/org/mariotaku/twidere/preference/MediaUploaderPreference.java => kotlin/org/mariotaku/twidere/preference/StatusShortenerPreference.kt} (50%) create mode 100644 twidere/src/main/kotlin/org/mariotaku/twidere/preference/ThemedListPreference.kt delete mode 100644 twidere/src/main/kotlin/org/mariotaku/twidere/util/EmojiSupportUtils.kt diff --git a/.gitattributes b/.gitattributes new file mode 100644 index 000000000..69b47b5ad --- /dev/null +++ b/.gitattributes @@ -0,0 +1 @@ +*.bat text eol=crlf diff --git a/twidere/src/main/java/org/mariotaku/twidere/preference/ComponentPickerPreference.java b/twidere/src/main/java/org/mariotaku/twidere/preference/ComponentPickerPreference.java deleted file mode 100644 index e08776366..000000000 --- a/twidere/src/main/java/org/mariotaku/twidere/preference/ComponentPickerPreference.java +++ /dev/null @@ -1,80 +0,0 @@ -/* - * Twidere - Twitter client for Android - * - * Copyright (C) 2012-2015 Mariotaku Lee - * - * 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. - * - * This program 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 this program. If not, see . - */ - -package org.mariotaku.twidere.preference; - -import android.content.ComponentName; -import android.content.Context; -import android.content.Intent; -import android.content.pm.PackageManager; -import android.content.pm.ResolveInfo; -import android.text.TextUtils; -import android.util.AttributeSet; - -import java.util.List; - -public abstract class ComponentPickerPreference extends ThemedListPreference { - - protected final PackageManager packageManager; - - public ComponentPickerPreference(final Context context) { - this(context, null); - } - - public ComponentPickerPreference(final Context context, final AttributeSet attrs) { - super(context, attrs); - packageManager = context.getPackageManager(); - init(); - } - - @Override - public CharSequence getSummary() { - if (isNoneValue(getValue())) return getNoneEntry(); - return super.getSummary(); - } - - protected abstract String getIntentAction(); - - protected abstract String getNoneEntry(); - - private void init() { - final Intent queryIntent = new Intent(getIntentAction()); - final List infoList = resolve(queryIntent); - final int infoListSize = infoList.size(); - final CharSequence[] entries = new CharSequence[infoListSize + 1], values = new CharSequence[infoListSize + 1]; - entries[0] = getNoneEntry(); - values[0] = ""; - for (int i = 0; i < infoListSize; i++) { - final ResolveInfo info = infoList.get(i); - entries[i + 1] = info.loadLabel(packageManager); - values[i + 1] = getComponentName(info).flattenToString(); - } - setEntries(entries); - setEntryValues(values); - } - - protected abstract ComponentName getComponentName(ResolveInfo info); - - protected abstract List resolve(Intent queryIntent); - - public static boolean isNoneValue(final String value) { - return TextUtils.isEmpty(value) || "none".equals(value); - } - -} diff --git a/twidere/src/main/java/org/mariotaku/twidere/preference/EmojiSupportPreference.java b/twidere/src/main/java/org/mariotaku/twidere/preference/EmojiSupportPreference.java deleted file mode 100644 index 5d42f8d68..000000000 --- a/twidere/src/main/java/org/mariotaku/twidere/preference/EmojiSupportPreference.java +++ /dev/null @@ -1,49 +0,0 @@ -/* - * Twidere - Twitter client for Android - * - * Copyright (C) 2012-2015 Mariotaku Lee - * - * 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. - * - * This program 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 this program. If not, see . - */ - -package org.mariotaku.twidere.preference; - -import android.content.Context; -import android.util.AttributeSet; - -import org.mariotaku.twidere.R; -import org.mariotaku.twidere.constant.IntentConstants; - -/** - * Created by mariotaku on 15/12/22. - */ -public class EmojiSupportPreference extends ActivityPickerPreference { - public EmojiSupportPreference(Context context) { - super(context); - } - - public EmojiSupportPreference(Context context, AttributeSet attrs) { - super(context, attrs); - } - - @Override - protected String getIntentAction() { - return IntentConstants.INTENT_ACTION_EMOJI_SUPPORT_ABOUT; - } - - @Override - protected String getNoneEntry() { - return getContext().getString(R.string.system_default); - } -} diff --git a/twidere/src/main/java/org/mariotaku/twidere/preference/ThemedListPreference.java b/twidere/src/main/java/org/mariotaku/twidere/preference/ThemedListPreference.java deleted file mode 100644 index c221237b5..000000000 --- a/twidere/src/main/java/org/mariotaku/twidere/preference/ThemedListPreference.java +++ /dev/null @@ -1,37 +0,0 @@ -package org.mariotaku.twidere.preference; - -import android.content.Context; -import android.support.v7.preference.ListPreference; -import android.support.v7.preference.PreferenceFragmentCompat; -import android.util.AttributeSet; - -import org.mariotaku.twidere.fragment.ThemedListPreferenceDialogFragmentCompat; -import org.mariotaku.twidere.preference.iface.IDialogPreference; - -/** - * Created by mariotaku on 16/3/15. - */ -public class ThemedListPreference extends ListPreference implements IDialogPreference { - public ThemedListPreference(Context context, AttributeSet attrs, int defStyleAttr, int defStyleRes) { - super(context, attrs, defStyleAttr, defStyleRes); - } - - public ThemedListPreference(Context context, AttributeSet attrs, int defStyleAttr) { - super(context, attrs, defStyleAttr); - } - - public ThemedListPreference(Context context, AttributeSet attrs) { - super(context, attrs); - } - - public ThemedListPreference(Context context) { - super(context); - } - - @Override - public void displayDialog(PreferenceFragmentCompat fragment) { - ThemedListPreferenceDialogFragmentCompat df = ThemedListPreferenceDialogFragmentCompat.newInstance(getKey()); - df.setTargetFragment(fragment, 0); - df.show(fragment.getFragmentManager(), getKey()); - } -} diff --git a/twidere/src/main/kotlin/org/mariotaku/ktextension/TextViewExtensions.kt b/twidere/src/main/kotlin/org/mariotaku/ktextension/TextViewExtensions.kt index b2e15fd18..f6aa28061 100644 --- a/twidere/src/main/kotlin/org/mariotaku/ktextension/TextViewExtensions.kt +++ b/twidere/src/main/kotlin/org/mariotaku/ktextension/TextViewExtensions.kt @@ -4,16 +4,22 @@ import android.graphics.Typeface import android.view.View import android.widget.TextView -val TextView.empty: Boolean +inline val TextView.empty: Boolean get() = length() <= 0 -var TextView.string: String? +inline var TextView.string: String? get() = text?.toString() set(value) { text = value } -var TextView.charSequence: CharSequence? +inline var TextView.spannable: CharSequence? + get() = text + set(value) { + setText(value, TextView.BufferType.SPANNABLE) + } + +inline var TextView.charSequence: CharSequence? get() = text set(value) { text = value @@ -25,9 +31,9 @@ fun TextView.applyFontFamily(lightFont: Boolean) { } } -fun TextView.hideIfEmpty() { +fun TextView.hideIfEmpty(hideVisibility: Int = View.GONE) { visibility = if (empty) { - View.GONE + hideVisibility } else { View.VISIBLE } diff --git a/twidere/src/main/kotlin/org/mariotaku/twidere/activity/ComposeActivity.kt b/twidere/src/main/kotlin/org/mariotaku/twidere/activity/ComposeActivity.kt index f7ba68259..48328e193 100644 --- a/twidere/src/main/kotlin/org/mariotaku/twidere/activity/ComposeActivity.kt +++ b/twidere/src/main/kotlin/org/mariotaku/twidere/activity/ComposeActivity.kt @@ -89,7 +89,7 @@ import org.mariotaku.twidere.model.draft.UpdateStatusActionExtras import org.mariotaku.twidere.model.schedule.ScheduleInfo import org.mariotaku.twidere.model.util.AccountUtils import org.mariotaku.twidere.model.util.ParcelableLocationUtils -import org.mariotaku.twidere.preference.ServicePickerPreference +import org.mariotaku.twidere.preference.ComponentPickerPreference import org.mariotaku.twidere.provider.TwidereDataStore.Drafts import org.mariotaku.twidere.service.LengthyOperationsService import org.mariotaku.twidere.task.compose.AbsAddMediaTask @@ -250,7 +250,7 @@ class ComposeActivity : BaseActivity(), OnMenuItemClickListener, OnClickListener accountSelectorButton.setOnClickListener(this) replyLabel.setOnClickListener(this) - hintLabel.text = HtmlSpanBuilder.fromHtml(getString(R.string.hint_status_reply_to_user_removed)).apply { + hintLabel.spannable = HtmlSpanBuilder.fromHtml(getString(R.string.hint_status_reply_to_user_removed)).apply { val dialogSpan = getSpans(0, length, URLSpan::class.java).firstOrNull { "#dialog" == it.url } @@ -357,8 +357,8 @@ class ComposeActivity : BaseActivity(), OnMenuItemClickListener, OnClickListener override fun onStart() { super.onStart() - imageUploaderUsed = !ServicePickerPreference.isNoneValue(kPreferences[mediaUploaderKey]) - statusShortenerUsed = !ServicePickerPreference.isNoneValue(kPreferences[statusShortenerKey]) + imageUploaderUsed = !ComponentPickerPreference.isNoneValue(kPreferences[mediaUploaderKey]) + statusShortenerUsed = !ComponentPickerPreference.isNoneValue(kPreferences[statusShortenerKey]) if (kPreferences[attachLocationKey]) { if (checkAnySelfPermissionsGranted(AndroidPermission.ACCESS_COARSE_LOCATION, AndroidPermission.ACCESS_FINE_LOCATION)) { @@ -1162,7 +1162,7 @@ class ComposeActivity : BaseActivity(), OnMenuItemClickListener, OnClickListener return false } val replyToName = userColorNameManager.getDisplayName(status, nameFirst) - replyLabel.text = getString(R.string.label_quote_name_text, replyToName, status.text_unescaped) + replyLabel.spannable = getString(R.string.label_quote_name_text, replyToName, status.text_unescaped) replyLabel.visibility = View.VISIBLE editText.hint = getString(R.string.label_quote_name, replyToName) return true @@ -1174,7 +1174,7 @@ class ComposeActivity : BaseActivity(), OnMenuItemClickListener, OnClickListener return false } val replyToName = userColorNameManager.getDisplayName(status, nameFirst) - replyLabel.text = getString(R.string.label_reply_name_text, replyToName, status.text_unescaped) + replyLabel.spannable = getString(R.string.label_reply_name_text, replyToName, status.text_unescaped) replyLabel.visibility = View.VISIBLE editText.hint = getString(R.string.label_reply_name, replyToName) return true @@ -1362,7 +1362,7 @@ class ComposeActivity : BaseActivity(), OnMenuItemClickListener, OnClickListener if (location != null) { val attachPreciseLocation = kPreferences[attachPreciseLocationKey] if (attachPreciseLocation) { - locationLabel.text = ParcelableLocationUtils.getHumanReadableString(location, 3) + locationLabel.spannable = ParcelableLocationUtils.getHumanReadableString(location, 3) } else { if (locationLabel.tag == null || location != recentLocation) { val task = DisplayPlaceNameTask() @@ -2086,12 +2086,12 @@ class ComposeActivity : BaseActivity(), OnMenuItemClickListener, OnClickListener val attachPreciseLocation = preferences[attachPreciseLocationKey] if (attachLocation) { if (attachPreciseLocation) { - textView.text = ParcelableLocationUtils.getHumanReadableString(location, 3) + textView.spannable = ParcelableLocationUtils.getHumanReadableString(location, 3) textView.tag = location } else { val tag = textView.tag if (tag is Address) { - textView.text = tag.locality + textView.spannable = tag.locality } else if (tag is NoAddress) { textView.setText(R.string.label_location_your_coarse_location) } else { @@ -2112,20 +2112,20 @@ class ComposeActivity : BaseActivity(), OnMenuItemClickListener, OnClickListener if (attachLocation) { if (attachPreciseLocation) { val location = params - textView.text = ParcelableLocationUtils.getHumanReadableString(location, 3) + textView.spannable = ParcelableLocationUtils.getHumanReadableString(location, 3) textView.tag = location } else if (addresses == null || addresses.isEmpty()) { val tag = textView.tag if (tag is Address) { - textView.text = tag.locality + textView.spannable = tag.locality } else { textView.setText(R.string.label_location_your_coarse_location) textView.tag = NoAddress() } } else { val address = addresses[0] + textView.spannable = address.locality textView.tag = address - textView.text = address.locality } } else { textView.setText(R.string.no_location) diff --git a/twidere/src/main/kotlin/org/mariotaku/twidere/activity/QuickSearchBarActivity.kt b/twidere/src/main/kotlin/org/mariotaku/twidere/activity/QuickSearchBarActivity.kt index bb2317671..0d7e8665f 100644 --- a/twidere/src/main/kotlin/org/mariotaku/twidere/activity/QuickSearchBarActivity.kt +++ b/twidere/src/main/kotlin/org/mariotaku/twidere/activity/QuickSearchBarActivity.kt @@ -47,6 +47,7 @@ import jopt.csp.util.SortableIntList import kotlinx.android.synthetic.main.activity_quick_search_bar.* import org.mariotaku.kpreferences.get import org.mariotaku.ktextension.empty +import org.mariotaku.ktextension.spannable import org.mariotaku.twidere.BuildConfig import org.mariotaku.twidere.R import org.mariotaku.twidere.TwidereConstants.QUERY_PARAM_ACCOUNT_KEY @@ -392,23 +393,23 @@ class QuickSearchBarActivity : BaseActivity(), OnClickListener, LoaderCallbacks< val holder = view.tag as SearchViewHolder val title = cursor.getString(indices.title) holder.edit_query.tag = title - holder.text1.text = title + holder.text1.spannable = title holder.icon.setImageResource(R.drawable.ic_action_history) } VIEW_TYPE_SAVED_SEARCH -> { val holder = view.tag as SearchViewHolder val title = cursor.getString(indices.title) holder.edit_query.tag = title - holder.text1.text = title + holder.text1.spannable = title holder.icon.setImageResource(R.drawable.ic_action_save) } VIEW_TYPE_USER_SUGGESTION_ITEM -> { val holder = view.tag as UserViewHolder val userKey = UserKey.valueOf(cursor.getString(indices.extra_id)) - holder.text1.text = userColorNameManager.getUserNickname(userKey, + holder.text1.spannable = userColorNameManager.getUserNickname(userKey, cursor.getString(indices.title)) holder.text2.visibility = View.VISIBLE - holder.text2.text = "@${cursor.getString(indices.summary)}" + holder.text2.spannable = "@${cursor.getString(indices.summary)}" holder.icon.clearColorFilter() requestManager.loadProfileImage(context, cursor.getString(indices.icon), profileImageStyle, cornerRadius = holder.icon.cornerRadius, @@ -417,7 +418,7 @@ class QuickSearchBarActivity : BaseActivity(), OnClickListener, LoaderCallbacks< } VIEW_TYPE_USER_SCREEN_NAME -> { val holder = view.tag as UserViewHolder - holder.text1.text = "@${cursor.getString(indices.title)}" + holder.text1.spannable = "@${cursor.getString(indices.title)}" holder.text2.visibility = View.GONE holder.icon.setColorFilter(holder.text1.currentTextColor, Mode.SRC_ATOP) //TODO cancel image load diff --git a/twidere/src/main/kotlin/org/mariotaku/twidere/adapter/AccountsSpinnerAdapter.kt b/twidere/src/main/kotlin/org/mariotaku/twidere/adapter/AccountsSpinnerAdapter.kt index 020add9d4..34da4aa19 100644 --- a/twidere/src/main/kotlin/org/mariotaku/twidere/adapter/AccountsSpinnerAdapter.kt +++ b/twidere/src/main/kotlin/org/mariotaku/twidere/adapter/AccountsSpinnerAdapter.kt @@ -19,12 +19,12 @@ package org.mariotaku.twidere.adapter -import android.annotation.SuppressLint import android.content.Context import android.view.View import android.view.ViewGroup import com.bumptech.glide.RequestManager import kotlinx.android.synthetic.main.list_item_simple_user.view.* +import org.mariotaku.ktextension.spannable import org.mariotaku.twidere.R import org.mariotaku.twidere.extension.loadProfileImage import org.mariotaku.twidere.model.AccountDetails @@ -61,10 +61,9 @@ class AccountsSpinnerAdapter( val icon = view.profileImage if (!item.dummy) { text1?.visibility = View.VISIBLE - text1?.text = item.user.name + text1?.spannable = item.user.name text2?.visibility = View.VISIBLE - @SuppressLint("SetTextI18n") - text2?.text = "@${item.user.screen_name}" + text2?.spannable = "@${item.user.screen_name}" if (icon != null) { if (profileImageEnabled) { icon.visibility = View.VISIBLE @@ -76,7 +75,7 @@ class AccountsSpinnerAdapter( } } else { text1?.visibility = View.VISIBLE - text1?.text = dummyItemText + text1?.spannable = dummyItemText text2?.visibility = View.GONE icon?.visibility = View.GONE } diff --git a/twidere/src/main/kotlin/org/mariotaku/twidere/adapter/ComposeAutoCompleteAdapter.kt b/twidere/src/main/kotlin/org/mariotaku/twidere/adapter/ComposeAutoCompleteAdapter.kt index 68f272d0c..148affe85 100644 --- a/twidere/src/main/kotlin/org/mariotaku/twidere/adapter/ComposeAutoCompleteAdapter.kt +++ b/twidere/src/main/kotlin/org/mariotaku/twidere/adapter/ComposeAutoCompleteAdapter.kt @@ -28,6 +28,7 @@ import android.view.View import android.widget.TextView import com.bumptech.glide.RequestManager import org.mariotaku.kpreferences.get +import org.mariotaku.ktextension.spannable import org.mariotaku.twidere.R import org.mariotaku.twidere.TwidereConstants.* import org.mariotaku.twidere.constant.displayProfileImageKey @@ -71,9 +72,9 @@ class ComposeAutoCompleteAdapter(context: Context, val requestManager: RequestMa icon.style = profileImageStyle if (Suggestions.AutoComplete.TYPE_USERS == cursor.getString(indices.type)) { - text1.text = userColorNameManager.getUserNickname(cursor.getString(indices.extra_id), + text1.spannable = userColorNameManager.getUserNickname(cursor.getString(indices.extra_id), cursor.getString(indices.title)) - text2.text = String.format("@%s", cursor.getString(indices.summary)) + text2.spannable = String.format("@%s", cursor.getString(indices.summary)) if (displayProfileImage) { val profileImageUrl = cursor.getString(indices.icon) requestManager.loadProfileImage(context, profileImageUrl, profileImageStyle).into(icon) @@ -83,7 +84,7 @@ class ComposeAutoCompleteAdapter(context: Context, val requestManager: RequestMa icon.clearColorFilter() } else { - text1.text = String.format("#%s", cursor.getString(indices.title)) + text1.spannable = String.format("#%s", cursor.getString(indices.title)) text2.setText(R.string.hashtag) icon.setImageResource(R.drawable.ic_action_hashtag) diff --git a/twidere/src/main/kotlin/org/mariotaku/twidere/adapter/UserAutoCompleteAdapter.kt b/twidere/src/main/kotlin/org/mariotaku/twidere/adapter/UserAutoCompleteAdapter.kt index e136d4c1c..1eae802ba 100644 --- a/twidere/src/main/kotlin/org/mariotaku/twidere/adapter/UserAutoCompleteAdapter.kt +++ b/twidere/src/main/kotlin/org/mariotaku/twidere/adapter/UserAutoCompleteAdapter.kt @@ -30,6 +30,7 @@ import android.view.View import android.widget.TextView import com.bumptech.glide.RequestManager import org.mariotaku.kpreferences.get +import org.mariotaku.ktextension.spannable import org.mariotaku.library.objectcursor.ObjectCursor import org.mariotaku.sqliteqb.library.Columns import org.mariotaku.sqliteqb.library.Expression @@ -38,7 +39,7 @@ import org.mariotaku.twidere.R import org.mariotaku.twidere.constant.displayProfileImageKey import org.mariotaku.twidere.constant.profileImageStyleKey import org.mariotaku.twidere.extension.loadProfileImage -import org.mariotaku.twidere.model.ParcelableUser +import org.mariotaku.twidere.model.ParcelableLiteUser import org.mariotaku.twidere.model.UserKey import org.mariotaku.twidere.provider.TwidereDataStore.CachedUsers import org.mariotaku.twidere.util.UserColorNameManager @@ -61,7 +62,7 @@ class UserAutoCompleteAdapter( private val displayProfileImage: Boolean private var profileImageStyle: Int - private var indices: ObjectCursor.CursorIndices? = null + private var indices: ObjectCursor.CursorIndices? = null var accountKey: UserKey? = null @@ -72,25 +73,22 @@ class UserAutoCompleteAdapter( } override fun bindView(view: View, context: Context, cursor: Cursor) { - val indices = this.indices!! + val user = indices!!.newObject(cursor) val text1 = view.findViewById(android.R.id.text1) as TextView val text2 = view.findViewById(android.R.id.text2) as TextView val icon = view.findViewById(android.R.id.icon) as ProfileImageView icon.style = profileImageStyle - text1.text = userColorNameManager.getUserNickname(cursor.getString(indices[CachedUsers.USER_KEY]), cursor.getString(indices[CachedUsers.NAME])) - @SuppressLint("SetTextI18n") - text2.text = "@${cursor.getString(indices[CachedUsers.SCREEN_NAME])}" + text1.spannable = userColorNameManager.getUserNickname(user.key, user.name) + text2.spannable = "@${user.screen_name}" if (displayProfileImage) { - val profileImageUrl = cursor.getString(indices[CachedUsers.PROFILE_IMAGE_URL]) - requestManager.loadProfileImage(context, profileImageUrl, profileImageStyle).into(icon) + requestManager.loadProfileImage(context, user, profileImageStyle).into(icon) } else { //TODO cancel image load } icon.visibility = if (displayProfileImage) View.VISIBLE else View.GONE - super.bindView(view, context, cursor) } fun closeCursor() { @@ -100,8 +98,8 @@ class UserAutoCompleteAdapter( } } - override fun convertToString(cursor: Cursor?): CharSequence { - return cursor!!.getString(indices!![CachedUsers.SCREEN_NAME]) + override fun convertToString(cursor: Cursor): CharSequence { + return cursor.getString(indices!![CachedUsers.SCREEN_NAME]) } override fun runQueryOnBackgroundThread(constraint: CharSequence): Cursor? { @@ -127,7 +125,7 @@ class UserAutoCompleteAdapter( } override fun swapCursor(cursor: Cursor?): Cursor? { - indices = cursor?.let { ObjectCursor.indicesFrom(it, ParcelableUser::class.java) } + indices = cursor?.let { ObjectCursor.indicesFrom(it, ParcelableLiteUser::class.java) } return super.swapCursor(cursor) } diff --git a/twidere/src/main/kotlin/org/mariotaku/twidere/extension/EmojiExtensions.kt b/twidere/src/main/kotlin/org/mariotaku/twidere/extension/EmojiExtensions.kt new file mode 100644 index 000000000..c5fc65de8 --- /dev/null +++ b/twidere/src/main/kotlin/org/mariotaku/twidere/extension/EmojiExtensions.kt @@ -0,0 +1,155 @@ +/* + * Twidere - Twitter client for Android + * + * Copyright (C) 2012-2017 Mariotaku Lee + * + * 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. + * + * This program 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 this program. If not, see . + */ + +package org.mariotaku.twidere.extension + +import android.graphics.drawable.Drawable +import android.text.Spannable +import android.text.Spanned +import android.widget.TextView +import org.mariotaku.commons.text.CodePointArray +import org.mariotaku.commons.text.get +import org.mariotaku.twidere.text.style.EmojiSpan +import org.mariotaku.twidere.text.util.EmojiEditableFactory +import org.mariotaku.twidere.text.util.EmojiSpannableFactory +import org.mariotaku.twidere.util.ExternalThemeManager + +/** + * Created by mariotaku on 2017/5/13. + */ + +fun TextView.setupEmojiFactory() { + if (isInEditMode) return + setSpannableFactory(EmojiSpannableFactory(this)) + setEditableFactory(EmojiEditableFactory(this)) +} + +fun ExternalThemeManager.Emoji.applyTo(text: Spannable, textStart: Int = 0, textLength: Int = text.length) { + if (!isSupported) return + val array = CodePointArray(text) + var arrayIdx = array.length() - 1 + while (arrayIdx >= 0) { + val codePoint = array[arrayIdx] + if (isEmoji(codePoint)) { + val arrayEnd = arrayIdx + 1 + var arrayIdxOffset = 0 + val textIdx = array.indexOfText(codePoint, arrayIdx) + var textIdxOffset = 0 + var skippedIndex = 0 + if (textIdx == -1 || textIdx < textStart) { + arrayIdx-- + continue + } + val textEnd = textIdx + Character.charCount(codePoint) + if (arrayIdx > 0) { + val prevCodePoint = array[arrayIdx - 1] + when { + isRegionalIndicatorSymbol(codePoint) -> if (isRegionalIndicatorSymbol(prevCodePoint)) { + arrayIdxOffset = -1 + textIdxOffset = -Character.charCount(prevCodePoint) + skippedIndex = -1 + } + isModifier(codePoint) -> if (isEmoji(prevCodePoint)) { + arrayIdxOffset = -1 + textIdxOffset = -Character.charCount(prevCodePoint) + skippedIndex = -1 + } + isKeyCap(codePoint) -> if (isPhoneNumberSymbol(prevCodePoint)) { + arrayIdxOffset = -1 + textIdxOffset = -Character.charCount(prevCodePoint) + skippedIndex = -1 + } + isZeroWidthJoin(prevCodePoint) -> { + var notValidControlCount = 0 + var charCount = 0 + for (i in arrayIdx - 1 downTo 0) { + val cp = array.get(i) + charCount += Character.charCount(cp) + if (isZeroWidthJoin(cp) || isVariationSelector(cp)) { + // Ignore + notValidControlCount = 0 + continue + } + notValidControlCount++ + if (notValidControlCount > 1 || i == 0) { + arrayIdxOffset = i - arrayIdx + 1 + textIdxOffset = -charCount + Character.charCount(cp) + skippedIndex = i - arrayIdx + 1 + break + } + } + } + } + } + if (textEnd > textStart + textLength) { + arrayIdx-- + continue + } + var spans = text.getSpans(textIdx + textIdxOffset, textEnd, EmojiSpan::class.java) + if (spans.isEmpty()) { + var drawable: Drawable? = getEmojiDrawableFor(*array[arrayIdx + arrayIdxOffset..arrayEnd]) + if (drawable == null) { + // Not emoji combination, just use fallback + textIdxOffset = 0 + arrayIdxOffset = 0 + skippedIndex = 0 + spans = text.getSpans(textIdx + textIdxOffset, textEnd, EmojiSpan::class.java) + if (spans.isEmpty()) { + drawable = getEmojiDrawableFor(*array[arrayIdx + arrayIdxOffset..arrayEnd]) + } + } + if (drawable != null) { + text.setSpan(EmojiSpan(drawable), textIdx + textIdxOffset, textEnd, + Spanned.SPAN_EXCLUSIVE_EXCLUSIVE) + } + } + arrayIdx += skippedIndex + } + arrayIdx-- + } +} + + +private fun isVariationSelector(codePoint: Int): Boolean { + return codePoint == 0xfe0f +} + +private fun isZeroWidthJoin(codePoint: Int): Boolean { + return codePoint == 0x200d +} + +private fun isPhoneNumberSymbol(codePoint: Int): Boolean { + return codePoint == 0x0023 || codePoint == 0x002a || codePoint in 0x0030..0x0039 +} + +private fun isModifier(codePoint: Int): Boolean { + return codePoint in 0x1f3fb..0x1f3ff +} + +private fun isEmoji(codePoint: Int): Boolean { + return !Character.isLetterOrDigit(codePoint) +} + +private fun isRegionalIndicatorSymbol(codePoint: Int): Boolean { + return codePoint in 0x1f1e6..0x1f1ff +} + +private fun isKeyCap(codePoint: Int): Boolean { + return codePoint == 0x20e3 +} \ No newline at end of file diff --git a/twidere/src/main/kotlin/org/mariotaku/twidere/extension/ViewExtensions.kt b/twidere/src/main/kotlin/org/mariotaku/twidere/extension/ViewExtensions.kt index 936c5938c..ef13a818d 100644 --- a/twidere/src/main/kotlin/org/mariotaku/twidere/extension/ViewExtensions.kt +++ b/twidere/src/main/kotlin/org/mariotaku/twidere/extension/ViewExtensions.kt @@ -23,6 +23,8 @@ import android.graphics.Rect import android.graphics.RectF import android.support.annotation.UiThread import android.view.View +import android.widget.TextView +import org.mariotaku.ktextension.empty private val tempLocation = IntArray(2) private val tempRect = Rect() @@ -70,6 +72,14 @@ fun View.removeSystemUiVisibility(systemUiVisibility: Int) { this.systemUiVisibility = this.systemUiVisibility and systemUiVisibility.inv() } +fun View.hideIfEmpty(dependency: TextView, hideVisibility: Int = View.GONE) { + visibility = if (dependency.empty) { + hideVisibility + } else { + View.VISIBLE + } +} + private fun offsetToRoot(view: View, rect: Rect) { var parent = view.parent as? View while (parent != null) { diff --git a/twidere/src/main/kotlin/org/mariotaku/twidere/fragment/AbsUserMuteBlockDialogFragment.kt b/twidere/src/main/kotlin/org/mariotaku/twidere/fragment/AbsUserMuteBlockDialogFragment.kt index 90db62a96..579d8e80c 100644 --- a/twidere/src/main/kotlin/org/mariotaku/twidere/fragment/AbsUserMuteBlockDialogFragment.kt +++ b/twidere/src/main/kotlin/org/mariotaku/twidere/fragment/AbsUserMuteBlockDialogFragment.kt @@ -25,6 +25,7 @@ import android.os.Bundle import android.support.v7.app.AlertDialog import android.widget.CheckBox import android.widget.TextView +import org.mariotaku.ktextension.spannable import org.mariotaku.twidere.R import org.mariotaku.twidere.constant.IntentConstants.EXTRA_USER import org.mariotaku.twidere.extension.applyTheme @@ -61,7 +62,7 @@ abstract class AbsUserMuteBlockDialogFragment : BaseDialogFragment(), DialogInte MessageDialogFragment.show(childFragmentManager, title = getString(R.string.filter_everywhere), message = getString(R.string.filter_everywhere_description), tag = "filter_everywhere_help") } - confirmMessageView.text = getMessage(user) + confirmMessageView.spannable = getMessage(user) } return dialog } diff --git a/twidere/src/main/kotlin/org/mariotaku/twidere/fragment/AccountsDashboardFragment.kt b/twidere/src/main/kotlin/org/mariotaku/twidere/fragment/AccountsDashboardFragment.kt index bcf73518b..68373b3a9 100644 --- a/twidere/src/main/kotlin/org/mariotaku/twidere/fragment/AccountsDashboardFragment.kt +++ b/twidere/src/main/kotlin/org/mariotaku/twidere/fragment/AccountsDashboardFragment.kt @@ -55,10 +55,7 @@ import kotlinx.android.synthetic.main.header_drawer_account_selector.view.* import org.mariotaku.chameleon.Chameleon import org.mariotaku.kpreferences.get import org.mariotaku.kpreferences.set -import org.mariotaku.ktextension.addOnAccountsUpdatedListenerSafe -import org.mariotaku.ktextension.removeOnAccountsUpdatedListenerSafe -import org.mariotaku.ktextension.setItemAvailability -import org.mariotaku.ktextension.setMenuItemIcon +import org.mariotaku.ktextension.* import org.mariotaku.twidere.Constants.EXTRA_FEATURES_NOTICE_VERSION import org.mariotaku.twidere.R import org.mariotaku.twidere.TwidereConstants.* @@ -546,8 +543,8 @@ class AccountsDashboardFragment : BaseFragment(), LoaderCallbacks, private fun displayCurrentAccount(profileImageSnapshot: Drawable?) { if (context == null || isDetached || (activity?.isFinishing ?: true)) return val account = accountsAdapter.selectedAccount ?: return - accountProfileNameView.text = account.user.name - accountProfileScreenNameView.text = "@${account.user.screen_name}" + accountProfileNameView.spannable = account.user.name + accountProfileScreenNameView.spannable = "@${account.user.screen_name}" Glide.with(this).loadProfileImage(context, account, preferences[profileImageStyleKey], accountProfileImageView.cornerRadius, accountProfileImageView.cornerRadiusRatio, ProfileImageSize.REASONABLY_SMALL).placeholder(profileImageSnapshot).into(accountProfileImageView) diff --git a/twidere/src/main/kotlin/org/mariotaku/twidere/fragment/BaseFragment.kt b/twidere/src/main/kotlin/org/mariotaku/twidere/fragment/BaseFragment.kt index 44c7247c1..fc5ffb1ae 100644 --- a/twidere/src/main/kotlin/org/mariotaku/twidere/fragment/BaseFragment.kt +++ b/twidere/src/main/kotlin/org/mariotaku/twidere/fragment/BaseFragment.kt @@ -81,6 +81,8 @@ open class BaseFragment : Fragment(), IBaseFragment { lateinit var dns: Dns @Inject lateinit var syncPreferences: SyncPreferences + @Inject + lateinit var externalThemeManager: ExternalThemeManager protected val statusScheduleProvider: StatusScheduleProvider? get() = statusScheduleProviderFactory.newInstance(context) diff --git a/twidere/src/main/kotlin/org/mariotaku/twidere/fragment/CustomTabsFragment.kt b/twidere/src/main/kotlin/org/mariotaku/twidere/fragment/CustomTabsFragment.kt index 9eccf99a1..6ecfbb730 100644 --- a/twidere/src/main/kotlin/org/mariotaku/twidere/fragment/CustomTabsFragment.kt +++ b/twidere/src/main/kotlin/org/mariotaku/twidere/fragment/CustomTabsFragment.kt @@ -34,7 +34,6 @@ import android.support.v4.app.LoaderManager.LoaderCallbacks import android.support.v4.content.CursorLoader import android.support.v4.content.Loader import android.support.v7.app.AlertDialog -import android.text.TextUtils import android.util.SparseArray import android.view.* import android.widget.* @@ -48,6 +47,7 @@ import org.mariotaku.chameleon.Chameleon import org.mariotaku.ktextension.Bundle import org.mariotaku.ktextension.contains import org.mariotaku.ktextension.set +import org.mariotaku.ktextension.spannable import org.mariotaku.library.objectcursor.ObjectCursor import org.mariotaku.sqliteqb.library.Columns.Column import org.mariotaku.sqliteqb.library.Expression @@ -439,7 +439,7 @@ class CustomTabsFragment : BaseFragment(), LoaderCallbacks, MultiChoice view.findViewById(android.R.id.text2).visibility = View.GONE val text1 = view.findViewById(android.R.id.text1) as TextView val item = getItem(position) - text1.text = item.name + text1.spannable = item.name bindIconView(item, view) return view } @@ -484,12 +484,12 @@ class CustomTabsFragment : BaseFragment(), LoaderCallbacks, MultiChoice val iconKey = tempTab.icon if (type != null && CustomTabUtils.isTabTypeValid(type)) { val typeName = CustomTabUtils.getTabTypeName(context, type) - holder.text1.text = if (TextUtils.isEmpty(name)) typeName else name + holder.text1.spannable = name?.takeIf(String::isNotEmpty) ?: typeName holder.text1.paintFlags = holder.text1.paintFlags and Paint.STRIKE_THRU_TEXT_FLAG.inv() holder.text2.visibility = View.VISIBLE holder.text2.text = typeName } else { - holder.text1.text = name + holder.text1.spannable = name holder.text1.paintFlags = holder.text1.paintFlags or Paint.STRIKE_THRU_TEXT_FLAG holder.text2.setText(R.string.invalid_tab) } diff --git a/twidere/src/main/kotlin/org/mariotaku/twidere/fragment/StatusFragment.kt b/twidere/src/main/kotlin/org/mariotaku/twidere/fragment/StatusFragment.kt index 2308b901b..ca0e31951 100644 --- a/twidere/src/main/kotlin/org/mariotaku/twidere/fragment/StatusFragment.kt +++ b/twidere/src/main/kotlin/org/mariotaku/twidere/fragment/StatusFragment.kt @@ -62,10 +62,7 @@ import kotlinx.android.synthetic.main.header_status.view.* import kotlinx.android.synthetic.main.layout_content_fragment_common.* import org.mariotaku.abstask.library.TaskStarter import org.mariotaku.kpreferences.get -import org.mariotaku.ktextension.applyFontFamily -import org.mariotaku.ktextension.contains -import org.mariotaku.ktextension.findPositionByItemId -import org.mariotaku.ktextension.hideIfEmpty +import org.mariotaku.ktextension.* import org.mariotaku.library.objectcursor.ObjectCursor import org.mariotaku.microblog.library.MicroBlog import org.mariotaku.microblog.library.MicroBlogException @@ -424,7 +421,7 @@ class StatusFragment : BaseFragment(), LoaderCallbacks { HtmlSpanBuilder.fromHtml(context.getString(R.string.status_format_time_source, timeString, source)) @@ -882,13 +874,13 @@ class StatusFragment : BaseFragment(), LoaderCallbacks user.name + else -> getString(R.string.name_with_nickname, user.name, user.nickname) + }), TextView.BufferType.SPANNABLE) val typeIconRes = Utils.getUserTypeIconRes(user.is_verified, user.is_protected) if (typeIconRes != 0) { profileType.setImageResource(typeIconRes) @@ -443,7 +443,7 @@ class UserFragment : BaseFragment(), OnClickListener, OnLinkClickListener, profileType.visibility = View.GONE } @SuppressLint("SetTextI18n") - profileNameContainer.screenName.text = "@${user.acct}" + profileNameContainer.screenName.spannable = "@${user.acct}" val linkHighlightOption = preferences[linkHighlightOptionKey] val linkify = TwidereLinkify(this, linkHighlightOption) if (user.description_unescaped != null) { @@ -451,22 +451,22 @@ class UserFragment : BaseFragment(), OnClickListener, OnLinkClickListener, user.description_spans?.applyTo(this) linkify.applyAllLinks(this, user.account_key, false, false) } - descriptionContainer.description.text = text + descriptionContainer.description.spannable = text } else { - descriptionContainer.description.text = user.description_plain + descriptionContainer.description.spannable = user.description_plain Linkify.addLinks(descriptionContainer.description, Linkify.WEB_URLS) } - descriptionContainer.visibility = if (descriptionContainer.description.empty) View.GONE else View.VISIBLE + descriptionContainer.hideIfEmpty(descriptionContainer.description) - locationContainer.location.text = user.location + locationContainer.location.spannable = user.location locationContainer.visibility = if (locationContainer.location.empty) View.GONE else View.VISIBLE - urlContainer.url.text = user.urlPreferred?.let { + urlContainer.url.spannable = user.urlPreferred?.let { val ssb = SpannableStringBuilder(it) ssb.setSpan(TwidereURLSpan(it, highlightStyle = linkHighlightOption), 0, ssb.length, Spanned.SPAN_EXCLUSIVE_EXCLUSIVE) return@let ssb } - urlContainer.visibility = if (urlContainer.url.empty) View.GONE else View.VISIBLE + urlContainer.hideIfEmpty(urlContainer.url) if (user.created_at >= 0) { val createdAt = Utils.formatToLongTimeString(activity, user.created_at) val daysSinceCreation = (System.currentTimeMillis() - user.created_at) / 1000 / 60 / 60 / 24.toFloat() @@ -509,8 +509,10 @@ class UserFragment : BaseFragment(), OnClickListener, OnLinkClickListener, if (relationship == null) { getFriendship() } - activity.title = UserColorNameManager.decideDisplayName(user.nickname, user.name, - user.screen_name, nameFirst) + activity.title = SpannableStringBuilder.valueOf(UserColorNameManager.decideDisplayName(user.nickname, user.name, + user.screen_name, nameFirst)).also { + externalThemeManager.emoji?.applyTo(it) + } val userCreationDay = condition@ if (user.created_at >= 0) { val cal = Calendar.getInstance() diff --git a/twidere/src/main/kotlin/org/mariotaku/twidere/fragment/filter/FilteredUsersFragment.kt b/twidere/src/main/kotlin/org/mariotaku/twidere/fragment/filter/FilteredUsersFragment.kt index 36245bc72..e93c7a964 100644 --- a/twidere/src/main/kotlin/org/mariotaku/twidere/fragment/filter/FilteredUsersFragment.kt +++ b/twidere/src/main/kotlin/org/mariotaku/twidere/fragment/filter/FilteredUsersFragment.kt @@ -20,6 +20,7 @@ import android.widget.TextView import kotlinx.android.synthetic.main.fragment_content_listview.* import org.mariotaku.kpreferences.KPreferences import org.mariotaku.ktextension.setItemAvailability +import org.mariotaku.ktextension.spannable import org.mariotaku.library.objectcursor.ObjectCursor import org.mariotaku.twidere.R import org.mariotaku.twidere.TwidereConstants.* @@ -177,7 +178,7 @@ class FilteredUsersFragment : BaseFiltersFragment() { val screenName = cursor.getString(indices[Filters.Users.SCREEN_NAME]) val displayName = userColorNameManager.getDisplayName(userKey, name, screenName, nameFirst) - text1.text = displayName + text1.spannable = displayName val ssb = SpannableStringBuilder(displayName) if (cursor.getLong(indices[Filters.Users.SOURCE]) >= 0) { @@ -188,8 +189,8 @@ class FilteredUsersFragment : BaseFiltersFragment() { drawable.setColorFilter(secondaryTextColor, PorterDuff.Mode.SRC_ATOP) ssb.setSpan(EmojiSpan(drawable), start, end, Spanned.SPAN_EXCLUSIVE_EXCLUSIVE) } - text1.text = ssb - text2.text = userKey.host + text1.spannable = ssb + text2.spannable = userKey.host } override fun swapCursor(c: Cursor?): Cursor? { diff --git a/twidere/src/main/kotlin/org/mariotaku/twidere/fragment/message/MessageConversationInfoFragment.kt b/twidere/src/main/kotlin/org/mariotaku/twidere/fragment/message/MessageConversationInfoFragment.kt index c1ce749bd..628fc5c9f 100644 --- a/twidere/src/main/kotlin/org/mariotaku/twidere/fragment/message/MessageConversationInfoFragment.kt +++ b/twidere/src/main/kotlin/org/mariotaku/twidere/fragment/message/MessageConversationInfoFragment.kt @@ -57,6 +57,7 @@ import org.mariotaku.chameleon.ChameleonUtils import org.mariotaku.kpreferences.get import org.mariotaku.ktextension.mapToArray import org.mariotaku.ktextension.setItemAvailability +import org.mariotaku.ktextension.spannable import org.mariotaku.ktextension.useCursor import org.mariotaku.library.objectcursor.ObjectCursor import org.mariotaku.microblog.library.MicroBlog @@ -260,14 +261,14 @@ class MessageConversationInfoFragment : BaseFragment(), IToolBarSupportFragment, val profileImageStyle = preferences[profileImageStyleKey] requestManager.loadProfileImage(context, data, profileImageStyle).into(conversationAvatar) requestManager.loadProfileImage(context, data, profileImageStyle, size = ProfileImageSize.REASONABLY_SMALL).into(appBarIcon) - appBarTitle.text = name - conversationTitle.text = name + appBarTitle.spannable = name + conversationTitle.spannable = name if (summary != null) { appBarSubtitle.visibility = View.VISIBLE conversationSubtitle.visibility = View.VISIBLE - appBarSubtitle.text = summary - conversationSubtitle.text = summary + appBarSubtitle.spannable = summary + conversationSubtitle.spannable = summary } else { appBarSubtitle.visibility = View.GONE conversationSubtitle.visibility = View.GONE diff --git a/twidere/src/main/kotlin/org/mariotaku/twidere/fragment/message/MessagesConversationFragment.kt b/twidere/src/main/kotlin/org/mariotaku/twidere/fragment/message/MessagesConversationFragment.kt index 42c18e96f..b4ca3bd4d 100644 --- a/twidere/src/main/kotlin/org/mariotaku/twidere/fragment/message/MessagesConversationFragment.kt +++ b/twidere/src/main/kotlin/org/mariotaku/twidere/fragment/message/MessagesConversationFragment.kt @@ -493,10 +493,10 @@ class MessagesConversationFragment : AbsContentListRecyclerViewFragment. */ -package org.mariotaku.twidere.preference; +package org.mariotaku.twidere.preference -import android.content.ComponentName; -import android.content.Context; -import android.content.Intent; -import android.content.pm.ResolveInfo; -import android.util.AttributeSet; +import android.content.ComponentName +import android.content.Context +import android.content.Intent +import android.content.pm.ResolveInfo +import android.util.AttributeSet -import java.util.List; +abstract class ActivityPickerPreference(context: Context, attrs: AttributeSet? = null) : + ComponentPickerPreference(context, attrs) { -public abstract class ActivityPickerPreference extends ComponentPickerPreference { - - public ActivityPickerPreference(final Context context) { - this(context, null); + override fun getComponentName(info: ResolveInfo): ComponentName { + return ComponentName(info.activityInfo.packageName, info.activityInfo.name) } - public ActivityPickerPreference(final Context context, final AttributeSet attrs) { - super(context, attrs); - } - - @Override - protected final ComponentName getComponentName(ResolveInfo info) { - return new ComponentName(info.activityInfo.packageName, info.activityInfo.name); - } - - @Override - protected List resolve(Intent queryIntent) { - return packageManager.queryIntentActivities(queryIntent, 0); + override fun resolve(queryIntent: Intent): List { + return packageManager.queryIntentActivities(queryIntent, 0) } } diff --git a/twidere/src/main/kotlin/org/mariotaku/twidere/preference/ComponentPickerPreference.kt b/twidere/src/main/kotlin/org/mariotaku/twidere/preference/ComponentPickerPreference.kt new file mode 100644 index 000000000..3c7b479b5 --- /dev/null +++ b/twidere/src/main/kotlin/org/mariotaku/twidere/preference/ComponentPickerPreference.kt @@ -0,0 +1,74 @@ +/* + * Twidere - Twitter client for Android + * + * Copyright (C) 2012-2017 Mariotaku Lee + * + * 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. + * + * This program 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 this program. If not, see . + */ + +package org.mariotaku.twidere.preference + +import android.content.ComponentName +import android.content.Context +import android.content.Intent +import android.content.pm.PackageManager +import android.content.pm.ResolveInfo +import android.util.AttributeSet + +abstract class ComponentPickerPreference(context: Context, attrs: AttributeSet? = null) : ThemedListPreference(context, attrs) { + + protected val packageManager: PackageManager = context.packageManager + + init { + setup() + } + + override fun getSummary(): CharSequence { + if (isNoneValue(value)) return noneEntry + return entry + } + + protected abstract val intentAction: String + + protected abstract val noneEntry: String + + protected abstract fun getComponentName(info: ResolveInfo): ComponentName + + protected abstract fun resolve(queryIntent: Intent): List + + private fun setup() { + val queryIntent = Intent(intentAction) + val infoList = resolve(queryIntent) + val infoListSize = infoList.size + val entries = arrayOfNulls(infoListSize + 1) + val values = arrayOfNulls(infoListSize + 1) + entries[0] = noneEntry + values[0] = "" + for (i in 0..infoListSize - 1) { + val info = infoList[i] + entries[i + 1] = info.loadLabel(packageManager) + values[i + 1] = getComponentName(info).flattenToString() + } + setEntries(entries) + entryValues = values + } + + companion object { + + fun isNoneValue(value: String?): Boolean { + return value.isNullOrEmpty() || "none" == value + } + } + +} diff --git a/twidere/src/main/kotlin/org/mariotaku/twidere/preference/EmojiSupportPreference.kt b/twidere/src/main/kotlin/org/mariotaku/twidere/preference/EmojiSupportPreference.kt new file mode 100644 index 000000000..8d99f7c2f --- /dev/null +++ b/twidere/src/main/kotlin/org/mariotaku/twidere/preference/EmojiSupportPreference.kt @@ -0,0 +1,39 @@ +/* + * Twidere - Twitter client for Android + * + * Copyright (C) 2012-2017 Mariotaku Lee + * + * 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. + * + * This program 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 this program. If not, see . + */ + +package org.mariotaku.twidere.preference + +import android.content.Context +import android.util.AttributeSet + +import org.mariotaku.twidere.R +import org.mariotaku.twidere.constant.IntentConstants + +/** + * Created by mariotaku on 15/12/22. + */ +class EmojiSupportPreference(context: Context, attrs: AttributeSet? = null) : + ActivityPickerPreference(context, attrs) { + + override val intentAction: String + get() = IntentConstants.INTENT_ACTION_EMOJI_SUPPORT_ABOUT + + override val noneEntry: String + get() = context.getString(R.string.system_default) +} diff --git a/twidere/src/main/java/org/mariotaku/twidere/preference/StatusShortenerPreference.java b/twidere/src/main/kotlin/org/mariotaku/twidere/preference/MediaUploaderPreference.kt similarity index 51% rename from twidere/src/main/java/org/mariotaku/twidere/preference/StatusShortenerPreference.java rename to twidere/src/main/kotlin/org/mariotaku/twidere/preference/MediaUploaderPreference.kt index bfa6df468..e7efe4f14 100644 --- a/twidere/src/main/java/org/mariotaku/twidere/preference/StatusShortenerPreference.java +++ b/twidere/src/main/kotlin/org/mariotaku/twidere/preference/MediaUploaderPreference.kt @@ -17,32 +17,22 @@ * along with this program. If not, see . */ -package org.mariotaku.twidere.preference; +package org.mariotaku.twidere.preference -import android.content.Context; -import android.util.AttributeSet; +import android.content.Context +import android.util.AttributeSet -import org.mariotaku.twidere.Constants; -import org.mariotaku.twidere.R; +import org.mariotaku.twidere.R -public class StatusShortenerPreference extends ServicePickerPreference implements Constants { +import org.mariotaku.twidere.constant.IntentConstants.INTENT_ACTION_EXTENSION_UPLOAD_MEDIA - public StatusShortenerPreference(final Context context) { - this(context, null); - } +class MediaUploaderPreference(context: Context, attrs: AttributeSet? = null) : + ServicePickerPreference(context, attrs) { - public StatusShortenerPreference(final Context context, final AttributeSet attrs) { - super(context, attrs); - } + override val intentAction: String + get() = INTENT_ACTION_EXTENSION_UPLOAD_MEDIA - @Override - protected String getIntentAction() { - return INTENT_ACTION_EXTENSION_SHORTEN_STATUS; - } - - @Override - protected String getNoneEntry() { - return getContext().getString(R.string.status_shortener_default); - } + override val noneEntry: String + get() = context.getString(R.string.media_uploader_default) } diff --git a/twidere/src/main/java/org/mariotaku/twidere/preference/ServicePickerPreference.java b/twidere/src/main/kotlin/org/mariotaku/twidere/preference/ServicePickerPreference.kt similarity index 52% rename from twidere/src/main/java/org/mariotaku/twidere/preference/ServicePickerPreference.java rename to twidere/src/main/kotlin/org/mariotaku/twidere/preference/ServicePickerPreference.kt index 14c64d018..cee48b704 100644 --- a/twidere/src/main/java/org/mariotaku/twidere/preference/ServicePickerPreference.java +++ b/twidere/src/main/kotlin/org/mariotaku/twidere/preference/ServicePickerPreference.kt @@ -17,33 +17,22 @@ * along with this program. If not, see . */ -package org.mariotaku.twidere.preference; +package org.mariotaku.twidere.preference -import android.content.ComponentName; -import android.content.Context; -import android.content.Intent; -import android.content.pm.ResolveInfo; -import android.util.AttributeSet; +import android.content.ComponentName +import android.content.Context +import android.content.Intent +import android.content.pm.ResolveInfo +import android.util.AttributeSet -import java.util.List; +abstract class ServicePickerPreference(context: Context, attrs: AttributeSet?) : + ComponentPickerPreference(context, attrs) { -public abstract class ServicePickerPreference extends ComponentPickerPreference { - - public ServicePickerPreference(final Context context) { - this(context, null); + override fun getComponentName(info: ResolveInfo): ComponentName { + return ComponentName(info.serviceInfo.packageName, info.serviceInfo.name) } - public ServicePickerPreference(final Context context, final AttributeSet attrs) { - super(context, attrs); - } - - @Override - protected final ComponentName getComponentName(ResolveInfo info) { - return new ComponentName(info.serviceInfo.packageName, info.serviceInfo.name); - } - - @Override - protected List resolve(Intent queryIntent) { - return packageManager.queryIntentServices(queryIntent, 0); + override fun resolve(queryIntent: Intent): List { + return packageManager.queryIntentServices(queryIntent, 0) } } diff --git a/twidere/src/main/java/org/mariotaku/twidere/preference/MediaUploaderPreference.java b/twidere/src/main/kotlin/org/mariotaku/twidere/preference/StatusShortenerPreference.kt similarity index 50% rename from twidere/src/main/java/org/mariotaku/twidere/preference/MediaUploaderPreference.java rename to twidere/src/main/kotlin/org/mariotaku/twidere/preference/StatusShortenerPreference.kt index c20b2babd..8d1bcbb14 100644 --- a/twidere/src/main/java/org/mariotaku/twidere/preference/MediaUploaderPreference.java +++ b/twidere/src/main/kotlin/org/mariotaku/twidere/preference/StatusShortenerPreference.kt @@ -17,33 +17,21 @@ * along with this program. If not, see . */ -package org.mariotaku.twidere.preference; +package org.mariotaku.twidere.preference -import android.content.Context; -import android.util.AttributeSet; +import android.content.Context +import android.util.AttributeSet +import org.mariotaku.twidere.Constants +import org.mariotaku.twidere.R +import org.mariotaku.twidere.constant.IntentConstants.INTENT_ACTION_EXTENSION_SHORTEN_STATUS -import org.mariotaku.twidere.R; +class StatusShortenerPreference(context: Context, attrs: AttributeSet?) : + ServicePickerPreference(context, attrs), Constants { -import static org.mariotaku.twidere.constant.IntentConstants.INTENT_ACTION_EXTENSION_UPLOAD_MEDIA; + override val intentAction: String + get() = INTENT_ACTION_EXTENSION_SHORTEN_STATUS -public class MediaUploaderPreference extends ServicePickerPreference { - - public MediaUploaderPreference(final Context context) { - super(context); - } - - public MediaUploaderPreference(final Context context, final AttributeSet attrs) { - super(context, attrs); - } - - @Override - protected String getIntentAction() { - return INTENT_ACTION_EXTENSION_UPLOAD_MEDIA; - } - - @Override - protected String getNoneEntry() { - return getContext().getString(R.string.media_uploader_default); - } + override val noneEntry: String + get() = context.getString(R.string.status_shortener_default) } diff --git a/twidere/src/main/kotlin/org/mariotaku/twidere/preference/ThemedListPreference.kt b/twidere/src/main/kotlin/org/mariotaku/twidere/preference/ThemedListPreference.kt new file mode 100644 index 000000000..b9fd8af7f --- /dev/null +++ b/twidere/src/main/kotlin/org/mariotaku/twidere/preference/ThemedListPreference.kt @@ -0,0 +1,22 @@ +package org.mariotaku.twidere.preference + +import android.content.Context +import android.support.v7.preference.ListPreference +import android.support.v7.preference.PreferenceFragmentCompat +import android.util.AttributeSet + +import org.mariotaku.twidere.fragment.ThemedListPreferenceDialogFragmentCompat +import org.mariotaku.twidere.preference.iface.IDialogPreference + +/** + * Created by mariotaku on 16/3/15. + */ +open class ThemedListPreference(context: Context, attrs: AttributeSet? = null) : + ListPreference(context, attrs), IDialogPreference { + + override fun displayDialog(fragment: PreferenceFragmentCompat) { + val df = ThemedListPreferenceDialogFragmentCompat.newInstance(key) + df.setTargetFragment(fragment, 0) + df.show(fragment.fragmentManager, key) + } +} diff --git a/twidere/src/main/kotlin/org/mariotaku/twidere/task/twitter/UpdateStatusTask.kt b/twidere/src/main/kotlin/org/mariotaku/twidere/task/twitter/UpdateStatusTask.kt index 3a98923a8..2ed25fff8 100644 --- a/twidere/src/main/kotlin/org/mariotaku/twidere/task/twitter/UpdateStatusTask.kt +++ b/twidere/src/main/kotlin/org/mariotaku/twidere/task/twitter/UpdateStatusTask.kt @@ -51,7 +51,7 @@ import org.mariotaku.twidere.model.account.AccountExtras import org.mariotaku.twidere.model.analyzer.UpdateStatus import org.mariotaku.twidere.model.schedule.ScheduleInfo import org.mariotaku.twidere.model.util.ParcelableLocationUtils -import org.mariotaku.twidere.preference.ServicePickerPreference +import org.mariotaku.twidere.preference.ComponentPickerPreference import org.mariotaku.twidere.provider.TwidereDataStore.Drafts import org.mariotaku.twidere.task.BaseAbstractTask import org.mariotaku.twidere.util.* @@ -496,7 +496,7 @@ class UpdateStatusTask( @Throws(UploaderNotFoundException::class, UploadException::class, ShortenerNotFoundException::class, ShortenException::class) private fun getStatusShortener(app: TwidereApplication): StatusShortenerInterface? { val shortenerComponent = preferences.getString(KEY_STATUS_SHORTENER, null) - if (ServicePickerPreference.isNoneValue(shortenerComponent)) return null + if (ComponentPickerPreference.isNoneValue(shortenerComponent)) return null val shortener = StatusShortenerInterface.getInstance(app, shortenerComponent) ?: throw ShortenerNotFoundException() try { @@ -518,7 +518,7 @@ class UpdateStatusTask( @Throws(UploaderNotFoundException::class, UploadException::class) private fun getMediaUploader(app: TwidereApplication): MediaUploaderInterface? { val uploaderComponent = preferences.getString(KEY_MEDIA_UPLOADER, null) - if (ServicePickerPreference.isNoneValue(uploaderComponent)) return null + if (ComponentPickerPreference.isNoneValue(uploaderComponent)) return null val uploader = MediaUploaderInterface.getInstance(app, uploaderComponent) ?: throw UploaderNotFoundException(context.getString(R.string.error_message_media_uploader_not_found)) try { diff --git a/twidere/src/main/kotlin/org/mariotaku/twidere/text/util/EmojiEditableFactory.kt b/twidere/src/main/kotlin/org/mariotaku/twidere/text/util/EmojiEditableFactory.kt index 0a4ef1cd0..898b8294d 100644 --- a/twidere/src/main/kotlin/org/mariotaku/twidere/text/util/EmojiEditableFactory.kt +++ b/twidere/src/main/kotlin/org/mariotaku/twidere/text/util/EmojiEditableFactory.kt @@ -23,12 +23,10 @@ import android.text.Editable import android.text.Spanned import android.text.TextWatcher import android.widget.TextView +import org.mariotaku.twidere.extension.applyTo import org.mariotaku.twidere.text.SafeSpannableStringBuilder - -import org.mariotaku.twidere.util.EmojiSupportUtils import org.mariotaku.twidere.util.ExternalThemeManager import org.mariotaku.twidere.util.dagger.GeneralComponent - import javax.inject.Inject /** @@ -45,7 +43,8 @@ class EmojiEditableFactory(textView: TextView) : Editable.Factory() { override fun newEditable(source: CharSequence): Editable { val editable = SafeSpannableStringBuilder(source) - EmojiSupportUtils.applyEmoji(externalThemeManager, editable) + val emoji = externalThemeManager.emoji + emoji?.applyTo(editable) editable.setSpan(object : TextWatcher { override fun beforeTextChanged(s: CharSequence, start: Int, count: Int, after: Int) { @@ -53,7 +52,7 @@ class EmojiEditableFactory(textView: TextView) : Editable.Factory() { override fun onTextChanged(s: CharSequence, start: Int, before: Int, count: Int) { if (count <= 0) return - EmojiSupportUtils.applyEmoji(externalThemeManager, editable, start, count) + emoji?.applyTo(editable) } override fun afterTextChanged(s: Editable) { diff --git a/twidere/src/main/kotlin/org/mariotaku/twidere/text/util/EmojiSpannableFactory.kt b/twidere/src/main/kotlin/org/mariotaku/twidere/text/util/EmojiSpannableFactory.kt index cf887b6f6..37bd53b80 100644 --- a/twidere/src/main/kotlin/org/mariotaku/twidere/text/util/EmojiSpannableFactory.kt +++ b/twidere/src/main/kotlin/org/mariotaku/twidere/text/util/EmojiSpannableFactory.kt @@ -21,12 +21,10 @@ package org.mariotaku.twidere.text.util import android.text.Spannable import android.widget.TextView +import org.mariotaku.twidere.extension.applyTo import org.mariotaku.twidere.text.SafeSpannableString - -import org.mariotaku.twidere.util.EmojiSupportUtils import org.mariotaku.twidere.util.ExternalThemeManager import org.mariotaku.twidere.util.dagger.GeneralComponent - import javax.inject.Inject /** @@ -43,7 +41,7 @@ class EmojiSpannableFactory(textView: TextView) : Spannable.Factory() { override fun newSpannable(source: CharSequence): Spannable { val spannable = SafeSpannableString(source) - EmojiSupportUtils.applyEmoji(externalThemeManager, spannable) + externalThemeManager.emoji?.applyTo(spannable) return spannable } } diff --git a/twidere/src/main/kotlin/org/mariotaku/twidere/util/EmojiSupportUtils.kt b/twidere/src/main/kotlin/org/mariotaku/twidere/util/EmojiSupportUtils.kt deleted file mode 100644 index 6099b5083..000000000 --- a/twidere/src/main/kotlin/org/mariotaku/twidere/util/EmojiSupportUtils.kt +++ /dev/null @@ -1,159 +0,0 @@ -/* - * Twidere - Twitter client for Android - * - * Copyright (C) 2012-2015 Mariotaku Lee - * - * 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. - * - * This program 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 this program. If not, see . - */ - -package org.mariotaku.twidere.util - -import android.graphics.drawable.Drawable -import android.text.Spannable -import android.text.Spanned -import android.widget.TextView -import org.mariotaku.commons.text.CodePointArray -import org.mariotaku.commons.text.get - -import org.mariotaku.twidere.text.style.EmojiSpan -import org.mariotaku.twidere.text.util.EmojiEditableFactory -import org.mariotaku.twidere.text.util.EmojiSpannableFactory - -/** - * Created by mariotaku on 15/12/20. - */ -object EmojiSupportUtils { - - fun initForTextView(textView: TextView) { - if (textView.isInEditMode) return - textView.setSpannableFactory(EmojiSpannableFactory(textView)) - textView.setEditableFactory(EmojiEditableFactory(textView)) - } - - fun applyEmoji(manager: ExternalThemeManager, text: Spannable, - textStart: Int = 0, textLength: Int = text.length) { - val emoji = manager.emoji - if (emoji == null || !emoji.isSupported) return - val array = CodePointArray(text) - var arrayIdx = array.length() - 1 - while (arrayIdx >= 0) { - val codePoint = array[arrayIdx] - if (isEmoji(codePoint)) { - val arrayEnd = arrayIdx + 1 - var arrayIdxOffset = 0 - val textIdx = array.indexOfText(codePoint, arrayIdx) - var textIdxOffset = 0 - var skippedIndex = 0 - if (textIdx == -1 || textIdx < textStart) { - arrayIdx-- - continue - } - val textEnd = textIdx + Character.charCount(codePoint) - if (arrayIdx > 0) { - val prevCodePoint = array[arrayIdx - 1] - when { - isRegionalIndicatorSymbol(codePoint) -> if (isRegionalIndicatorSymbol(prevCodePoint)) { - arrayIdxOffset = -1 - textIdxOffset = -Character.charCount(prevCodePoint) - skippedIndex = -1 - } - isModifier(codePoint) -> if (isEmoji(prevCodePoint)) { - arrayIdxOffset = -1 - textIdxOffset = -Character.charCount(prevCodePoint) - skippedIndex = -1 - } - isKeyCap(codePoint) -> if (isPhoneNumberSymbol(prevCodePoint)) { - arrayIdxOffset = -1 - textIdxOffset = -Character.charCount(prevCodePoint) - skippedIndex = -1 - } - isZeroWidthJoin(prevCodePoint) -> { - var notValidControlCount = 0 - var charCount = 0 - for (i in arrayIdx - 1 downTo 0) { - val cp = array.get(i) - charCount += Character.charCount(cp) - if (isZeroWidthJoin(cp) || isVariationSelector(cp)) { - // Ignore - notValidControlCount = 0 - continue - } - notValidControlCount++ - if (notValidControlCount > 1 || i == 0) { - arrayIdxOffset = i - arrayIdx + 1 - textIdxOffset = -charCount + Character.charCount(cp) - skippedIndex = i - arrayIdx + 1 - break - } - } - } - } - } - if (textEnd > textStart + textLength) { - arrayIdx-- - continue - } - var spans = text.getSpans(textIdx + textIdxOffset, textEnd, EmojiSpan::class.java) - if (spans.isEmpty()) { - var drawable: Drawable? = emoji.getEmojiDrawableFor(*array[arrayIdx + arrayIdxOffset..arrayEnd]) - if (drawable == null) { - // Not emoji combination, just use fallback - textIdxOffset = 0 - arrayIdxOffset = 0 - skippedIndex = 0 - spans = text.getSpans(textIdx + textIdxOffset, textEnd, EmojiSpan::class.java) - if (spans.isEmpty()) { - drawable = emoji.getEmojiDrawableFor(*array[arrayIdx + arrayIdxOffset..arrayEnd]) - } - } - if (drawable != null) { - text.setSpan(EmojiSpan(drawable), textIdx + textIdxOffset, textEnd, - Spanned.SPAN_EXCLUSIVE_EXCLUSIVE) - } - } - arrayIdx += skippedIndex - } - arrayIdx-- - } - } - - private fun isVariationSelector(codePoint: Int): Boolean { - return codePoint == 0xfe0f - } - - private fun isZeroWidthJoin(codePoint: Int): Boolean { - return codePoint == 0x200d - } - - private fun isPhoneNumberSymbol(codePoint: Int): Boolean { - return codePoint == 0x0023 || codePoint == 0x002a || codePoint in 0x0030..0x0039 - } - - private fun isModifier(codePoint: Int): Boolean { - return codePoint in 0x1f3fb..0x1f3ff - } - - private fun isEmoji(codePoint: Int): Boolean { - return !Character.isLetterOrDigit(codePoint) - } - - private fun isRegionalIndicatorSymbol(codePoint: Int): Boolean { - return codePoint in 0x1f1e6..0x1f1ff - } - - private fun isKeyCap(codePoint: Int): Boolean { - return codePoint == 0x20e3 - } - -} diff --git a/twidere/src/main/kotlin/org/mariotaku/twidere/util/ExternalThemeManager.kt b/twidere/src/main/kotlin/org/mariotaku/twidere/util/ExternalThemeManager.kt index 5fba32ecc..1bd6e859e 100644 --- a/twidere/src/main/kotlin/org/mariotaku/twidere/util/ExternalThemeManager.kt +++ b/twidere/src/main/kotlin/org/mariotaku/twidere/util/ExternalThemeManager.kt @@ -28,7 +28,6 @@ import android.graphics.drawable.Drawable import android.support.v4.content.res.ResourcesCompat import android.util.LruCache import org.mariotaku.twidere.constant.SharedPreferenceConstants.KEY_EMOJI_SUPPORT -import java.util.* /** * Created by mariotaku on 15/12/20. @@ -105,7 +104,11 @@ class ExternalThemeManager(private val context: Context, private val preferences if (i != 0) { sb.append("_") } - sb.append(String.format(Locale.US, "%04x", codePoints[i])) + val hex = Integer.toHexString(codePoints[i]) + for (j in 0 until 4 - hex.length) { + sb.append("0") + } + sb.append(hex) } val identifier = resources.getIdentifier(sb.toString(), if (useMipmap) "mipmap" else "drawable", packageName) diff --git a/twidere/src/main/kotlin/org/mariotaku/twidere/util/dagger/GeneralComponent.kt b/twidere/src/main/kotlin/org/mariotaku/twidere/util/dagger/GeneralComponent.kt index 53a19e073..25613740f 100644 --- a/twidere/src/main/kotlin/org/mariotaku/twidere/util/dagger/GeneralComponent.kt +++ b/twidere/src/main/kotlin/org/mariotaku/twidere/util/dagger/GeneralComponent.kt @@ -151,8 +151,8 @@ interface GeneralComponent { fun inject(service: BaseService) companion object { - private var instance: GeneralComponent? = null + private var instance: GeneralComponent? = null fun get(context: Context): GeneralComponent { return instance ?: run { val helper = DaggerGeneralComponent.builder().applicationModule(ApplicationModule.get(context)).build() @@ -160,5 +160,6 @@ interface GeneralComponent { return@run helper } } + } } diff --git a/twidere/src/main/kotlin/org/mariotaku/twidere/view/ComposeEditText.kt b/twidere/src/main/kotlin/org/mariotaku/twidere/view/ComposeEditText.kt index 399b9fb38..04fbb1a17 100644 --- a/twidere/src/main/kotlin/org/mariotaku/twidere/view/ComposeEditText.kt +++ b/twidere/src/main/kotlin/org/mariotaku/twidere/view/ComposeEditText.kt @@ -36,8 +36,8 @@ import com.bumptech.glide.Glide import org.mariotaku.chameleon.view.ChameleonMultiAutoCompleteTextView import org.mariotaku.ktextension.contains import org.mariotaku.twidere.adapter.ComposeAutoCompleteAdapter +import org.mariotaku.twidere.extension.setupEmojiFactory import org.mariotaku.twidere.model.UserKey -import org.mariotaku.twidere.util.EmojiSupportUtils import org.mariotaku.twidere.util.widget.StatusTextTokenizer @@ -55,7 +55,7 @@ class ComposeEditText( } init { - EmojiSupportUtils.initForTextView(this) + setupEmojiFactory() setTokenizer(StatusTextTokenizer()) onItemClickListener = AdapterView.OnItemClickListener { parent, view, position, id -> removeIMESuggestions() diff --git a/twidere/src/main/kotlin/org/mariotaku/twidere/view/FixedEditText.kt b/twidere/src/main/kotlin/org/mariotaku/twidere/view/FixedEditText.kt index d4a1a73a8..96ab3eb0c 100644 --- a/twidere/src/main/kotlin/org/mariotaku/twidere/view/FixedEditText.kt +++ b/twidere/src/main/kotlin/org/mariotaku/twidere/view/FixedEditText.kt @@ -3,8 +3,7 @@ package org.mariotaku.twidere.view import android.content.Context import android.util.AttributeSet import org.mariotaku.chameleon.view.ChameleonEditText -import org.mariotaku.twidere.text.util.SafeEditableFactory -import org.mariotaku.twidere.text.util.SafeSpannableFactory +import org.mariotaku.twidere.extension.setupEmojiFactory /** * Created by mariotaku on 2017/2/3. @@ -13,8 +12,7 @@ import org.mariotaku.twidere.text.util.SafeSpannableFactory class FixedEditText(context: Context, attrs: AttributeSet? = null) : ChameleonEditText(context, attrs) { init { - setSpannableFactory(SafeSpannableFactory) - setEditableFactory(SafeEditableFactory) + setupEmojiFactory() } override fun onTextContextMenuItem(id: Int): Boolean { diff --git a/twidere/src/main/kotlin/org/mariotaku/twidere/view/FixedTextView.kt b/twidere/src/main/kotlin/org/mariotaku/twidere/view/FixedTextView.kt index a18a43f40..a0d4f62e0 100644 --- a/twidere/src/main/kotlin/org/mariotaku/twidere/view/FixedTextView.kt +++ b/twidere/src/main/kotlin/org/mariotaku/twidere/view/FixedTextView.kt @@ -3,8 +3,7 @@ package org.mariotaku.twidere.view import android.content.Context import android.util.AttributeSet import org.mariotaku.chameleon.view.ChameleonTextView -import org.mariotaku.twidere.text.util.SafeEditableFactory -import org.mariotaku.twidere.text.util.SafeSpannableFactory +import org.mariotaku.twidere.extension.setupEmojiFactory /** * Created by mariotaku on 2017/2/3. @@ -13,8 +12,7 @@ import org.mariotaku.twidere.text.util.SafeSpannableFactory open class FixedTextView(context: Context, attrs: AttributeSet? = null) : ChameleonTextView(context, attrs) { init { - setSpannableFactory(SafeSpannableFactory) - setEditableFactory(SafeEditableFactory) + setupEmojiFactory() } override fun onTextContextMenuItem(id: Int): Boolean { @@ -25,4 +23,5 @@ open class FixedTextView(context: Context, attrs: AttributeSet? = null) : Chamel return true } } + } diff --git a/twidere/src/main/kotlin/org/mariotaku/twidere/view/NameView.kt b/twidere/src/main/kotlin/org/mariotaku/twidere/view/NameView.kt index 6ac2d92cc..358cf42c5 100644 --- a/twidere/src/main/kotlin/org/mariotaku/twidere/view/NameView.kt +++ b/twidere/src/main/kotlin/org/mariotaku/twidere/view/NameView.kt @@ -31,7 +31,6 @@ import android.text.style.StyleSpan import android.util.AttributeSet import android.util.TypedValue import org.mariotaku.twidere.R -import org.mariotaku.twidere.util.EmojiSupportUtils /** * Created by mariotaku on 15/5/28. @@ -60,7 +59,6 @@ class NameView(context: Context, attrs: AttributeSet? = null) : FixedTextView(co private var secondaryTextSize: AbsoluteSizeSpan? = null init { - EmojiSupportUtils.initForTextView(this) ellipsize = TextUtils.TruncateAt.END val a = context.obtainStyledAttributes(attrs, R.styleable.NameView, 0, 0) setPrimaryTextColor(a.getColor(R.styleable.NameView_nv_primaryTextColor, 0)) @@ -123,7 +121,7 @@ class NameView(context: Context, attrs: AttributeSet? = null) : FixedTextView(co sb.setSpan(secondaryTextStyle, start, end, Spanned.SPAN_EXCLUSIVE_EXCLUSIVE) sb.setSpan(secondaryTextSize, start, end, Spanned.SPAN_EXCLUSIVE_EXCLUSIVE) } - text = sb + setText(sb, BufferType.SPANNABLE) } fun setPrimaryTextSize(textSize: Float) { diff --git a/twidere/src/main/kotlin/org/mariotaku/twidere/view/TimelineContentTextView.kt b/twidere/src/main/kotlin/org/mariotaku/twidere/view/TimelineContentTextView.kt index e034deb68..7d3b0060d 100644 --- a/twidere/src/main/kotlin/org/mariotaku/twidere/view/TimelineContentTextView.kt +++ b/twidere/src/main/kotlin/org/mariotaku/twidere/view/TimelineContentTextView.kt @@ -28,7 +28,7 @@ import android.view.KeyEvent import android.view.MotionEvent import android.widget.TextView import org.mariotaku.chameleon.view.ChameleonTextView -import org.mariotaku.twidere.util.EmojiSupportUtils +import org.mariotaku.twidere.extension.setupEmojiFactory /** * Returns true when not clicking links @@ -41,7 +41,7 @@ class TimelineContentTextView @JvmOverloads constructor( ) : ChameleonTextView(context, attrs, defStyle) { init { - EmojiSupportUtils.initForTextView(this) + setupEmojiFactory() } override fun dispatchTouchEvent(event: MotionEvent): Boolean { diff --git a/twidere/src/main/kotlin/org/mariotaku/twidere/view/controller/twitter/card/CardPollViewController.kt b/twidere/src/main/kotlin/org/mariotaku/twidere/view/controller/twitter/card/CardPollViewController.kt index a627ca29a..1aeb8732c 100644 --- a/twidere/src/main/kotlin/org/mariotaku/twidere/view/controller/twitter/card/CardPollViewController.kt +++ b/twidere/src/main/kotlin/org/mariotaku/twidere/view/controller/twitter/card/CardPollViewController.kt @@ -34,6 +34,7 @@ import nl.komponents.kovenant.task import nl.komponents.kovenant.ui.successUi import org.mariotaku.abstask.library.AbstractTask import org.mariotaku.abstask.library.TaskStarter +import org.mariotaku.ktextension.spannable import org.mariotaku.ktextension.toLongOr import org.mariotaku.microblog.library.MicroBlogException import org.mariotaku.microblog.library.twitter.TwitterCaps @@ -189,7 +190,7 @@ class CardPollViewController : ContainerView.ViewController() { val value = card.getAsInteger("choice${choiceIndex}_count", 0) if (label == null) throw NullPointerException() val choicePercent = if (votesSum == 0) 0f else value / votesSum.toFloat() - choiceLabelView.text = label + choiceLabelView.spannable = label choicePercentView.text = String.format(Locale.US, "%d%%", Math.round(choicePercent * 100)) pollItem.setOnClickListener(clickListener) @@ -214,7 +215,7 @@ class CardPollViewController : ContainerView.ViewController() { val nVotes = context.resources.getQuantityString(R.plurals.N_votes, votesSum, votesSum) val timeLeft = DateUtils.getRelativeTimeSpanString(context, endDatetimeUtc.time, true) - view.pollSummary.text = context.getString(R.string.poll_summary_format, nVotes, timeLeft) + view.pollSummary.spannable = context.getString(R.string.poll_summary_format, nVotes, timeLeft) } private class PercentDrawable internal constructor( diff --git a/twidere/src/main/kotlin/org/mariotaku/twidere/view/holder/AccountViewHolder.kt b/twidere/src/main/kotlin/org/mariotaku/twidere/view/holder/AccountViewHolder.kt index 71732a5d9..274039732 100644 --- a/twidere/src/main/kotlin/org/mariotaku/twidere/view/holder/AccountViewHolder.kt +++ b/twidere/src/main/kotlin/org/mariotaku/twidere/view/holder/AccountViewHolder.kt @@ -19,13 +19,12 @@ package org.mariotaku.twidere.view.holder -import android.annotation.SuppressLint import android.support.v7.widget.RecyclerView import android.view.View import android.widget.CompoundButton import android.widget.ImageView import android.widget.TextView - +import org.mariotaku.ktextension.spannable import org.mariotaku.twidere.R import org.mariotaku.twidere.adapter.AccountDetailsAdapter import org.mariotaku.twidere.extension.loadProfileImage @@ -60,10 +59,9 @@ class AccountViewHolder( dragHandle.visibility = if (enabled) View.VISIBLE else View.GONE } - @SuppressLint("SetTextI18n") fun display(details: AccountDetails) { - name.text = details.user.name - screenName.text = "@${details.user.screen_name}" + name.spannable = details.user.name + screenName.spannable = "@${details.user.screen_name}" setAccountColor(details.color) profileImage.visibility = View.VISIBLE adapter.requestManager.loadProfileImage(adapter.context, details, adapter.profileImageStyle, diff --git a/twidere/src/main/kotlin/org/mariotaku/twidere/view/holder/DraftViewHolder.kt b/twidere/src/main/kotlin/org/mariotaku/twidere/view/holder/DraftViewHolder.kt index cbcc8a6c0..a524ee945 100644 --- a/twidere/src/main/kotlin/org/mariotaku/twidere/view/holder/DraftViewHolder.kt +++ b/twidere/src/main/kotlin/org/mariotaku/twidere/view/holder/DraftViewHolder.kt @@ -25,6 +25,7 @@ import android.view.View import com.bumptech.glide.RequestManager import kotlinx.android.synthetic.main.list_item_draft.view.* import org.mariotaku.ktextension.mapToArray +import org.mariotaku.ktextension.spannable import org.mariotaku.twidere.R import org.mariotaku.twidere.extension.model.getActionName import org.mariotaku.twidere.model.Draft @@ -76,11 +77,11 @@ class DraftViewHolder(itemView: View) : RecyclerView.ViewHolder(itemView) { contentView.drawEnd() } if (summaryText != null) { - textView.text = summaryText + textView.spannable = summaryText } else if (draft.text.isNullOrEmpty()) { textView.setText(R.string.empty_content) } else { - textView.text = draft.text + textView.spannable = draft.text } if (draft.timestamp > 0) { diff --git a/twidere/src/main/kotlin/org/mariotaku/twidere/view/holder/GroupViewHolder.kt b/twidere/src/main/kotlin/org/mariotaku/twidere/view/holder/GroupViewHolder.kt index fc742f4bd..520b69134 100644 --- a/twidere/src/main/kotlin/org/mariotaku/twidere/view/holder/GroupViewHolder.kt +++ b/twidere/src/main/kotlin/org/mariotaku/twidere/view/holder/GroupViewHolder.kt @@ -20,9 +20,10 @@ package org.mariotaku.twidere.view.holder import android.support.v7.widget.RecyclerView.ViewHolder -import android.text.TextUtils import android.view.View import kotlinx.android.synthetic.main.card_item_group_compact.view.* +import org.mariotaku.ktextension.hideIfEmpty +import org.mariotaku.ktextension.spannable import org.mariotaku.ktextension.toLocalizedString import org.mariotaku.twidere.R import org.mariotaku.twidere.adapter.iface.IGroupsAdapter @@ -63,7 +64,7 @@ class GroupViewHolder(private val adapter: IGroupsAdapter<*>, itemView: View) : externalIndicator.visibility = View.GONE } else { externalIndicator.visibility = View.VISIBLE - externalIndicator.text = context.getString(R.string.external_group_host_format, + externalIndicator.spannable = context.getString(R.string.external_group_host_format, groupHost) } if (adapter.profileImageEnabled) { @@ -74,8 +75,8 @@ class GroupViewHolder(private val adapter: IGroupsAdapter<*>, itemView: View) : } else { profileImageView.visibility = View.GONE } - descriptionView.visibility = if (TextUtils.isEmpty(group.description)) View.GONE else View.VISIBLE - descriptionView.text = formatter.unicodeWrap(group.description) + descriptionView.spannable = formatter.unicodeWrap(group.description) + descriptionView.hideIfEmpty() membersCountView.text = group.member_count.toLocalizedString() adminsCountView.text = group.admin_count.toLocalizedString() } diff --git a/twidere/src/main/kotlin/org/mariotaku/twidere/view/holder/MediaStatusViewHolder.kt b/twidere/src/main/kotlin/org/mariotaku/twidere/view/holder/MediaStatusViewHolder.kt index 5b11c1a46..aecb6b700 100644 --- a/twidere/src/main/kotlin/org/mariotaku/twidere/view/holder/MediaStatusViewHolder.kt +++ b/twidere/src/main/kotlin/org/mariotaku/twidere/view/holder/MediaStatusViewHolder.kt @@ -24,6 +24,7 @@ import android.view.View import android.widget.ImageView import com.commonsware.cwac.layouts.AspectLockedFrameLayout import kotlinx.android.synthetic.main.adapter_item_media_status.view.* +import org.mariotaku.ktextension.spannable import org.mariotaku.twidere.R import org.mariotaku.twidere.adapter.iface.IStatusesAdapter import org.mariotaku.twidere.extension.loadProfileImage @@ -62,9 +63,9 @@ class MediaStatusViewHolder(private val adapter: IStatusesAdapter<*>, itemView: val displayEnd = status.extras?.display_text_range?.getOrNull(1) ?: -1 if (displayEnd >= 0) { - mediaTextView.text = status.text_unescaped.subSequence(0, displayEnd) + mediaTextView.spannable = status.text_unescaped.subSequence(0, displayEnd) } else { - mediaTextView.text = status.text_unescaped + mediaTextView.spannable = status.text_unescaped } adapter.requestManager.loadProfileImage(context, status, adapter.profileImageStyle, profileImageView.cornerRadius, diff --git a/twidere/src/main/kotlin/org/mariotaku/twidere/view/holder/SimpleUserListViewHolder.kt b/twidere/src/main/kotlin/org/mariotaku/twidere/view/holder/SimpleUserListViewHolder.kt index 7086ab481..a13e467c3 100644 --- a/twidere/src/main/kotlin/org/mariotaku/twidere/view/holder/SimpleUserListViewHolder.kt +++ b/twidere/src/main/kotlin/org/mariotaku/twidere/view/holder/SimpleUserListViewHolder.kt @@ -4,6 +4,7 @@ import android.support.v7.widget.RecyclerView import android.view.View import android.widget.TextView import kotlinx.android.synthetic.main.list_item_simple_user_list.view.* +import org.mariotaku.ktextension.spannable import org.mariotaku.twidere.R import org.mariotaku.twidere.adapter.iface.IUserListsAdapter import org.mariotaku.twidere.extension.loadProfileImage @@ -28,8 +29,8 @@ class SimpleUserListViewHolder( } fun display(userList: ParcelableUserList) { - nameView.text = userList.name - createdByView.text = createdByView.context.getString(R.string.created_by, + nameView.spannable = userList.name + createdByView.spannable = createdByView.context.getString(R.string.created_by, adapter.userColorNameManager.getDisplayName(userList, false)) if (adapter.profileImageEnabled) { profileImageView.visibility = View.VISIBLE diff --git a/twidere/src/main/kotlin/org/mariotaku/twidere/view/holder/SimpleUserViewHolder.kt b/twidere/src/main/kotlin/org/mariotaku/twidere/view/holder/SimpleUserViewHolder.kt index 558a28f40..3e80fe7da 100644 --- a/twidere/src/main/kotlin/org/mariotaku/twidere/view/holder/SimpleUserViewHolder.kt +++ b/twidere/src/main/kotlin/org/mariotaku/twidere/view/holder/SimpleUserViewHolder.kt @@ -5,6 +5,7 @@ import android.view.View import android.widget.CheckBox import android.widget.TextView import kotlinx.android.synthetic.main.list_item_simple_user.view.* +import org.mariotaku.ktextension.spannable import org.mariotaku.twidere.R import org.mariotaku.twidere.adapter.iface.IContentAdapter import org.mariotaku.twidere.extension.loadProfileImage @@ -30,8 +31,8 @@ open class SimpleUserViewHolder( } open fun displayUser(user: ParcelableUser) { - nameView.text = user.name - secondaryNameView.text = "@${user.screen_name}" + nameView.spannable = user.name + secondaryNameView.spannable = "@${user.screen_name}" if (adapter.profileImageEnabled) { val context = itemView.context adapter.requestManager.loadProfileImage(context, user, adapter.profileImageStyle, diff --git a/twidere/src/main/kotlin/org/mariotaku/twidere/view/holder/StatusViewHolder.kt b/twidere/src/main/kotlin/org/mariotaku/twidere/view/holder/StatusViewHolder.kt index 007f5d393..7416a2348 100644 --- a/twidere/src/main/kotlin/org/mariotaku/twidere/view/holder/StatusViewHolder.kt +++ b/twidere/src/main/kotlin/org/mariotaku/twidere/view/holder/StatusViewHolder.kt @@ -17,10 +17,7 @@ import android.widget.ImageView import android.widget.TextView import com.bumptech.glide.RequestManager import kotlinx.android.synthetic.main.list_item_status.view.* -import org.mariotaku.ktextension.applyFontFamily -import org.mariotaku.ktextension.empty -import org.mariotaku.ktextension.hideIfEmpty -import org.mariotaku.ktextension.isNotNullOrEmpty +import org.mariotaku.ktextension.* import org.mariotaku.microblog.library.mastodon.annotation.StatusVisibility import org.mariotaku.twidere.Constants.* import org.mariotaku.twidere.R @@ -116,12 +113,12 @@ class StatusViewHolder(private val adapter: IStatusesAdapter<*>, itemView: View) nameView.updateText(adapter.bidiFormatter) summaryView.hideIfEmpty() if (adapter.linkHighlightingStyle == VALUE_LINK_HIGHLIGHT_OPTION_CODE_NONE) { - textView.text = toPlainText(TWIDERE_PREVIEW_TEXT_HTML) + textView.spannable = toPlainText(TWIDERE_PREVIEW_TEXT_HTML) } else { val linkify = adapter.twidereLinkify val text = HtmlSpanBuilder.fromHtml(TWIDERE_PREVIEW_TEXT_HTML) linkify.applyAllLinks(text, null, -1, false, adapter.linkHighlightingStyle, true) - textView.text = text + textView.spannable = text } timeView.time = System.currentTimeMillis() val showCardActions = isCardActionsShown @@ -177,7 +174,7 @@ class StatusViewHolder(private val adapter: IStatusesAdapter<*>, itemView: View) } else if (status.retweet_id != null) { val retweetedBy = colorNameManager.getDisplayName(status.retweeted_by_user_key!!, status.retweeted_by_user_name, status.retweeted_by_user_acct!!, nameFirst) - statusInfoLabel.text = context.getString(R.string.name_retweeted, formatter.unicodeWrap(retweetedBy)) + statusInfoLabel.spannable = context.getString(R.string.name_retweeted, formatter.unicodeWrap(retweetedBy)) statusInfoIcon.setImageResource(R.drawable.ic_activity_action_retweet) statusInfoLabel.visibility = View.VISIBLE statusInfoIcon.visibility = View.VISIBLE @@ -187,9 +184,9 @@ class StatusViewHolder(private val adapter: IStatusesAdapter<*>, itemView: View) if (status.in_reply_to_name != null && status.in_reply_to_screen_name != null) { val inReplyTo = colorNameManager.getDisplayName(status.in_reply_to_user_key!!, status.in_reply_to_name, status.in_reply_to_screen_name, nameFirst) - statusInfoLabel.text = context.getString(R.string.in_reply_to_name, formatter.unicodeWrap(inReplyTo)) + statusInfoLabel.spannable = context.getString(R.string.in_reply_to_name, formatter.unicodeWrap(inReplyTo)) } else { - statusInfoLabel.text = context.getString(R.string.label_status_type_reply) + statusInfoLabel.spannable = context.getString(R.string.label_status_type_reply) } statusInfoIcon.setImageResource(R.drawable.ic_activity_action_reply) statusInfoLabel.visibility = View.VISIBLE @@ -231,9 +228,9 @@ class StatusViewHolder(private val adapter: IStatusesAdapter<*>, itemView: View) quotedText = status.quoted_text_unescaped } if (quotedDisplayEnd != -1 && quotedDisplayEnd <= quotedText.length) { - quotedTextView.text = quotedText.subSequence(0, quotedDisplayEnd) + quotedTextView.spannable = quotedText.subSequence(0, quotedDisplayEnd) } else { - quotedTextView.text = quotedText + quotedTextView.spannable = quotedText } if (quotedTextView.length() == 0) { @@ -263,7 +260,7 @@ class StatusViewHolder(private val adapter: IStatusesAdapter<*>, itemView: View) quotedMediaLabel.visibility = View.GONE } - quotedTextView.text = if (!quoteContentAvailable) { + quotedTextView.spannable = if (!quoteContentAvailable) { // Display 'not available' label SpannableString.valueOf(context.getString(R.string.label_status_not_available)).apply { setSpan(ForegroundColorSpan(ThemeUtils.getColorFromAttribute(context, @@ -359,7 +356,7 @@ class StatusViewHolder(private val adapter: IStatusesAdapter<*>, itemView: View) mediaPreview.visibility = View.GONE } - summaryView.text = status.extras?.summary_text + summaryView.spannable = status.extras?.summary_text summaryView.hideIfEmpty() val text: CharSequence @@ -385,19 +382,18 @@ class StatusViewHolder(private val adapter: IStatusesAdapter<*>, itemView: View) } if (displayEnd != -1 && displayEnd <= text.length) { - textView.text = text.subSequence(0, displayEnd) + textView.spannable = text.subSequence(0, displayEnd) } else { - textView.text = text + textView.spannable = text } textView.hideIfEmpty() if (replyCount > 0) { - replyCountView.text = UnitConvertUtils.calculateProperCount(replyCount) - replyCountView.visibility = View.VISIBLE + replyCountView.spannable = UnitConvertUtils.calculateProperCount(replyCount) } else { - replyCountView.text = null - replyCountView.visibility = View.GONE + replyCountView.spannable = null } + replyCountView.hideIfEmpty() when (status.extras?.visibility) { StatusVisibility.PRIVATE -> { @@ -428,12 +424,12 @@ class StatusViewHolder(private val adapter: IStatusesAdapter<*>, itemView: View) retweetCount = status.retweet_count if (retweetCount > 0) { - retweetCountView.text = UnitConvertUtils.calculateProperCount(retweetCount) - retweetCountView.visibility = View.VISIBLE + retweetCountView.spannable = UnitConvertUtils.calculateProperCount(retweetCount) } else { - retweetCountView.text = null - retweetCountView.visibility = View.GONE + retweetCountView.spannable = null } + retweetCountView.hideIfEmpty() + if (DestroyFavoriteTask.isDestroyingFavorite(status.account_key, status.id)) { favoriteIcon.isActivated = false } else { @@ -441,13 +437,13 @@ class StatusViewHolder(private val adapter: IStatusesAdapter<*>, itemView: View) favoriteIcon.isActivated = creatingFavorite || status.is_favorite } favoriteCount = status.favorite_count + if (favoriteCount > 0) { - favoriteCountView.text = UnitConvertUtils.calculateProperCount(favoriteCount) - favoriteCountView.visibility = View.VISIBLE + favoriteCountView.spannable = UnitConvertUtils.calculateProperCount(favoriteCount) } else { - favoriteCountView.text = null - favoriteCountView.visibility = View.GONE + favoriteCountView.spannable = null } + favoriteCountView.hideIfEmpty() nameView.updateText(formatter) quotedNameView.updateText(formatter) diff --git a/twidere/src/main/kotlin/org/mariotaku/twidere/view/holder/UserListViewHolder.kt b/twidere/src/main/kotlin/org/mariotaku/twidere/view/holder/UserListViewHolder.kt index 0190f418f..d5bff1bbe 100644 --- a/twidere/src/main/kotlin/org/mariotaku/twidere/view/holder/UserListViewHolder.kt +++ b/twidere/src/main/kotlin/org/mariotaku/twidere/view/holder/UserListViewHolder.kt @@ -20,10 +20,11 @@ package org.mariotaku.twidere.view.holder import android.support.v7.widget.RecyclerView.ViewHolder -import android.text.TextUtils import android.view.View import android.widget.TextView import kotlinx.android.synthetic.main.list_item_user_list.view.* +import org.mariotaku.ktextension.hideIfEmpty +import org.mariotaku.ktextension.spannable import org.mariotaku.twidere.R import org.mariotaku.twidere.adapter.iface.IUserListsAdapter import org.mariotaku.twidere.extension.loadProfileImage @@ -68,10 +69,10 @@ class UserListViewHolder( val manager = adapter.userColorNameManager itemContent.drawStart(manager.getUserColor(userList.user_key)) - nameView.text = userList.name + nameView.spannable = userList.name val nameFirst = adapter.nameFirst val createdByDisplayName = manager.getDisplayName(userList, nameFirst) - createdByView.text = context.getString(R.string.created_by, createdByDisplayName) + createdByView.spannable = context.getString(R.string.created_by, createdByDisplayName) if (adapter.profileImageEnabled) { profileImageView.visibility = View.VISIBLE @@ -80,8 +81,8 @@ class UserListViewHolder( } else { profileImageView.visibility = View.GONE } - descriptionView.visibility = if (TextUtils.isEmpty(userList.description)) View.GONE else View.VISIBLE - descriptionView.text = userList.description + descriptionView.spannable = userList.description + descriptionView.hideIfEmpty() membersCountView.text = Utils.getLocalizedNumber(Locale.getDefault(), userList.members_count) subscribersCountView.text = Utils.getLocalizedNumber(Locale.getDefault(), userList.subscribers_count) } diff --git a/twidere/src/main/kotlin/org/mariotaku/twidere/view/holder/UserViewHolder.kt b/twidere/src/main/kotlin/org/mariotaku/twidere/view/holder/UserViewHolder.kt index b4d562745..9cd509115 100644 --- a/twidere/src/main/kotlin/org/mariotaku/twidere/view/holder/UserViewHolder.kt +++ b/twidere/src/main/kotlin/org/mariotaku/twidere/view/holder/UserViewHolder.kt @@ -26,6 +26,7 @@ import android.view.View.OnClickListener import android.view.View.OnLongClickListener import android.widget.RelativeLayout import kotlinx.android.synthetic.main.list_item_user.view.* +import org.mariotaku.ktextension.spannable import org.mariotaku.twidere.R import org.mariotaku.twidere.adapter.iface.IUsersAdapter import org.mariotaku.twidere.adapter.iface.IUsersAdapter.* @@ -166,11 +167,11 @@ class UserViewHolder( if (!simple) { descriptionView.visibility = if (TextUtils.isEmpty(user.description_unescaped)) View.GONE else View.VISIBLE - descriptionView.text = user.description_unescaped + descriptionView.spannable = user.description_unescaped locationView.visibility = if (TextUtils.isEmpty(user.location)) View.GONE else View.VISIBLE - locationView.text = user.location + locationView.spannable = user.location urlView.visibility = if (TextUtils.isEmpty(user.url_expanded)) View.GONE else View.VISIBLE - urlView.text = user.url_expanded + urlView.spannable = user.url_expanded val locale = Locale.getDefault() statusesCountView.text = Utils.getLocalizedNumber(locale, user.statuses_count) followersCountView.text = Utils.getLocalizedNumber(locale, user.followers_count) diff --git a/twidere/src/main/kotlin/org/mariotaku/twidere/view/holder/message/MessageEntryViewHolder.kt b/twidere/src/main/kotlin/org/mariotaku/twidere/view/holder/message/MessageEntryViewHolder.kt index 4d73b4028..e17fff644 100644 --- a/twidere/src/main/kotlin/org/mariotaku/twidere/view/holder/message/MessageEntryViewHolder.kt +++ b/twidere/src/main/kotlin/org/mariotaku/twidere/view/holder/message/MessageEntryViewHolder.kt @@ -22,6 +22,7 @@ package org.mariotaku.twidere.view.holder.message import android.support.v7.widget.RecyclerView import android.view.View import kotlinx.android.synthetic.main.list_item_message_entry.view.* +import org.mariotaku.ktextension.spannable import org.mariotaku.twidere.R import org.mariotaku.twidere.adapter.MessagesEntriesAdapter import org.mariotaku.twidere.extension.loadProfileImage @@ -80,8 +81,8 @@ class MessageEntryViewHolder(itemView: View, val adapter: MessagesEntriesAdapter this.name.name = name this.name.screenName = secondaryName this.name.updateText(adapter.bidiFormatter) - this.text.text = conversation.getSummaryText(itemView.context, adapter.userColorNameManager, - adapter.nameFirst) + this.text.spannable = conversation.getSummaryText(itemView.context, + adapter.userColorNameManager, adapter.nameFirst) if (conversation.is_outgoing) { readIndicator.visibility = View.VISIBLE readIndicator.setImageResource(R.drawable.ic_message_type_outgoing) diff --git a/twidere/src/main/kotlin/org/mariotaku/twidere/view/holder/message/MessageViewHolder.kt b/twidere/src/main/kotlin/org/mariotaku/twidere/view/holder/message/MessageViewHolder.kt index 378769b14..a459372f4 100644 --- a/twidere/src/main/kotlin/org/mariotaku/twidere/view/holder/message/MessageViewHolder.kt +++ b/twidere/src/main/kotlin/org/mariotaku/twidere/view/holder/message/MessageViewHolder.kt @@ -24,6 +24,7 @@ import android.view.View import kotlinx.android.synthetic.main.list_item_message_conversation_text.view.* import org.mariotaku.ktextension.empty import org.mariotaku.ktextension.isNullOrEmpty +import org.mariotaku.ktextension.spannable import org.mariotaku.messagebubbleview.library.MessageBubbleView import org.mariotaku.twidere.R import org.mariotaku.twidere.adapter.MessagesConversationAdapter @@ -92,7 +93,7 @@ class MessageViewHolder(itemView: View, adapter: MessagesConversationAdapter) : } - text.text = SpannableStringBuilder.valueOf(message.text_unescaped).apply { + text.spannable = SpannableStringBuilder.valueOf(message.text_unescaped).apply { message.spans?.applyTo(this) adapter.linkify.applyAllLinks(this, message.account_key, layoutPosition.toLong(), false, adapter.linkHighlightingStyle, true) diff --git a/twidere/src/main/kotlin/org/mariotaku/twidere/view/holder/message/NoticeSummaryEventViewHolder.kt b/twidere/src/main/kotlin/org/mariotaku/twidere/view/holder/message/NoticeSummaryEventViewHolder.kt index 22cf545a1..44445358f 100644 --- a/twidere/src/main/kotlin/org/mariotaku/twidere/view/holder/message/NoticeSummaryEventViewHolder.kt +++ b/twidere/src/main/kotlin/org/mariotaku/twidere/view/holder/message/NoticeSummaryEventViewHolder.kt @@ -21,6 +21,7 @@ package org.mariotaku.twidere.view.holder.message import android.view.View import kotlinx.android.synthetic.main.list_item_message_conversation_notice.view.* +import org.mariotaku.ktextension.spannable import org.mariotaku.twidere.R import org.mariotaku.twidere.adapter.MessagesConversationAdapter import org.mariotaku.twidere.extension.model.getSummaryText @@ -35,7 +36,7 @@ class NoticeSummaryEventViewHolder(itemView: View, adapter: MessagesConversation override fun display(message: ParcelableMessage, showDate: Boolean) { super.display(message, showDate) - text.text = message.getSummaryText(adapter.context, adapter.userColorNameManager, + text.spannable = message.getSummaryText(adapter.context, adapter.userColorNameManager, adapter.conversation, adapter.nameFirst) } diff --git a/twidere/src/main/res-localized/values-zh-rCN/strings.xml b/twidere/src/main/res-localized/values-zh-rCN/strings.xml index 2fcba3004..e8b481045 100644 --- a/twidere/src/main/res-localized/values-zh-rCN/strings.xml +++ b/twidere/src/main/res-localized/values-zh-rCN/strings.xml @@ -432,7 +432,7 @@ 刷新和同步服务 刷新服务 回复 %s - 引用 %1$s%2$s + 回复 %1$s%2$s Buffer 默认 发送于 敏感内容 diff --git a/twidere/src/main/res/layout/list_item_status.xml b/twidere/src/main/res/layout/list_item_status.xml index 7b3290415..55f3c17e3 100644 --- a/twidere/src/main/res/layout/list_item_status.xml +++ b/twidere/src/main/res/layout/list_item_status.xml @@ -50,7 +50,7 @@ tools:tint="?android:textColorSecondary" tools:visibility="visible"/> -