diff --git a/.gitignore b/.gitignore index 2e546a48477eb69a69f779aa42b8b6108d868668..37a4eb8b43d978ea05b2c6e3fb934f9b40dddd4d 100644 --- a/.gitignore +++ b/.gitignore @@ -1,11 +1,15 @@ *.iml .gradle /local.properties -/.idea/workspace.xml +/.idea/caches /.idea/libraries +/.idea/modules.xml +/.idea/workspace.xml +/.idea/navEditor.xml +/.idea/assetWizardSettings.xml .DS_Store /build /captures -/.idea -private_key_sender.asc -public_key_sender.asc \ No newline at end of file +.externalNativeBuild +/entry/.preview +.cxx diff --git a/CHANGELOG.md b/CHANGELOG.md new file mode 100644 index 0000000000000000000000000000000000000000..703b39277d3c15b17231330f330f74ebfe120ce2 --- /dev/null +++ b/CHANGELOG.md @@ -0,0 +1,6 @@ +## 0.0.1-SNAPSHOT +ohos 第一个版本 +* 完全实现了原库的大部分api +* 因为暂不支持跳转到新建联系人页面传递电话号,所以改成了跳转新建联系人页面 + + diff --git a/EXAMPLE.gif b/EXAMPLE.gif deleted file mode 100644 index c8f7ca620cdd9e2b662de3a2321cb36c3990417b..0000000000000000000000000000000000000000 Binary files a/EXAMPLE.gif and /dev/null differ diff --git a/LICENSE.md b/LICENSE similarity index 100% rename from LICENSE.md rename to LICENSE diff --git a/README.OPENSOURCE b/README.OPENSOURCE new file mode 100644 index 0000000000000000000000000000000000000000..a8b2f5be66e1e615b8cbb726f4030886b02e360e --- /dev/null +++ b/README.OPENSOURCE @@ -0,0 +1,10 @@ +[ + { + "Name": " Better-Link-Movement-Method ", + "License": " Apache License ", + "License File": " LICENSE ", + "Version Number": " Release v2.2.0 ", + "Upstream URL": " https://github.com/saket/Better-Link-Movement-Method ", + "Description": " 实现链接跳转系统应用 " + } +] diff --git a/README.md b/README.md index 2e09545da9651d0c91a59bc7fe1a6ab16bff762e..f8d1e17c3bff2f40c1a92073dfbd21008e9e53a3 100644 --- a/README.md +++ b/README.md @@ -1,47 +1,94 @@ -# BetterLinkMovementMethod +# Better-Link-Movement-Method -![Better-Link-Movement-Method](https://github.com/Saketme/Better-Link-Movement-Method/blob/master/EXAMPLE.gif) +#### 项目介绍 +- 项目名称:Better-Link-Movement-Method +- 所属系列:openharmony的第三方组件适配移植 +- 功能:实现链接跳转系统应用 +- 项目移植状态:主功能完成 +- 调用差异:无 +- 开发版本:sdk5,DevEco Studio 2.1 Release +- 基线版本:Release v2.2.0 -When `android:autoLink` or `Linkify.addLinks` is used to add links to a TextView, Android uses a class known as `LinkMovementMethod` for highlighting links when they're focused and dispatching Intents when they're clicked. +#### 效果演示 -BetterLinkMovementMethod improves over `LinkMovementMethod`, by fixing some of its flaws: +![效果演示](./img/example.gif) +![效果演示](./img/example2.gif) +![效果演示](./img/example3.gif) +![效果演示](./img/example4.gif) -* No support for custom click listeners. For e.g., phone numbers always show up in the dialer when clicked and there's no way to change that. -* Incorrect calculation of touch areas for links, resulting in ghost touch areas ([video](http://saket.me/wp-content/uploads/2016/09/Incorrect-touch-areas.mp4)). -* Unreliable highlighting of links ([video](http://saket.me/wp-content/uploads/2016/09/Unreliable-highlighting.mp4)). -A detailed explanation of why (and when) you should use `BetterLinkMovementMethod` can be read here: http://saket.me/better-url-handler-textview-android/ +#### 安装教程 -## Usage -`BetterLinkMovementMethod` is designed to be a drop-in replacement for `LinkMovementMethod`: -```gradle -dependencies { - implementation 'me.saket:better-link-movement-method:2.2.0' +1、在项目根目录下的build.gradle文件中 + ```gradle +allprojects { + repositories { + maven { + url 'https://s01.oss.sonatype.org/content/repositories/snapshots/' + } + } } ``` +2.在entry模块的build.gradle文件中 + ```gradle + dependencies { + implementation('com.gitee.chinasoft_ohos:Better-Link-Movement-Method:0.0.1-SNAPSHOT') + ...... + } +``` + +在sdk5,DevEco Studio 2.1 Release下项目可直接运行 +如无法运行,删除项目.gradle,.idea,build,gradle,build.gradle文件, +并依据自己的版本创建新项目,将新项目的对应文件复制到根目录下 + +#### 使用说明 + ``` + +使用该库非常简单,只需查看提供的示例的源代码。 +```示例XML + + + -```kotlin -val textView = findViewById(...) -Linkify.addLinks(textView, Linkify.ALL) -textView.movementMethod = BetterLinkMovementMethod.getInstance() ``` -Click listeners can be registered by creating a unique instance of `BetterLinkMovementMethod` for each `TextView`: - -```kotlin -textView.movementMethod = BetterLinkMovementMethod.newInstance().apply { - setOnLinkClickListener { textView, url -> - // Handle click or return false to let the framework handle this link. - true - } - setOnLinkLongClickListener { textView, url -> - // Handle long-click or return false to let the framework handle this link. - true - } +```java + Intent transferDialogKey = new Intent(); + Operation operation = new Intent.OperationBuilder() + .withAction(IntentConstants.ACTION_DIAL) + .withUri(Uri.parse("tel:" + "7899859911")) + .build(); + transferDialogKey.setOperation(operation); + startAbility(transferDialogKey); + ``` -## License +#### 测试信息 + +CodeCheck代码测试无异常 +CloudTest代码测试无异常 + +病毒安全检测通过 + +当前版本demo功能与原项目组件基本无差异 + + +#### 版本迭代 + +- 0.0.1-SNAPSHOT + +#### 版权和许可信息 ``` Copyright 2018 Saket Narayan. @@ -56,4 +103,5 @@ distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License. + ``` diff --git a/better-link-movement-method/build.gradle b/better-link-movement-method/build.gradle deleted file mode 100644 index f5696826f564ebdfe6cb586f108242b6adfdcdaf..0000000000000000000000000000000000000000 --- a/better-link-movement-method/build.gradle +++ /dev/null @@ -1,50 +0,0 @@ -apply plugin: 'com.android.library' - -android { - compileSdkVersion versions.compileSdk - resourcePrefix "bettermovementmethod_" - - defaultConfig { - minSdkVersion versions.minSdk - targetSdkVersion versions.targetSdk - versionCode versions.libraryVersionCode - versionName versions.libraryVersionName - } - - buildTypes { - release { - minifyEnabled false - proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro' - } - } -} - -ext { - bintrayRepo = 'maven' - bintrayName = 'better-link-movement-method' - - publishedGroupId = 'me.saket' - artifact = 'better-link-movement-method' - libraryName = 'BetterLinkMovementMethod' - libraryVersion = versions.libraryVersionName - - libraryDescription = 'A less buggy and customizable LinkMovementMethod for Android' - - siteUrl = 'https://github.com/Saketme/BetterLinkMovementMethod' - gitUrl = 'https://github.com/Saketme/BetterLinkMovementMethod.git' - - developerId = 'saketme' - developerName = 'Saket Narayan' - developerEmail = 'saket@saket.me' - - licenseName = 'Apache License v2' - licenseUrl = 'http://www.apache.org/licenses/LICENSE-2.0' - allLicenses = ["Apache-2.0"] -} - -dependencies { -} - -// Task for uploading JAR to maven: "./gradlew better-link-movement-method:bintrayUpload". -apply from: 'https://raw.githubusercontent.com/nuuneoi/JCenter/master/installv1.gradle' -apply from: 'https://raw.githubusercontent.com/nuuneoi/JCenter/master/bintrayv1.gradle' diff --git a/better-link-movement-method/proguard-rules.pro b/better-link-movement-method/proguard-rules.pro deleted file mode 100644 index d51f03aa9bec823aa89eb23e63623a2e05ebc0a9..0000000000000000000000000000000000000000 --- a/better-link-movement-method/proguard-rules.pro +++ /dev/null @@ -1,17 +0,0 @@ -# Add project specific ProGuard rules here. -# By default, the flags in this file are appended to flags specified -# in /Users/Saket/Library/Android/sdk/tools/proguard/proguard-android.txt -# You can edit the include path and order by changing the proguardFiles -# directive in build.gradle. -# -# For more details, see -# http://developer.android.com/guide/developing/tools/proguard.html - -# Add any project specific keep options here: - -# If your project uses WebView with JS, uncomment the following -# and specify the fully qualified class name to the JavaScript interface -# class: -#-keepclassmembers class fqcn.of.javascript.interface.for.webview { -# public *; -#} diff --git a/better-link-movement-method/src/main/AndroidManifest.xml b/better-link-movement-method/src/main/AndroidManifest.xml deleted file mode 100644 index 3947999037039ea6de041a415d5eaae6fd605d75..0000000000000000000000000000000000000000 --- a/better-link-movement-method/src/main/AndroidManifest.xml +++ /dev/null @@ -1 +0,0 @@ - diff --git a/better-link-movement-method/src/main/java/me/saket/bettermovementmethod/BetterLinkMovementMethod.java b/better-link-movement-method/src/main/java/me/saket/bettermovementmethod/BetterLinkMovementMethod.java deleted file mode 100644 index b2c229fe9938362bafb24973fbf8316c8c137386..0000000000000000000000000000000000000000 --- a/better-link-movement-method/src/main/java/me/saket/bettermovementmethod/BetterLinkMovementMethod.java +++ /dev/null @@ -1,455 +0,0 @@ -package me.saket.bettermovementmethod; - -import android.app.Activity; -import android.graphics.RectF; -import android.text.Layout; -import android.text.Selection; -import android.text.Spannable; -import android.text.Spanned; -import android.text.method.LinkMovementMethod; -import android.text.style.BackgroundColorSpan; -import android.text.style.ClickableSpan; -import android.text.style.URLSpan; -import android.text.util.Linkify; -import android.view.HapticFeedbackConstants; -import android.view.MotionEvent; -import android.view.View; -import android.view.ViewConfiguration; -import android.view.ViewGroup; -import android.view.Window; -import android.widget.TextView; - -/** - * Handles URL clicks on TextViews. Unlike the default implementation, this: - *

- *

    - *
  • Reliably applies a highlight color on links when they're touched.
  • - *
  • Let's you handle single and long clicks on URLs
  • - *
  • Correctly identifies focused URLs (Unlike the default implementation where a click is registered even if it's - * made outside of the URL's bounds if there is no more text in that direction.)
  • - *
