diff --git a/app/build.gradle b/app/build.gradle index 326646a45..e18594e6b 100644 --- a/app/build.gradle +++ b/app/build.gradle @@ -44,7 +44,7 @@ allprojects { } ext.supportLibraryVersion = '28.0.0' ext.glideLibraryVersion = '4.8.0' -ext.conscryptLibraryVersion = '1.4.0' +ext.conscryptLibraryVersion = '1.4.1' ext.evernoteLibraryVersion = '1.3.0-alpha08' ext.gsonLibraryVersion = '2.8.2' ext.guavaLibraryVersion = '24.1-android' diff --git a/app/src/main/java/fr/gouv/etalab/mastodon/activities/BaseMainActivity.java b/app/src/main/java/fr/gouv/etalab/mastodon/activities/BaseMainActivity.java index 208768720..a4fedbf4d 100644 --- a/app/src/main/java/fr/gouv/etalab/mastodon/activities/BaseMainActivity.java +++ b/app/src/main/java/fr/gouv/etalab/mastodon/activities/BaseMainActivity.java @@ -1219,13 +1219,18 @@ public abstract class BaseMainActivity extends BaseActivity .setPositiveButton(R.string.yes, new DialogInterface.OnClickListener() { public void onClick(DialogInterface dialog, int which) { // continue with delete - try { - String path = getCacheDir().getPath(); - File dir = new File(path); - if (dir.isDirectory()) { - Helper.deleteDir(dir); + AsyncTask.execute(new Runnable() { + @Override + public void run() { + try { + String path = getCacheDir().getPath(); + File dir = new File(path); + if (dir.isDirectory()) { + Helper.deleteDir(dir); + } + } catch (Exception ignored) {} } - } catch (Exception ignored) {} + }); Toast.makeText(BaseMainActivity.this, getString(R.string.toast_cache_clear,String.format("%s %s", String.format(Locale.getDefault(), "%.2f", finalCacheSize), getString(R.string.cache_units))), Toast.LENGTH_LONG).show(); dialog.dismiss(); } diff --git a/app/src/main/java/fr/gouv/etalab/mastodon/activities/ShowAccountActivity.java b/app/src/main/java/fr/gouv/etalab/mastodon/activities/ShowAccountActivity.java index 0feef46cb..30f35bd6d 100644 --- a/app/src/main/java/fr/gouv/etalab/mastodon/activities/ShowAccountActivity.java +++ b/app/src/main/java/fr/gouv/etalab/mastodon/activities/ShowAccountActivity.java @@ -792,10 +792,10 @@ public class ShowAccountActivity extends BaseActivity implements OnPostActionInt if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN) { valueView.setBackground(null); } - spannableValueString = Helper.clickableElementsDescription(ShowAccountActivity.this, value, account.getEmojis()); + spannableValueString = Helper.clickableElementsDescription(ShowAccountActivity.this, value); spannableValueString.setSpan(new ForegroundColorSpan(ContextCompat.getColor(ShowAccountActivity.this, R.color.verified_text)), 0, spannableValueString.toString().length(), Spannable.SPAN_EXCLUSIVE_EXCLUSIVE); }else { - spannableValueString = Helper.clickableElementsDescription(ShowAccountActivity.this, value, account.getEmojis()); + spannableValueString = Helper.clickableElementsDescription(ShowAccountActivity.this, value); } valueView.setText(spannableValueString, TextView.BufferType.SPANNABLE); valueView.setMovementMethod(LinkMovementMethod.getInstance()); @@ -807,7 +807,7 @@ public class ShowAccountActivity extends BaseActivity implements OnPostActionInt account_dn.setText(Helper.shortnameToUnicode(account.getDisplay_name(), true)); account_un.setText(String.format("@%s", account.getAcct())); - SpannableString spannableString = Helper.clickableElementsDescription(ShowAccountActivity.this, account.getNote(), account.getEmojis()); + SpannableString spannableString = Helper.clickableElementsDescription(ShowAccountActivity.this, account.getNote()); account.setNoteSpan(spannableString); account.makeEmojisAccountProfile(ShowAccountActivity.this, ShowAccountActivity.this); account_note.setText(account.getNoteSpan(), TextView.BufferType.SPANNABLE); diff --git a/app/src/main/java/fr/gouv/etalab/mastodon/helper/Helper.java b/app/src/main/java/fr/gouv/etalab/mastodon/helper/Helper.java index 054bf0880..653a11ac9 100644 --- a/app/src/main/java/fr/gouv/etalab/mastodon/helper/Helper.java +++ b/app/src/main/java/fr/gouv/etalab/mastodon/helper/Helper.java @@ -77,6 +77,7 @@ import android.text.SpannableString; import android.text.Spanned; import android.text.TextPaint; import android.text.style.ClickableSpan; +import android.text.style.URLSpan; import android.util.DisplayMetrics; import android.util.Log; import android.util.Patterns; @@ -165,7 +166,6 @@ import fr.gouv.etalab.mastodon.client.Entities.Attachment; import fr.gouv.etalab.mastodon.client.Entities.Emojis; import fr.gouv.etalab.mastodon.client.Entities.Filters; import fr.gouv.etalab.mastodon.client.Entities.Mention; -import fr.gouv.etalab.mastodon.client.Entities.Results; import fr.gouv.etalab.mastodon.client.Entities.Status; import fr.gouv.etalab.mastodon.client.Entities.Tag; import fr.gouv.etalab.mastodon.client.Entities.Version; @@ -397,7 +397,6 @@ public class Helper { STORE, TOOT } - private static boolean isPerformingSearch = false; /** * Converts emojis in input to unicode @@ -1593,27 +1592,44 @@ public class Helper { * @param fullContent String, should be the st * @return TextView */ - public static SpannableString clickableElementsDescription(final Context context, String fullContent, List emojis) { + public static SpannableString clickableElementsDescription(final Context context, String fullContent) { + SpannableString spannableString; fullContent = Helper.shortnameToUnicode(fullContent, true); + SpannableString spannableStringT = new SpannableString(fullContent); + Pattern aLink = Pattern.compile("(<\\s?a\\s?href=\"https?:\\/\\/([\\da-z\\.-]+\\.[a-z\\.]{2,10})\\/(@[\\/\\w._-]*)\"\\s?[^.]*<\\s?\\/\\s?a\\s?>)"); + Matcher matcherALink = aLink.matcher(spannableStringT.toString()); + ArrayList accountsMentionUnknown = new ArrayList<>(); + while (matcherALink.find()){ + String acct = matcherALink.group(3).replace("@",""); + String instance = matcherALink.group(2); + Account account = new Account(); + account.setAcct(acct); + account.setInstance(instance); + accountsMentionUnknown.add(account); + } if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) - spannableString = new SpannableString(Html.fromHtml(fullContent, Html.FROM_HTML_MODE_LEGACY)); + spannableString = new SpannableString(Html.fromHtml(spannableStringT.toString().replaceAll("^

","").replaceAll("

","

").replaceAll("

",""), Html.FROM_HTML_MODE_LEGACY)); else //noinspection deprecation - spannableString = new SpannableString(Html.fromHtml(fullContent)); + spannableString = new SpannableString(Html.fromHtml(spannableStringT.toString().replaceAll("^

","").replaceAll("

","

").replaceAll("

",""))); + + URLSpan[] urls = spannableString.getSpans(0, spannableString.length(), URLSpan.class); + for(URLSpan span : urls) + spannableString.removeSpan(span); SharedPreferences sharedpreferences = context.getSharedPreferences(Helper.APP_PREFS, Context.MODE_PRIVATE); - boolean embedded_browser = sharedpreferences.getBoolean(Helper.SET_EMBEDDED_BROWSER, true); - if( embedded_browser){ - Matcher matcher; - if (Build.VERSION.SDK_INT > Build.VERSION_CODES.KITKAT) - matcher = Patterns.WEB_URL.matcher(spannableString); - else - matcher = urlPattern.matcher(spannableString); - while (matcher.find()){ - int matchStart = matcher.start(1); - int matchEnd = matcher.end(); - final String url = spannableString.toString().substring(matchStart, matchEnd); + int theme = sharedpreferences.getInt(Helper.SET_THEME, Helper.THEME_DARK); + Matcher matcher; + if (Build.VERSION.SDK_INT > Build.VERSION_CODES.KITKAT) + matcher = Patterns.WEB_URL.matcher(spannableString); + else + matcher = urlPattern.matcher(spannableString); + while (matcher.find()){ + int matchStart = matcher.start(1); + int matchEnd = matcher.end(); + final String url = spannableString.toString().substring(matchStart, matchEnd); + if( matchEnd <= spannableString.toString().length() && matchEnd >= matchStart) spannableString.setSpan(new ClickableSpan() { @Override public void onClick(View textView) { @@ -1622,85 +1638,76 @@ public class Helper { @Override public void updateDrawState(TextPaint ds) { super.updateDrawState(ds); + ds.setUnderlineText(false); + if (theme == THEME_DARK) + ds.setColor(ContextCompat.getColor(context, R.color.dark_link_toot)); + else if (theme == THEME_BLACK) + ds.setColor(ContextCompat.getColor(context, R.color.black_link_toot)); + else if (theme == THEME_LIGHT) + ds.setColor(ContextCompat.getColor(context, R.color.mastodonC4)); } }, matchStart, matchEnd, Spanned.SPAN_INCLUSIVE_EXCLUSIVE); - } } - Matcher matcher = hashtagPattern.matcher(spannableString); + matcher = hashtagPattern.matcher(spannableString); while (matcher.find()){ int matchStart = matcher.start(1); int matchEnd = matcher.end(); final String tag = spannableString.toString().substring(matchStart, matchEnd); - spannableString.setSpan(new ClickableSpan() { - @Override - public void onClick(View textView) { - Intent intent = new Intent(context, HashTagActivity.class); - Bundle b = new Bundle(); - b.putString("tag", tag.substring(1)); - intent.putExtras(b); - context.startActivity(intent); - } - @Override - public void updateDrawState(TextPaint ds) { - super.updateDrawState(ds); - } - }, matchStart, matchEnd, Spanned.SPAN_INCLUSIVE_EXCLUSIVE); + if( matchEnd <= spannableString.toString().length() && matchEnd >= matchStart) + spannableString.setSpan(new ClickableSpan() { + @Override + public void onClick(View textView) { + Intent intent = new Intent(context, HashTagActivity.class); + Bundle b = new Bundle(); + b.putString("tag", tag.substring(1)); + intent.putExtras(b); + context.startActivity(intent); + } + @Override + public void updateDrawState(TextPaint ds) { + super.updateDrawState(ds); + ds.setUnderlineText(false); + if (theme == THEME_DARK) + ds.setColor(ContextCompat.getColor(context, R.color.dark_link_toot)); + else if (theme == THEME_BLACK) + ds.setColor(ContextCompat.getColor(context, R.color.black_link_toot)); + else if (theme == THEME_LIGHT) + ds.setColor(ContextCompat.getColor(context, R.color.mastodonC4)); + } + }, matchStart, matchEnd, Spanned.SPAN_INCLUSIVE_EXCLUSIVE); } - - Matcher matcherMention = mentionPattern.matcher(spannableString); - while (matcherMention.find()){ - int matchStart = matcherMention.start(1); - int matchEnd = matcherMention.end(); - final String search = spannableString.toString().substring(matchStart, matchEnd); - final String finalFullContent = fullContent; - spannableString.setSpan(new ClickableSpan() { - @Override - public void onClick(View textView) { - if(!isPerformingSearch){ - isPerformingSearch = true; - - AsyncTask.execute(new Runnable() { - @Override - public void run() { - try { - String[] val = search.split("@"); - if( val.length > 0 ) { - String username; - if( val.length == 2){ - username = val[1]; - Pattern urlAccountPattern = Pattern.compile( - "https://[\\w._-]+/@"+ username); - Matcher matcherAccount = urlAccountPattern.matcher(finalFullContent); - while (matcherAccount.find()){ - String url = matcherAccount.group(0); - API api = new API(context); - Results results = api.search(url); - if( results.getAccounts().size() > 0 ){ - Account account = results.getAccounts().get(0); - Intent intent = new Intent(context, ShowAccountActivity.class); - Bundle b = new Bundle(); - b.putString("accountId", account.getId()); - intent.putExtras(b); - context.startActivity(intent); - } - } - } - } - isPerformingSearch = false; - }catch (Exception e){ - isPerformingSearch = false; - Toast.makeText(context,R.string.toast_error, Toast.LENGTH_SHORT).show(); - } - } - }); + if( accountsMentionUnknown.size() > 0 ) { + for(Account account: accountsMentionUnknown){ + String targetedAccount = "@" + account.getAcct(); + if (spannableString.toString().toLowerCase().contains(targetedAccount.toLowerCase())) { + //Accounts can be mentioned several times so we have to loop + for(int startPosition = -1 ; (startPosition = spannableString.toString().toLowerCase().indexOf(targetedAccount.toLowerCase(), startPosition + 1)) != -1 ; startPosition++){ + int endPosition = startPosition + targetedAccount.length(); + if( endPosition <= spannableString.toString().length() && endPosition >= startPosition) + spannableString.setSpan(new ClickableSpan() { + @Override + public void onClick(View textView) { + CrossActions.doCrossProfile(context,account); + } + @Override + public void updateDrawState(TextPaint ds) { + super.updateDrawState(ds); + ds.setUnderlineText(false); + if (theme == THEME_DARK) + ds.setColor(ContextCompat.getColor(context, R.color.dark_link_toot)); + else if (theme == THEME_BLACK) + ds.setColor(ContextCompat.getColor(context, R.color.black_link_toot)); + else if (theme == THEME_LIGHT) + ds.setColor(ContextCompat.getColor(context, R.color.mastodonC4)); + } + }, + startPosition, endPosition, + Spanned.SPAN_INCLUSIVE_EXCLUSIVE); } } - @Override - public void updateDrawState(TextPaint ds) { - super.updateDrawState(ds); - } - }, matchStart, matchEnd, Spanned.SPAN_INCLUSIVE_EXCLUSIVE); + } } + return spannableString; } @@ -2277,23 +2284,29 @@ public class Helper { } } if( url == null) { - Glide.with(imageView.getContext()) - .load(R.drawable.missing) - .apply(new RequestOptions().transforms(new CenterCrop(), new RoundedCorners(10))) - .into(imageView); - return; + try { + Glide.with(imageView.getContext()) + .load(R.drawable.missing) + .apply(new RequestOptions().transforms(new CenterCrop(), new RoundedCorners(10))) + .into(imageView); + }catch (Exception ignored){} + return; } if( !disableGif) - Glide.with(imageView.getContext()) - .load(url) - .apply(new RequestOptions().transforms(new CenterCrop(), new RoundedCorners(10))) - .into(imageView); + try { + Glide.with(imageView.getContext()) + .load(url) + .apply(new RequestOptions().transforms(new CenterCrop(), new RoundedCorners(10))) + .into(imageView); + }catch (Exception ignored){} else + try { Glide.with(context) .asBitmap() .apply(new RequestOptions().transforms(new CenterCrop(), new RoundedCorners(10))) .load(url) .into(imageView); + }catch (Exception ignored){} } /** diff --git a/app/src/main/java/fr/gouv/etalab/mastodon/services/LiveNotificationService.java b/app/src/main/java/fr/gouv/etalab/mastodon/services/LiveNotificationService.java index 0b4599f1a..b277b25af 100644 --- a/app/src/main/java/fr/gouv/etalab/mastodon/services/LiveNotificationService.java +++ b/app/src/main/java/fr/gouv/etalab/mastodon/services/LiveNotificationService.java @@ -182,7 +182,8 @@ public class LiveNotificationService extends Service implements NetworkStateRece AsyncHttpRequest.setDefaultHeaders(headers, url); if( webSocketFutures.containsKey(urlKey) ){ try { - webSocketFutures.get(urlKey).get().close(); + if( webSocketFutures.get(urlKey) != null && webSocketFutures.get(urlKey).get() != null) + webSocketFutures.get(urlKey).get().close(); } catch (ExecutionException e) { e.printStackTrace(); } catch (InterruptedException e) {