use of com.github.moko256.latte.client.base.entity.Emoji in project twicalico by moko256.
the class TwitterStringUtils method setLinkedSequenceTo.
@SuppressLint("StaticFieldLeak")
public static void setLinkedSequenceTo(Status item, TextView textView) {
Context context = textView.getContext();
String tweet = item.getText();
if (GlobalApplication.clientType == Type.MASTODON) {
Spanned html = Html.fromHtml(tweet);
int length = html.length();
// Trim unless \n\n made by fromHtml() after post
if (length == 3 && item.getMediaEntities().length > 0 && html.charAt(0) == ".".charAt(0)) {
// If post has media only, context of post from Mastodon is "."
textView.setText("");
return;
} else if (length >= 2) {
html = (Spanned) html.subSequence(0, length - 2);
}
SpannableStringBuilder builder = convertUrlSpanToCustomTabs(html, context);
textView.setText(builder);
List<Emoji> list = ((StatusCacheMap.CachedStatus) item).getEmojis();
if (list != null) {
Matcher matcher = containsEmoji.matcher(builder);
boolean matches = matcher.matches();
int imageSize;
if (matches) {
imageSize = (int) Math.floor((textView.getTextSize() * 1.15) * 2.0);
} else {
imageSize = (int) Math.floor(textView.getTextSize() * 1.15);
}
new AsyncTask<Void, Void, Map<String, Drawable>>() {
@Override
protected Map<String, Drawable> doInBackground(Void... params) {
Map<String, Drawable> map = new ArrayMap<>();
GlideRequests glideRequests = GlideApp.with(context);
for (Emoji emoji : list) {
try {
Drawable value = glideRequests.load(emoji.getUrl()).submit().get();
value.setBounds(0, 0, imageSize, imageSize);
map.put(emoji.getShortCode(), value);
} catch (InterruptedException | ExecutionException e) {
e.printStackTrace();
}
}
return map;
}
@Override
protected void onPostExecute(Map<String, Drawable> map) {
if (TextUtils.equals(builder, textView.getText())) {
boolean found = matches || matcher.find();
while (found) {
String shortCode = matcher.group(1);
Drawable drawable = map.get(shortCode);
if (drawable != null) {
builder.setSpan(new ImageSpan(drawable), matcher.start(), matcher.end(), Spanned.SPAN_EXCLUSIVE_EXCLUSIVE);
}
found = matcher.find();
}
textView.setText(builder);
}
}
}.execute();
}
return;
}
SpannableStringBuilder spannableStringBuilder = new SpannableStringBuilder(tweet);
for (SymbolEntity symbolEntity : item.getSymbolEntities()) {
spannableStringBuilder.setSpan(new ClickableSpan() {
@Override
public void onClick(View view) {
context.startActivity(SearchResultActivity.getIntent(context, symbolEntity.getText()));
}
}, tweet.offsetByCodePoints(0, symbolEntity.getStart()), tweet.offsetByCodePoints(0, symbolEntity.getEnd()), Spanned.SPAN_INCLUSIVE_INCLUSIVE);
}
for (HashtagEntity hashtagEntity : item.getHashtagEntities()) {
spannableStringBuilder.setSpan(new ClickableSpan() {
@Override
public void onClick(View view) {
context.startActivity(SearchResultActivity.getIntent(context, "#" + hashtagEntity.getText()));
}
}, tweet.offsetByCodePoints(0, hashtagEntity.getStart()), tweet.offsetByCodePoints(0, hashtagEntity.getEnd()), Spanned.SPAN_INCLUSIVE_INCLUSIVE);
}
for (UserMentionEntity userMentionEntity : item.getUserMentionEntities()) {
spannableStringBuilder.setSpan(new ClickableSpan() {
@Override
public void onClick(View view) {
context.startActivity(ShowUserActivity.getIntent(context, userMentionEntity.getScreenName()));
}
}, tweet.offsetByCodePoints(0, userMentionEntity.getStart()), tweet.offsetByCodePoints(0, userMentionEntity.getEnd()), Spanned.SPAN_INCLUSIVE_INCLUSIVE);
}
boolean hasMedia = item.getMediaEntities().length > 0;
List<URLEntity> urlEntities = new ArrayList<>(item.getURLEntities().length + (hasMedia ? 1 : 0));
urlEntities.addAll(Arrays.asList(item.getURLEntities()));
if (hasMedia) {
urlEntities.add(item.getMediaEntities()[0]);
}
int tweetLength = tweet.codePointCount(0, tweet.length());
int sp = 0;
for (URLEntity entity : urlEntities) {
String url = entity.getURL();
String displayUrl = entity.getDisplayURL();
int urlLength = url.codePointCount(0, url.length());
int displayUrlLength = displayUrl.codePointCount(0, displayUrl.length());
if (entity.getStart() <= tweetLength && entity.getEnd() <= tweetLength) {
int dusp = displayUrlLength - urlLength;
spannableStringBuilder.replace(tweet.offsetByCodePoints(0, entity.getStart()) + sp, tweet.offsetByCodePoints(0, entity.getEnd()) + sp, displayUrl);
spannableStringBuilder.setSpan(new ClickableSpan() {
@Override
public void onClick(View view) {
AppCustomTabsKt.launchChromeCustomTabs(context, entity.getExpandedURL());
}
}, tweet.offsetByCodePoints(0, entity.getStart()) + sp, tweet.offsetByCodePoints(0, entity.getEnd()) + sp + dusp, Spanned.SPAN_INCLUSIVE_INCLUSIVE);
sp += dusp;
}
}
textView.setText(spannableStringBuilder);
}
use of com.github.moko256.latte.client.base.entity.Emoji in project twicalico by moko256.
the class PostActivity method onCreate.
@SuppressLint("SetTextI18n")
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_post);
TypedValue value = new TypedValue();
getTheme().resolveAttribute(android.R.attr.textColorTertiary, value, true);
COLOR_STABLE = ContextCompat.getColor(this, value.resourceId);
getTheme().resolveAttribute(R.attr.colorError, value, true);
COLOR_ERROR = ContextCompat.getColor(this, value.resourceId);
RequestManager requestManager = Glide.with(this);
client = GlobalApplicationKt.getClient(this);
model = new PostStatusModelImpl(getContentResolver(), client.getApiClient());
disposable = new CompositeDisposable();
rootViewGroup = findViewById(android.R.id.content);
actionBar = Objects.requireNonNull(getSupportActionBar());
actionBar.setDisplayHomeAsUpEnabled(true);
actionBar.setHomeAsUpIndicator(R.drawable.ic_clear_white_24dp);
actionBar.setDisplayShowCustomEnabled(true);
userIcon = new ImageView(this);
final int dp36 = DpToPxKt.dpToPx(this, 36);
ActionBar.LayoutParams userIconParams = new ActionBar.LayoutParams(dp36, dp36);
userIconParams.setMarginStart(dp36 / 3 * 2);
actionBar.setCustomView(userIcon, userIconParams);
disposable.add(Single.create(new VerifyCredentialOnSubscribe(client)).subscribeOn(Schedulers.io()).observeOn(AndroidSchedulers.mainThread()).subscribe(user -> requestManager.load(client.getMediaUrlConverter().convertProfileIconUriBySize(user, dp36)).circleCrop().into(userIcon), Throwable::printStackTrace));
counterTextView = findViewById(R.id.tweet_text_edit_counter);
editText = findViewById(R.id.tweet_text_edit);
editText.addTextChangedListener(new TextWatcher() {
@Override
public void beforeTextChanged(CharSequence s, int start, int count, int after) {
}
@Override
public void onTextChanged(CharSequence s, int start, int before, int count) {
model.getUpdateStatus().setContext(s.toString());
updateCounter();
}
@Override
public void afterTextChanged(Editable s) {
}
});
editText.setFilters(NoSpanInputFilterKt.getNoSpanInputFilter());
editText.setImageAddedListener(imageUri -> {
if (model.getUriList().size() < model.getUriListSizeLimit()) {
addedImagesAdapter.addImageAndUpdateView(imageUri);
model.getUriList().add(imageUri);
updateCounter();
isPossiblySensitive.setEnabled(true);
return true;
} else {
return false;
}
});
editText.setOnKeyListener((v, keyCode, event) -> {
if (!isPosting && event.getAction() == KeyEvent.ACTION_DOWN && event.isCtrlPressed() && keyCode == KeyEvent.KEYCODE_ENTER) {
postOrConfirmIfValid();
return true;
}
return false;
});
imagesRecyclerView = findViewById(R.id.activity_tweet_send_images_recycler_view);
addedImagesAdapter = new AddedImagesAdapter(this, requestManager);
imagesRecyclerView.setLayoutManager(new LinearLayoutManager(this, LinearLayoutManager.HORIZONTAL, false));
imagesRecyclerView.addItemDecoration(new RecyclerView.ItemDecoration() {
@Override
public void getItemOffsets(@NonNull Rect outRect, @NonNull View view, @NonNull RecyclerView parent, @NonNull RecyclerView.State state) {
outRect.right = DpToPxKt.oneDpToPx(getResources().getDisplayMetrics().density);
}
});
addedImagesAdapter.limit = model.getUriListSizeLimit();
addedImagesAdapter.onAddButtonClickListener = v -> startActivityForResult(Intent.createChooser(new Intent(Intent.ACTION_GET_CONTENT).addCategory(Intent.CATEGORY_OPENABLE).putExtra(Intent.EXTRA_MIME_TYPES, new String[] { "image/*", "video/*" }).putExtra(Intent.EXTRA_ALLOW_MULTIPLE, true).setType("*/*"), getString(R.string.add_media)), REQUEST_GET_IMAGE);
addedImagesAdapter.onDeleteButtonListener = position -> {
addedImagesAdapter.removeImageAndUpdateView(position);
model.getUriList().remove(position);
boolean enabled = model.getUriList().size() > 0;
updateCounter();
isPossiblySensitive.setEnabled(enabled);
isPossiblySensitive.setChecked(isPossiblySensitive.isChecked() && enabled);
};
addedImagesAdapter.onImageClickListener = position -> {
try {
Intent open = new Intent(Intent.ACTION_VIEW).setData(model.getUriList().get(position)).addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION);
startActivity(Intent.createChooser(open, getString(R.string.open_media)));
} catch (Throwable e) {
e.printStackTrace();
}
};
imagesRecyclerView.setAdapter(addedImagesAdapter);
emojiInputRecyclerView = findViewById(R.id.suggestions_of_emoji);
isPossiblySensitive = findViewById(R.id.activity_tweet_is_possibly_sensitive);
isPossiblySensitive.setEnabled(addedImagesAdapter.getImagesList().size() > 0);
isPossiblySensitive.setOnCheckedChangeListener((buttonView, isChecked) -> model.getUpdateStatus().setPossiblySensitive(isChecked));
postVisibility = findViewById(R.id.activity_tweet_visibility_spinner);
contentWarningText = findViewById(R.id.tweet_text_warning);
contentWarningEnabled = findViewById(R.id.activity_tweet_add_content_warning);
pollsText = new EditText[] { findViewById(R.id.edit_poll_0), findViewById(R.id.edit_poll_1), findViewById(R.id.edit_poll_2), findViewById(R.id.edit_poll_3) };
pollsExpiredAt = findViewById(R.id.edit_poll_expired_at);
pollsExpireAtLabel = findViewById(R.id.edit_poll_expired_at_description);
pollsMultiple = findViewById(R.id.poll_multiple);
pollsHideTotals = findViewById(R.id.hide_totals);
addPoll = findViewById(R.id.activity_add_poll);
ArraysKt.forEach(pollsText, editText -> {
editText.setVisibility(View.GONE);
return Unit.INSTANCE;
});
pollsExpiredAt.setVisibility(View.GONE);
pollsMultiple.setVisibility(View.GONE);
pollsHideTotals.setVisibility(View.GONE);
pollsExpireAtLabel.setVisibility(View.GONE);
if (client.getAccessToken().getClientType() == CLIENT_TYPE_MASTODON) {
emojiAdapter = new EmojiAdapter(emojiList, this, requestManager, emoji -> {
int selectionEnd = editText.getSelectionEnd();
String shortCode = emoji.getShortCode();
Editable text = editText.getText();
if (text != null) {
text.insert(selectionEnd, ":").insert(selectionEnd + 1, shortCode).insert(selectionEnd + 1 + shortCode.length(), ": ");
} else {
editText.setText(":" + shortCode + ": ");
}
editText.setSelection(selectionEnd + shortCode.length() + 3);
return Unit.INSTANCE;
}, () -> {
disposable.add(model.requestCustomEmojis().subscribeOn(Schedulers.io()).observeOn(AndroidSchedulers.mainThread()).subscribe(emojis -> {
emojiList.clear();
emojiList.addAll(emojis);
emojiAdapter.notifyDataSetChanged();
}, this::errorNotify));
return Unit.INSTANCE;
});
contentWarningText.setFilters(NoSpanInputFilterKt.getNoSpanInputFilter());
emojiInputRecyclerView.setLayoutManager(new LinearLayoutManager(this, RecyclerView.HORIZONTAL, false));
emojiInputRecyclerView.setAdapter(emojiAdapter);
contentWarningText.addTextChangedListener(new TextWatcher() {
@Override
public void beforeTextChanged(CharSequence s, int start, int count, int after) {
}
@Override
public void onTextChanged(CharSequence s, int start, int before, int count) {
model.getUpdateStatus().setContentWarning(s.toString());
updateCounter();
}
@Override
public void afterTextChanged(Editable s) {
}
});
contentWarningEnabled.setOnCheckedChangeListener((buttonView, isChecked) -> {
if (isChecked) {
contentWarningText.setVisibility(View.VISIBLE);
contentWarningText.requestFocus();
} else {
contentWarningText.setVisibility(View.GONE);
model.getUpdateStatus().setContentWarning("");
}
});
postVisibility.setOnItemSelectedListener(new AdapterView.OnItemSelectedListener() {
@Override
public void onItemSelected(AdapterView<?> parent, View view, int position, long id) {
model.getUpdateStatus().setVisibility(POST_VISIBILITY[position]);
}
@Override
public void onNothingSelected(AdapterView<?> parent) {
}
});
ArrayList<String> options = new ArrayList<>(4);
ArraysKt.forEachIndexed(pollsText, (i, editText) -> {
if (i < 2) {
editText.setHint(getString(R.string.poll_n, i + 1));
} else {
editText.setHint(getString(R.string.poll_n_optional, i + 1));
}
options.add("");
editText.addTextChangedListener(new TextWatcher() {
@Override
public void beforeTextChanged(CharSequence s, int start, int count, int after) {
}
@Override
public void onTextChanged(CharSequence s, int start, int before, int count) {
options.set(i, s.toString());
model.getUpdateStatus().setPollList(options);
}
@Override
public void afterTextChanged(Editable s) {
}
});
return Unit.INSTANCE;
});
pollsExpiredAt.setOnItemSelectedListener(new AdapterView.OnItemSelectedListener() {
@Override
public void onItemSelected(AdapterView<?> parent, View view, int position, long id) {
model.getUpdateStatus().setPollExpiredSecond(POLL_EXPIRED_AT[position]);
}
@Override
public void onNothingSelected(AdapterView<?> parent) {
}
});
pollsMultiple.setOnCheckedChangeListener((buttonView, isChecked) -> model.getUpdateStatus().setPollSelectableMultiple(isChecked));
pollsHideTotals.setOnCheckedChangeListener((buttonView, isChecked) -> model.getUpdateStatus().setPollHideTotalsUntilExpired(isChecked));
addPoll.setOnCheckedChangeListener((buttonView, isChecked) -> {
if (isChecked) {
ArraysKt.forEach(pollsText, editText -> {
editText.setVisibility(View.VISIBLE);
return Unit.INSTANCE;
});
pollsExpiredAt.setVisibility(View.VISIBLE);
pollsMultiple.setVisibility(View.VISIBLE);
pollsHideTotals.setVisibility(View.VISIBLE);
pollsExpireAtLabel.setVisibility(View.VISIBLE);
} else {
model.getUpdateStatus().setPollList(null);
ArraysKt.forEach(pollsText, editText -> {
editText.setVisibility(View.GONE);
editText.setText("");
return Unit.INSTANCE;
});
pollsExpiredAt.setVisibility(View.GONE);
pollsMultiple.setVisibility(View.GONE);
pollsHideTotals.setVisibility(View.GONE);
pollsExpireAtLabel.setVisibility(View.GONE);
}
});
} else {
emojiInputRecyclerView.setVisibility(View.GONE);
contentWarningEnabled.setVisibility(View.GONE);
postVisibility.setVisibility(View.GONE);
findViewById(R.id.activity_tweet_visibility_description).setVisibility(View.GONE);
addPoll.setVisibility(View.GONE);
}
addLocation = findViewById(R.id.activity_tweet_add_location);
if (client.getAccessToken().getClientType() == CLIENT_TYPE_TWITTER) {
addLocation.setVisibility(View.VISIBLE);
addLocation.setOnCheckedChangeListener((buttonView, isChecked) -> {
if (isChecked) {
locationText.setVisibility(View.VISIBLE);
if (PermissionChecker.checkSelfPermission(this, Manifest.permission.ACCESS_FINE_LOCATION) == PermissionChecker.PERMISSION_GRANTED) {
updateLocation();
} else {
ActivityCompat.requestPermissions(this, new String[] { Manifest.permission.ACCESS_FINE_LOCATION }, REQUEST_CODE_PERMISSION_LOCATION);
}
} else {
model.getUpdateStatus().setLocation(null);
locationText.setVisibility(View.GONE);
locationText.setText("");
}
});
} else {
addLocation.setVisibility(View.GONE);
}
locationText = findViewById(R.id.activity_tweet_location_result);
locationText.setVisibility(View.GONE);
if (getIntent() != null) {
model.getUpdateStatus().setInReplyToStatusId(getIntent().getLongExtra(INTENT_EXTRA_IN_REPLY_TO_STATUS_ID, -1));
if (savedInstanceState == null) {
String text = getIntent().getStringExtra(INTENT_EXTRA_TWEET_TEXT);
if (text != null) {
editText.setText(text);
editText.setSelection(text.length());
} else {
editText.setText("");
}
ArrayList<Uri> uris = getIntent().getParcelableArrayListExtra(INTENT_EXTRA_IMAGE_URI);
if (uris != null) {
addedImagesAdapter.getImagesList().addAll(uris);
model.getUriList().addAll(uris);
updateCounter();
isPossiblySensitive.setEnabled(true);
}
}
}
editText.setHint(model.isReply() ? R.string.reply : R.string.post);
}
use of com.github.moko256.latte.client.base.entity.Emoji in project twicalico by moko256.
the class UserInfoFragment method setShowUserInfo.
private void setShowUserInfo(User user) {
if (user.getId() == client.getAccessToken().getUserId()) {
userIsYouOrFollowedYou.setVisibility(View.VISIBLE);
userIsYouOrFollowedYou.setText(R.string.you);
}
MediaUrlConverter mediaUrlConverter = client.getMediaUrlConverter();
String headerUrl = mediaUrlConverter.convertProfileBannerLargeUrl(user);
if (headerUrl != null) {
glideRequests.load(headerUrl).transition(DrawableTransitionOptions.withCrossFade()).into(header);
header.setOnClickListener(v -> startActivity(ShowMediasActivity.Companion.getIntent(v.getContext(), new Media[] { new Media(null, headerUrl, null, Media.MediaType.PICTURE.getValue()) }, CLIENT_TYPE_NOTHING, 0)));
}
glideRequests.load(mediaUrlConverter.convertProfileIconUriBySize(user, DpToPxKt.dpToPx(this, 68))).circleCrop().transition(DrawableTransitionOptions.withCrossFade()).into(icon);
icon.setOnClickListener(v -> startActivity(ShowMediasActivity.Companion.getIntent(v.getContext(), new Media[] { new Media(null, mediaUrlConverter.convertProfileIconOriginalUrl(user), null, Media.MediaType.PICTURE.getValue()) }, CLIENT_TYPE_NOTHING, 0)));
CharSequence userName = TwitterStringUtils.plusUserMarks(user.getName(), userNameText, user.isProtected(), user.isVerified());
CharSequence userBio = TwitterStringUtils.getLinkedSequence(client.getAccessToken(), user.getDescription(), user.getDescriptionLinks());
userNameText.setText(userName);
userBioText.setText(userBio);
Emoji[] userNameEmojis = user.getEmojis();
if (userNameEmojis != null) {
EmojiToTextViewSetter nameSetter = new EmojiToTextViewSetter(glideRequests, userNameText, userName, userNameEmojis);
EmojiToTextViewSetter bioSetter = new EmojiToTextViewSetter(glideRequests, userBioText, userBio, userNameEmojis);
getLifecycle().addObserver(new LifecycleEventObserver() {
@Override
public void onStateChanged(@NonNull LifecycleOwner source, @NonNull Lifecycle.Event event) {
if (event == Lifecycle.Event.ON_DESTROY) {
nameSetter.dispose();
bioSetter.dispose();
getLifecycle().removeObserver(this);
}
}
});
}
userIdText.setText(TwitterStringUtils.plusAtMark(user.getScreenName()));
if (!TextUtils.isEmpty(user.getLocation())) {
userLocation.setText(getString(R.string.location_is, user.getLocation()));
} else {
userLocation.setVisibility(View.GONE);
}
final String url = user.getUrl();
if (!TextUtils.isEmpty(url)) {
String text = getString(R.string.url_is, url);
SpannableString spannableString = new SpannableString(text);
int start = text.indexOf(url);
spannableString.setSpan(new ClickableNoLineSpan() {
@Override
public void onClick(@NonNull View widget) {
AppCustomTabsKt.launchChromeCustomTabs(widget.getContext(), url, false);
}
}, start, start + url.length(), Spanned.SPAN_EXCLUSIVE_EXCLUSIVE);
userUrl.setText(spannableString);
userUrl.setMovementMethod(LinkMovementMethod.getInstance());
} else {
userUrl.setVisibility(View.GONE);
}
userCreatedAt.setText(DateFormat.getDateTimeInstance(DateFormat.SHORT, DateFormat.FULL).format(user.getCreatedAt()));
userCounts.setText(getString(R.string.user_counts_is, user.getStatusesCount(), user.getFriendsCount(), user.getFollowersCount()));
}
use of com.github.moko256.latte.client.base.entity.Emoji in project twicalico by moko256.
the class UsersAdapter method onBindViewHolder.
@Override
public void onBindViewHolder(@NonNull ViewHolder viewHolder, final int i) {
User item = userCache.get(data.get(i));
if (item != null) {
requestManager.load(converter.convertProfileIconUriBySize(item, DpToPxKt.dpToPx(context, 40))).circleCrop().transition(DrawableTransitionOptions.withCrossFade()).into(viewHolder.userUserImage);
CharSequence userNameText = TwitterStringUtils.plusUserMarks(item.getName(), viewHolder.userUserName, item.isProtected(), item.isVerified());
viewHolder.userUserName.setText(userNameText);
Emoji[] userNameEmojis = item.getEmojis();
if (userNameEmojis != null) {
viewHolder.disposable.add(new EmojiToTextViewSetter(requestManager, viewHolder.userUserName, userNameText, userNameEmojis));
}
viewHolder.userUserId.setText(TwitterStringUtils.plusAtMark(item.getScreenName()));
viewHolder.itemView.setOnClickListener(v -> {
ActivityOptionsCompat animation = ActivityOptionsCompat.makeSceneTransitionAnimation(((Activity) context), viewHolder.userUserImage, "icon_image");
context.startActivity(GlobalApplicationKt.setAccountKeyForActivity(ShowUserActivity.getIntent(context, item.getId()), ((Activity) context)), animation.toBundle());
});
}
}
Aggregations