- */ -public class BetterLinkMovementMethod extends LinkMovementMethod { - - private static BetterLinkMovementMethod singleInstance; - private static final int LINKIFY_NONE = -2; - - private OnLinkClickListener onLinkClickListener; - private OnLinkLongClickListener onLinkLongClickListener; - private final RectF touchedLineBounds = new RectF(); - private boolean isUrlHighlighted; - private ClickableSpan clickableSpanUnderTouchOnActionDown; - private int activeTextViewHashcode; - private LongPressTimer ongoingLongPressTimer; - private boolean wasLongPressRegistered; - - public interface OnLinkClickListener { - /** - * @param textView The TextView on which a click was registered. - * @param url The clicked URL. - * @return True if this click was handled. False to let Android handle the URL. - */ - boolean onClick(TextView textView, String url); - } - - public interface OnLinkLongClickListener { - /** - * @param textView The TextView on which a long-click was registered. - * @param url The long-clicked URL. - * @return True if this long-click was handled. False to let Android handle the URL (as a short-click). - */ - boolean onLongClick(TextView textView, String url); - } - - /** - * Return a new instance of BetterLinkMovementMethod. - */ - public static BetterLinkMovementMethod newInstance() { - return new BetterLinkMovementMethod(); - } - - /** - * @param linkifyMask One of {@link Linkify#ALL}, {@link Linkify#PHONE_NUMBERS}, {@link Linkify#MAP_ADDRESSES}, - * {@link Linkify#WEB_URLS} and {@link Linkify#EMAIL_ADDRESSES}. - * @param textViews The TextViews on which a {@link BetterLinkMovementMethod} should be registered. - * @return The registered {@link BetterLinkMovementMethod} on the TextViews. - */ - public static BetterLinkMovementMethod linkify(int linkifyMask, TextView... textViews) { - BetterLinkMovementMethod movementMethod = newInstance(); - for (TextView textView : textViews) { - addLinks(linkifyMask, movementMethod, textView); - } - return movementMethod; - } - - /** - * Like {@link #linkify(int, TextView...)}, but can be used for TextViews with HTML links. - * - * @param textViews The TextViews on which a {@link BetterLinkMovementMethod} should be registered. - * @return The registered {@link BetterLinkMovementMethod} on the TextViews. - */ - public static BetterLinkMovementMethod linkifyHtml(TextView... textViews) { - return linkify(LINKIFY_NONE, textViews); - } - - /** - * Recursively register a {@link BetterLinkMovementMethod} on every TextView inside a layout. - * - * @param linkifyMask One of {@link Linkify#ALL}, {@link Linkify#PHONE_NUMBERS}, {@link Linkify#MAP_ADDRESSES}, - * {@link Linkify#WEB_URLS} and {@link Linkify#EMAIL_ADDRESSES}. - * @return The registered {@link BetterLinkMovementMethod} on the TextViews. - */ - public static BetterLinkMovementMethod linkify(int linkifyMask, ViewGroup viewGroup) { - BetterLinkMovementMethod movementMethod = newInstance(); - rAddLinks(linkifyMask, viewGroup, movementMethod); - return movementMethod; - } - - /** - * Like {@link #linkify(int, TextView...)}, but can be used for TextViews with HTML links. - * - * @return The registered {@link BetterLinkMovementMethod} on the TextViews. - */ - @SuppressWarnings("unused") - public static BetterLinkMovementMethod linkifyHtml(ViewGroup viewGroup) { - return linkify(LINKIFY_NONE, viewGroup); - } - - /** - * Recursively register a {@link BetterLinkMovementMethod} on every TextView inside a layout. - * - * @param linkifyMask One of {@link Linkify#ALL}, {@link Linkify#PHONE_NUMBERS}, {@link Linkify#MAP_ADDRESSES}, - * {@link Linkify#WEB_URLS} and {@link Linkify#EMAIL_ADDRESSES}. - * @return The registered {@link BetterLinkMovementMethod} on the TextViews. - */ - public static BetterLinkMovementMethod linkify(int linkifyMask, Activity activity) { - // Find the layout passed to setContentView(). - ViewGroup activityLayout = ((ViewGroup) ((ViewGroup) activity.findViewById(Window.ID_ANDROID_CONTENT)).getChildAt(0)); - - BetterLinkMovementMethod movementMethod = newInstance(); - rAddLinks(linkifyMask, activityLayout, movementMethod); - return movementMethod; - } - - /** - * Like {@link #linkify(int, TextView...)}, but can be used for TextViews with HTML links. - * - * @return The registered {@link BetterLinkMovementMethod} on the TextViews. - */ - @SuppressWarnings("unused") - public static BetterLinkMovementMethod linkifyHtml(Activity activity) { - return linkify(LINKIFY_NONE, activity); - } - - /** - * Get a static instance of BetterLinkMovementMethod. Do note that registering a click listener on the returned - * instance is not supported because it will potentially be shared on multiple TextViews. - */ - @SuppressWarnings("unused") - public static BetterLinkMovementMethod getInstance() { - if (singleInstance == null) { - singleInstance = new BetterLinkMovementMethod(); - } - return singleInstance; - } - - protected BetterLinkMovementMethod() { - } - - /** - * Set a listener that will get called whenever any link is clicked on the TextView. - */ - public BetterLinkMovementMethod setOnLinkClickListener(OnLinkClickListener clickListener) { - if (this == singleInstance) { - throw new UnsupportedOperationException("Setting a click listener on the instance returned by getInstance() is not supported to avoid memory " + - "leaks. Please use newInstance() or any of the linkify() methods instead."); - } - - this.onLinkClickListener = clickListener; - return this; - } - - /** - * Set a listener that will get called whenever any link is clicked on the TextView. - */ - public BetterLinkMovementMethod setOnLinkLongClickListener(OnLinkLongClickListener longClickListener) { - if (this == singleInstance) { - throw new UnsupportedOperationException("Setting a long-click listener on the instance returned by getInstance() is not supported to avoid " + - "memory leaks. Please use newInstance() or any of the linkify() methods instead."); - } - - this.onLinkLongClickListener = longClickListener; - return this; - } - -// ======== PUBLIC APIs END ======== // - - private static void rAddLinks(int linkifyMask, ViewGroup viewGroup, BetterLinkMovementMethod movementMethod) { - for (int i = 0; i < viewGroup.getChildCount(); i++) { - View child = viewGroup.getChildAt(i); - - if (child instanceof ViewGroup) { - // Recursively find child TextViews. - rAddLinks(linkifyMask, ((ViewGroup) child), movementMethod); - } else if (child instanceof TextView) { - TextView textView = (TextView) child; - addLinks(linkifyMask, movementMethod, textView); - } - } - } - - private static void addLinks(int linkifyMask, BetterLinkMovementMethod movementMethod, TextView textView) { - textView.setMovementMethod(movementMethod); - if (linkifyMask != LINKIFY_NONE) { - Linkify.addLinks(textView, linkifyMask); - } - } - - @Override - public boolean onTouchEvent(final TextView textView, Spannable text, MotionEvent event) { - if (activeTextViewHashcode != textView.hashCode()) { - // Bug workaround: TextView stops calling onTouchEvent() once any URL is highlighted. - // A hacky solution is to reset any "autoLink" property set in XML. But we also want - // to do this once per TextView. - activeTextViewHashcode = textView.hashCode(); - textView.setAutoLinkMask(0); - } - - final ClickableSpan clickableSpanUnderTouch = findClickableSpanUnderTouch(textView, text, event); - if (event.getAction() == MotionEvent.ACTION_DOWN) { - clickableSpanUnderTouchOnActionDown = clickableSpanUnderTouch; - } - final boolean touchStartedOverAClickableSpan = clickableSpanUnderTouchOnActionDown != null; - - switch (event.getAction()) { - case MotionEvent.ACTION_DOWN: - if (clickableSpanUnderTouch != null) { - highlightUrl(textView, clickableSpanUnderTouch, text); - } - - if (touchStartedOverAClickableSpan && onLinkLongClickListener != null) { - LongPressTimer.OnTimerReachedListener longClickListener = new LongPressTimer.OnTimerReachedListener() { - @Override - public void onTimerReached() { - wasLongPressRegistered = true; - textView.performHapticFeedback(HapticFeedbackConstants.LONG_PRESS); - removeUrlHighlightColor(textView); - dispatchUrlLongClick(textView, clickableSpanUnderTouch); - } - }; - startTimerForRegisteringLongClick(textView, longClickListener); - } - return touchStartedOverAClickableSpan; - - case MotionEvent.ACTION_UP: - // Register a click only if the touch started and ended on the same URL. - if (!wasLongPressRegistered && touchStartedOverAClickableSpan && clickableSpanUnderTouch == clickableSpanUnderTouchOnActionDown) { - dispatchUrlClick(textView, clickableSpanUnderTouch); - } - cleanupOnTouchUp(textView); - - // Consume this event even if we could not find any spans to avoid letting Android handle this event. - // Android's TextView implementation has a bug where links get clicked even when there is no more text - // next to the link and the touch lies outside its bounds in the same direction. - return touchStartedOverAClickableSpan; - - case MotionEvent.ACTION_CANCEL: - cleanupOnTouchUp(textView); - return false; - - case MotionEvent.ACTION_MOVE: - // Stop listening for a long-press as soon as the user wanders off to unknown lands. - if (clickableSpanUnderTouch != clickableSpanUnderTouchOnActionDown) { - removeLongPressCallback(textView); - } - - if (!wasLongPressRegistered) { - // Toggle highlight. - if (clickableSpanUnderTouch != null) { - highlightUrl(textView, clickableSpanUnderTouch, text); - } else { - removeUrlHighlightColor(textView); - } - } - - return touchStartedOverAClickableSpan; - - default: - return false; - } - } - - private void cleanupOnTouchUp(TextView textView) { - wasLongPressRegistered = false; - clickableSpanUnderTouchOnActionDown = null; - removeUrlHighlightColor(textView); - removeLongPressCallback(textView); - } - - /** - * Determines the touched location inside the TextView's text and returns the ClickableSpan found under it (if any). - * - * @return The touched ClickableSpan or null. - */ - protected ClickableSpan findClickableSpanUnderTouch(TextView textView, Spannable text, MotionEvent event) { - // So we need to find the location in text where touch was made, regardless of whether the TextView - // has scrollable text. That is, not the entire text is currently visible. - int touchX = (int) event.getX(); - int touchY = (int) event.getY(); - - // Ignore padding. - touchX -= textView.getTotalPaddingLeft(); - touchY -= textView.getTotalPaddingTop(); - - // Account for scrollable text. - touchX += textView.getScrollX(); - touchY += textView.getScrollY(); - - final Layout layout = textView.getLayout(); - final int touchedLine = layout.getLineForVertical(touchY); - final int touchOffset = layout.getOffsetForHorizontal(touchedLine, touchX); - - touchedLineBounds.left = layout.getLineLeft(touchedLine); - touchedLineBounds.top = layout.getLineTop(touchedLine); - touchedLineBounds.right = layout.getLineWidth(touchedLine) + touchedLineBounds.left; - touchedLineBounds.bottom = layout.getLineBottom(touchedLine); - - if (touchedLineBounds.contains(touchX, touchY)) { - // Find a ClickableSpan that lies under the touched area. - final Object[] spans = text.getSpans(touchOffset, touchOffset, ClickableSpan.class); - for (final Object span : spans) { - if (span instanceof ClickableSpan) { - return (ClickableSpan) span; - } - } - // No ClickableSpan found under the touched location. - return null; - - } else { - // Touch lies outside the line's horizontal bounds where no spans should exist. - return null; - } - } - - /** - * Adds a background color span at clickableSpan's location. - */ - protected void highlightUrl(TextView textView, ClickableSpan clickableSpan, Spannable text) { - if (isUrlHighlighted) { - return; - } - isUrlHighlighted = true; - - int spanStart = text.getSpanStart(clickableSpan); - int spanEnd = text.getSpanEnd(clickableSpan); - BackgroundColorSpan highlightSpan = new BackgroundColorSpan(textView.getHighlightColor()); - text.setSpan(highlightSpan, spanStart, spanEnd, Spannable.SPAN_INCLUSIVE_INCLUSIVE); - - textView.setTag(R.id.bettermovementmethod_highlight_background_span, highlightSpan); - - Selection.setSelection(text, spanStart, spanEnd); - } - - /** - * Removes the highlight color under the Url. - */ - protected void removeUrlHighlightColor(TextView textView) { - if (!isUrlHighlighted) { - return; - } - isUrlHighlighted = false; - - Spannable text = (Spannable) textView.getText(); - BackgroundColorSpan highlightSpan = (BackgroundColorSpan) textView.getTag(R.id.bettermovementmethod_highlight_background_span); - text.removeSpan(highlightSpan); - - Selection.removeSelection(text); - } - - protected void startTimerForRegisteringLongClick(TextView textView, LongPressTimer.OnTimerReachedListener longClickListener) { - ongoingLongPressTimer = new LongPressTimer(); - ongoingLongPressTimer.setOnTimerReachedListener(longClickListener); - textView.postDelayed(ongoingLongPressTimer, ViewConfiguration.getLongPressTimeout()); - } - - /** - * Remove the long-press detection timer. - */ - protected void removeLongPressCallback(TextView textView) { - if (ongoingLongPressTimer != null) { - textView.removeCallbacks(ongoingLongPressTimer); - ongoingLongPressTimer = null; - } - } - - protected void dispatchUrlClick(TextView textView, ClickableSpan clickableSpan) { - ClickableSpanWithText clickableSpanWithText = ClickableSpanWithText.ofSpan(textView, clickableSpan); - boolean handled = onLinkClickListener != null && onLinkClickListener.onClick(textView, clickableSpanWithText.text()); - - if (!handled) { - // Let Android handle this click. - clickableSpanWithText.span().onClick(textView); - } - } - - protected void dispatchUrlLongClick(TextView textView, ClickableSpan clickableSpan) { - ClickableSpanWithText clickableSpanWithText = ClickableSpanWithText.ofSpan(textView, clickableSpan); - boolean handled = onLinkLongClickListener != null && onLinkLongClickListener.onLongClick(textView, clickableSpanWithText.text()); - - if (!handled) { - // Let Android handle this long click as a short-click. - clickableSpanWithText.span().onClick(textView); - } - } - - protected static final class LongPressTimer implements Runnable { - private OnTimerReachedListener onTimerReachedListener; - - protected interface OnTimerReachedListener { - void onTimerReached(); - } - - @Override - public void run() { - onTimerReachedListener.onTimerReached(); - } - - public void setOnTimerReachedListener(OnTimerReachedListener listener) { - onTimerReachedListener = listener; - } - } - - /** - * A wrapper to support all {@link ClickableSpan}s that may or may not provide URLs. - */ - protected static class ClickableSpanWithText { - private ClickableSpan span; - private String text; - - protected static ClickableSpanWithText ofSpan(TextView textView, ClickableSpan span) { - Spanned s = (Spanned) textView.getText(); - String text; - if (span instanceof URLSpan) { - text = ((URLSpan) span).getURL(); - } else { - int start = s.getSpanStart(span); - int end = s.getSpanEnd(span); - text = s.subSequence(start, end).toString(); - } - return new ClickableSpanWithText(span, text); - } - - protected ClickableSpanWithText(ClickableSpan span, String text) { - this.span = span; - this.text = text; - } - - protected ClickableSpan span() { - return span; - } - - protected String text() { - return text; - } - } -} diff --git a/better-link-movement-method/src/main/res/values/betterlinkmovementmethod.xml b/better-link-movement-method/src/main/res/values/betterlinkmovementmethod.xml deleted file mode 100644 index 2db12fc87c640d9e2ad421be2946b89410e363b3..0000000000000000000000000000000000000000 --- a/better-link-movement-method/src/main/res/values/betterlinkmovementmethod.xml +++ /dev/null @@ -1,4 +0,0 @@ - - - - diff --git a/better-link-movement-method/.gitignore b/better_link_movement_method/.gitignore similarity index 100% rename from better-link-movement-method/.gitignore rename to better_link_movement_method/.gitignore diff --git a/better_link_movement_method/build.gradle b/better_link_movement_method/build.gradle new file mode 100644 index 0000000000000000000000000000000000000000..25ddf0cf1daa8f1533639ebc05ce352f4019b8ff --- /dev/null +++ b/better_link_movement_method/build.gradle @@ -0,0 +1,21 @@ +apply plugin: 'com.huawei.ohos.library' +ohos { + compileSdkVersion 5 + defaultConfig { + compatibleSdkVersion 5 + } + buildTypes { + release { + proguardOpt { + proguardEnabled false + rulesFiles 'proguard-rules.pro' + } + } + } + +} + +dependencies { + implementation fileTree(dir: 'libs', include: ['*.jar']) + testImplementation 'junit:junit:4.13' +} diff --git a/better_link_movement_method/proguard-rules.pro b/better_link_movement_method/proguard-rules.pro new file mode 100644 index 0000000000000000000000000000000000000000..f7666e47561d514b2a76d5a7dfbb43ede86da92a --- /dev/null +++ b/better_link_movement_method/proguard-rules.pro @@ -0,0 +1 @@ +# config module specific ProGuard rules here. \ No newline at end of file diff --git a/better_link_movement_method/src/main/config.json b/better_link_movement_method/src/main/config.json new file mode 100644 index 0000000000000000000000000000000000000000..36cb1409172c8ee5c72e6834b54455874fb629d6 --- /dev/null +++ b/better_link_movement_method/src/main/config.json @@ -0,0 +1,28 @@ +{ + "app": { + "bundleName": "me.saket.bettermovementmethod.sample", + "vendor": "saket", + "version": { + "code": 1000000, + "name": "1.0.0" + }, + "apiVersion": { + "compatible": 5, + "target": 5, + "releaseType": "Release" + } + }, + "deviceConfig": { + }, + "module": { + "package": "me.saket.bettermovementmethod", + "deviceType": [ + "phone" + ], + "distro": { + "deliveryWithInstall": true, + "moduleName": "better_link_movement_method", + "moduleType": "har" + } + } +} \ No newline at end of file diff --git a/better_link_movement_method/src/main/java/me/saket/bettermovementmethod/BetterLinkMovementMethod.java b/better_link_movement_method/src/main/java/me/saket/bettermovementmethod/BetterLinkMovementMethod.java new file mode 100644 index 0000000000000000000000000000000000000000..348653f06f58f0f42983e8c007c68e49fac29673 --- /dev/null +++ b/better_link_movement_method/src/main/java/me/saket/bettermovementmethod/BetterLinkMovementMethod.java @@ -0,0 +1,184 @@ +package me.saket.bettermovementmethod; + +import ohos.aafwk.ability.AbilitySlice; +import ohos.agp.components.Component; +import ohos.agp.components.ComponentContainer; +import ohos.agp.components.Text; +import ohos.agp.text.Layout; +import ohos.agp.utils.RectFloat; +import ohos.agp.window.service.Window; +import ohos.multimodalinput.event.TouchEvent; + +/** + * Handles URL clicks on TextViews. Unlike the default implementation, this: + *

+ *

    + *
  • Reliably applies a highlight color on links when they're touched.
  • + *
  • Let's you handle single and long clicks on URLs
  • + *
  • Correctly identifies focused URLs (Unlike the default implementation where a click is registered even if it's + * made outside of the URL's bounds if there is no more text in that direction.)
  • + *
+ */ +public class BetterLinkMovementMethod{ + + private static BetterLinkMovementMethod singleInstance; + private static final int LINKIFY_NONE = -2; + + private OnLinkClickListener onLinkClickListener; + private OnLinkLongClickListener onLinkLongClickListener; + private final RectFloat touchedLineBounds = new RectFloat(); + private boolean isUrlHighlighted; +// private ClickableSpan clickableSpanUnderTouchOnActionDown; + private int activeTextViewHashcode; + private LongPressTimer ongoingLongPressTimer; + private boolean wasLongPressRegistered; + + public interface OnLinkClickListener { + /** + * @param textView The TextView on which a click was registered. + * @param url The clicked URL. + * @return True if this click was handled. False to let handle the URL. + */ + boolean onClick(Text textView, String url); + } + + public interface OnLinkLongClickListener { + /** + * @param textView The TextView on which a long-click was registered. + * @param url The long-clicked URL. + * @return True if this long-click was handled. False to let handle the URL (as a short-click). + */ + boolean onLongClick(Text textView, String url); + } + + /** + * Return a new instance of BetterLinkMovementMethod. + */ + public static BetterLinkMovementMethod newInstance() { + return new BetterLinkMovementMethod(); + } + + public static BetterLinkMovementMethod linkify(int linkifyMask, Text... textViews) { + BetterLinkMovementMethod movementMethod = newInstance(); + for (Text textView : textViews) { + } + return movementMethod; + } + + /** + * Like {@link #linkify(int, Text...)}, but can be used for TextViews with HTML links. + * + * @param textViews The TextViews on which a {@link BetterLinkMovementMethod} should be registered. + * @return The registered {@link BetterLinkMovementMethod} on the TextViews. + */ + public static BetterLinkMovementMethod linkifyHtml(Text... textViews) { + return linkify(LINKIFY_NONE, textViews); + } + + public static BetterLinkMovementMethod linkify(int linkifyMask, ComponentContainer viewGroup) { + BetterLinkMovementMethod movementMethod = newInstance(); + rAddLinks(linkifyMask, viewGroup, movementMethod); + return movementMethod; + } + + /** + * Like {@link #linkify(int, Text...)}, but can be used for TextViews with HTML links. + * + * @return The registered {@link BetterLinkMovementMethod} on the TextViews. + */ + @SuppressWarnings("unused") + public static BetterLinkMovementMethod linkifyHtml(ComponentContainer viewGroup) { + return linkify(LINKIFY_NONE, viewGroup); + } + + public static BetterLinkMovementMethod linkify(int linkifyMask, AbilitySlice activity) { + + BetterLinkMovementMethod movementMethod = newInstance(); + return movementMethod; + } + + /** + * Like {@link #linkify(int, Text...)}, but can be used for TextViews with HTML links. + * + * @return The registered {@link BetterLinkMovementMethod} on the TextViews. + */ + @SuppressWarnings("unused") + public static BetterLinkMovementMethod linkifyHtml(AbilitySlice activity) { + return linkify(LINKIFY_NONE, activity); + } + + /** + * Get a static instance of BetterLinkMovementMethod. Do note that registering a click listener on the returned + * instance is not supported because it will potentially be shared on multiple TextViews. + */ + @SuppressWarnings("unused") + public static BetterLinkMovementMethod getInstance() { + if (singleInstance == null) { + singleInstance = new BetterLinkMovementMethod(); + } + return singleInstance; + } + + protected BetterLinkMovementMethod() { + } + + /** + * Set a listener that will get called whenever any link is clicked on the TextView. + */ + public BetterLinkMovementMethod setOnLinkClickListener(OnLinkClickListener clickListener) { + /*if (this == singleInstance) { + throw new UnsupportedOperationException("Setting a click listener on the instance returned by getInstance() is not supported to avoid memory " + + "leaks. Please use newInstance() or any of the linkify() methods instead."); + }*/ + + this.onLinkClickListener = clickListener; + return this; + } + + /** + * Set a listener that will get called whenever any link is clicked on the TextView. + */ + public BetterLinkMovementMethod setOnLinkLongClickListener(OnLinkLongClickListener longClickListener) { + if (this == singleInstance) { + throw new UnsupportedOperationException("Setting a long-click listener on the instance returned by getInstance() is not supported to avoid " + + "memory leaks. Please use newInstance() or any of the linkify() methods instead."); + } + + this.onLinkLongClickListener = longClickListener; + return this; + } + +// ======== PUBLIC APIs END ======== // + + private static void rAddLinks(int linkifyMask, ComponentContainer viewGroup, BetterLinkMovementMethod movementMethod) { + for (int i = 0; i < viewGroup.getChildCount(); i++) { + Component child = viewGroup.getComponentAt(i); + + if (child instanceof ComponentContainer) { + // Recursively find child TextViews. + rAddLinks(linkifyMask, ((ComponentContainer) child), movementMethod); + } else if (child instanceof Text) { + Text textView = (Text) child; + } + } + } + + + + protected static final class LongPressTimer implements Runnable { + private OnTimerReachedListener onTimerReachedListener; + + protected interface OnTimerReachedListener { + void onTimerReached(); + } + + @Override + public void run() { + onTimerReachedListener.onTimerReached(); + } + + public void setOnTimerReachedListener(OnTimerReachedListener listener) { + onTimerReachedListener = listener; + } + } +} diff --git a/better_link_movement_method/src/main/java/me/saket/bettermovementmethod/util/MyToast.java b/better_link_movement_method/src/main/java/me/saket/bettermovementmethod/util/MyToast.java new file mode 100644 index 0000000000000000000000000000000000000000..385b8017367389f6954e76d77a6761806865ab58 --- /dev/null +++ b/better_link_movement_method/src/main/java/me/saket/bettermovementmethod/util/MyToast.java @@ -0,0 +1,99 @@ +/* + * Copyright (C) 2021 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package me.saket.bettermovementmethod.util; + +import ohos.agp.colors.RgbColor; +import ohos.agp.components.DirectionalLayout; +import ohos.agp.components.Text; +import ohos.agp.components.element.ShapeElement; +import ohos.agp.utils.Color; +import ohos.agp.utils.LayoutAlignment; +import ohos.agp.window.dialog.ToastDialog; +import ohos.agp.window.service.DisplayAttributes; +import ohos.agp.window.service.DisplayManager; +import ohos.app.Context; + +/** + * author zhaoxudong + * Version 1.0 + * ModifiedBy + * date 2021-03-05 14:46 + * description 自定义Toast + */ +public class MyToast { + public static final int LENGTH_SHORT = 2000; + private static ToastDialog toastDialog; + + public enum ToastLayout { + CENTER, + TOP, + BOTTOM, + } + + public static void show(Context mContext, String content, ToastLayout layout) { + createTost(mContext, content, LENGTH_SHORT, layout); + } + + private static void createTost(Context mContext, String content, int duration, ToastLayout layout) { + DirectionalLayout toastLayout = new DirectionalLayout(mContext); + DirectionalLayout.LayoutConfig textConfig = new DirectionalLayout.LayoutConfig(DirectionalLayout.LayoutConfig.MATCH_CONTENT, DirectionalLayout.LayoutConfig.MATCH_CONTENT); + Text text = new Text(mContext); + text.setText(content); + text.setTextColor(new Color(Color.getIntColor("#000000"))); + text.setPadding(vp2px(mContext, 16), vp2px(mContext, 10), vp2px(mContext, 16), vp2px(mContext, 10)); + text.setTextSize(vp2px(mContext, 12)); + text.setBackground(buildDrawableByColorRadius(Color.getIntColor("#ffffff"), vp2px(mContext, 50))); + text.setLayoutConfig(textConfig); + text.setMarginBottom(20); + toastLayout.addComponent(text); + int mLayout = LayoutAlignment.CENTER; + switch (layout) { + case TOP: + mLayout = LayoutAlignment.TOP; + break; + case BOTTOM: + mLayout = LayoutAlignment.BOTTOM; + break; + case CENTER: + mLayout = LayoutAlignment.CENTER; + break; + } + if (toastDialog != null) { + toastDialog.cancel(); + toastDialog = null; + } + toastDialog = new ToastDialog(mContext); + toastDialog.setComponent(toastLayout); + toastDialog.setSize(DirectionalLayout.LayoutConfig.MATCH_CONTENT, DirectionalLayout.LayoutConfig.MATCH_CONTENT); + toastDialog.setAlignment(mLayout); + toastDialog.setTransparent(true); + toastDialog.setDuration(duration); + toastDialog.show(); + } + + + private static ohos.agp.components.element.Element buildDrawableByColorRadius(int color, float radius) { + ShapeElement drawable = new ShapeElement(); + drawable.setShape(0); + drawable.setRgbColor(RgbColor.fromArgbInt(color)); + drawable.setCornerRadius(radius); + return drawable; + } + + private static int vp2px(Context context, float vp) { + DisplayAttributes attributes = DisplayManager.getInstance().getDefaultDisplay(context).get().getAttributes(); + return (int) (attributes.densityPixels * vp); + } +} diff --git a/better_link_movement_method/src/main/resources/base/element/string.json b/better_link_movement_method/src/main/resources/base/element/string.json new file mode 100644 index 0000000000000000000000000000000000000000..3fe19fe348d0a2a148dc8a0ed854d748ec3190fd --- /dev/null +++ b/better_link_movement_method/src/main/resources/base/element/string.json @@ -0,0 +1,8 @@ +{ + "string": [ + { + "name": "app_name", + "value": "better_link_movement_method" + } + ] +} diff --git a/better_link_movement_method/src/test/java/me/saket/bettermovementmethod/ExampleTest.java b/better_link_movement_method/src/test/java/me/saket/bettermovementmethod/ExampleTest.java new file mode 100644 index 0000000000000000000000000000000000000000..cbafee7a7d5a9d291ac980532d76ee1e8c6c7234 --- /dev/null +++ b/better_link_movement_method/src/test/java/me/saket/bettermovementmethod/ExampleTest.java @@ -0,0 +1,9 @@ +package me.saket.bettermovementmethod; + +import org.junit.Test; + +public class ExampleTest { + @Test + public void onStart() { + } +} diff --git a/build.gradle b/build.gradle index feb08d70c3a36dbf92e97f78063f077b2a88a1eb..dcc836d3424ecce6abf55f171eada17d16277b54 100644 --- a/build.gradle +++ b/build.gradle @@ -1,35 +1,38 @@ // Top-level build file where you can add configuration options common to all sub-projects/modules. -buildscript { - ext.versions = [ - 'compileSdk' : 26, - 'minSdk' : 16, - 'targetSdk' : 26, - 'libraryVersionName': '2.3.0', - 'libraryVersionCode': 7, - ] - - repositories { - jcenter() - google() - } +apply plugin: 'com.huawei.ohos.app' - dependencies { - classpath 'com.android.tools.build:gradle:3.1.4' - classpath 'com.github.dcendents:android-maven-gradle-plugin:1.5' - } +ohos { + compileSdkVersion 5 + defaultConfig { + compatibleSdkVersion 5 + } } -plugins { - id "com.jfrog.bintray" version "1.7" +buildscript { + repositories { + maven { + url 'https://repo.huaweicloud.com/repository/maven/' + } + maven { + url 'https://developer.huawei.com/repo/' + } + jcenter() + } + dependencies { + classpath 'com.huawei.ohos:hap:2.4.4.2' + classpath 'com.huawei.ohos:decctest:1.0.0.7' + } } allprojects { - repositories { - jcenter() - google() - } -} - -task clean(type: Delete) { - delete rootProject.buildDir + repositories { + maven { + url 'https://repo.huaweicloud.com/repository/maven/' + } + maven { + url 'https://developer.huawei.com/repo/' + } + jcenter() + mavenCentral() + } } diff --git a/sample/.gitignore b/entry/.gitignore similarity index 100% rename from sample/.gitignore rename to entry/.gitignore diff --git a/entry/build.gradle b/entry/build.gradle new file mode 100644 index 0000000000000000000000000000000000000000..269b43b0120a83029f22b14a13026832667585b4 --- /dev/null +++ b/entry/build.gradle @@ -0,0 +1,27 @@ +apply plugin: 'com.huawei.ohos.hap' +apply plugin: 'com.huawei.ohos.decctest' +ohos { + compileSdkVersion 5 + defaultConfig { + compatibleSdkVersion 5 + } + buildTypes { + release { + proguardOpt { + proguardEnabled false + rulesFiles 'proguard-rules.pro' + } + } + } + +} + +dependencies { + implementation fileTree(dir: 'libs', include: ['*.jar', '*.har']) + implementation project(path: ':better_link_movement_method') + testImplementation 'junit:junit:4.13' + ohosTestImplementation 'com.huawei.ohos.testkit:runner:1.0.0.100' +} +decc { + supportType = ['html', 'xml'] +} diff --git a/entry/proguard-rules.pro b/entry/proguard-rules.pro new file mode 100644 index 0000000000000000000000000000000000000000..f7666e47561d514b2a76d5a7dfbb43ede86da92a --- /dev/null +++ b/entry/proguard-rules.pro @@ -0,0 +1 @@ +# config module specific ProGuard rules here. \ No newline at end of file diff --git a/entry/src/main/config.json b/entry/src/main/config.json new file mode 100644 index 0000000000000000000000000000000000000000..8de509bf5dd3787be630cca4676030c05c09b8a9 --- /dev/null +++ b/entry/src/main/config.json @@ -0,0 +1,149 @@ +{ + "app": { + "bundleName": "me.saket.bettermovementmethod.sample", + "vendor": "saket", + "version": { + "code": 1000000, + "name": "1.0.0" + }, + "apiVersion": { + "compatible": 5, + "target": 5, + "releaseType": "Release" + } + }, + "deviceConfig": {}, + "module": { + "package": "me.saket.bettermovementmethod.sample", + "name": ".MyApplication", + "deviceType": [ + "phone" + ], + "distro": { + "deliveryWithInstall": true, + "moduleName": "entry", + "moduleType": "entry", + "installationFree": false + }, + "abilities": [ + { + "skills": [ + { + "entities": [ + "entity.system.home" + ], + "actions": [ + "action.system.home" + ] + } + ], + "orientation": "unspecified", + "name": "me.saket.bettermovementmethod.sample.MainAbility", + "icon": "$media:icon", + "description": "$string:mainability_description", + "label": "$string:app_name", + "type": "page", + "launchType": "standard" + } + ], + "reqPermissions": [ + { + "name": "ohos.permission.DISTRIBUTED_DEVICE_STATE_CHANGE" + }, + { + "name": "ohos.permission.GET_DISTRIBUTED_DEVICE_INFO" + }, + { + "name": "ohos.permission.GET_BUNDLE_INFO" + }, + { + "name": "ohos.permission.DISTRIBUTED_DATASYNC" + }, + { + "name": "ohos.permission.INTERNET" + }, + { + "name": "ohos.permission.READ_USER_STORAGE" + }, + { + "name": "ohos.permission.WRITE_USER_STORAGE" + }, + { + "name": "ohos.permission.servicebus.ACCESS_SERVICE" + }, + { + "name": "com.huawei.hwddmp.servicebus.BIND_SERVICE" + }, + { + "name": "ohos.permission.ACCESS_NETWORK_STATE" + }, + { + "name": "ohos.permission.ACCESS_WIFI_STATE" + }, + { + "name": "ohos.permission.GET_NETWORK_INFO", + "reason": "Get internet information" + }, + { + "name": "ohos.permission.SET_NETWORK_INFO", + "reason": "Get internet information" + }, + { + "name": "ohos.permission.ACCELEROMETER" + }, + { + "name": "ohos.permission.GYROSCOPE" + }, + { + "name": "com.huawei.permission.ACCESS_DISTRIBUTED_ABILITY_GROUP" + }, + { + "name": "com.huawei.developerguide.DataAbilityShellProvider.PROVIDER" + }, + { + "name": "ohos.permission.servicebus.DISTRIBUTED_DEVICE_STATE_CHANGE" + }, + { + "name": "ohos.permission.servicebus.GET_BUNDLE_INFO" + }, + { + "name": "com.huawei.permission.MANAGE_DISTRIBUTED_PERMISSION" + }, + { + "name": "ohos.permission.CAMERA" + }, + { + "name": "ohos.permission.MICROPHONE" + }, + { + "name": "ohos.permission.LOCATION", + "reason": "location", + "usedScene": { + "ability": [ + "com.example.daxiguaforharmony.slice.GameAbilitySlice" + ], + "when": "always" + } + }, + { + "name": "ohos.permission.LOCATION_IN_BACKGROUND", + "reason": "location", + "usedScene": { + "ability": [ + "com.example.daxiguaforharmony.slice.GameAbilitySlice" + ], + "when": "always" + } + } + ], + "metaData":{ + "customizeData":[ + { + "name": "hwc-theme", + "value": "androidhwext:style/Theme.Emui.NoTitleBar", + "extra":"" + } + ] + } + } +} \ No newline at end of file diff --git a/entry/src/main/java/me/saket/bettermovementmethod/sample/MainAbility.java b/entry/src/main/java/me/saket/bettermovementmethod/sample/MainAbility.java new file mode 100644 index 0000000000000000000000000000000000000000..d6fa8a8198eeea016dd3fc90fa2edb02910961fe --- /dev/null +++ b/entry/src/main/java/me/saket/bettermovementmethod/sample/MainAbility.java @@ -0,0 +1,13 @@ +package me.saket.bettermovementmethod.sample; + +import me.saket.bettermovementmethod.sample.slice.MainAbilitySlice; +import ohos.aafwk.ability.Ability; +import ohos.aafwk.content.Intent; + +public class MainAbility extends Ability { + @Override + public void onStart(Intent intent) { + super.onStart(intent); + super.setMainRoute(MainAbilitySlice.class.getName()); + } +} diff --git a/entry/src/main/java/me/saket/bettermovementmethod/sample/MyApplication.java b/entry/src/main/java/me/saket/bettermovementmethod/sample/MyApplication.java new file mode 100644 index 0000000000000000000000000000000000000000..1420981124671f69176698ee70ce6dbb72d203f0 --- /dev/null +++ b/entry/src/main/java/me/saket/bettermovementmethod/sample/MyApplication.java @@ -0,0 +1,10 @@ +package me.saket.bettermovementmethod.sample; + +import ohos.aafwk.ability.AbilityPackage; + +public class MyApplication extends AbilityPackage { + @Override + public void onInitialize() { + super.onInitialize(); + } +} diff --git a/entry/src/main/java/me/saket/bettermovementmethod/sample/slice/MainAbilitySlice.java b/entry/src/main/java/me/saket/bettermovementmethod/sample/slice/MainAbilitySlice.java new file mode 100644 index 0000000000000000000000000000000000000000..6601a227ddbc605c07d30326560be9f1fdcd340e --- /dev/null +++ b/entry/src/main/java/me/saket/bettermovementmethod/sample/slice/MainAbilitySlice.java @@ -0,0 +1,284 @@ +package me.saket.bettermovementmethod.sample.slice; + +import me.saket.bettermovementmethod.sample.ResourceTable; +import me.saket.bettermovementmethod.util.MyToast; +import ohos.aafwk.ability.AbilitySlice; +import ohos.aafwk.content.Intent; +import ohos.aafwk.content.Operation; +import ohos.agp.colors.RgbColor; +import ohos.agp.components.*; +import ohos.agp.components.element.ShapeElement; +import ohos.agp.window.dialog.PopupDialog; +import ohos.agp.window.dialog.ToastDialog; +import ohos.miscservices.pasteboard.PasteData; +import ohos.miscservices.pasteboard.SystemPasteboard; +import ohos.utils.IntentConstants; +import ohos.utils.PacMap; +import ohos.utils.net.Uri; + +public class MainAbilitySlice extends AbilitySlice { + private static final String ADDITON_KEY = "ADDITION_KEY"; + + @Override + public void onStart(Intent intent) { + super.onStart(intent); + super.setUIContent(ResourceTable.Layout_ability_main); + + Text tv_Gotham = (Text) findComponentById(ResourceTable.Id_tv_Gotham); + findComponentById(ResourceTable.Id_tv_Gotham).setClickedListener(new Component.ClickedListener() { + @Override + public void onClick(Component component) { + clickElement(tv_Gotham); + longClick("https://en.wikipedia.org/wiki/Gotham_City"); + } + }); + + findComponentById(ResourceTable.Id_tv_Gotham).setLongClickedListener(new Component.LongClickedListener() { + @Override + public void onLongClicked(Component component) { + clickElement(tv_Gotham); + longClick("Long-click:https://en.wikipedia.org/wiki/Gotham_City"); + } + }); + Text tv_tower = (Text) findComponentById(ResourceTable.Id_tv_tower); + findComponentById(ResourceTable.Id_tv_tower).setClickedListener(new Component.ClickedListener() { + @Override + public void onClick(Component component) { + clickElementTwo(tv_tower); + longClick("http://batman.wikia.com/wiki/Wayne_Tower_(Nolan_Films)"); + } + }); + findComponentById(ResourceTable.Id_tv_tower).setLongClickedListener(new Component.LongClickedListener() { + @Override + public void onLongClicked(Component component) { + clickElementTwo(tv_tower); + onClick("Long-click:http://batman.wikia.com/wiki/Wayne_Tower_(Nolan_Films)"); + } + }); + + Text tv_phone = (Text) findComponentById(ResourceTable.Id_tv_phone); + findComponentById(ResourceTable.Id_tv_phone).setLongClickedListener(new Component.LongClickedListener() { + @Override + public void onLongClicked(Component component) { + clickElement(tv_phone); + longClick("Long-click:tel:7899859911"); + } + }); + Text tv_bruce = (Text) findComponentById(ResourceTable.Id_tv_bruce); + findComponentById(ResourceTable.Id_tv_bruce).setLongClickedListener(new Component.LongClickedListener() { + @Override + public void onLongClicked(Component component) { + clickElement(tv_bruce); + longClick("Long-click:mailto:bruce@wayne.org"); + } + }); + Text tv_location = (Text) findComponentById(ResourceTable.Id_tv_location); + findComponentById(ResourceTable.Id_tv_location).setLongClickedListener(new Component.LongClickedListener() { + @Override + public void onLongClicked(Component component) { + clickElement(tv_location); + longClick("Long-click:https://goo.gl/maps/KfRBC6KLHA12"); + } + }); + findComponentById(ResourceTable.Id_tv_phone).setClickedListener(new Component.ClickedListener() { + @Override + public void onClick(Component component) { + + clickElement(tv_phone); + PopupDialog menuDialog = new PopupDialog(getContext(), tv_phone); + DirectionalLayout menuComponent = (DirectionalLayout) LayoutScatter.getInstance(getContext()) + .parse(ResourceTable.Layout_ability_dialog_phone, null, false); + menuDialog.setCustomComponent(menuComponent); + Text tv_call = (Text) menuComponent.findComponentById(ResourceTable.Id_tv_call); + Text tv_send_message = (Text) menuComponent.findComponentById(ResourceTable.Id_tv_send_message); + Text tv_copy_number = (Text) menuComponent.findComponentById(ResourceTable.Id_tv_copy_number); + Text tv_save_to_contacts = (Text) menuComponent.findComponentById(ResourceTable.Id_tv_save_to_contacts); + tv_call.setClickedListener(new Component.ClickedListener() { + @Override + public void onClick(Component component) { + Intent transferDialogKey = new Intent(); + Operation operation = new Intent.OperationBuilder() + .withAction(IntentConstants.ACTION_DIAL) + .withUri(Uri.parse("tel:" + "7899859911")) + .build(); + transferDialogKey.setOperation(operation); + startAbility(transferDialogKey); + menuDialog.destroy(); + } + }); + + tv_send_message.setClickedListener(new Component.ClickedListener() { + @Override + public void onClick(Component component) { + Intent transferDialogKeys = new Intent(); + Operation operations = new Intent.OperationBuilder() + .withAction(IntentConstants.ACTION_SEND_SMS) + .withUri(Uri.parse("smsto:" + "7899859911")) + .build(); + transferDialogKeys.setOperation(operations); + startAbility(transferDialogKeys); + menuDialog.destroy(); + } + }); + tv_copy_number.setClickedListener(new Component.ClickedListener() { + @Override + public void onClick(Component component) { + setCopyTextClick("tel:7899859911"); + longClick("Copied to clipboard"); + menuDialog.destroy(); + } + }); + tv_save_to_contacts.setClickedListener(new Component.ClickedListener() { + @Override + public void onClick(Component component) { + Intent transferDialog = new Intent(); + Operation operation1 = new Intent.OperationBuilder() + //.withAction(IntentConstants.ACTION_CHOOSE) + .withFlags(Intent.FLAG_NOT_OHOS_COMPONENT) + .withBundleName("com.huawei.contacts") + .withAbilityName("com.android.contacts.activities.PeopleActivity") + .withUri(Uri.parse("tel:" + "7899859911")) + .build(); + transferDialog.setOperation(operation1); + startAbility(transferDialog); + menuDialog.destroy(); + } + }); + + menuDialog.show(); + menuDialog.setAutoClosable(true); + } + }); + + findComponentById(ResourceTable.Id_tv_bruce).setClickedListener(new Component.ClickedListener() { + @Override + public void onClick(Component component) { + clickElement(tv_bruce); + PopupDialog menuDialog = new PopupDialog(getContext(), tv_bruce); + DirectionalLayout menuComponent = (DirectionalLayout) LayoutScatter.getInstance(getContext()) + .parse(ResourceTable.Layout_ability_dialog_email, null, false); + menuDialog.setCustomComponent(menuComponent); + Text tv_send_email = (Text) menuComponent.findComponentById(ResourceTable.Id_tv_send_email); + Text tv_capy_email_address = (Text) menuComponent.findComponentById(ResourceTable.Id_tv_capy_email_address); + tv_send_email.setClickedListener(new Component.ClickedListener() { + @Override + public void onClick(Component component) { + menuDialog.destroy(); + } + }); + + tv_capy_email_address.setClickedListener(new Component.ClickedListener() { + @Override + public void onClick(Component component) { + setCopyTextClick("mailto:bruce@wayne.org"); + menuDialog.destroy(); + } + }); + + menuDialog.show(); + menuDialog.setAutoClosable(true); + } + }); + + findComponentById(ResourceTable.Id_tv_location).setClickedListener(new Component.ClickedListener() { + @Override + public void onClick(Component component) { + clickElement(tv_location); + + PopupDialog menuDialog = new PopupDialog(getContext(), tv_location); + DirectionalLayout menuComponent = (DirectionalLayout) LayoutScatter.getInstance(getContext()) + .parse(ResourceTable.Layout_ability_dialog_navigate, null, false); + menuDialog.setCustomComponent(menuComponent); + Text tv_navigate = (Text) menuComponent.findComponentById(ResourceTable.Id_tv_navigate); + Text tv_copy_address = (Text) menuComponent.findComponentById(ResourceTable.Id_tv_copy_address); + tv_navigate.setClickedListener(new Component.ClickedListener() { + @Override + public void onClick(Component component) { + menuDialog.destroy(); + } + }); + + tv_copy_address.setClickedListener(new Component.ClickedListener() { + @Override + public void onClick(Component component) { + setCopyTextClick("https://goo.gl/maps/KfRBC6KLHA12"); + menuDialog.destroy(); + } + }); + menuDialog.show(); + menuDialog.setAutoClosable(true); + } + }); + + } + + private void setCopyTextClick(String address) { + SystemPasteboard mPasteboard = SystemPasteboard.getSystemPasteboard(MainAbilitySlice.this); + PasteData pasteData = new PasteData(); + pasteData.addTextRecord(address); + PacMap pacMap = new PacMap(); + pacMap.putString(ADDITON_KEY, "ADDITION_VALUE_OF_TEXT"); + pasteData.getProperty().setAdditions(pacMap); + pasteData.getProperty().setTag("USER_TAG"); + pasteData.getProperty().setLocalOnly(true); + mPasteboard.setPasteData(pasteData); + } + + private void clickElement(Text text) { + getUITaskDispatcher().delayDispatch(() -> { + ShapeElement element = new ShapeElement(); + element.setRgbColor(new RgbColor(252, 245, 245)); + text.setBackground(element); + }, 300); + ShapeElement element = new ShapeElement(); + element.setRgbColor(new RgbColor(255, 64, 129, 40)); + text.setBackground(element); + } + + private void clickElementTwo(Text text) { + getUITaskDispatcher().delayDispatch(() -> { + ShapeElement element = new ShapeElement(); + element.setRgbColor(new RgbColor(222, 253, 255)); + text.setBackground(element); + }, 300); + ShapeElement element = new ShapeElement(); + element.setRgbColor(new RgbColor(255, 64, 129, 40)); + text.setBackground(element); + } + + private void longClick(String text) { + MyToast.show(getContext(), text, MyToast.ToastLayout.BOTTOM); + } + + private void onClick(String text) { + ToastDialog toastDialog = new ToastDialog(getContext()); + toastDialog.setSize(ComponentContainer.LayoutConfig.MATCH_PARENT, ComponentContainer.LayoutConfig.MATCH_CONTENT); + Component parse = LayoutScatter.getInstance(getContext()).parse(ResourceTable.Layout_layout_toast, null, false); + toastDialog.setComponent(parse); + toastDialog.setTransparent(true); + ((Text) parse.findComponentById(ResourceTable.Id_msg_toast)).setText(text); + toastDialog.show(); + } + + private boolean isPhoneNumber(String url) { + return url.startsWith("tel:"); + } + + private boolean isEmailAddress(String url) { + return url.contains("@"); + } + + private boolean isMapAddress(String url) { + return url.contains("goo.gl/maps"); + } + + @Override + public void onActive() { + super.onActive(); + } + + @Override + public void onForeground(Intent intent) { + super.onForeground(intent); + } +} diff --git a/entry/src/main/resources/base/element/color.json b/entry/src/main/resources/base/element/color.json new file mode 100644 index 0000000000000000000000000000000000000000..355ab573b36e0200966639b40b0315ba6b175c56 --- /dev/null +++ b/entry/src/main/resources/base/element/color.json @@ -0,0 +1,60 @@ +{ + "color": [ + { + "name": "advertise", + "value": "#FFFFFF" + }, + { + "name": "skip", + "value": "#FFFFFF" + }, + { + "name": "copyright_text_color", + "value": "#99000000" + }, + { + "name": "app_title_text_color", + "value": "#E6000000" + }, + { + "name": "purple_200", + "value": "#FFBB86FC" + }, + { + "name": "purple_300", + "value": "#9933FA" + }, + { + "name": "purple_500", + "value": "#FF6200EE" + }, + { + "name": "purple_700", + "value": "#FF3700B3" + }, + { + "name": "teal_200", + "value": "#FF03DAC5" + }, + { + "name": "teal_700", + "value": "#FF018786" + }, + { + "name": "colorPrimary", + "value": "#FFFFFF" + }, + { + "name": "colorPrimaryDark", + "value": "#AAA" + }, + { + "name": "colorAccent", + "value": "#FF4081" + }, + { + "name": "wayneTower", + "value": "#2c4089ff" + } + ] +} \ No newline at end of file diff --git a/entry/src/main/resources/base/element/string.json b/entry/src/main/resources/base/element/string.json new file mode 100644 index 0000000000000000000000000000000000000000..b9a9e736217a79696f15857a1ef53cdab7330301 --- /dev/null +++ b/entry/src/main/resources/base/element/string.json @@ -0,0 +1,28 @@ +{ + "string": [ + { + "name": "app_name", + "value": "Better_Link_Movement_Method_master" + }, + { + "name": "mainability_description", + "value": "Java_Phone_Empty Feature Ability" + }, + { + "name": "mainability_HelloWorld", + "value": "Hello World" + }, + { + "name": "bettermovementmethod_app_name", + "value": "BetterLinkMovement" + }, + { + "name": "bettermovementmethod_dummy_number", + "value": "7899859911" + }, + { + "name": "bettermovementmethod_dummy_text_long", + "value": "Gotham has been good to our family, but the citys been suffering. People less fortunate than us have been enduring very hard times. So we built a new, cheap, public transportation system to unite the city. And at the center,Wayne tower\n" + } + ] +} \ No newline at end of file diff --git a/entry/src/main/resources/base/graphic/background_ability_main.xml b/entry/src/main/resources/base/graphic/background_ability_main.xml new file mode 100644 index 0000000000000000000000000000000000000000..c0c0a3df480fa387a452b9c40ca191cc918a3fc0 --- /dev/null +++ b/entry/src/main/resources/base/graphic/background_ability_main.xml @@ -0,0 +1,6 @@ + + + + \ No newline at end of file diff --git a/entry/src/main/resources/base/graphic/background_dialog.xml b/entry/src/main/resources/base/graphic/background_dialog.xml new file mode 100644 index 0000000000000000000000000000000000000000..31e875c302e1cae968ec1ed9a21865218d3a2999 --- /dev/null +++ b/entry/src/main/resources/base/graphic/background_dialog.xml @@ -0,0 +1,10 @@ + + + + + + \ No newline at end of file diff --git a/entry/src/main/resources/base/graphic/background_line.xml b/entry/src/main/resources/base/graphic/background_line.xml new file mode 100644 index 0000000000000000000000000000000000000000..8ca50fb0f96a094559fc61e4334696736b097699 --- /dev/null +++ b/entry/src/main/resources/base/graphic/background_line.xml @@ -0,0 +1,7 @@ + + + + \ No newline at end of file diff --git a/entry/src/main/resources/base/graphic/background_toast.xml b/entry/src/main/resources/base/graphic/background_toast.xml new file mode 100644 index 0000000000000000000000000000000000000000..60c9ba41e51abc57a252423f830798fa71aed5c9 --- /dev/null +++ b/entry/src/main/resources/base/graphic/background_toast.xml @@ -0,0 +1,8 @@ + + + + + \ No newline at end of file diff --git a/entry/src/main/resources/base/layout/ability_dialog_email.xml b/entry/src/main/resources/base/layout/ability_dialog_email.xml new file mode 100644 index 0000000000000000000000000000000000000000..aa75737f2653ef4e16fcbe791c572938ff847e2a --- /dev/null +++ b/entry/src/main/resources/base/layout/ability_dialog_email.xml @@ -0,0 +1,25 @@ + + + + + + + + \ No newline at end of file diff --git a/entry/src/main/resources/base/layout/ability_dialog_navigate.xml b/entry/src/main/resources/base/layout/ability_dialog_navigate.xml new file mode 100644 index 0000000000000000000000000000000000000000..9be53f6334b35263078b89c622fcf7449522639f --- /dev/null +++ b/entry/src/main/resources/base/layout/ability_dialog_navigate.xml @@ -0,0 +1,25 @@ + + + + + + + + \ No newline at end of file diff --git a/entry/src/main/resources/base/layout/ability_dialog_phone.xml b/entry/src/main/resources/base/layout/ability_dialog_phone.xml new file mode 100644 index 0000000000000000000000000000000000000000..f96e3165937b5d685ed0c706ef366a789711210e --- /dev/null +++ b/entry/src/main/resources/base/layout/ability_dialog_phone.xml @@ -0,0 +1,41 @@ + + + + + + + + + + + + \ No newline at end of file diff --git a/entry/src/main/resources/base/layout/ability_main.xml b/entry/src/main/resources/base/layout/ability_main.xml new file mode 100644 index 0000000000000000000000000000000000000000..a2270baf2d78fe0ce8abc76e78369b4ad554752a --- /dev/null +++ b/entry/src/main/resources/base/layout/ability_main.xml @@ -0,0 +1,226 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/entry/src/main/resources/base/layout/layout_toast.xml b/entry/src/main/resources/base/layout/layout_toast.xml new file mode 100644 index 0000000000000000000000000000000000000000..af5eb6290403e4a8a898a2c019afc2faeeb6c3fc --- /dev/null +++ b/entry/src/main/resources/base/layout/layout_toast.xml @@ -0,0 +1,21 @@ + + + + + + \ No newline at end of file diff --git a/entry/src/main/resources/base/media/icon.png b/entry/src/main/resources/base/media/icon.png new file mode 100644 index 0000000000000000000000000000000000000000..ce307a8827bd75456441ceb57d530e4c8d45d36c Binary files /dev/null and b/entry/src/main/resources/base/media/icon.png differ diff --git a/entry/src/ohosTest/config.json b/entry/src/ohosTest/config.json new file mode 100644 index 0000000000000000000000000000000000000000..b26a83db82cabb04aa831fc7819a82461de1390e --- /dev/null +++ b/entry/src/ohosTest/config.json @@ -0,0 +1,41 @@ +{ + "app": { + "bundleName": "me.saket.bettermovementmethod.sample", + "vendor": "saket", + "version": { + "code": 1000000, + "name": "1.0.0" + }, + "apiVersion": { + "compatible": 5, + "target": 5, + "releaseType": "Release" + } + }, + "deviceConfig": {}, + "module": { + "package": "me.saket.bettermovementmethod.sample", + "name": "testModule", + "deviceType": [ + "phone" + ], + "distro": { + "deliveryWithInstall": true, + "moduleName": "entry_test", + "moduleType": "feature", + "installationFree": true + }, + "abilities": [ + { + "name": "decc.testkit.runner.EntryAbility", + "description": "Test Entry Ability", + "icon": "$media:icon", + "label": "$string:app_name", + "launchType": "standard", + "orientation": "landscape", + "visible": true, + "type": "page" + } + ] + } +} \ No newline at end of file diff --git a/entry/src/ohosTest/java/me/saket/bettermovementmethod/sample/ExampleOhosTest.java b/entry/src/ohosTest/java/me/saket/bettermovementmethod/sample/ExampleOhosTest.java new file mode 100644 index 0000000000000000000000000000000000000000..2713ef922744d7fd7f7f7027956cccc41cbba074 --- /dev/null +++ b/entry/src/ohosTest/java/me/saket/bettermovementmethod/sample/ExampleOhosTest.java @@ -0,0 +1,15 @@ +package me.saket.bettermovementmethod.sample; + +import ohos.aafwk.ability.delegation.AbilityDelegatorRegistry; +import org.junit.Test; + +import static org.junit.Assert.assertEquals; + +public class ExampleOhosTest { + //纯UI组件,无法单独执行单元测试 + @Test + public void testBundleName() { + final String actualBundleName = AbilityDelegatorRegistry.getArguments().getTestBundleName(); + assertEquals("me.saket.bettermovementmethod.sample", actualBundleName); + } +} \ No newline at end of file diff --git a/entry/src/test/java/me/saket/bettermovementmethod/sample/ExampleTest.java b/entry/src/test/java/me/saket/bettermovementmethod/sample/ExampleTest.java new file mode 100644 index 0000000000000000000000000000000000000000..6e04bba6978ca5b178501c2b9883f9a45df66f0e --- /dev/null +++ b/entry/src/test/java/me/saket/bettermovementmethod/sample/ExampleTest.java @@ -0,0 +1,9 @@ +package me.saket.bettermovementmethod.sample; + +import org.junit.Test; + +public class ExampleTest { + @Test + public void onStart() { + } +} diff --git a/gradle.properties b/gradle.properties deleted file mode 100644 index 89e0d99e2173fc80f669c753b0c0107637016cf8..0000000000000000000000000000000000000000 --- a/gradle.properties +++ /dev/null @@ -1,18 +0,0 @@ -# Project-wide Gradle settings. - -# IDE (e.g. Android Studio) users: -# Gradle settings configured through the IDE *will override* -# any settings specified in this file. - -# For more details on how to configure your build environment visit -# http://www.gradle.org/docs/current/userguide/build_environment.html - -# Specifies the JVM arguments used for the daemon process. -# The setting is particularly useful for tweaking memory settings. -# Default value: -Xmx10248m -XX:MaxPermSize=256m -# org.gradle.jvmargs=-Xmx2048m -XX:MaxPermSize=512m -XX:+HeapDumpOnOutOfMemoryError -Dfile.encoding=UTF-8 - -# When configured, Gradle will run in incubating parallel mode. -# This option should only be used with decoupled projects. More details, visit -# http://www.gradle.org/docs/current/userguide/multi_project_builds.html#sec:decoupled_projects -# org.gradle.parallel=true diff --git a/gradle/wrapper/gradle-wrapper.jar b/gradle/wrapper/gradle-wrapper.jar deleted file mode 100644 index 13372aef5e24af05341d49695ee84e5f9b594659..0000000000000000000000000000000000000000 Binary files a/gradle/wrapper/gradle-wrapper.jar and /dev/null differ diff --git a/gradle/wrapper/gradle-wrapper.properties b/gradle/wrapper/gradle-wrapper.properties deleted file mode 100644 index 6b63d39952b42ee1a97dc811ebbce486535c255a..0000000000000000000000000000000000000000 --- a/gradle/wrapper/gradle-wrapper.properties +++ /dev/null @@ -1,6 +0,0 @@ -#Wed Apr 11 10:13:05 IST 2018 -distributionBase=GRADLE_USER_HOME -distributionPath=wrapper/dists -zipStoreBase=GRADLE_USER_HOME -zipStorePath=wrapper/dists -distributionUrl=https\://services.gradle.org/distributions/gradle-4.6-all.zip diff --git a/gradlew b/gradlew deleted file mode 100755 index 9d82f78915133e1c35a6ea51252590fb38efac2f..0000000000000000000000000000000000000000 --- a/gradlew +++ /dev/null @@ -1,160 +0,0 @@ -#!/usr/bin/env bash - -############################################################################## -## -## Gradle start up script for UN*X -## -############################################################################## - -# Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. -DEFAULT_JVM_OPTS="" - -APP_NAME="Gradle" -APP_BASE_NAME=`basename "$0"` - -# Use the maximum available, or set MAX_FD != -1 to use that value. -MAX_FD="maximum" - -warn ( ) { - echo "$*" -} - -die ( ) { - echo - echo "$*" - echo - exit 1 -} - -# OS specific support (must be 'true' or 'false'). -cygwin=false -msys=false -darwin=false -case "`uname`" in - CYGWIN* ) - cygwin=true - ;; - Darwin* ) - darwin=true - ;; - MINGW* ) - msys=true - ;; -esac - -# Attempt to set APP_HOME -# Resolve links: $0 may be a link -PRG="$0" -# Need this for relative symlinks. -while [ -h "$PRG" ] ; do - ls=`ls -ld "$PRG"` - link=`expr "$ls" : '.*-> \(.*\)$'` - if expr "$link" : '/.*' > /dev/null; then - PRG="$link" - else - PRG=`dirname "$PRG"`"/$link" - fi -done -SAVED="`pwd`" -cd "`dirname \"$PRG\"`/" >/dev/null -APP_HOME="`pwd -P`" -cd "$SAVED" >/dev/null - -CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar - -# Determine the Java command to use to start the JVM. -if [ -n "$JAVA_HOME" ] ; then - if [ -x "$JAVA_HOME/jre/sh/java" ] ; then - # IBM's JDK on AIX uses strange locations for the executables - JAVACMD="$JAVA_HOME/jre/sh/java" - else - JAVACMD="$JAVA_HOME/bin/java" - fi - if [ ! -x "$JAVACMD" ] ; then - die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME - -Please set the JAVA_HOME variable in your environment to match the -location of your Java installation." - fi -else - JAVACMD="java" - which java >/dev/null 2>&1 || die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. - -Please set the JAVA_HOME variable in your environment to match the -location of your Java installation." -fi - -# Increase the maximum file descriptors if we can. -if [ "$cygwin" = "false" -a "$darwin" = "false" ] ; then - MAX_FD_LIMIT=`ulimit -H -n` - if [ $? -eq 0 ] ; then - if [ "$MAX_FD" = "maximum" -o "$MAX_FD" = "max" ] ; then - MAX_FD="$MAX_FD_LIMIT" - fi - ulimit -n $MAX_FD - if [ $? -ne 0 ] ; then - warn "Could not set maximum file descriptor limit: $MAX_FD" - fi - else - warn "Could not query maximum file descriptor limit: $MAX_FD_LIMIT" - fi -fi - -# For Darwin, add options to specify how the application appears in the dock -if $darwin; then - GRADLE_OPTS="$GRADLE_OPTS \"-Xdock:name=$APP_NAME\" \"-Xdock:icon=$APP_HOME/media/gradle.icns\"" -fi - -# For Cygwin, switch paths to Windows format before running java -if $cygwin ; then - APP_HOME=`cygpath --path --mixed "$APP_HOME"` - CLASSPATH=`cygpath --path --mixed "$CLASSPATH"` - JAVACMD=`cygpath --unix "$JAVACMD"` - - # We build the pattern for arguments to be converted via cygpath - ROOTDIRSRAW=`find -L / -maxdepth 1 -mindepth 1 -type d 2>/dev/null` - SEP="" - for dir in $ROOTDIRSRAW ; do - ROOTDIRS="$ROOTDIRS$SEP$dir" - SEP="|" - done - OURCYGPATTERN="(^($ROOTDIRS))" - # Add a user-defined pattern to the cygpath arguments - if [ "$GRADLE_CYGPATTERN" != "" ] ; then - OURCYGPATTERN="$OURCYGPATTERN|($GRADLE_CYGPATTERN)" - fi - # Now convert the arguments - kludge to limit ourselves to /bin/sh - i=0 - for arg in "$@" ; do - CHECK=`echo "$arg"|egrep -c "$OURCYGPATTERN" -` - CHECK2=`echo "$arg"|egrep -c "^-"` ### Determine if an option - - if [ $CHECK -ne 0 ] && [ $CHECK2 -eq 0 ] ; then ### Added a condition - eval `echo args$i`=`cygpath --path --ignore --mixed "$arg"` - else - eval `echo args$i`="\"$arg\"" - fi - i=$((i+1)) - done - case $i in - (0) set -- ;; - (1) set -- "$args0" ;; - (2) set -- "$args0" "$args1" ;; - (3) set -- "$args0" "$args1" "$args2" ;; - (4) set -- "$args0" "$args1" "$args2" "$args3" ;; - (5) set -- "$args0" "$args1" "$args2" "$args3" "$args4" ;; - (6) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" ;; - (7) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" ;; - (8) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" ;; - (9) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" "$args8" ;; - esac -fi - -# Split up the JVM_OPTS And GRADLE_OPTS values into an array, following the shell quoting and substitution rules -function splitJvmOpts() { - JVM_OPTS=("$@") -} -eval splitJvmOpts $DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS -JVM_OPTS[${#JVM_OPTS[*]}]="-Dorg.gradle.appname=$APP_BASE_NAME" - -exec "$JAVACMD" "${JVM_OPTS[@]}" -classpath "$CLASSPATH" org.gradle.wrapper.GradleWrapperMain "$@" diff --git a/gradlew.bat b/gradlew.bat deleted file mode 100644 index 8a0b282aa6885fb573c106b3551f7275c5f17e8e..0000000000000000000000000000000000000000 --- a/gradlew.bat +++ /dev/null @@ -1,90 +0,0 @@ -@if "%DEBUG%" == "" @echo off -@rem ########################################################################## -@rem -@rem Gradle startup script for Windows -@rem -@rem ########################################################################## - -@rem Set local scope for the variables with windows NT shell -if "%OS%"=="Windows_NT" setlocal - -@rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. -set DEFAULT_JVM_OPTS= - -set DIRNAME=%~dp0 -if "%DIRNAME%" == "" set DIRNAME=. -set APP_BASE_NAME=%~n0 -set APP_HOME=%DIRNAME% - -@rem Find java.exe -if defined JAVA_HOME goto findJavaFromJavaHome - -set JAVA_EXE=java.exe -%JAVA_EXE% -version >NUL 2>&1 -if "%ERRORLEVEL%" == "0" goto init - -echo. -echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. -echo. -echo Please set the JAVA_HOME variable in your environment to match the -echo location of your Java installation. - -goto fail - -:findJavaFromJavaHome -set JAVA_HOME=%JAVA_HOME:"=% -set JAVA_EXE=%JAVA_HOME%/bin/java.exe - -if exist "%JAVA_EXE%" goto init - -echo. -echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME% -echo. -echo Please set the JAVA_HOME variable in your environment to match the -echo location of your Java installation. - -goto fail - -:init -@rem Get command-line arguments, handling Windowz variants - -if not "%OS%" == "Windows_NT" goto win9xME_args -if "%@eval[2+2]" == "4" goto 4NT_args - -:win9xME_args -@rem Slurp the command line arguments. -set CMD_LINE_ARGS= -set _SKIP=2 - -:win9xME_args_slurp -if "x%~1" == "x" goto execute - -set CMD_LINE_ARGS=%* -goto execute - -:4NT_args -@rem Get arguments from the 4NT Shell from JP Software -set CMD_LINE_ARGS=%$ - -:execute -@rem Setup the command line - -set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar - -@rem Execute Gradle -"%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %CMD_LINE_ARGS% - -:end -@rem End local scope for the variables with windows NT shell -if "%ERRORLEVEL%"=="0" goto mainEnd - -:fail -rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of -rem the _cmd.exe /c_ return code! -if not "" == "%GRADLE_EXIT_CONSOLE%" exit 1 -exit /b 1 - -:mainEnd -if "%OS%"=="Windows_NT" endlocal - -:omega diff --git a/img/example.gif b/img/example.gif new file mode 100644 index 0000000000000000000000000000000000000000..b36bf01bea8745fb666ac428adb476e03f46c52a Binary files /dev/null and b/img/example.gif differ diff --git a/img/example2.gif b/img/example2.gif new file mode 100644 index 0000000000000000000000000000000000000000..b593419aeb12b72554785bc8a7740f4034a0cde7 Binary files /dev/null and b/img/example2.gif differ diff --git a/img/example3.gif b/img/example3.gif new file mode 100644 index 0000000000000000000000000000000000000000..585a375ccf042a17b66dcc5e7847a321140a3dbb Binary files /dev/null and b/img/example3.gif differ diff --git a/img/example4.gif b/img/example4.gif new file mode 100644 index 0000000000000000000000000000000000000000..22bf01c082125e2eade6f570e401dd1bdb7df2c1 Binary files /dev/null and b/img/example4.gif differ diff --git a/sample/build.gradle b/sample/build.gradle deleted file mode 100644 index 2ecc2c11c0add63523b5e32c065f43ab854461b1..0000000000000000000000000000000000000000 --- a/sample/build.gradle +++ /dev/null @@ -1,36 +0,0 @@ -apply plugin: 'com.android.application' - -repositories { - jcenter() -} - -android { - compileSdkVersion versions.compileSdk - - defaultConfig { - applicationId 'me.saket.betterlinkmovement.sample' - minSdkVersion 23 - targetSdkVersion versions.targetSdk - versionCode versions.libraryVersionCode - versionName versions.libraryVersionName - } - - buildTypes { - release { - minifyEnabled false - proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro' - } - } - - compileOptions { - targetCompatibility 1.8 - sourceCompatibility 1.8 - } - - productFlavors { - } -} - -dependencies { - implementation project(path: ':better-link-movement-method') -} diff --git a/sample/proguard-rules.pro b/sample/proguard-rules.pro deleted file mode 100644 index d51f03aa9bec823aa89eb23e63623a2e05ebc0a9..0000000000000000000000000000000000000000 --- a/sample/proguard-rules.pro +++ /dev/null @@ -1,17 +0,0 @@ -# Add project specific ProGuard rules here. -# By default, the flags in this file are appended to flags specified -# in /Users/Saket/Library/Android/sdk/tools/proguard/proguard-android.txt -# You can edit the include path and order by changing the proguardFiles -# directive in build.gradle. -# -# For more details, see -# http://developer.android.com/guide/developing/tools/proguard.html - -# Add any project specific keep options here: - -# If your project uses WebView with JS, uncomment the following -# and specify the fully qualified class name to the JavaScript interface -# class: -#-keepclassmembers class fqcn.of.javascript.interface.for.webview { -# public *; -#} diff --git a/sample/src/main/AndroidManifest.xml b/sample/src/main/AndroidManifest.xml deleted file mode 100644 index b34e6cc7ecb50e9d002d75741c722f49e5efa125..0000000000000000000000000000000000000000 --- a/sample/src/main/AndroidManifest.xml +++ /dev/null @@ -1,20 +0,0 @@ - - - - - - - - - - - - - - diff --git a/sample/src/main/java/me/saket/bettermovementmethod/sample/EmailLinkPopupMenu.java b/sample/src/main/java/me/saket/bettermovementmethod/sample/EmailLinkPopupMenu.java deleted file mode 100644 index 2ebcd99e193188324ff36588f2228ed2425524b6..0000000000000000000000000000000000000000 --- a/sample/src/main/java/me/saket/bettermovementmethod/sample/EmailLinkPopupMenu.java +++ /dev/null @@ -1,13 +0,0 @@ -package me.saket.bettermovementmethod.sample; - -import android.content.Context; -import android.view.View; -import android.widget.PopupMenu; - -public class EmailLinkPopupMenu extends PopupMenu { - - public EmailLinkPopupMenu(Context context, View anchor) { - super(context, anchor); - getMenuInflater().inflate(R.menu.email_link_menu, getMenu()); - } -} diff --git a/sample/src/main/java/me/saket/bettermovementmethod/sample/MainActivity.java b/sample/src/main/java/me/saket/bettermovementmethod/sample/MainActivity.java deleted file mode 100644 index 67fa1b29efd30243f6b0147b28fd3ce0d82ceee4..0000000000000000000000000000000000000000 --- a/sample/src/main/java/me/saket/bettermovementmethod/sample/MainActivity.java +++ /dev/null @@ -1,77 +0,0 @@ -package me.saket.bettermovementmethod.sample; - -import android.app.Activity; -import android.os.Bundle; -import android.text.Html; -import android.text.Spannable; -import android.text.Spanned; -import android.text.style.BackgroundColorSpan; -import android.text.util.Linkify; -import android.widget.TextView; -import android.widget.Toast; - -import me.saket.bettermovementmethod.BetterLinkMovementMethod; -import me.saket.bettermovementmethod.BetterLinkMovementMethod.OnLinkLongClickListener; - -public class MainActivity extends Activity { - - @Override - protected void onCreate(Bundle savedInstanceState) { - super.onCreate(savedInstanceState); - setContentView(R.layout.activity_main); - - // Add links to all TextViews. - BetterLinkMovementMethod.linkify(Linkify.ALL, this) - .setOnLinkClickListener(urlClickListener) - .setOnLinkLongClickListener(longClickListener); - - TextView wayneTowerIntroView = findViewById(R.id.wayne_tower_intro); - wayneTowerIntroView.setText(Html.fromHtml(getString(R.string.bettermovementmethod_dummy_text_long))); - BetterLinkMovementMethod.linkifyHtml(wayneTowerIntroView) - .setOnLinkClickListener(urlClickListener) - .setOnLinkLongClickListener(longClickListener); - - // https://github.com/Saketme/Better-Link-Movement-Method/issues/8 - Spannable introductionText = (Spannable) wayneTowerIntroView.getText(); - int start = introductionText.toString().indexOf("Wayne tower"); - int end = start + "Wayne tower".length(); - introductionText.setSpan(new BackgroundColorSpan(getColor(R.color.wayneTower)), start, end, Spanned.SPAN_EXCLUSIVE_EXCLUSIVE); - } - - private final BetterLinkMovementMethod.OnLinkClickListener urlClickListener = (view, url) -> { - if (isPhoneNumber(url)) { - PhoneLinkPopupMenu phonePopupMenu = new PhoneLinkPopupMenu(this, view, url); - phonePopupMenu.show(); - - } else if (isEmailAddress(url)) { - EmailLinkPopupMenu emailPopupMenu = new EmailLinkPopupMenu(this, view); - emailPopupMenu.show(); - - } else if (isMapAddress(url)) { - MapLinkPopupMenu mapPopupMenu = new MapLinkPopupMenu(this, view); - mapPopupMenu.show(); - - } else { - Toast.makeText(this, url, Toast.LENGTH_SHORT).show(); - } - - return true; - }; - - private final OnLinkLongClickListener longClickListener = (textView, url) -> { - Toast.makeText(this, "Long-click: " + url, Toast.LENGTH_SHORT).show(); - return true; - }; - - private boolean isPhoneNumber(String url) { - return url.startsWith("tel:"); - } - - private boolean isEmailAddress(String url) { - return url.contains("@"); - } - - private boolean isMapAddress(String url) { - return url.contains("goo.gl/maps"); - } -} diff --git a/sample/src/main/java/me/saket/bettermovementmethod/sample/MapLinkPopupMenu.java b/sample/src/main/java/me/saket/bettermovementmethod/sample/MapLinkPopupMenu.java deleted file mode 100644 index 19b24177880bac112037865520f7e16118c14aec..0000000000000000000000000000000000000000 --- a/sample/src/main/java/me/saket/bettermovementmethod/sample/MapLinkPopupMenu.java +++ /dev/null @@ -1,13 +0,0 @@ -package me.saket.bettermovementmethod.sample; - -import android.content.Context; -import android.view.View; -import android.widget.PopupMenu; - -public class MapLinkPopupMenu extends PopupMenu { - - public MapLinkPopupMenu(Context context, View anchor) { - super(context, anchor); - getMenuInflater().inflate(R.menu.map_link_menu, getMenu()); - } -} diff --git a/sample/src/main/java/me/saket/bettermovementmethod/sample/PhoneLinkPopupMenu.java b/sample/src/main/java/me/saket/bettermovementmethod/sample/PhoneLinkPopupMenu.java deleted file mode 100644 index ef0f2b93a168ff099050e5c519ba843368db8ad2..0000000000000000000000000000000000000000 --- a/sample/src/main/java/me/saket/bettermovementmethod/sample/PhoneLinkPopupMenu.java +++ /dev/null @@ -1,68 +0,0 @@ -package me.saket.bettermovementmethod.sample; - -import android.content.ClipData; -import android.content.ClipboardManager; -import android.content.Context; -import android.content.Intent; -import android.net.Uri; -import android.provider.ContactsContract; -import android.view.View; -import android.widget.PopupMenu; -import android.widget.Toast; - -public class PhoneLinkPopupMenu extends PopupMenu { - - public PhoneLinkPopupMenu(Context context, View anchor, String phoneUrl) { - super(context, anchor); - - getMenuInflater().inflate(R.menu.phone_link_menu, getMenu()); - setOnMenuItemClickListener(item -> { - switch (item.getItemId()) { - case R.id.action_call: - callNumber(context, phoneUrl); - return true; - - case R.id.action_sms: - onComposeNewSmsClick(context, phoneUrl); - return true; - - case R.id.action_copy: - onCopyAddressClick(context, phoneUrl); - return true; - - case R.id.action_save: - onAddToContactsClick(context, phoneUrl); - return true; - - default: - throw new UnsupportedOperationException(); - } - }); - } - - private void callNumber(Context context, String phoneUrl) { - Intent dialIntent = new Intent(Intent.ACTION_DIAL, Uri.parse(phoneUrl)).addFlags(Intent.FLAG_ACTIVITY_NEW_TASK); - context.startActivity(dialIntent); - } - - private void onComposeNewSmsClick(Context context, String phoneUrl) { - Intent smsIntent = new Intent(Intent.ACTION_SENDTO, Uri.parse("smsto:" + phoneUrl)); - context.startActivity(smsIntent); - } - - private void onCopyAddressClick(Context context, String phoneUrl) { - final ClipboardManager clipboard = (ClipboardManager) context.getSystemService(Context.CLIPBOARD_SERVICE); - final ClipData clip = ClipData.newPlainText(context.getPackageName(), phoneUrl); - //noinspection ConstantConditions - clipboard.setPrimaryClip(clip); - Toast.makeText(context, "Copied to clipboard", Toast.LENGTH_SHORT).show(); - } - - private void onAddToContactsClick(Context context, String phoneUrl) { - final Intent addContactIntent = new Intent(ContactsContract.Intents.SHOW_OR_CREATE_CONTACT); - addContactIntent.putExtra("finishActivityOnSaveCompleted", true); - addContactIntent.setData(Uri.fromParts("tel", phoneUrl, null)); - addContactIntent.putExtra(ContactsContract.Intents.EXTRA_FORCE_CREATE, true); - context.startActivity(addContactIntent); - } -} diff --git a/sample/src/main/res/layout/activity_main.xml b/sample/src/main/res/layout/activity_main.xml deleted file mode 100644 index 7d775d2d639f092f00f043a6ef4f751b96696e0e..0000000000000000000000000000000000000000 --- a/sample/src/main/res/layout/activity_main.xml +++ /dev/null @@ -1,79 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/sample/src/main/res/menu/email_link_menu.xml b/sample/src/main/res/menu/email_link_menu.xml deleted file mode 100644 index 22f6fe01005ceb7c1b36541b151253aa762e1288..0000000000000000000000000000000000000000 --- a/sample/src/main/res/menu/email_link_menu.xml +++ /dev/null @@ -1,9 +0,0 @@ - - - - - - - diff --git a/sample/src/main/res/menu/map_link_menu.xml b/sample/src/main/res/menu/map_link_menu.xml deleted file mode 100644 index 3d132d883f8c34a3e2e5ea5971330cf15d855bc5..0000000000000000000000000000000000000000 --- a/sample/src/main/res/menu/map_link_menu.xml +++ /dev/null @@ -1,9 +0,0 @@ - - - - - - - diff --git a/sample/src/main/res/menu/phone_link_menu.xml b/sample/src/main/res/menu/phone_link_menu.xml deleted file mode 100644 index d01ead01ed7e1f09fed707275c4fd2f0bbbe6a97..0000000000000000000000000000000000000000 --- a/sample/src/main/res/menu/phone_link_menu.xml +++ /dev/null @@ -1,20 +0,0 @@ - - - - - - - - - - diff --git a/sample/src/main/res/mipmap-hdpi/ic_launcher.png b/sample/src/main/res/mipmap-hdpi/ic_launcher.png deleted file mode 100644 index cde69bcccec65160d92116f20ffce4fce0b5245c..0000000000000000000000000000000000000000 Binary files a/sample/src/main/res/mipmap-hdpi/ic_launcher.png and /dev/null differ diff --git a/sample/src/main/res/mipmap-mdpi/ic_launcher.png b/sample/src/main/res/mipmap-mdpi/ic_launcher.png deleted file mode 100644 index c133a0cbd379f5af6dbf1a899a0293ca5eccfad0..0000000000000000000000000000000000000000 Binary files a/sample/src/main/res/mipmap-mdpi/ic_launcher.png and /dev/null differ diff --git a/sample/src/main/res/mipmap-xhdpi/ic_launcher.png b/sample/src/main/res/mipmap-xhdpi/ic_launcher.png deleted file mode 100644 index bfa42f0e7b91d006d22352c9ff2f134e504e3c1d..0000000000000000000000000000000000000000 Binary files a/sample/src/main/res/mipmap-xhdpi/ic_launcher.png and /dev/null differ diff --git a/sample/src/main/res/mipmap-xxhdpi/ic_launcher.png b/sample/src/main/res/mipmap-xxhdpi/ic_launcher.png deleted file mode 100644 index 324e72cdd7480cb983fa1bcc7ce686e51ef87fe7..0000000000000000000000000000000000000000 Binary files a/sample/src/main/res/mipmap-xxhdpi/ic_launcher.png and /dev/null differ diff --git a/sample/src/main/res/mipmap-xxxhdpi/ic_launcher.png b/sample/src/main/res/mipmap-xxxhdpi/ic_launcher.png deleted file mode 100644 index aee44e138434630332d88b1680f33c4b24c70ab3..0000000000000000000000000000000000000000 Binary files a/sample/src/main/res/mipmap-xxxhdpi/ic_launcher.png and /dev/null differ diff --git a/sample/src/main/res/values/colors.xml b/sample/src/main/res/values/colors.xml deleted file mode 100644 index 2af5abb10fc7f213c8cf912a395382914e9c7044..0000000000000000000000000000000000000000 --- a/sample/src/main/res/values/colors.xml +++ /dev/null @@ -1,8 +0,0 @@ - - - #FFFFFF - #AAA - #FF4081 - - #2c4089ff - diff --git a/sample/src/main/res/values/strings.xml b/sample/src/main/res/values/strings.xml deleted file mode 100644 index 6a09689dd931abc8c93b28c2d509ec8dbf46aab8..0000000000000000000000000000000000000000 --- a/sample/src/main/res/values/strings.xml +++ /dev/null @@ -1,9 +0,0 @@ - - BetterLinkMovement - Gotham has been good to our family, but the city\u2019s been suffering. People less fortunate -than us have been enduring very hard times. So we built a new, cheap, public transportation system to unite the city. And at the center, -Wayne tower. - ]]> - 7899859911 - diff --git a/sample/src/main/res/values/styles.xml b/sample/src/main/res/values/styles.xml deleted file mode 100644 index 2ba33ef0b9e393d5b69642ecb0a2fe8441319693..0000000000000000000000000000000000000000 --- a/sample/src/main/res/values/styles.xml +++ /dev/null @@ -1,26 +0,0 @@ - - - - - - - - diff --git a/settings.gradle b/settings.gradle index de805007298686a8a5f57b0360fa02a27dc6002d..c75f2aae4b6d9a5d2008606566e75fa83bfb7adc 100644 --- a/settings.gradle +++ b/settings.gradle @@ -1 +1 @@ -include ':sample', ':better-link-movement-method' +include ':entry', ':better_link_movement_method'