Update
This commit is contained in:
1 parent
f3a7bcbfd3
commit
705dd16541
37 files changed
+624
-597
No files matched your search
@@ -0,0 +1,65 @@
|
||||
/*
|
||||
* Copyright 2015 Hippo Seven
|
||||
*
|
||||
* 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 com.hippo.util;
|
||||
|
||||
import android.app.Activity;
|
||||
import android.content.ActivityNotFoundException;
|
||||
import android.content.Intent;
|
||||
import android.net.Uri;
|
||||
import android.support.annotation.NonNull;
|
||||
import android.support.annotation.Nullable;
|
||||
import android.widget.Toast;
|
||||
|
||||
import com.hippo.ehviewer.R;
|
||||
|
||||
public class ActivityHelper {
|
||||
|
||||
public static boolean sendEmail(@NonNull Activity from, @NonNull String address,
|
||||
@Nullable String subject, @Nullable String text) {
|
||||
Intent i = new Intent(Intent.ACTION_SENDTO);
|
||||
i.setData(Uri.parse("mailto:" + address));
|
||||
if (subject != null) {
|
||||
i.putExtra(Intent.EXTRA_SUBJECT, subject);
|
||||
}
|
||||
if (text != null) {
|
||||
i.putExtra(Intent.EXTRA_TEXT, text);
|
||||
}
|
||||
|
||||
try {
|
||||
from.startActivity(i);
|
||||
return true;
|
||||
} catch (ActivityNotFoundException e) {
|
||||
Toast.makeText(from, R.string.error_cant_find_activity, Toast.LENGTH_SHORT).show();
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
public static boolean share(@NonNull Activity from, String text) {
|
||||
Intent sendIntent = new Intent();
|
||||
sendIntent.setAction(Intent.ACTION_SEND);
|
||||
sendIntent.putExtra(Intent.EXTRA_TEXT, text);
|
||||
sendIntent.setType("text/plain");
|
||||
|
||||
try {
|
||||
from.startActivity(sendIntent);
|
||||
return true;
|
||||
} catch (ActivityNotFoundException e) {
|
||||
Toast.makeText(from, R.string.error_cant_find_activity, Toast.LENGTH_SHORT).show();
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,24 @@
|
||||
/*
|
||||
* Copyright 2016 Hippo Seven
|
||||
*
|
||||
* 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 com.hippo.util;
|
||||
|
||||
import android.os.Build;
|
||||
|
||||
public final class ApiHelper {
|
||||
|
||||
public static final boolean SUPPORT_TRANSITION = Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP;
|
||||
}
|
||||
@@ -0,0 +1,167 @@
|
||||
// Copyright 2015 Google Inc. All Rights Reserved.
|
||||
//
|
||||
// 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 com.hippo.util;
|
||||
|
||||
import android.content.Context;
|
||||
import android.content.Intent;
|
||||
import android.content.IntentFilter;
|
||||
import android.content.pm.PackageManager;
|
||||
import android.content.pm.ResolveInfo;
|
||||
import android.net.Uri;
|
||||
import android.text.TextUtils;
|
||||
import android.util.Log;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* Helper class for Custom Tabs.
|
||||
*/
|
||||
public class CustomTabsHelper {
|
||||
private static final String TAG = "CustomTabsHelper";
|
||||
static final String STABLE_PACKAGE = "com.android.chrome";
|
||||
static final String BETA_PACKAGE = "com.chrome.beta";
|
||||
static final String DEV_PACKAGE = "com.chrome.dev";
|
||||
static final String LOCAL_PACKAGE = "com.google.android.apps.chrome";
|
||||
private static final String ACTION_CUSTOM_TABS_CONNECTION =
|
||||
"android.support.customtabs.action.CustomTabsService";
|
||||
private static final String UNINITIALIZED = "UNINITIALIZED";
|
||||
|
||||
private static String sPackageNameToUse = UNINITIALIZED;
|
||||
|
||||
private CustomTabsHelper() {}
|
||||
|
||||
/**
|
||||
* The browser of smartisan is terrible
|
||||
*/
|
||||
public static String getPackageNameToUseFixed(Context context) {
|
||||
if (!UNINITIALIZED.equals(sPackageNameToUse)) return sPackageNameToUse;
|
||||
|
||||
PackageManager pm = context.getPackageManager();
|
||||
|
||||
// Get all apps that can handle VIEW intents.
|
||||
Intent activityIntent = new Intent(Intent.ACTION_VIEW, Uri.parse("http://www.example.com"));
|
||||
List<ResolveInfo> resolvedActivityList = pm.queryIntentActivities(activityIntent, 0);
|
||||
List<String> packagesSupportingCustomTabs = new ArrayList<>();
|
||||
for (ResolveInfo info : resolvedActivityList) {
|
||||
Intent serviceIntent = new Intent();
|
||||
serviceIntent.setAction(ACTION_CUSTOM_TABS_CONNECTION);
|
||||
serviceIntent.setPackage(info.activityInfo.packageName);
|
||||
if (pm.resolveService(serviceIntent, 0) != null) {
|
||||
packagesSupportingCustomTabs.add(info.activityInfo.packageName);
|
||||
}
|
||||
}
|
||||
|
||||
// Now packagesSupportingCustomTabs contains all apps that can handle both VIEW intents
|
||||
// and service calls.
|
||||
if (packagesSupportingCustomTabs.isEmpty()) {
|
||||
sPackageNameToUse = null;
|
||||
} else if (packagesSupportingCustomTabs.contains(STABLE_PACKAGE)) {
|
||||
sPackageNameToUse = STABLE_PACKAGE;
|
||||
} else if (packagesSupportingCustomTabs.contains(BETA_PACKAGE)) {
|
||||
sPackageNameToUse = BETA_PACKAGE;
|
||||
} else if (packagesSupportingCustomTabs.contains(DEV_PACKAGE)) {
|
||||
sPackageNameToUse = DEV_PACKAGE;
|
||||
} else if (packagesSupportingCustomTabs.contains(LOCAL_PACKAGE)) {
|
||||
sPackageNameToUse = LOCAL_PACKAGE;
|
||||
} else {
|
||||
sPackageNameToUse = null;
|
||||
}
|
||||
return sPackageNameToUse;
|
||||
}
|
||||
|
||||
/**
|
||||
* Goes through all apps that handle VIEW intents and have a warmup service. Picks
|
||||
* the one chosen by the user if there is one, otherwise makes a best effort to return a
|
||||
* valid package name.
|
||||
*
|
||||
* This is <strong>not</strong> threadsafe.
|
||||
*
|
||||
* @param context {@link Context} to use for accessing {@link PackageManager}.
|
||||
* @return The package name recommended to use for connecting to custom tabs related components.
|
||||
*/
|
||||
public static String getPackageNameToUse(Context context) {
|
||||
if (sPackageNameToUse != null) return sPackageNameToUse;
|
||||
|
||||
PackageManager pm = context.getPackageManager();
|
||||
// Get default VIEW intent handler.
|
||||
Intent activityIntent = new Intent(Intent.ACTION_VIEW, Uri.parse("http://www.example.com"));
|
||||
ResolveInfo defaultViewHandlerInfo = pm.resolveActivity(activityIntent, 0);
|
||||
String defaultViewHandlerPackageName = null;
|
||||
if (defaultViewHandlerInfo != null) {
|
||||
defaultViewHandlerPackageName = defaultViewHandlerInfo.activityInfo.packageName;
|
||||
}
|
||||
|
||||
// Get all apps that can handle VIEW intents.
|
||||
List<ResolveInfo> resolvedActivityList = pm.queryIntentActivities(activityIntent, 0);
|
||||
List<String> packagesSupportingCustomTabs = new ArrayList<>();
|
||||
for (ResolveInfo info : resolvedActivityList) {
|
||||
Intent serviceIntent = new Intent();
|
||||
serviceIntent.setAction(ACTION_CUSTOM_TABS_CONNECTION);
|
||||
serviceIntent.setPackage(info.activityInfo.packageName);
|
||||
if (pm.resolveService(serviceIntent, 0) != null) {
|
||||
packagesSupportingCustomTabs.add(info.activityInfo.packageName);
|
||||
}
|
||||
}
|
||||
|
||||
// Now packagesSupportingCustomTabs contains all apps that can handle both VIEW intents
|
||||
// and service calls.
|
||||
if (packagesSupportingCustomTabs.isEmpty()) {
|
||||
sPackageNameToUse = null;
|
||||
} else if (packagesSupportingCustomTabs.size() == 1) {
|
||||
sPackageNameToUse = packagesSupportingCustomTabs.get(0);
|
||||
} else if (!TextUtils.isEmpty(defaultViewHandlerPackageName)
|
||||
&& !hasSpecializedHandlerIntents(context, activityIntent)
|
||||
&& packagesSupportingCustomTabs.contains(defaultViewHandlerPackageName)) {
|
||||
sPackageNameToUse = defaultViewHandlerPackageName;
|
||||
} else if (packagesSupportingCustomTabs.contains(STABLE_PACKAGE)) {
|
||||
sPackageNameToUse = STABLE_PACKAGE;
|
||||
} else if (packagesSupportingCustomTabs.contains(BETA_PACKAGE)) {
|
||||
sPackageNameToUse = BETA_PACKAGE;
|
||||
} else if (packagesSupportingCustomTabs.contains(DEV_PACKAGE)) {
|
||||
sPackageNameToUse = DEV_PACKAGE;
|
||||
} else if (packagesSupportingCustomTabs.contains(LOCAL_PACKAGE)) {
|
||||
sPackageNameToUse = LOCAL_PACKAGE;
|
||||
}
|
||||
return sPackageNameToUse;
|
||||
}
|
||||
|
||||
/**
|
||||
* Used to check whether there is a specialized handler for a given intent.
|
||||
* @param intent The intent to check with.
|
||||
* @return Whether there is a specialized handler for the given intent.
|
||||
*/
|
||||
private static boolean hasSpecializedHandlerIntents(Context context, Intent intent) {
|
||||
try {
|
||||
PackageManager pm = context.getPackageManager();
|
||||
List<ResolveInfo> handlers = pm.queryIntentActivities(
|
||||
intent,
|
||||
PackageManager.GET_RESOLVED_FILTER);
|
||||
if (handlers == null || handlers.size() == 0) {
|
||||
return false;
|
||||
}
|
||||
for (ResolveInfo resolveInfo : handlers) {
|
||||
IntentFilter filter = resolveInfo.filter;
|
||||
if (filter == null) continue;
|
||||
if (filter.countDataAuthorities() == 0 || filter.countDataPaths() == 0) continue;
|
||||
if (resolveInfo.activityInfo == null) continue;
|
||||
return true;
|
||||
}
|
||||
} catch (RuntimeException e) {
|
||||
Log.e(TAG, "Runtime exception while getting specialized handlers");
|
||||
}
|
||||
return false;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,63 @@
|
||||
/*
|
||||
* Copyright 2016 Hippo Seven
|
||||
*
|
||||
* 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 com.hippo.util;
|
||||
|
||||
|
||||
import android.content.Context;
|
||||
import android.support.annotation.NonNull;
|
||||
|
||||
import com.hippo.ehviewer.R;
|
||||
import com.hippo.ehviewer.client.exception.EhException;
|
||||
import com.hippo.network.StatusCodeException;
|
||||
|
||||
import org.apache.http.conn.ConnectTimeoutException;
|
||||
|
||||
import java.net.ProtocolException;
|
||||
import java.net.SocketException;
|
||||
import java.net.SocketTimeoutException;
|
||||
import java.net.UnknownHostException;
|
||||
|
||||
public final class ExceptionUtils {
|
||||
|
||||
private static final String TAG = ExceptionUtils.class.getSimpleName();
|
||||
|
||||
@NonNull
|
||||
public static String getReadableString(@NonNull Context context, @NonNull Exception e) {
|
||||
if (e instanceof ConnectTimeoutException ||
|
||||
e instanceof SocketTimeoutException) {
|
||||
return context.getString(R.string.error_timeout);
|
||||
} else if (e instanceof UnknownHostException) {
|
||||
return context.getString(R.string.error_unknown_host);
|
||||
} else if (e instanceof StatusCodeException) {
|
||||
StatusCodeException sce = (StatusCodeException) e;
|
||||
StringBuilder sb = new StringBuilder();
|
||||
sb.append(context.getString(R.string.error_bad_status_code, sce.getResponseCode()));
|
||||
if (sce.isIdentifiedResponseCode()) {
|
||||
sb.append(", ").append(sce.getMessage());
|
||||
}
|
||||
return sb.toString();
|
||||
} else if (e instanceof ProtocolException && e.getMessage().startsWith("Too many follow-up requests:")) {
|
||||
return context.getString(R.string.error_redirection);
|
||||
} else if (e instanceof ProtocolException || e instanceof SocketException) {
|
||||
return context.getString(R.string.error_socket);
|
||||
} else if (e instanceof EhException) {
|
||||
return e.getMessage();
|
||||
} else {
|
||||
return context.getString(R.string.error_unknown);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,178 @@
|
||||
/*
|
||||
* Copyright 2015 Hippo Seven
|
||||
*
|
||||
* 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 com.hippo.util;
|
||||
|
||||
import android.content.Context;
|
||||
import android.content.res.Resources;
|
||||
|
||||
import com.hippo.ehviewer.R;
|
||||
|
||||
import java.text.SimpleDateFormat;
|
||||
import java.util.Calendar;
|
||||
import java.util.Date;
|
||||
import java.util.Locale;
|
||||
import java.util.TimeZone;
|
||||
|
||||
public final class ReadableTime {
|
||||
|
||||
private static Resources sResources;
|
||||
|
||||
public static final long SECOND_MILLIS = 1000;
|
||||
public static final long MINUTE_MILLIS = 60 * SECOND_MILLIS;
|
||||
public static final long HOUR_MILLIS = 60 * MINUTE_MILLIS;
|
||||
public static final long DAY_MILLIS = 24 * HOUR_MILLIS;
|
||||
public static final long WEEK_MILLIS = 7 * DAY_MILLIS;
|
||||
public static final long YEAR_MILLIS = 365 * DAY_MILLIS;
|
||||
|
||||
public static final int SIZE = 5;
|
||||
|
||||
public static final long[] MULTIPLES = {
|
||||
YEAR_MILLIS,
|
||||
DAY_MILLIS,
|
||||
HOUR_MILLIS,
|
||||
MINUTE_MILLIS,
|
||||
SECOND_MILLIS
|
||||
};
|
||||
|
||||
public static final int[] UNITS = {
|
||||
R.plurals.year,
|
||||
R.plurals.day,
|
||||
R.plurals.hour,
|
||||
R.plurals.minute,
|
||||
R.plurals.second
|
||||
};
|
||||
|
||||
private static final Calendar sCalendar = Calendar.getInstance(TimeZone.getTimeZone("GMT+08:00"));
|
||||
private static final Object sCalendarLock = new Object();
|
||||
|
||||
private static final SimpleDateFormat DATE_FORMAT_WITHOUT_YEAR =
|
||||
new SimpleDateFormat("MMM d", Locale.getDefault());
|
||||
|
||||
private static final SimpleDateFormat DATE_FORMAT_WIT_YEAR =
|
||||
new SimpleDateFormat("yyy MMM d", Locale.getDefault());
|
||||
|
||||
private static final SimpleDateFormat DATE_FORMAT =
|
||||
new SimpleDateFormat("yy-MM-dd HH:mm", Locale.getDefault());
|
||||
private static final Object sDateFormatLock1 = new Object();
|
||||
|
||||
private static final SimpleDateFormat FILENAMABLE_DATE_FORMAT =
|
||||
new SimpleDateFormat("yyyy-MM-dd-HH-mm-ss-SSS", Locale.getDefault());
|
||||
private static final Object sDateFormatLock2 = new Object();
|
||||
|
||||
static {
|
||||
// The website use GMT+08:00, so tell user the same
|
||||
DATE_FORMAT.setTimeZone(TimeZone.getTimeZone("GMT+08:00"));
|
||||
|
||||
DATE_FORMAT_WITHOUT_YEAR.setTimeZone(TimeZone.getTimeZone("GMT+08:00"));
|
||||
}
|
||||
|
||||
public static void initialize(Context context) {
|
||||
sResources = context.getApplicationContext().getResources();
|
||||
}
|
||||
|
||||
/*
|
||||
public static String getDisplayTime(long time) {
|
||||
if (Settings.getPrettyTime()) {
|
||||
return getTimeAgo(time);
|
||||
} else {
|
||||
return getPlainTime(time);
|
||||
}
|
||||
}
|
||||
*/
|
||||
|
||||
public static String getPlainTime(long time) {
|
||||
synchronized (sDateFormatLock1) {
|
||||
return DATE_FORMAT.format(new Date(time));
|
||||
}
|
||||
}
|
||||
|
||||
public static String getTimeAgo(long time) {
|
||||
Resources resources = sResources;
|
||||
|
||||
long now = System.currentTimeMillis();
|
||||
if (time > now + (2 * MINUTE_MILLIS) || time <= 0) {
|
||||
return resources.getString(R.string.from_the_future);
|
||||
}
|
||||
|
||||
final long diff = now - time;
|
||||
if (diff < MINUTE_MILLIS) {
|
||||
return resources.getString(R.string.just_now);
|
||||
} else if (diff < 2 * MINUTE_MILLIS) {
|
||||
return resources.getQuantityString(R.plurals.some_minutes_ago, 1, 1);
|
||||
} else if (diff < 50 * MINUTE_MILLIS) {
|
||||
int minutes = (int) (diff / MINUTE_MILLIS);
|
||||
return resources.getQuantityString(R.plurals.some_minutes_ago, minutes, minutes);
|
||||
} else if (diff < 90 * MINUTE_MILLIS) {
|
||||
return resources.getQuantityString(R.plurals.some_hours_ago, 1, 1);
|
||||
} else if (diff < 24 * HOUR_MILLIS) {
|
||||
int hours = (int) (diff / HOUR_MILLIS);
|
||||
return resources.getQuantityString(R.plurals.some_hours_ago, hours, hours);
|
||||
} else if (diff < 48 * HOUR_MILLIS) {
|
||||
return resources.getString(R.string.yesterday);
|
||||
} else if (diff < WEEK_MILLIS) {
|
||||
int days = (int) (diff / DAY_MILLIS);
|
||||
return resources.getString(R.string.some_days_ago, days);
|
||||
} else {
|
||||
synchronized (sCalendarLock) {
|
||||
Date nowDate = new Date(now);
|
||||
Date timeDate = new Date(time);
|
||||
sCalendar.setTime(nowDate);
|
||||
int nowYear = sCalendar.get(Calendar.YEAR);
|
||||
sCalendar.setTime(timeDate);
|
||||
int timeYear = sCalendar.get(Calendar.YEAR);
|
||||
|
||||
if (nowYear == timeYear) {
|
||||
return DATE_FORMAT_WITHOUT_YEAR.format(timeDate);
|
||||
} else {
|
||||
return DATE_FORMAT_WIT_YEAR.format(timeDate);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public static String getTimeInterval(long time) {
|
||||
StringBuilder sb = new StringBuilder();
|
||||
Resources resources = sResources;
|
||||
|
||||
long leftover = time;
|
||||
boolean start = false;
|
||||
|
||||
for (int i = 0; i < SIZE; i++) {
|
||||
long multiple = MULTIPLES[i];
|
||||
long quotient = leftover / multiple;
|
||||
long remainder = leftover % multiple;
|
||||
if (start || quotient != 0 || i == SIZE - 1) {
|
||||
if (start) {
|
||||
sb.append(" ");
|
||||
}
|
||||
sb.append(quotient)
|
||||
.append(" ")
|
||||
.append(resources.getQuantityString(UNITS[i], (int) quotient));
|
||||
start = true;
|
||||
}
|
||||
leftover = remainder;
|
||||
}
|
||||
|
||||
return sb.toString();
|
||||
}
|
||||
|
||||
public static String getFilenamableTime(long time) {
|
||||
synchronized (sDateFormatLock2) {
|
||||
return FILENAMABLE_DATE_FORMAT.format(new Date(time));
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,59 @@
|
||||
/*
|
||||
* Copyright 2016 Hippo Seven
|
||||
*
|
||||
* 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 com.hippo.util;
|
||||
|
||||
import android.text.Spannable;
|
||||
import android.text.SpannableString;
|
||||
import android.text.style.URLSpan;
|
||||
|
||||
import java.util.regex.Matcher;
|
||||
import java.util.regex.Pattern;
|
||||
|
||||
public final class TextUrl {
|
||||
|
||||
private static final Pattern URL_PATTERN = Pattern.compile("(http|https)://[a-z0-9A-Z%-]+(\\.[a-z0-9A-Z%-]+)+(:\\d{1,5})?(/[a-zA-Z0-9-_~:#@!&',;=%/\\*\\.\\?\\+\\$\\[\\]\\(\\)]+)?/?");
|
||||
|
||||
public static CharSequence handleTextUrl(CharSequence content) {
|
||||
Matcher m = URL_PATTERN.matcher(content);
|
||||
|
||||
Spannable spannable = null;
|
||||
while (m.find()) {
|
||||
// Ensure spannable
|
||||
if (spannable == null) {
|
||||
if (content instanceof Spannable) {
|
||||
spannable = (Spannable) content;
|
||||
} else {
|
||||
spannable = new SpannableString(content);
|
||||
}
|
||||
}
|
||||
|
||||
int start = m.start();
|
||||
int end = m.end();
|
||||
|
||||
URLSpan[] links = spannable.getSpans(start, end, URLSpan.class);
|
||||
if (links.length > 0) {
|
||||
// There has been URLSpan already, leave it alone
|
||||
continue;
|
||||
}
|
||||
|
||||
URLSpan urlSpan = new URLSpan(m.group(0));
|
||||
spannable.setSpan(urlSpan, start, end, Spannable.SPAN_EXCLUSIVE_EXCLUSIVE);
|
||||
}
|
||||
|
||||
return spannable == null ? content : spannable;
|
||||
}
|
||||
}
|
||||
Reference in new issue
Block a user