注:有任何问题时请确保你已起码浏览完该页面的全部标题部分,否则恕不解答。
21 | 22 |你必须拥有 Xposed 框架,并激活该模块,然后重启。
26 | 如果该 App 没有生效,请确保你成功激活该模块。本来我是想像其他 Xposed App 一样写一个检测是否启用的功能,但是我遇到了一些蛋疼的问题,其他人能生效的方式我这不行,下次更新吧。
常见的都能支持,自带的是一些主流 App,但是比如知乎啥的也是支持的。
29 |你可以试试自己添加该规则,如果不行那可能是不支持。
31 |通过「添加白名单」功能添加指定页面或域名即可,详情请看“使用方法”部分
33 | 34 |一共分成三步:首先点主页右下角的「添加应用」按钮,选择应用或输入包名,随后重启手机;
重启后打开本 App,把提示里的链接放入要屏蔽的 App 中(比如聊天、发表动态),然后打开它;(如果用户没法输入测试链接,请使用「自定义链接」功能)
如果成功,则会自动出现“步骤三”的页面,重启手机即可。(如果点击链接后没有出现“步骤三”页面,请判断是否需要使用「自定义链接」功能)
有些是App做了链接跳转(比如微博的所有链接都会变成 t.cn 的链接),你可以尝试新版本的“自定义链接”功能。有些是App代码实在是写的比较奇葩,你可以选择反馈给我。在反馈之前我希望你能确认下不是自己的问题,比如没重启或者找不到输链接的地方什么的(比如知乎的手机端你输链接,是不会被自动识别成链接的,需要到网页版编辑答案。这种请自己想办法)。
39 |不一定要重启,但是必须得杀掉屏蔽应用的后台,还是重启省事。
41 |实现比较规范的应用比如 QQ 是只有一个内置浏览器页面的,但是比如贴吧微博这种我就不忍心说了,请尝试长按需要修改的 App,选择「添加规则」。
43 |「添加规则」就是解决上面这个问题的,一些应用可能有好几个“内置浏览器”,我可能会有疏忽,你可以尝试自己添加。使用流程与屏蔽应用相同,点击 App 选「添加规则」菜单即可。
45 |这是因为有些 App 的某些页面其实还是用网页实现的,比如 QQ 的空间模块里,留言部分跟情侣空间两个按钮假如点击,会在本 App 的1.1版本用外置浏览器打开。我采用了一种简单粗暴的方法:这些链接都是应用的页面(非用户发布),所以直接忽略对应的域名就好了,比如QQ我自带忽略了 h5.qzone.qq.com、微博我忽略了 card.weibo.com。
使用方法:长按对应App,选择「添加白名单」,添加即可。具体添加的内容为你误打开页面的域名。
在1.5版本以后,白名单现在支持自动匹配二级域名了,比如 h5.qzone.qq.com 与 qzs.qzone.qq.com 你仅需输入 qzone.qq.com 即可。
有一些纯阅读类 App,用户是没法输入链接的,只有发布者才可以。还有一些 App 是做了链接转换(比如新浪微博、酷安),这些都没法通过 http://www.example.org/ex-link-test 这个链接完成步骤三匹配。
这时候就得用到「自定义链接」功能了。在“步骤二”页面的下方,输入需要识别的链接,确认即可。(大部分时候会有链接跳转,假如你没法找到正确的链接,请打开「调试模式」寻找)
在1.5版本后,可以通过仅输入域名来模糊判断。
当你打开调试模式后,会在Xposed的日志里输出Log。输出Log是个当遇到问题比较有用的功能,可以供用户判断问题。当用户使用“自定义链接”时,当url有问题时请在 Log 里查看原始 url。
51 |考虑到手动添加 App 还是比较麻烦的,因此这次做了导入导出功能,可以将别人的规则直接添加到你手机上。数据格式是 json。
53 | 54 |3.0版本后改了架构,因此跟以前的规则不兼容了,怪我当时没考虑好=。=
57 |你们可以导入/导出规则啦~
59 |我并没有做防呆设计,所以请严格按照步骤来。比如一次添加几个应用到测试列表、应用还没添加成功就添加规则了、添加规则到一半又不添加了这些情况我概不负责,遇到了任何问题请清空本 App 的数据。
61 | 62 |这个没法解决,因为整个群公告部分都是处在内置浏览器之中,因此是没法获取到某条群公告里的某个链接的。
64 |在1.5版本中我开始做链接净化了,目前在我测试中微博表现良好,贴吧我还没测试。
66 |众所周知,微信的一部分链接是需要用微信自带浏览器才能打开的,而由于它封闭的生态,无法区分这些链接。因此对于微信,在3.0版本后采用点击链接弹窗让用户选择打开方式。不喜欢可以关闭微信的支持,如果你更喜欢以前的模式,请反馈给我。
68 |_(:з)∠)_俩年前就有了......结果现在还是没做,随缘吧
70 |没有必要。但是,我希望每位反感这些国产流氓的用户,都能用自己的实际行动反抗它们。其实这些流氓企业一直是处于一种有恃无恐的状态,“你骂呗,骂完了之后你又不会有什么改变,还不是该用的用该不用的不用”。从阿里对 wp 用户明摆着的嘲讽和一些其他事上都可以看出,但是大部分网民呢?骂完了之后该干什么干什么。
拿贴吧举例,百度卖舰娘吧、卖 minecraft 吧,一片人骂它,批判它,爆吧什么的。但是这有丝毫作用么?百度它在乎么?骂的人越多关注度越高它还开心呢。因此应该做什么,应该是放弃贴吧,放弃百度产品。百度发现这样做只会导致用户流失,才有可能会收敛。
这种事,一个人或一些人去做当然影响不到它们,但是,我,问心无愧。
那你们呢?
Google Play与酷安
74 |
我大概会看这俩处的评论吧......主要是因为工作忙,看的会比较少。但是我要更新的话肯定会先看看收集意见的。
75 |
十分感谢 ContactFront 提供的英文翻译!
77 | 78 | 79 | -------------------------------------------------------------------------------- /app/src/main/java/com/xloger/exlink/app/activity/StepTwoActivity.java: -------------------------------------------------------------------------------- 1 | package com.xloger.exlink.app.activity; 2 | 3 | import android.content.ClipboardManager; 4 | import android.content.Context; 5 | import android.os.Bundle; 6 | import android.view.View; 7 | import android.widget.Button; 8 | import android.widget.EditText; 9 | import android.widget.TextView; 10 | import android.widget.Toast; 11 | 12 | import com.xloger.exlink.app.Constant; 13 | import com.xloger.exlink.app.R; 14 | import com.xloger.exlink.app.util.FileUtil; 15 | 16 | /** 17 | * Created by xloger on 1月6日. 18 | * Author:xloger 19 | * Email:phoenix@xloger.com 20 | */ 21 | public class StepTwoActivity extends BaseActivity implements View.OnClickListener { 22 | 23 | private TextView urlTextView; 24 | private Button differentUrlButton; 25 | private EditText differentUrlEditText; 26 | private boolean isShowCustom = false; 27 | private View customLayout; 28 | 29 | @Override 30 | protected void onCreate(Bundle savedInstanceState) { 31 | super.onCreate(savedInstanceState); 32 | setContentView(R.layout.step_two); 33 | 34 | String title = getIntent().getStringExtra("title"); 35 | if (title != null && !"".equals(title)) { 36 | setTitle(title); 37 | } else { 38 | setTitle(getResources().getString(R.string.step_two)); 39 | } 40 | 41 | urlTextView = findViewById(R.id.step_two_url); 42 | differentUrlEditText = findViewById(R.id.step_two_different_url); 43 | differentUrlButton = findViewById(R.id.step_two_different_button); 44 | Button customBtn = findViewById(R.id.step_two_custom_btn); 45 | Button defaultBtn = findViewById(R.id.step_two_back_default); 46 | customLayout = findViewById(R.id.step_two_custom_layout); 47 | urlTextView.setOnClickListener(this); 48 | differentUrlButton.setOnClickListener(this); 49 | customBtn.setOnClickListener(this); 50 | defaultBtn.setOnClickListener(this); 51 | 52 | FileUtil fileUtil = FileUtil.getInstance(); 53 | byte[] bytes = fileUtil.load(Constant.DIFFERENT_URL_FILE_NAME); 54 | if (bytes != null && bytes.length != 0 && bytes[0] == '1') { 55 | urlTextView.setText(new String(bytes, 1, bytes.length - 1)); 56 | } 57 | 58 | } 59 | 60 | 61 | @Override 62 | public void onClick(View v) { 63 | switch (v.getId()) { 64 | case R.id.step_two_url: 65 | ClipboardManager cmb = (ClipboardManager) this.getSystemService(Context.CLIPBOARD_SERVICE); 66 | cmb.setText(urlTextView.getText()); //将内容放入粘贴管理器,在别的地方长按选择"粘贴"即可 67 | // cm.getText();//获取粘贴信息 68 | Toast.makeText(this, getString(R.string.copy_succeed), Toast.LENGTH_SHORT).show(); 69 | break; 70 | case R.id.step_two_different_button: 71 | String differentUrl = differentUrlEditText.getText().toString(); 72 | if (differentUrl.length() != 0) { 73 | FileUtil fileUtil = FileUtil.getInstance(); 74 | fileUtil.save(Constant.DIFFERENT_URL_FILE_NAME, ("1" + differentUrl).getBytes()); 75 | fileUtil.setReadable(Constant.DIFFERENT_URL_FILE_NAME); 76 | urlTextView.setText(differentUrl); 77 | Toast.makeText(this, getString(R.string.change_succeed), Toast.LENGTH_SHORT).show(); 78 | } 79 | break; 80 | case R.id.step_two_custom_btn: 81 | if (isShowCustom) { 82 | customLayout.setVisibility(View.GONE); 83 | } else { 84 | customLayout.setVisibility(View.VISIBLE); 85 | } 86 | isShowCustom = !isShowCustom; 87 | break; 88 | case R.id.step_two_back_default: 89 | FileUtil.getInstance().save(Constant.DIFFERENT_URL_FILE_NAME, "0".getBytes()); 90 | urlTextView.setText(getString(R.string.test_url)); 91 | break; 92 | } 93 | 94 | } 95 | } 96 | -------------------------------------------------------------------------------- /app/src/main/java/com/xloger/exlink/app/adapter/AppAdapter.java: -------------------------------------------------------------------------------- 1 | package com.xloger.exlink.app.adapter; 2 | 3 | import android.content.Context; 4 | import android.view.LayoutInflater; 5 | import android.view.View; 6 | import android.view.ViewGroup; 7 | import android.widget.BaseAdapter; 8 | import android.widget.CheckBox; 9 | import android.widget.LinearLayout; 10 | import android.widget.TextView; 11 | 12 | import com.xloger.exlink.app.R; 13 | import com.xloger.exlink.app.activity.MainActivity; 14 | import com.xloger.exlink.app.entity.App; 15 | 16 | import java.util.List; 17 | 18 | /** 19 | * Created by xloger on 1月3日. 20 | * Author:xloger 21 | * Email:phoenix@xloger.com 22 | */ 23 | public class AppAdapter extends BaseAdapter implements View.OnClickListener, View.OnLongClickListener { 24 | private Context context; 25 | private ListSorry,this English translate is not update long time(this page is build by my friend).because I don't know how much englisher use it,and my English is very bad,and I am lazy...but if you need English update, please tell me use Google Play,I will do my best.
21 | 22 |You need to have Xposed framework installed on your device, activate this module and reboot your device. If the module is not activated after reboot, try another reboot.
If the module is still not functioning properly, I'm afraid there's nothing else I could do. If you wish to achieve similar functionality without the Xposed framework, I recommend this app from Coolapk: OpenLinksWithDefaultBrowser-Coolapk
As the developer is from mainland China, several apps from China are pre-loaded in this module. Twitter is currently supported however you will need to add it manually.
28 |Please refer to the “Current Issues” section. If you couldn't find an explanation, please leave the details (i.e. what webpage, what link) with a screenshot if possible in the comment section(Google Play)
30 |This can be solved by the "Add whitelist" function. Please refer to “How to Use” section for details
32 | 33 |Three steps: First click "Add Application", enter the package name and reboot your device; After reboot, relaunch this module, place the link from on-screen instruction to your application (e.g. send a message, post a status update) and open the link; If successful, a "Step Three" page would automatically appear, reboot your device and you are done.
36 |I believe Xposed users have the skills to find the package name for an application. As to why there's not an option to automatically select applications,it requires a lot of coding and I wish to focus my attention on core functionalities.
38 |Some applications are really improperly coded. Please send me the details. Please ensure you have followed all the instructions, e.g. make sure to reboot your device (Some applications such as Zhihu doesn't automatically recognize links, you need to make the edit on their webpage). I will run tests once I receive your feedback, and I will inform you if it's a compatibility issue
40 |Not necessary. But background service of your application needs to be killed. I suggest a reboot to make sure of that considering the malicious behaviour of many Chinese apps.
42 |A properly coded application should have only one internal browser. However some applications have multiple. Please long press on the application you wish to modify and select “Add Rules”.
44 |"Add Rules" is to solve the aforementioned issue. Please follow the same steps as "Add Applications"
46 |This is because some pages from inside the application are essentially webpages. For instance, comment section and lover's space in QZone will be opened by external browser with ExLink version 1.1. A simple workaround: All these links are pages inside an application (not user generated),therefore just whitelist the corresponding domain name, e.g For QQ I whitelisted h5.qzone.qq.com and for Weibo I whitelisted card.weibo.com。
How to use:Long press your application, select "Add Whitelist" and add the domain name
Hmmm, these features seem necessary but it would be a headache to code.Orz...I may get to these later on but at the moment if you run into problems please clear app data to reset. Sorry about this.
52 |Because I've been modifying the framework and it's difficult to achieve backward compatibility...I will try to solve this once I have a more stable framework.
54 |There're just too many applications with internal browsers. If I pre-load this in current version, I'll need to pre-load that in the next version. Not everyone uses all these applications. My plan is build a framework with maximum compatibility and support as many applications as possible. But only the following will be pre-loaded in the module: QQ, WeChat, Weibo, Baidu Tieba.
56 |This module is not foolproof. Please follow the instructions carefully. Do not add several applications simultaneously to the test list. Do not add rules before an application is successfully added. If you run into problems, clear app data to reset the module.
58 | 59 |This is due to the fact that some applications add user analytics to their links and are improperly coded. Please send me the details if you run into this problem and I will try to work it out.
61 |It was the plan but I thought it would be more convenient if module could be launched directly from home screen. I will consider adding an option to hide the icon.
63 | 64 |Thanks for ContactFront's translate!
65 | 66 | 67 | -------------------------------------------------------------------------------- /app/src/main/java/com/xloger/exlink/app/util/FileUtil.java: -------------------------------------------------------------------------------- 1 | package com.xloger.exlink.app.util; 2 | 3 | import android.annotation.SuppressLint; 4 | import android.content.Context; 5 | 6 | import java.io.File; 7 | import java.io.FileInputStream; 8 | import java.io.FileNotFoundException; 9 | import java.io.FileOutputStream; 10 | import java.io.IOException; 11 | import java.io.ObjectInputStream; 12 | import java.io.ObjectOutputStream; 13 | import java.io.StreamCorruptedException; 14 | import java.util.Arrays; 15 | import java.util.List; 16 | 17 | /** 18 | * Created by xloger on 1月1日. 19 | * Author:xloger 20 | * Email:phoenix@xloger.com 21 | */ 22 | @Deprecated 23 | public class FileUtil { 24 | private Context context; 25 | private static FileUtil fileUtil; 26 | 27 | private FileUtil(Context context) { 28 | this.context = context; 29 | } 30 | 31 | public static void createInstance(Context context) { 32 | if (fileUtil == null) { 33 | fileUtil = new FileUtil(context); 34 | } 35 | } 36 | 37 | public static FileUtil getInstance() { 38 | if (fileUtil == null) { 39 | throw new IllegalStateException("FileUtil create happen an Error "); 40 | } 41 | return fileUtil; 42 | } 43 | 44 | public void save(String fileName, byte[] content) { 45 | 46 | ExConfig.INSTANCE.saveFromApp(fileName, new String(content)); 47 | return; 48 | 49 | 50 | 51 | // File folder = context.getFilesDir(); 52 | // File file = new File(folder, fileName); 53 | // 54 | // FileOutputStream fout = null; 55 | // try { 56 | // fout = new FileOutputStream(file); 57 | // fout.write(content); 58 | // } catch (FileNotFoundException e) { 59 | // e.printStackTrace(); 60 | // } catch (IOException e) { 61 | // e.printStackTrace(); 62 | // } finally { 63 | // StreamUtil.close(fout); 64 | // } 65 | 66 | } 67 | 68 | public byte[] load(String fileName) { 69 | return ExConfig.INSTANCE.loadFromApp(fileName).getBytes(); 70 | 71 | 72 | // byte[] ret = null; 73 | // File folder = context.getFilesDir(); 74 | // ret = load(folder.toString(), fileName); 75 | // return ret; 76 | } 77 | 78 | public static byte[] load(String url, String fileName) { 79 | return ExConfig.INSTANCE.loadFromApp(fileName).getBytes(); 80 | 81 | // byte[] ret = null; 82 | // 83 | // File file = new File(url, fileName); 84 | // 85 | // if (file.exists() && file.canRead()) { 86 | // FileInputStream fin = null; 87 | // try { 88 | // fin = new FileInputStream(file); 89 | // ret = StreamUtil.readStream(fin); 90 | // } catch (FileNotFoundException e) { 91 | // MyLog.e(e.getMessage()); 92 | // }finally { 93 | // StreamUtil.close(fin); 94 | // } 95 | // } 96 | // 97 | // return ret; 98 | } 99 | 100 | public static byte[] loadOld(String url, String fileName) { 101 | byte[] ret = null; 102 | 103 | File file = new File(url, fileName); 104 | 105 | if (file.exists() && file.canRead()) { 106 | FileInputStream fin = null; 107 | try { 108 | fin = new FileInputStream(file); 109 | ret = StreamUtil.readStream(fin); 110 | } catch (FileNotFoundException e) { 111 | MyLog.e(e.getMessage()); 112 | }finally { 113 | StreamUtil.close(fin); 114 | } 115 | } 116 | 117 | return ret; 118 | } 119 | 120 | /** 121 | * @deprecated 新版已经由存取序列化对象改为存取json,因此不再推荐使用该方法,使用{@link JSONFile#saveJson(List)} 122 | */ 123 | @Deprecated 124 | public void saveObject(String fileName, Object object) { 125 | File folder = context.getFilesDir(); 126 | saveObject(folder.toString(), fileName, object); 127 | } 128 | 129 | /** 130 | * @deprecated 新版已经由存取序列化对象改为存取json,因此不再推荐使用该方法,使用{@link JSONFile#saveJson(List)} 131 | */ 132 | @Deprecated 133 | public static void saveObject(String url, String fileName, Object object) { 134 | File dir = new File(url); 135 | if (!dir.exists()) { 136 | dir.mkdirs(); 137 | dir.setExecutable(true, false); 138 | } 139 | File file = new File(url, fileName); 140 | file.setReadable(true, false); 141 | file.setWritable(true, false); 142 | FileOutputStream fout = null; 143 | try { 144 | fout = new FileOutputStream(file); 145 | ObjectOutputStream oos = new ObjectOutputStream(fout); 146 | oos.writeObject(object); 147 | oos.close(); 148 | } catch (FileNotFoundException e) { 149 | MyLog.log(e); 150 | e.printStackTrace(); 151 | } catch (IOException e) { 152 | MyLog.log(e); 153 | e.printStackTrace(); 154 | } finally { 155 | StreamUtil.close(fout); 156 | } 157 | } 158 | 159 | /** 160 | * @deprecated 新版已经由存取序列化对象改为存取json,因此不再推荐使用该方法,使用{@link JSONFile#getJson()} 161 | */ 162 | @Deprecated 163 | public Object loadObject(String fileName) { 164 | Object ret = null; 165 | File folder = context.getFilesDir(); 166 | ret = loadObject(folder.toString(), fileName); 167 | return ret; 168 | } 169 | 170 | /** 171 | * @deprecated 新版已经由存取序列化对象改为存取json,因此不再推荐使用该方法,使用{@link JSONFile#getJson()} 172 | */ 173 | @Deprecated 174 | public static Object loadObject(String url, String fileName) { 175 | Object ret = null; 176 | 177 | FileInputStream fin = null; 178 | try { 179 | fin = new FileInputStream(url + fileName); 180 | ObjectInputStream ois = new ObjectInputStream(fin); 181 | ret = ois.readObject(); 182 | } catch (FileNotFoundException e) { 183 | MyLog.log(e); 184 | e.printStackTrace(); 185 | } catch (StreamCorruptedException e) { 186 | MyLog.log(e); 187 | e.printStackTrace(); 188 | } catch (IOException e) { 189 | MyLog.log(e); 190 | e.printStackTrace(); 191 | } catch (ClassNotFoundException e) { 192 | MyLog.log(e); 193 | e.printStackTrace(); 194 | } finally { 195 | StreamUtil.close(fin); 196 | } 197 | 198 | 199 | return ret; 200 | } 201 | 202 | /** 203 | * @deprecated 不再需要这个功能了 204 | * @param fileName 205 | */ 206 | public void setReadable(String fileName) { 207 | File folder = context.getFilesDir(); 208 | File file = new File(folder, fileName); 209 | @SuppressLint("SetWorldReadable") boolean b = file.setReadable(true, false); 210 | MyLog.log("修改权限:" + b); 211 | } 212 | 213 | } 214 | -------------------------------------------------------------------------------- /app/src/main/java/com/xloger/exlink/app/util/StreamUtil.java: -------------------------------------------------------------------------------- 1 | package com.xloger.exlink.app.util; 2 | 3 | 4 | import android.media.MediaCodec; 5 | import android.net.Uri; 6 | import android.util.Patterns; 7 | import android.webkit.URLUtil; 8 | 9 | import com.xloger.exlink.app.Constant; 10 | import com.xloger.exlink.app.entity.Rule; 11 | 12 | import java.io.ByteArrayOutputStream; 13 | import java.io.IOException; 14 | import java.io.InputStream; 15 | import java.io.OutputStream; 16 | import java.io.Reader; 17 | import java.io.Writer; 18 | import java.net.HttpURLConnection; 19 | import java.net.URLDecoder; 20 | import java.util.List; 21 | import java.util.regex.Matcher; 22 | import java.util.regex.Pattern; 23 | 24 | /** 25 | * Created by xloger on 2015/10/26. 26 | * Author:xloger 27 | * Email:phoenix@xloger.com 28 | */ 29 | public class StreamUtil { 30 | private StreamUtil() { 31 | 32 | } 33 | 34 | public static void close(Object object) { 35 | try { 36 | if (object == null) { 37 | return; 38 | } 39 | if (object instanceof InputStream) { 40 | ((InputStream) object).close(); 41 | } else if (object instanceof OutputStream) { 42 | ((OutputStream) object).close(); 43 | } else if (object instanceof Reader) { 44 | ((Reader) object).close(); 45 | } else if (object instanceof Writer) { 46 | ((Writer) object).close(); 47 | } else if (object instanceof HttpURLConnection) { 48 | ((HttpURLConnection) object).disconnect(); 49 | } 50 | } catch (IOException e) { 51 | e.printStackTrace(); 52 | } 53 | } 54 | 55 | public static byte[] readStream(InputStream in) { 56 | byte[] bytes = null; 57 | if (in != null) { 58 | ByteArrayOutputStream bout = new ByteArrayOutputStream(); 59 | readStream(in, bout); 60 | bytes = bout.toByteArray(); 61 | close(bout); 62 | } 63 | return bytes; 64 | } 65 | 66 | public static void readStream(InputStream in, OutputStream out) { 67 | if (in != null && out != null) { 68 | int len = 0; 69 | byte[] bytes = new byte[1024]; 70 | 71 | try { 72 | while ((len = in.read(bytes)) != -1) { 73 | out.write(bytes, 0, len); 74 | } 75 | } catch (IOException e) { 76 | e.printStackTrace(); 77 | } 78 | 79 | } 80 | } 81 | 82 | public static boolean isMatch(String s1, String s2) { 83 | if (s1 == null || s2 == null) { 84 | return false; 85 | } 86 | 87 | if (s1.equals(s2)) { 88 | return true; 89 | } 90 | 91 | return s1.startsWith(s2) || s2.startsWith(s1); 92 | } 93 | 94 | public static String parseUrl(String s) { 95 | StringBuffer ret = new StringBuffer(); 96 | Pattern pattern = Pattern.compile("https?:\\/\\/(www\\.)?[-a-zA-Z0-9@:%._\\+~#=]{2,256}\\.[a-z]{2,6}\\b([-a-zA-Z0-9@:%_\\+.~#?&//=]*)");//匹配的模式 97 | // Pattern pattern =Pattern.compile("charset=(.+?)https?:\\/\\/");//匹配的模式 98 | 99 | //通配符中也要加入转移字符 (.+?)代表要查找的内容 100 | 101 | Matcher matcher = pattern.matcher(s); 102 | 103 | while (matcher.find()) 104 | 105 | { 106 | ret.append(matcher.group()); 107 | System.out.println(matcher.group()); //每次返回第一个即可 可用groupcount()方法来查看捕获的组数 个数 108 | 109 | } 110 | 111 | return ret.toString(); 112 | } 113 | 114 | public static boolean isSecondLevelDomain(String mainUrl, String testUrl) { 115 | boolean ret = false; 116 | if (testUrl.contains(mainUrl)) { 117 | ret = true; 118 | } 119 | 120 | return ret; 121 | } 122 | 123 | public static boolean isContain(String url) { 124 | if (url == null) { 125 | return false; 126 | } 127 | boolean ret = false; 128 | //获取自定义Url 129 | byte useDifferentUrl = '0'; 130 | String differentUrl = null; 131 | byte[] bytes = FileUtil.load(Constant.APP_URL, Constant.DIFFERENT_URL_FILE_NAME); 132 | if (bytes != null && bytes.length > 0) { 133 | useDifferentUrl = bytes[0]; 134 | differentUrl = new String(bytes, 1, bytes.length - 1); 135 | if (useDifferentUrl == '1') { 136 | MyLog.log("使用自定义Url:" + differentUrl); 137 | } 138 | } 139 | 140 | String testUrl; 141 | if (useDifferentUrl == '1' && differentUrl != null) { 142 | testUrl = differentUrl; 143 | } else { 144 | testUrl = "http://www.example.org/ex-link-test"; 145 | } 146 | 147 | if (url.equals(testUrl)) { 148 | ret = true; 149 | } else { 150 | ret = isContain(url, testUrl); 151 | } 152 | 153 | return ret; 154 | } 155 | 156 | public static boolean isContain(String longUrl, String shortUrl) { 157 | 158 | longUrl = URLDecoder.decode(longUrl); 159 | shortUrl = URLDecoder.decode(shortUrl); 160 | Uri shortUri = Uri.parse(shortUrl); 161 | //假如不是个链接,那直接看是否包含吧 162 | if (shortUri.getHost() == null) { 163 | return longUrl.contains(shortUrl); 164 | } 165 | 166 | if (shortUri.getPath() == null) { 167 | if (longUrl.contains(shortUri.getHost())) { 168 | return true; 169 | } 170 | } else { 171 | if (longUrl.contains(shortUri.getHost()) && longUrl.contains(shortUri.getPath())) { 172 | return true; 173 | } 174 | } 175 | 176 | return false; 177 | } 178 | 179 | public static boolean isUrl(String url) { 180 | if (Patterns.WEB_URL.matcher(url).matches() || URLUtil.isValidUrl(url)) { 181 | return true; 182 | } else { 183 | return false; 184 | } 185 | } 186 | 187 | /** 188 | * 针对特殊格式的链接进行特殊处理。注意已经默认传入 string为链接了,所以将不再返回 null 189 | * @param url 190 | * @return 191 | */ 192 | public static String clearUrl(String url) { 193 | String ret = null; 194 | 195 | if (url.startsWith("sinaweibo")) { 196 | Uri sinaUri = Uri.parse(url); 197 | String tempUrl = sinaUri.getQueryParameter("url"); 198 | if (tempUrl == null) { 199 | tempUrl = sinaUri.getQueryParameter("showurl"); 200 | } 201 | 202 | if (tempUrl != null) { 203 | ret = tempUrl; 204 | } 205 | 206 | if (ret == null || !ret.contains("http")) { 207 | //对秒拍链接特殊处理 208 | if (sinaUri.getQueryParameter("url_type").equals("39") && sinaUri.getQueryParameter("object_type").equals("video")) { 209 | String containerid = sinaUri.getQueryParameter("containerid"); 210 | ret = "http://m.weibo.cn/p/index/?containerid=" + containerid; 211 | } 212 | } 213 | } else { 214 | ret = url; 215 | } 216 | 217 | 218 | return ret; 219 | } 220 | 221 | public static boolean isContainUrl(String string) { 222 | if (string == null || string.equals("")) { 223 | return false; 224 | } 225 | return string.contains("http") || string.contains("sinaweibo://"); 226 | } 227 | 228 | public static boolean isContain(List