From bf98d474bf8390a9c4a29aadbc6838c105b4482f Mon Sep 17 00:00:00 2001 From: Alex Baker Date: Wed, 1 Apr 2020 09:35:47 -0500 Subject: [PATCH] Improve link click handling --- app/build.gradle.kts | 1 - app/licenses.yml | 6 - app/src/main/assets/licenses.json | 16 -- .../main/java/org/tasks/dialogs/Linkify.java | 141 +++++++++++------- .../java/org/tasks/tasklist/ViewHolder.java | 7 +- .../main/res/layout/task_adapter_row_body.xml | 4 +- 6 files changed, 91 insertions(+), 84 deletions(-) diff --git a/app/build.gradle.kts b/app/build.gradle.kts index 9451581d5..22581ec9d 100644 --- a/app/build.gradle.kts +++ b/app/build.gradle.kts @@ -160,7 +160,6 @@ dependencies { implementation("org.jetbrains.kotlin:kotlin-stdlib:${Versions.kotlin}") implementation("io.github.luizgrp.sectionedrecyclerviewadapter:sectionedrecyclerviewadapter:2.0.0") implementation("androidx.multidex:multidex:2.0.1") - implementation("me.saket:better-link-movement-method:2.2.0") implementation("com.squareup.okhttp3:okhttp:${Versions.okhttp}") implementation("com.google.code.gson:gson:2.8.6") implementation("com.google.android.material:material:1.1.0") diff --git a/app/licenses.yml b/app/licenses.yml index ee423e93e..bb8539ed8 100644 --- a/app/licenses.yml +++ b/app/licenses.yml @@ -477,12 +477,6 @@ license: The Apache Software License, Version 2.0 licenseUrl: http://www.apache.org/licenses/LICENSE-2.0.txt url: https://developer.android.com/topic/libraries/architecture/index.html -- artifact: me.saket:better-link-movement-method:+ - name: BetterLinkMovementMethod - copyrightHolder: Saket Narayan - license: The Apache Software License, Version 2.0 - licenseUrl: http://www.apache.org/licenses/LICENSE-2.0.txt - url: https://github.com/Saketme/BetterLinkMovementMethod - artifact: androidx.drawerlayout:drawerlayout:+ name: Android Support Library Drawer Layout copyrightHolder: Android Open Source Project diff --git a/app/src/main/assets/licenses.json b/app/src/main/assets/licenses.json index c841f47fe..436e97209 100644 --- a/app/src/main/assets/licenses.json +++ b/app/src/main/assets/licenses.json @@ -1280,22 +1280,6 @@ "version": "2.2.5" } }, - { - "notice": null, - "copyrightHolder": "Saket Narayan", - "copyrightStatement": "Copyright © Saket Narayan. All rights reserved.", - "license": "The Apache Software License, Version 2.0", - "licenseUrl": "http://www.apache.org/licenses/LICENSE-2.0.txt", - "normalizedLicense": "apache2", - "year": null, - "url": "https://github.com/Saketme/BetterLinkMovementMethod", - "libraryName": "BetterLinkMovementMethod", - "artifactId": { - "name": "better-link-movement-method", - "group": "me.saket", - "version": "2.2.0" - } - }, { "notice": null, "copyrightHolder": "Android Open Source Project", diff --git a/app/src/main/java/org/tasks/dialogs/Linkify.java b/app/src/main/java/org/tasks/dialogs/Linkify.java index b5a270104..8a53a8fcb 100644 --- a/app/src/main/java/org/tasks/dialogs/Linkify.java +++ b/app/src/main/java/org/tasks/dialogs/Linkify.java @@ -5,12 +5,16 @@ import static java.util.Arrays.asList; import android.content.Context; import android.content.Intent; import android.net.Uri; +import android.text.Spannable; +import android.text.SpannableString; +import android.text.SpannableStringBuilder; +import android.text.style.URLSpan; +import android.view.View; import android.widget.TextView; import com.google.common.base.Strings; import java.io.UnsupportedEncodingException; import java.net.URLDecoder; import javax.inject.Inject; -import me.saket.bettermovementmethod.BetterLinkMovementMethod; import org.tasks.R; import org.tasks.injection.ForActivity; @@ -26,68 +30,97 @@ public class Linkify { } public void linkify(TextView textView) { - linkify(textView, () -> {}, () -> {}); + linkify(textView, () -> {}); } - public void linkify(TextView textView, Runnable onClick, Runnable onLongClick) { + public void linkify(TextView textView, Runnable onClick) { if (textView.length() == 0) { return; } - BetterLinkMovementMethod.linkify(android.text.util.Linkify.ALL, textView) - .setOnLinkClickListener((tv, url) -> handleLink(url, onClick)) - .setOnLinkLongClickListener( - (tv, url) -> { - onLongClick.run(); - return true; - }); + android.text.util.Linkify.addLinks(textView, android.text.util.Linkify.ALL); + + textView.setOnClickListener( + v -> { + if (textView.getSelectionStart() == -1 && textView.getSelectionEnd() == -1) { + onClick.run(); + } + }); + + URLSpan[] spans; + Spannable spannable; + CharSequence text = textView.getText(); + if (text instanceof SpannableStringBuilder || text instanceof SpannableString) { + spannable = (Spannable) text; + } else { + return; + } + spans = spannable.getSpans(0, text.length(), URLSpan.class); + for (URLSpan span : spans) { + int start = spannable.getSpanStart(span); + int end = spannable.getSpanEnd(span); + + spannable.removeSpan(span); + spannable.setSpan(new ClickHandlingURLSpan(span.getURL(), onClick), start, end, 0); + } } - private boolean handleLink(String url, Runnable onEdit) { - String title; - String edit = context.getString(R.string.TAd_actionEditTask); - String action; - Uri uri = Uri.parse(url); - String scheme = uri.getScheme(); - if (Strings.isNullOrEmpty(scheme)) { - scheme = ""; + private class ClickHandlingURLSpan extends URLSpan { + + private final Runnable onEdit; + + ClickHandlingURLSpan(String url, Runnable onEdit) { + super(url); + this.onEdit = onEdit; } - switch (scheme) { - case "tel": - title = uri.getEncodedSchemeSpecificPart(); - action = context.getString(R.string.action_call); - break; - case "mailto": - title = uri.getEncodedSchemeSpecificPart(); - action = context.getString(R.string.action_open); - break; - case "geo": - title = uri.getEncodedQuery().replaceFirst("q=", ""); - try { - title = URLDecoder.decode(title, "utf-8"); - } catch (UnsupportedEncodingException ignored) { - } - action = context.getString(R.string.action_open); - break; - default: - title = url; - action = context.getString(R.string.action_open); - break; + + @Override + public void onClick(View widget) { + String title; + String edit = context.getString(R.string.TAd_actionEditTask); + String action; + String url = getURL(); + Uri uri = Uri.parse(url); + String scheme = uri.getScheme(); + if (Strings.isNullOrEmpty(scheme)) { + scheme = ""; + } + switch (scheme) { + case "tel": + title = uri.getEncodedSchemeSpecificPart(); + action = context.getString(R.string.action_call); + break; + case "mailto": + title = uri.getEncodedSchemeSpecificPart(); + action = context.getString(R.string.action_open); + break; + case "geo": + title = uri.getEncodedQuery().replaceFirst("q=", ""); + try { + title = URLDecoder.decode(title, "utf-8"); + } catch (UnsupportedEncodingException ignored) { + } + action = context.getString(R.string.action_open); + break; + default: + title = url; + action = context.getString(R.string.action_open); + break; + } + dialogBuilder + .newDialog(title) + .setItems( + asList(action, edit), + (dialogInterface, selected) -> { + if (selected == 0) { + Intent intent = new Intent(Intent.ACTION_VIEW); + intent.setData(uri); + context.startActivity(intent); + } else { + onEdit.run(); + } + }) + .show(); } - dialogBuilder - .newDialog(title) - .setItems( - asList(action, edit), - (dialogInterface, selected) -> { - if (selected == 0) { - Intent intent = new Intent(Intent.ACTION_VIEW); - intent.setData(uri); - context.startActivity(intent); - } else { - onEdit.run(); - } - }) - .show(); - return true; } } diff --git a/app/src/main/java/org/tasks/tasklist/ViewHolder.java b/app/src/main/java/org/tasks/tasklist/ViewHolder.java index 8947c8a47..4dbcc7c6d 100644 --- a/app/src/main/java/org/tasks/tasklist/ViewHolder.java +++ b/app/src/main/java/org/tasks/tasklist/ViewHolder.java @@ -25,7 +25,6 @@ import com.todoroo.astrid.ui.CheckableImageView; import java.util.List; import org.tasks.R; import org.tasks.data.TaskContainer; -import org.tasks.dialogs.DateTimePicker; import org.tasks.dialogs.Linkify; import org.tasks.locale.Locale; import org.tasks.preferences.Preferences; @@ -209,11 +208,9 @@ public class ViewHolder extends RecyclerView.ViewHolder { description.setVisibility(task.hasNotes() ? View.VISIBLE : View.GONE); } if (preferences.getBoolean(R.string.p_linkify_task_list, false)) { - linkify.linkify(nameView, this::onRowBodyClick, this::onRowBodyLongClick); - linkify.linkify(description, this::onRowBodyClick, this::onRowBodyLongClick); - nameView.setOnClickListener(view -> onRowBodyClick()); + linkify.linkify(nameView, this::onRowBodyClick); + linkify.linkify(description, this::onRowBodyClick); nameView.setOnLongClickListener(view -> onRowBodyLongClick()); - description.setOnClickListener(view -> onRowBodyClick()); description.setOnLongClickListener(view -> onRowBodyLongClick()); } if (chipGroup.getVisibility() == View.VISIBLE) { diff --git a/app/src/main/res/layout/task_adapter_row_body.xml b/app/src/main/res/layout/task_adapter_row_body.xml index a8707c954..359546183 100644 --- a/app/src/main/res/layout/task_adapter_row_body.xml +++ b/app/src/main/res/layout/task_adapter_row_body.xml @@ -34,7 +34,7 @@