improved emoji support
This commit is contained in:
parent
b2cf3bd2cb
commit
c59fc51434
|
@ -0,0 +1 @@
|
|||
*.bat text eol=crlf
|
|
@ -1,80 +0,0 @@
|
|||
/*
|
||||
* Twidere - Twitter client for Android
|
||||
*
|
||||
* Copyright (C) 2012-2015 Mariotaku Lee <mariotaku.lee@gmail.com>
|
||||
*
|
||||
* 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 <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
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<ResolveInfo> 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<ResolveInfo> resolve(Intent queryIntent);
|
||||
|
||||
public static boolean isNoneValue(final String value) {
|
||||
return TextUtils.isEmpty(value) || "none".equals(value);
|
||||
}
|
||||
|
||||
}
|
|
@ -1,49 +0,0 @@
|
|||
/*
|
||||
* Twidere - Twitter client for Android
|
||||
*
|
||||
* Copyright (C) 2012-2015 Mariotaku Lee <mariotaku.lee@gmail.com>
|
||||
*
|
||||
* 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 <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
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);
|
||||
}
|
||||
}
|
|
@ -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());
|
||||
}
|
||||
}
|
|
@ -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
|
||||
}
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
}
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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<ParcelableUser>? = null
|
||||
private var indices: ObjectCursor.CursorIndices<ParcelableLiteUser>? = 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)
|
||||
}
|
||||
|
||||
|
|
|
@ -0,0 +1,155 @@
|
|||
/*
|
||||
* Twidere - Twitter client for Android
|
||||
*
|
||||
* Copyright (C) 2012-2017 Mariotaku Lee <mariotaku.lee@gmail.com>
|
||||
*
|
||||
* 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 <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
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
|
||||
}
|
|
@ -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) {
|
||||
|
|
|
@ -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
|
||||
}
|
||||
|
|
|
@ -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<AccountsInfo>,
|
|||
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)
|
||||
|
|
|
@ -81,6 +81,8 @@ open class BaseFragment : Fragment(), IBaseFragment<BaseFragment> {
|
|||
lateinit var dns: Dns
|
||||
@Inject
|
||||
lateinit var syncPreferences: SyncPreferences
|
||||
@Inject
|
||||
lateinit var externalThemeManager: ExternalThemeManager
|
||||
|
||||
protected val statusScheduleProvider: StatusScheduleProvider?
|
||||
get() = statusScheduleProviderFactory.newInstance(context)
|
||||
|
|
|
@ -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<Cursor?>, 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<Cursor?>, 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)
|
||||
}
|
||||
|
|
|
@ -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<SingleResponse<Parcelable
|
|||
adapter.loadMoreSupportedPosition = ILoadMoreSupportAdapter.NONE
|
||||
setState(STATE_ERROR)
|
||||
val errorInfo = StatusCodeMessageUtils.getErrorInfo(context, data.exception!!)
|
||||
errorText.text = errorInfo.message
|
||||
errorText.spannable = errorInfo.message
|
||||
errorIcon.setImageResource(errorInfo.icon)
|
||||
}
|
||||
activity.supportInvalidateOptionsMenu()
|
||||
|
@ -742,10 +739,10 @@ class StatusFragment : BaseFragment(), LoaderCallbacks<SingleResponse<Parcelable
|
|||
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)
|
||||
retweetedByView.text = context.getString(R.string.name_retweeted, retweetedBy)
|
||||
retweetedByView.spannable = context.getString(R.string.name_retweeted, retweetedBy)
|
||||
retweetedByView.visibility = View.VISIBLE
|
||||
} else {
|
||||
retweetedByView.text = null
|
||||
retweetedByView.spannable = null
|
||||
retweetedByView.visibility = View.GONE
|
||||
}
|
||||
|
||||
|
@ -776,16 +773,11 @@ class StatusFragment : BaseFragment(), LoaderCallbacks<SingleResponse<Parcelable
|
|||
linkify.applyAllLinks(quotedText, status.account_key, layoutPosition.toLong(),
|
||||
status.is_possibly_sensitive, skipLinksInText)
|
||||
if (quotedDisplayEnd != -1 && quotedDisplayEnd <= quotedText.length) {
|
||||
itemView.quotedText.text = quotedText.subSequence(0, quotedDisplayEnd)
|
||||
itemView.quotedText.spannable = quotedText.subSequence(0, quotedDisplayEnd)
|
||||
} else {
|
||||
itemView.quotedText.text = quotedText
|
||||
}
|
||||
if (itemView.quotedText.length() == 0) {
|
||||
// No text
|
||||
itemView.quotedText.visibility = View.GONE
|
||||
} else {
|
||||
itemView.quotedText.visibility = View.VISIBLE
|
||||
itemView.quotedText.spannable = quotedText
|
||||
}
|
||||
itemView.quotedText.hideIfEmpty()
|
||||
|
||||
val quotedUserColor = colorNameManager.getUserColor(status.quoted_user_key!!)
|
||||
if (quotedUserColor != 0) {
|
||||
|
@ -821,7 +813,7 @@ class StatusFragment : BaseFragment(), LoaderCallbacks<SingleResponse<Parcelable
|
|||
string.setSpan(ForegroundColorSpan(ThemeUtils.getColorFromAttribute(context,
|
||||
android.R.attr.textColorTertiary, textView.currentTextColor)), 0,
|
||||
string.length, Spanned.SPAN_EXCLUSIVE_EXCLUSIVE)
|
||||
itemView.quotedText.text = string
|
||||
itemView.quotedText.spannable = string
|
||||
|
||||
itemView.quotedView.drawStart(ThemeUtils.getColorFromAttribute(context,
|
||||
R.attr.quoteIndicatorBackgroundColor))
|
||||
|
@ -864,7 +856,7 @@ class StatusFragment : BaseFragment(), LoaderCallbacks<SingleResponse<Parcelable
|
|||
|
||||
val timeString = Utils.formatToLongTimeString(context, timestamp)?.takeIf(String::isNotEmpty)
|
||||
val source = status.source?.takeIf(String::isNotEmpty)
|
||||
itemView.timeSource.text = when {
|
||||
itemView.timeSource.spannable = when {
|
||||
timeString != null && source != null -> {
|
||||
HtmlSpanBuilder.fromHtml(context.getString(R.string.status_format_time_source,
|
||||
timeString, source))
|
||||
|
@ -882,13 +874,13 @@ class StatusFragment : BaseFragment(), LoaderCallbacks<SingleResponse<Parcelable
|
|||
status.is_possibly_sensitive, skipLinksInText)
|
||||
}
|
||||
|
||||
summaryView.text = status.extras?.summary_text
|
||||
summaryView.spannable = status.extras?.summary_text
|
||||
summaryView.hideIfEmpty()
|
||||
|
||||
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()
|
||||
|
||||
|
@ -897,7 +889,7 @@ class StatusFragment : BaseFragment(), LoaderCallbacks<SingleResponse<Parcelable
|
|||
|
||||
if (!TextUtils.isEmpty(placeFullName)) {
|
||||
locationView.visibility = View.VISIBLE
|
||||
locationView.text = placeFullName
|
||||
locationView.spannable = placeFullName
|
||||
locationView.isClickable = ParcelableLocationUtils.isValidLocation(location)
|
||||
} else if (ParcelableLocationUtils.isValidLocation(location)) {
|
||||
locationView.visibility = View.VISIBLE
|
||||
|
@ -905,7 +897,7 @@ class StatusFragment : BaseFragment(), LoaderCallbacks<SingleResponse<Parcelable
|
|||
locationView.isClickable = true
|
||||
} else {
|
||||
locationView.visibility = View.GONE
|
||||
locationView.text = null
|
||||
locationView.spannable = null
|
||||
}
|
||||
|
||||
val interactUsersAdapter = itemView.countsUsers.adapter as CountsUsersAdapter
|
||||
|
|
|
@ -69,6 +69,7 @@ import android.view.*
|
|||
import android.view.View.OnClickListener
|
||||
import android.view.View.OnTouchListener
|
||||
import android.view.animation.AnimationUtils
|
||||
import android.widget.TextView
|
||||
import android.widget.Toast
|
||||
import com.bumptech.glide.Glide
|
||||
import com.squareup.otto.Subscribe
|
||||
|
@ -429,11 +430,10 @@ class UserFragment : BaseFragment(), OnClickListener, OnLinkClickListener,
|
|||
this.user = user
|
||||
profileImage.setBorderColor(if (user.color != 0) user.color else Color.WHITE)
|
||||
profileNameContainer.drawEnd(user.account_color)
|
||||
profileNameContainer.name.text = bidiFormatter.unicodeWrap(if (user.nickname.isNullOrEmpty()) {
|
||||
user.name
|
||||
} else {
|
||||
getString(R.string.name_with_nickname, user.name, user.nickname)
|
||||
})
|
||||
profileNameContainer.name.setText(bidiFormatter.unicodeWrap(when {
|
||||
user.nickname.isNullOrEmpty() -> 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()
|
||||
|
|
|
@ -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? {
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -493,10 +493,10 @@ class MessagesConversationFragment : AbsContentListRecyclerViewFragment<Messages
|
|||
sendMessage.isEnabled = !readOnly
|
||||
editText.isEnabled = !readOnly
|
||||
|
||||
conversationTitle.text = title
|
||||
conversationTitle.spannable = title
|
||||
if (subtitle != null) {
|
||||
conversationSubtitle.visibility = View.VISIBLE
|
||||
conversationSubtitle.text = subtitle
|
||||
conversationSubtitle.spannable = subtitle
|
||||
} else {
|
||||
conversationSubtitle.visibility = View.GONE
|
||||
}
|
||||
|
|
|
@ -32,6 +32,7 @@ import android.view.MenuInflater
|
|||
import android.view.MenuItem
|
||||
import android.widget.TextView
|
||||
import org.mariotaku.chameleon.Chameleon
|
||||
import org.mariotaku.ktextension.spannable
|
||||
import org.mariotaku.twidere.Constants.*
|
||||
import org.mariotaku.twidere.R
|
||||
import org.mariotaku.twidere.activity.ComposeActivity
|
||||
|
@ -105,7 +106,7 @@ class SearchFragment : AbsToolbarTabPagesFragment(), RefreshScrollTopInterface,
|
|||
val customView = actionBar.customView
|
||||
val editQuery = customView.findViewById(R.id.editQuery) as TextView
|
||||
editQuery.setTextColor(ThemeUtils.getColorDependent(theme.colorToolbar))
|
||||
editQuery.text = query
|
||||
editQuery.spannable = query
|
||||
customView.setOnClickListener {
|
||||
val searchIntent = Intent(context, QuickSearchBarActivity::class.java).apply {
|
||||
putExtra(EXTRA_QUERY, query)
|
||||
|
|
|
@ -17,33 +17,22 @@
|
|||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
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<ResolveInfo> resolve(Intent queryIntent) {
|
||||
return packageManager.queryIntentActivities(queryIntent, 0);
|
||||
override fun resolve(queryIntent: Intent): List<ResolveInfo> {
|
||||
return packageManager.queryIntentActivities(queryIntent, 0)
|
||||
}
|
||||
}
|
|
@ -0,0 +1,74 @@
|
|||
/*
|
||||
* Twidere - Twitter client for Android
|
||||
*
|
||||
* Copyright (C) 2012-2017 Mariotaku Lee <mariotaku.lee@gmail.com>
|
||||
*
|
||||
* 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 <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
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<ResolveInfo>
|
||||
|
||||
private fun setup() {
|
||||
val queryIntent = Intent(intentAction)
|
||||
val infoList = resolve(queryIntent)
|
||||
val infoListSize = infoList.size
|
||||
val entries = arrayOfNulls<CharSequence>(infoListSize + 1)
|
||||
val values = arrayOfNulls<CharSequence>(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
|
||||
}
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,39 @@
|
|||
/*
|
||||
* Twidere - Twitter client for Android
|
||||
*
|
||||
* Copyright (C) 2012-2017 Mariotaku Lee <mariotaku.lee@gmail.com>
|
||||
*
|
||||
* 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 <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
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)
|
||||
}
|
|
@ -17,32 +17,22 @@
|
|||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
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)
|
||||
|
||||
}
|
|
@ -17,33 +17,22 @@
|
|||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
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<ResolveInfo> resolve(Intent queryIntent) {
|
||||
return packageManager.queryIntentServices(queryIntent, 0);
|
||||
override fun resolve(queryIntent: Intent): List<ResolveInfo> {
|
||||
return packageManager.queryIntentServices(queryIntent, 0)
|
||||
}
|
||||
}
|
|
@ -17,33 +17,21 @@
|
|||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
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)
|
||||
|
||||
}
|
|
@ -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)
|
||||
}
|
||||
}
|
|
@ -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 {
|
||||
|
|
|
@ -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) {
|
||||
|
|
|
@ -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
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,159 +0,0 @@
|
|||
/*
|
||||
* Twidere - Twitter client for Android
|
||||
*
|
||||
* Copyright (C) 2012-2015 Mariotaku Lee <mariotaku.lee@gmail.com>
|
||||
*
|
||||
* 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 <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
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
|
||||
}
|
||||
|
||||
}
|
|
@ -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)
|
||||
|
|
|
@ -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
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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()
|
||||
|
|
|
@ -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 {
|
||||
|
|
|
@ -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
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -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) {
|
||||
|
|
|
@ -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 {
|
||||
|
|
|
@ -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(
|
||||
|
|
|
@ -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,
|
||||
|
|
|
@ -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) {
|
||||
|
|
|
@ -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()
|
||||
}
|
||||
|
|
|
@ -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,
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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<out A : IContentAdapter>(
|
|||
}
|
||||
|
||||
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,
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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)
|
||||
}
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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)
|
||||
}
|
||||
|
||||
|
|
|
@ -432,7 +432,7 @@
|
|||
<string name="label_refresh_and_sync_service">刷新和同步服务</string>
|
||||
<string name="label_refresh_service">刷新服务</string>
|
||||
<string name="label_reply_name">回复 <xliff:g id="user_name">%s</xliff:g></string>
|
||||
<string name="label_reply_name_text">引用 <xliff:g id="name">%1$s</xliff:g>:<xliff:g id="text">%2$s</xliff:g></string>
|
||||
<string name="label_reply_name_text">回复 <xliff:g id="name">%1$s</xliff:g>:<xliff:g id="text">%2$s</xliff:g></string>
|
||||
<string name="label_schedule_time_buffer_default">Buffer 默认</string>
|
||||
<string name="label_send_at">发送于</string>
|
||||
<string name="label_sensitive_content">敏感内容</string>
|
||||
|
|
|
@ -50,7 +50,7 @@
|
|||
tools:tint="?android:textColorSecondary"
|
||||
tools:visibility="visible"/>
|
||||
|
||||
<org.mariotaku.twidere.view.ActionIconThemedTextView
|
||||
<org.mariotaku.twidere.view.FixedTextView
|
||||
android:id="@+id/statusInfoLabel"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
|
|
Loading…
Reference in New Issue