├── app ├── .gitignore ├── src │ ├── main │ │ ├── res │ │ │ ├── drawable │ │ │ │ ├── logo.png │ │ │ │ ├── ic_back.xml │ │ │ │ ├── style_bottom_linefff.xml │ │ │ │ ├── ic_arrow_drop_down_white.xml │ │ │ │ ├── ic_search.xml │ │ │ │ ├── ic_cancel.xml │ │ │ │ ├── ic_sort.xml │ │ │ │ ├── ic_source.xml │ │ │ │ ├── ic_xl.xml │ │ │ │ ├── ic_web_page.xml │ │ │ │ ├── ic_copy.xml │ │ │ │ └── ic_launcher_background.xml │ │ │ ├── mipmap-hdpi │ │ │ │ ├── ic_launcher.png │ │ │ │ └── ic_launcher_round.png │ │ │ ├── mipmap-mdpi │ │ │ │ ├── ic_launcher.png │ │ │ │ └── ic_launcher_round.png │ │ │ ├── mipmap-xhdpi │ │ │ │ ├── ic_launcher.png │ │ │ │ └── ic_launcher_round.png │ │ │ ├── mipmap-xxhdpi │ │ │ │ ├── ic_launcher.png │ │ │ │ └── ic_launcher_round.png │ │ │ ├── mipmap-xxxhdpi │ │ │ │ ├── ic_launcher.png │ │ │ │ └── ic_launcher_round.png │ │ │ ├── mipmap-anydpi-v26 │ │ │ │ ├── ic_launcher.xml │ │ │ │ └── ic_launcher_round.xml │ │ │ ├── menu │ │ │ │ └── magnet_option.xml │ │ │ ├── layout │ │ │ │ ├── activity_browse.xml │ │ │ │ ├── view_top_bar.xml │ │ │ │ ├── view_sear_bar.xml │ │ │ │ ├── item_search_magnet_result.xml │ │ │ │ └── activity_magnet_search.xml │ │ │ ├── values │ │ │ │ ├── strings.xml │ │ │ │ ├── styles.xml │ │ │ │ ├── dimens.xml │ │ │ │ └── colors.xml │ │ │ └── drawable-v24 │ │ │ │ └── ic_launcher_foreground.xml │ │ ├── java │ │ │ └── cn │ │ │ │ └── sddman │ │ │ │ └── bt │ │ │ │ ├── mvp │ │ │ │ ├── p │ │ │ │ │ ├── MagnetSearchPresenter.java │ │ │ │ │ └── MagnetSearchPresenterImp.java │ │ │ │ ├── v │ │ │ │ │ └── MagnetSearchView.java │ │ │ │ ├── e │ │ │ │ │ ├── MagnetSearchBean.java │ │ │ │ │ ├── MagnetInfo.java │ │ │ │ │ └── MagnetRule.java │ │ │ │ └── m │ │ │ │ │ ├── MagnetWServiceModel.java │ │ │ │ │ └── MagnetWServiceModelImp.java │ │ │ │ ├── listener │ │ │ │ └── MagnetSearchListener.java │ │ │ │ ├── App.java │ │ │ │ ├── common │ │ │ │ ├── Const.java │ │ │ │ ├── BaseActivity.java │ │ │ │ └── AppManager.java │ │ │ │ ├── util │ │ │ │ ├── StringUtil.java │ │ │ │ ├── Util.java │ │ │ │ ├── AlertUtil.java │ │ │ │ └── GsonUtil.java │ │ │ │ ├── thread │ │ │ │ └── MangetSearchTask.java │ │ │ │ ├── adapter │ │ │ │ └── MagnetSearchListAdapter.java │ │ │ │ └── activity │ │ │ │ └── MagnetSearchActivity.java │ │ ├── assets │ │ │ └── rule.json │ │ └── AndroidManifest.xml │ ├── test │ │ └── java │ │ │ └── cn │ │ │ └── sddman │ │ │ └── bt │ │ │ └── ExampleUnitTest.java │ └── androidTest │ │ └── java │ │ └── cn │ │ └── sddman │ │ └── bt │ │ └── ExampleInstrumentedTest.java ├── proguard-rules.pro └── build.gradle ├── settings.gradle ├── README.md └── .gitignore /app/.gitignore: -------------------------------------------------------------------------------- 1 | /build 2 | /app.iml -------------------------------------------------------------------------------- /settings.gradle: -------------------------------------------------------------------------------- 1 | include ':app' 2 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # cilimao-android 2 | [磁力猫客户端](https://www.52pojie.cn/thread-988140-1-1.html) 3 | -------------------------------------------------------------------------------- /app/src/main/res/drawable/logo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/qiuqiu3/cilimao-android/HEAD/app/src/main/res/drawable/logo.png -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | /local.properties 2 | /.idea 3 | .gradle 4 | /gradle.properties 5 | /gradle 6 | /gradlew 7 | /gradlew.bat 8 | /AndroidMagnetSearch.iml -------------------------------------------------------------------------------- /app/src/main/res/mipmap-hdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/qiuqiu3/cilimao-android/HEAD/app/src/main/res/mipmap-hdpi/ic_launcher.png -------------------------------------------------------------------------------- /app/src/main/res/mipmap-mdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/qiuqiu3/cilimao-android/HEAD/app/src/main/res/mipmap-mdpi/ic_launcher.png -------------------------------------------------------------------------------- /app/src/main/res/mipmap-xhdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/qiuqiu3/cilimao-android/HEAD/app/src/main/res/mipmap-xhdpi/ic_launcher.png -------------------------------------------------------------------------------- /app/src/main/res/mipmap-xxhdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/qiuqiu3/cilimao-android/HEAD/app/src/main/res/mipmap-xxhdpi/ic_launcher.png -------------------------------------------------------------------------------- /app/src/main/res/mipmap-xxxhdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/qiuqiu3/cilimao-android/HEAD/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png -------------------------------------------------------------------------------- /app/src/main/res/mipmap-hdpi/ic_launcher_round.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/qiuqiu3/cilimao-android/HEAD/app/src/main/res/mipmap-hdpi/ic_launcher_round.png -------------------------------------------------------------------------------- /app/src/main/res/mipmap-mdpi/ic_launcher_round.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/qiuqiu3/cilimao-android/HEAD/app/src/main/res/mipmap-mdpi/ic_launcher_round.png -------------------------------------------------------------------------------- /app/src/main/res/mipmap-xhdpi/ic_launcher_round.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/qiuqiu3/cilimao-android/HEAD/app/src/main/res/mipmap-xhdpi/ic_launcher_round.png -------------------------------------------------------------------------------- /app/src/main/res/mipmap-xxhdpi/ic_launcher_round.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/qiuqiu3/cilimao-android/HEAD/app/src/main/res/mipmap-xxhdpi/ic_launcher_round.png -------------------------------------------------------------------------------- /app/src/main/res/mipmap-xxxhdpi/ic_launcher_round.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/qiuqiu3/cilimao-android/HEAD/app/src/main/res/mipmap-xxxhdpi/ic_launcher_round.png -------------------------------------------------------------------------------- /app/src/main/java/cn/sddman/bt/mvp/p/MagnetSearchPresenter.java: -------------------------------------------------------------------------------- 1 | package cn.sddman.bt.mvp.p; 2 | 3 | import cn.sddman.bt.mvp.e.MagnetRule; 4 | 5 | public interface MagnetSearchPresenter { 6 | void searchMagnet(MagnetRule rule, String keyword,String sort, int page ); 7 | } 8 | -------------------------------------------------------------------------------- /app/src/main/java/cn/sddman/bt/listener/MagnetSearchListener.java: -------------------------------------------------------------------------------- 1 | package cn.sddman.bt.listener; 2 | 3 | import java.util.List; 4 | 5 | import cn.sddman.bt.mvp.e.MagnetInfo; 6 | 7 | public interface MagnetSearchListener { 8 | void success(List info); 9 | void fail(String error); 10 | } 11 | -------------------------------------------------------------------------------- /app/src/main/java/cn/sddman/bt/mvp/v/MagnetSearchView.java: -------------------------------------------------------------------------------- 1 | package cn.sddman.bt.mvp.v; 2 | 3 | import java.util.List; 4 | 5 | import cn.sddman.bt.mvp.e.MagnetInfo; 6 | 7 | public interface MagnetSearchView { 8 | void refreshData(List info); 9 | void moreOption(MagnetInfo magnet); 10 | } 11 | -------------------------------------------------------------------------------- /app/src/main/res/mipmap-anydpi-v26/ic_launcher.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | -------------------------------------------------------------------------------- /app/src/main/res/mipmap-anydpi-v26/ic_launcher_round.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | -------------------------------------------------------------------------------- /app/src/main/res/menu/magnet_option.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | -------------------------------------------------------------------------------- /app/src/main/java/cn/sddman/bt/App.java: -------------------------------------------------------------------------------- 1 | package cn.sddman.bt; 2 | 3 | import android.app.Application; 4 | 5 | import org.xutils.x; 6 | 7 | public class App extends Application { 8 | public static App instance = null; 9 | @Override 10 | public void onCreate() { 11 | super.onCreate(); 12 | x.Ext.init(this); 13 | instance = this; 14 | } 15 | public static App appInstance() { 16 | return instance; 17 | } 18 | 19 | } 20 | -------------------------------------------------------------------------------- /app/src/main/java/cn/sddman/bt/common/Const.java: -------------------------------------------------------------------------------- 1 | package cn.sddman.bt.common; 2 | 3 | 4 | 5 | public class Const { 6 | 7 | public static final int SUCCESS_ALERT=1; 8 | public static final int ERROR_ALERT=2; 9 | public static final int WARNING_ALERT=3; 10 | 11 | 12 | public final static String SEARCH_SORT_SIZE = "size"; 13 | public final static String SEARCH_SORT_HOT = "hot"; 14 | public final static String SEARCH_SORT_DATE = "date"; 15 | } 16 | -------------------------------------------------------------------------------- /app/src/test/java/cn/sddman/bt/ExampleUnitTest.java: -------------------------------------------------------------------------------- 1 | package cn.sddman.bt; 2 | 3 | import org.junit.Test; 4 | 5 | import static org.junit.Assert.*; 6 | 7 | /** 8 | * Example local unit test, which will execute on the development machine (host). 9 | * 10 | * @see Testing documentation 11 | */ 12 | public class ExampleUnitTest { 13 | @Test 14 | public void addition_isCorrect() { 15 | assertEquals(4, 2 + 2); 16 | } 17 | } -------------------------------------------------------------------------------- /app/src/main/res/drawable/ic_back.xml: -------------------------------------------------------------------------------- 1 | 2 | 7 | 8 | 12 | -------------------------------------------------------------------------------- /app/src/main/res/drawable/style_bottom_linefff.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 7 | 8 | 9 | 12 | 13 | 14 | -------------------------------------------------------------------------------- /app/src/main/res/layout/activity_browse.xml: -------------------------------------------------------------------------------- 1 | 2 | 6 | 9 | 13 | 14 | -------------------------------------------------------------------------------- /app/src/main/java/cn/sddman/bt/util/StringUtil.java: -------------------------------------------------------------------------------- 1 | package cn.sddman.bt.util; 2 | 3 | import java.util.regex.Matcher; 4 | import java.util.regex.Pattern; 5 | 6 | public class StringUtil { 7 | public static boolean isEmpty(CharSequence str) { 8 | return (str == null || str.length() == 0); 9 | } 10 | 11 | public static String getStringNoBlank(String str) { 12 | if(str!=null && !"".equals(str)) { 13 | Pattern p = Pattern.compile("\\s*|\t|\r|\n"); 14 | Matcher m = p.matcher(str); 15 | String strNoBlank = m.replaceAll(""); 16 | return strNoBlank; 17 | }else { 18 | return str; 19 | } 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /app/src/main/res/drawable/ic_arrow_drop_down_white.xml: -------------------------------------------------------------------------------- 1 | 2 | 7 | 8 | 15 | -------------------------------------------------------------------------------- /app/src/main/assets/rule.json: -------------------------------------------------------------------------------- 1 | [ 2 | { 3 | "site": "磁力猫", 4 | "waiting": "0", 5 | "group": "//div[starts-with(@class,'Search__result___')]", 6 | "magnet": ".//a[1]//@href", 7 | "name": ".//a", 8 | "size": ".//em[./text()='文件大小:']/following::text()[2]", 9 | "count": ".//em[./text()='创建时间:']/following::text()[2]", 10 | "hot": ".//em[./text()='文件数量:']/following::text()[2]", 11 | "url": "https://www.cilimao.at", 12 | "source": "https://www.cilimao.at/search?word=%1$s&sortProperties=%2$s&page=%3$s&resourceSource=0", 13 | "hotsort": "download_count", 14 | "datesort": "created_time", 15 | "sizesort": "content_size" 16 | } 17 | ] -------------------------------------------------------------------------------- /app/src/main/res/values/strings.xml: -------------------------------------------------------------------------------- 1 | 2 | 磁力搜索 3 | com.xunlei.downloadprovider 4 | 5 | 提示 6 | 返回 7 | 取消 8 | 时间 9 | 热度 10 | 大小 11 | 选择操作 12 | 13 | 日期:%1$s 14 | 大小:%1$s 15 | 数量:%1$s 16 | 搜索来源:%1$s 17 | 结果排序:%1$s 18 | 19 | -------------------------------------------------------------------------------- /app/proguard-rules.pro: -------------------------------------------------------------------------------- 1 | # Add project specific ProGuard rules here. 2 | # You can control the set of applied configuration files using the 3 | # proguardFiles setting in build.gradle. 4 | # 5 | # For more details, see 6 | # http://developer.android.com/guide/developing/tools/proguard.html 7 | 8 | # If your project uses WebView with JS, uncomment the following 9 | # and specify the fully qualified class name to the JavaScript interface 10 | # class: 11 | #-keepclassmembers class fqcn.of.javascript.interface.for.webview { 12 | # public *; 13 | #} 14 | 15 | # Uncomment this to preserve the line number information for 16 | # debugging stack traces. 17 | #-keepattributes SourceFile,LineNumberTable 18 | 19 | # If you keep the line number information, uncomment this to 20 | # hide the original source file name. 21 | #-renamesourcefileattribute SourceFile 22 | -------------------------------------------------------------------------------- /app/src/androidTest/java/cn/sddman/bt/ExampleInstrumentedTest.java: -------------------------------------------------------------------------------- 1 | package cn.sddman.bt; 2 | 3 | import android.content.Context; 4 | import android.support.test.InstrumentationRegistry; 5 | import android.support.test.runner.AndroidJUnit4; 6 | 7 | import org.junit.Test; 8 | import org.junit.runner.RunWith; 9 | 10 | import static org.junit.Assert.*; 11 | 12 | /** 13 | * Instrumented test, which will execute on an Android device. 14 | * 15 | * @see Testing documentation 16 | */ 17 | @RunWith(AndroidJUnit4.class) 18 | public class ExampleInstrumentedTest { 19 | @Test 20 | public void useAppContext() { 21 | // Context of the app under test. 22 | Context appContext = InstrumentationRegistry.getTargetContext(); 23 | 24 | assertEquals("cn.sddman.sddmanbt", appContext.getPackageName()); 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /app/src/main/java/cn/sddman/bt/mvp/e/MagnetSearchBean.java: -------------------------------------------------------------------------------- 1 | package cn.sddman.bt.mvp.e; 2 | 3 | public class MagnetSearchBean { 4 | private String keyword; 5 | private String sort; 6 | private int page; 7 | private MagnetRule rule; 8 | 9 | public String getKeyword() { 10 | return keyword; 11 | } 12 | 13 | public void setKeyword(String keyword) { 14 | this.keyword = keyword; 15 | } 16 | 17 | public String getSort() { 18 | return sort; 19 | } 20 | 21 | public void setSort(String sort) { 22 | this.sort = sort; 23 | } 24 | 25 | public int getPage() { 26 | return page; 27 | } 28 | 29 | public void setPage(int page) { 30 | this.page = page; 31 | } 32 | 33 | public MagnetRule getRule() { 34 | return rule; 35 | } 36 | 37 | public void setRule(MagnetRule rule) { 38 | this.rule = rule; 39 | } 40 | } 41 | -------------------------------------------------------------------------------- /app/src/main/res/drawable/ic_search.xml: -------------------------------------------------------------------------------- 1 | 2 | 7 | 8 | 20 | -------------------------------------------------------------------------------- /app/src/main/res/values/styles.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 10 | 16 | 22 | 23 | 24 | -------------------------------------------------------------------------------- /app/src/main/java/cn/sddman/bt/mvp/m/MagnetWServiceModel.java: -------------------------------------------------------------------------------- 1 | package cn.sddman.bt.mvp.m; 2 | 3 | import org.htmlcleaner.XPatherException; 4 | 5 | import java.io.IOException; 6 | import java.util.List; 7 | 8 | import javax.xml.parsers.ParserConfigurationException; 9 | import javax.xml.xpath.XPathExpressionException; 10 | 11 | import cn.sddman.bt.mvp.e.MagnetInfo; 12 | import cn.sddman.bt.mvp.e.MagnetRule; 13 | import cn.sddman.bt.mvp.e.MagnetSearchBean; 14 | 15 | public interface MagnetWServiceModel { 16 | int transformPage(Integer page); 17 | List parser(MagnetSearchBean bean) throws IOException, XPathExpressionException, ParserConfigurationException, XPatherException; 18 | List parser(MagnetRule rule, String keyword,String sort, int page) throws IOException, XPathExpressionException, ParserConfigurationException, XPatherException; 19 | List parser(String rootUrl, String url, String keyword,String sort, int page, String group, String magnet, String name, String size, String count,String hot) throws IOException, XPathExpressionException, ParserConfigurationException, XPatherException; 20 | } 21 | -------------------------------------------------------------------------------- /app/src/main/java/cn/sddman/bt/util/Util.java: -------------------------------------------------------------------------------- 1 | package cn.sddman.bt.util; 2 | 3 | import android.content.ClipData; 4 | import android.content.ClipboardManager; 5 | import android.content.Context; 6 | import android.content.pm.ApplicationInfo; 7 | import android.content.pm.PackageManager; 8 | 9 | import org.xutils.x; 10 | 11 | public class Util { 12 | 13 | public static boolean checkApkExist( String packageName){ 14 | if (StringUtil.isEmpty(packageName)) 15 | return false; 16 | try { 17 | ApplicationInfo info = x.app().getApplicationContext().getPackageManager() 18 | .getApplicationInfo(packageName, 19 | PackageManager.GET_UNINSTALLED_PACKAGES); 20 | return true; 21 | } catch (PackageManager.NameNotFoundException e) { 22 | return false; 23 | } 24 | } 25 | 26 | public static void putTextIntoClip(String text){ 27 | ClipboardManager clipboardManager = (ClipboardManager) x.app().getApplicationContext().getSystemService(Context.CLIPBOARD_SERVICE); 28 | ClipData clipData = ClipData.newPlainText(text, text); 29 | clipboardManager.setPrimaryClip(clipData); 30 | } 31 | } 32 | -------------------------------------------------------------------------------- /app/src/main/res/drawable/ic_cancel.xml: -------------------------------------------------------------------------------- 1 | 2 | 7 | 8 | 18 | 23 | -------------------------------------------------------------------------------- /app/src/main/res/values/dimens.xml: -------------------------------------------------------------------------------- 1 | 2 | 10dp 3 | 5dp 4 | 5 | 200dp 6 | 50dp 7 | 160dp 8 | 60dp 9 | 10dp 10 | 20dp 11 | 12sp 12 | 13sp 13 | 10dp 14 | 15 | 50dp 16 | 100dp 17 | 80dp 18 | 16sp 19 | 14sp 20 | 20dp 21 | 22 | 50dp 23 | 24 | 5dp 25 | 10sp 26 | 25dp 27 | 12sp 28 | 29 | 30 | -------------------------------------------------------------------------------- /app/src/main/AndroidManifest.xml: -------------------------------------------------------------------------------- 1 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 18 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | -------------------------------------------------------------------------------- /app/src/main/res/drawable/ic_sort.xml: -------------------------------------------------------------------------------- 1 | 2 | 7 | 8 | 14 | 25 | -------------------------------------------------------------------------------- /app/src/main/java/cn/sddman/bt/thread/MangetSearchTask.java: -------------------------------------------------------------------------------- 1 | package cn.sddman.bt.thread; 2 | 3 | import android.os.AsyncTask; 4 | 5 | import java.util.ArrayList; 6 | import java.util.List; 7 | 8 | import cn.sddman.bt.listener.MagnetSearchListener; 9 | import cn.sddman.bt.mvp.e.MagnetInfo; 10 | import cn.sddman.bt.mvp.e.MagnetRule; 11 | import cn.sddman.bt.mvp.e.MagnetSearchBean; 12 | import cn.sddman.bt.mvp.m.MagnetWServiceModel; 13 | import cn.sddman.bt.mvp.m.MagnetWServiceModelImp; 14 | 15 | public class MangetSearchTask extends AsyncTask> { 16 | private MagnetWServiceModel magnetWService; 17 | private MagnetSearchListener listener; 18 | 19 | public MangetSearchTask(MagnetSearchListener listener){ 20 | this.listener=listener; 21 | magnetWService=new MagnetWServiceModelImp(); 22 | 23 | } 24 | 25 | @Override 26 | protected List doInBackground(MagnetSearchBean... parm) { 27 | List infos = new ArrayList(); 28 | try { 29 | infos = magnetWService.parser(parm[0]); 30 | } catch (Exception e) { 31 | e.printStackTrace(); 32 | return null; 33 | } 34 | return infos; 35 | } 36 | @Override 37 | protected void onPostExecute(List info) { 38 | listener.success(info); 39 | // super.onPostExecute(info); 40 | } 41 | } -------------------------------------------------------------------------------- /app/src/main/res/drawable/ic_source.xml: -------------------------------------------------------------------------------- 1 | 2 | 7 | 8 | 16 | 27 | -------------------------------------------------------------------------------- /app/src/main/java/cn/sddman/bt/mvp/p/MagnetSearchPresenterImp.java: -------------------------------------------------------------------------------- 1 | package cn.sddman.bt.mvp.p; 2 | 3 | import android.os.AsyncTask; 4 | 5 | 6 | import java.util.List; 7 | import cn.sddman.bt.listener.MagnetSearchListener; 8 | import cn.sddman.bt.mvp.e.MagnetInfo; 9 | import cn.sddman.bt.mvp.e.MagnetRule; 10 | import cn.sddman.bt.mvp.e.MagnetSearchBean; 11 | 12 | import cn.sddman.bt.mvp.v.MagnetSearchView; 13 | import cn.sddman.bt.thread.MangetSearchTask; 14 | 15 | public class MagnetSearchPresenterImp implements MagnetSearchPresenter { 16 | private MagnetSearchView magnetSearchView; 17 | public MagnetSearchPresenterImp(MagnetSearchView magnetSearchView){ 18 | this.magnetSearchView=magnetSearchView; 19 | } 20 | 21 | @Override 22 | public void searchMagnet(MagnetRule rule, String keyword,String sort, int page) { 23 | MangetSearchTask mangetSearchTask=new MangetSearchTask(new MagnetSearchListener() { 24 | @Override 25 | public void success(List info) { 26 | magnetSearchView.refreshData(info); 27 | } 28 | 29 | @Override 30 | public void fail(String error) { 31 | 32 | } 33 | }); 34 | MagnetSearchBean bean=new MagnetSearchBean(); 35 | bean.setKeyword(keyword); 36 | bean.setPage(page); 37 | bean.setRule(rule); 38 | bean.setSort(sort); 39 | mangetSearchTask.executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR,bean); 40 | } 41 | } 42 | -------------------------------------------------------------------------------- /app/src/main/res/drawable/ic_xl.xml: -------------------------------------------------------------------------------- 1 | 2 | 7 | 8 | 26 | -------------------------------------------------------------------------------- /app/build.gradle: -------------------------------------------------------------------------------- 1 | apply plugin: 'com.android.application' 2 | 3 | android { 4 | compileSdkVersion 28 5 | defaultConfig { 6 | applicationId "cn.sddman.bt" 7 | minSdkVersion 19 8 | targetSdkVersion 28 9 | versionCode 1 10 | versionName "1.0" 11 | testInstrumentationRunner "android.support.test.runner.AndroidJUnitRunner" 12 | } 13 | buildTypes { 14 | release { 15 | minifyEnabled false 16 | proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro' 17 | } 18 | } 19 | } 20 | 21 | dependencies { 22 | implementation fileTree(dir: 'libs', include: ['*.jar']) 23 | implementation 'com.android.support:appcompat-v7:28.0.0' 24 | testImplementation 'junit:junit:4.12' 25 | androidTestImplementation 'com.android.support.test:runner:1.0.2' 26 | androidTestImplementation 'com.android.support.test.espresso:espresso-core:3.0.2' 27 | implementation 'org.xutils:xutils:3.5.0' 28 | implementation 'org.jsoup:jsoup:1.11.3' 29 | implementation 'com.google.code.gson:gson:2.8.5' 30 | implementation group: 'net.sourceforge.htmlcleaner', name: 'htmlcleaner', version: '2.8' 31 | implementation 'com.irozon.sneaker:sneaker:1.0.3' 32 | implementation 'com.lcodecorex:tkrefreshlayout:1.0.7' 33 | implementation 'com.github.chenBingX:SuperTextView:v3.0.0' 34 | implementation 'com.cocosw:bottomsheet:1.+@aar' 35 | implementation 'com.yarolegovich:lovely-dialog:1.1.0' 36 | } 37 | 38 | configurations.all { 39 | resolutionStrategy.eachDependency { DependencyResolveDetails details -> 40 | def requested = details.requested 41 | if (requested.group == 'com.android.support') { 42 | if (!requested.name.startsWith("multidex")) { 43 | details.useVersion '28.0.0' 44 | } 45 | } 46 | } 47 | } 48 | -------------------------------------------------------------------------------- /app/src/main/res/drawable/ic_web_page.xml: -------------------------------------------------------------------------------- 1 | 2 | 7 | 8 | 31 | -------------------------------------------------------------------------------- /app/src/main/res/drawable-v24/ic_launcher_foreground.xml: -------------------------------------------------------------------------------- 1 | 7 | 12 | 13 | 19 | 22 | 25 | 26 | 27 | 28 | 34 | 35 | -------------------------------------------------------------------------------- /app/src/main/java/cn/sddman/bt/mvp/e/MagnetInfo.java: -------------------------------------------------------------------------------- 1 | package cn.sddman.bt.mvp.e; 2 | 3 | import java.io.Serializable; 4 | 5 | 6 | public class MagnetInfo implements Serializable{ 7 | 8 | private String group; 9 | private String magnet; 10 | private String name; 11 | private String formatSize="请看来源页"; 12 | private long size=0; 13 | private String count="请看来源页"; 14 | private String hot="请看来源页"; 15 | private String detailUrl; 16 | 17 | private String resolution;//清晰度 18 | 19 | public String getDetailUrl() { 20 | return detailUrl; 21 | } 22 | 23 | public void setDetailUrl(String detailUrl) { 24 | this.detailUrl = detailUrl; 25 | } 26 | 27 | public String getFormatSize() { 28 | return formatSize; 29 | } 30 | 31 | public void setFormatSize(String formatSize) { 32 | this.formatSize = formatSize; 33 | } 34 | 35 | public void setSize(long size) { 36 | this.size = size; 37 | } 38 | 39 | public String getResolution() { 40 | return resolution; 41 | } 42 | 43 | public void setResolution(String resolution) { 44 | this.resolution = resolution; 45 | } 46 | 47 | public String getGroup() { 48 | return group; 49 | } 50 | 51 | public void setGroup(String group) { 52 | this.group = group; 53 | } 54 | 55 | public String getMagnet() { 56 | return magnet; 57 | } 58 | 59 | public void setMagnet(String magnet) { 60 | this.magnet = magnet; 61 | } 62 | 63 | public String getName() { 64 | return name; 65 | } 66 | 67 | public void setName(String name) { 68 | this.name = name; 69 | } 70 | 71 | public long getSize() { 72 | return size; 73 | } 74 | 75 | public String getCount() { 76 | return count; 77 | } 78 | 79 | public void setCount(String count) { 80 | this.count = count; 81 | } 82 | 83 | public String getHot() { 84 | return hot; 85 | } 86 | 87 | public void setHot(String hot) { 88 | this.hot = hot; 89 | } 90 | } 91 | -------------------------------------------------------------------------------- /app/src/main/res/layout/view_top_bar.xml: -------------------------------------------------------------------------------- 1 | 2 | 7 | 8 | 23 | 24 | 33 | 34 | 45 | -------------------------------------------------------------------------------- /app/src/main/res/layout/view_sear_bar.xml: -------------------------------------------------------------------------------- 1 | 2 | 8 | 9 | 20 | 21 | 38 | 51 | -------------------------------------------------------------------------------- /app/src/main/java/cn/sddman/bt/util/AlertUtil.java: -------------------------------------------------------------------------------- 1 | package cn.sddman.bt.util; 2 | 3 | import android.app.Activity; 4 | 5 | import com.irozon.sneaker.Sneaker; 6 | 7 | import cn.sddman.bt.R; 8 | import cn.sddman.bt.common.AppManager; 9 | import cn.sddman.bt.common.Const; 10 | 11 | public class AlertUtil { 12 | 13 | public static void successToast(String msg){ 14 | Toast(AppManager.getAppManager().currentActivity(), msg,Const.SUCCESS_ALERT); 15 | } 16 | public static void errorToast(String msg){ 17 | Toast(AppManager.getAppManager().currentActivity(), msg,Const.ERROR_ALERT); 18 | } 19 | public static void warningToast(String msg){ 20 | Toast(AppManager.getAppManager().currentActivity(), msg,Const.WARNING_ALERT); 21 | } 22 | public static void Toast(Activity activity, String msg, int msgType){ 23 | if(Const.ERROR_ALERT==msgType) { 24 | Sneaker.with(activity) 25 | .setTitle(activity.getResources().getString(R.string.title_dialog), R.color.white) 26 | .setMessage(msg, R.color.white) 27 | .setDuration(2000) 28 | .autoHide(true) 29 | .setIcon(R.drawable.ic_error, R.color.white, false) 30 | .sneak(R.color.colorAccent); 31 | }else if(Const.SUCCESS_ALERT==msgType) { 32 | Sneaker.with(activity) 33 | .setTitle(activity.getResources().getString(R.string.title_dialog), R.color.white) 34 | .setMessage(msg, R.color.white) 35 | .setDuration(2000) 36 | .autoHide(true) 37 | .setIcon(R.drawable.ic_success, R.color.white, false) 38 | .sneak(R.color.success); 39 | }else if(Const.WARNING_ALERT==msgType) { 40 | Sneaker.with(activity) 41 | .setTitle(activity.getResources().getString(R.string.title_dialog), R.color.white) 42 | .setMessage(msg, R.color.white) 43 | .setDuration(2000) 44 | .autoHide(true) 45 | .setIcon(R.drawable.ic_warning, R.color.white, false) 46 | .sneak(R.color.warning); 47 | } 48 | } 49 | } 50 | -------------------------------------------------------------------------------- /app/src/main/java/cn/sddman/bt/common/BaseActivity.java: -------------------------------------------------------------------------------- 1 | package cn.sddman.bt.common; 2 | 3 | import android.graphics.Color; 4 | import android.os.Bundle; 5 | import android.support.v7.app.AppCompatActivity; 6 | import android.view.View; 7 | import android.widget.TextView; 8 | 9 | import com.coorchice.library.SuperTextView; 10 | import org.xutils.view.annotation.Event; 11 | import org.xutils.x; 12 | 13 | import cn.sddman.bt.R; 14 | 15 | public class BaseActivity extends AppCompatActivity { 16 | private TextView topBarTitle=null; 17 | private SuperTextView closeView=null; 18 | private SuperTextView rightView=null; 19 | private boolean isClose=true; 20 | @Override 21 | protected void onCreate(Bundle savedInstanceState) { 22 | super.onCreate(savedInstanceState); 23 | AppManager.getAppManager().addActivity(this); 24 | x.view().inject(this); 25 | } 26 | protected void setTopBarTitle(int title) { 27 | if(topBarTitle==null) { 28 | topBarTitle = findViewById(R.id.topBarTitle); 29 | } 30 | topBarTitle.setText(title); 31 | } 32 | protected void setTopBarTitle(String title) { 33 | if(topBarTitle==null) { 34 | topBarTitle = findViewById(R.id.topBarTitle); 35 | } 36 | topBarTitle.setText(title); 37 | } 38 | 39 | protected void setTopCloseText(String text) { 40 | if(closeView==null) { 41 | closeView = findViewById(R.id.close_view); 42 | } 43 | closeView.setText(text); 44 | } 45 | 46 | protected SuperTextView getRightView(){ 47 | if(rightView==null) { 48 | rightView = findViewById(R.id.right_view); 49 | } 50 | return rightView; 51 | } 52 | 53 | protected void hideCloseView(){ 54 | if(closeView==null){ 55 | closeView=findViewById(R.id.close_view); 56 | } 57 | closeView.setVisibility(View.INVISIBLE); 58 | } 59 | protected void isClose(boolean b){ 60 | isClose=b; 61 | } 62 | @Event(value = R.id.close_view) 63 | private void closeView(View view) { 64 | if(isClose) 65 | finish(); 66 | } 67 | 68 | @Override 69 | protected void onDestroy() { 70 | super.onDestroy(); 71 | AppManager.getAppManager().finishActivity(this); 72 | } 73 | } 74 | -------------------------------------------------------------------------------- /app/src/main/res/layout/item_search_magnet_result.xml: -------------------------------------------------------------------------------- 1 | 2 | 8 | 19 | 23 | 32 | 33 | 42 | 43 | 52 | 53 | 59 | 60 | -------------------------------------------------------------------------------- /app/src/main/java/cn/sddman/bt/common/AppManager.java: -------------------------------------------------------------------------------- 1 | package cn.sddman.bt.common; 2 | 3 | import java.util.Stack; 4 | 5 | import android.app.Activity; 6 | import android.content.Context; 7 | 8 | /** 9 | * Activity管理类:用于管理Activity和退出程序 10 | */ 11 | public class AppManager { 12 | 13 | // Activity栈 14 | private static Stack activityStack; 15 | // 单例模式 16 | private static AppManager instance; 17 | 18 | private AppManager() { 19 | activityStack = new Stack(); 20 | } 21 | 22 | /** 23 | * 单一实例 24 | */ 25 | public static synchronized AppManager getAppManager() { 26 | if (instance == null) { 27 | instance = new AppManager(); 28 | } 29 | return instance; 30 | } 31 | 32 | /** 33 | * 添加Activity到堆栈 34 | */ 35 | public void addActivity(Activity activity) { 36 | activityStack.add(activity); 37 | } 38 | 39 | /** 40 | * 获取当前Activity(堆栈中最后一个压入的) 41 | */ 42 | public Activity currentActivity() { 43 | Activity activity = activityStack.lastElement(); 44 | return activity; 45 | } 46 | 47 | /** 48 | * 结束当前Activity(堆栈中最后一个压入的) 49 | */ 50 | public void finishActivity() { 51 | Activity activity = activityStack.lastElement(); 52 | finishActivity(activity); 53 | } 54 | 55 | /** 56 | * 结束指定的Activity 57 | */ 58 | public void finishActivity(Activity activity) { 59 | if (activity != null) { 60 | activityStack.remove(activity); 61 | activity.finish(); 62 | activity = null; 63 | } 64 | } 65 | 66 | /** 67 | * 结束指定类名的Activity 68 | */ 69 | public void finishActivity(Class cls) { 70 | for (Activity activity : activityStack) { 71 | if (activity.getClass().equals(cls)) { 72 | finishActivity(activity); 73 | break; 74 | } 75 | } 76 | } 77 | 78 | /** 79 | * 结束所有Activity 80 | */ 81 | public void finishAllActivity() { 82 | for (int i = 0; i < activityStack.size(); i++) { 83 | if (null != activityStack.get(i)) { 84 | activityStack.get(i).finish(); 85 | } 86 | } 87 | activityStack.clear(); 88 | } 89 | 90 | /** 91 | * 退出应用程序 92 | */ 93 | public void AppExit(Context context) { 94 | try { 95 | finishAllActivity(); 96 | //退出程序 97 | android.os.Process.killProcess(android.os.Process.myPid()); 98 | System.exit(1); 99 | } catch (Exception e) { 100 | } 101 | } 102 | } 103 | 104 | -------------------------------------------------------------------------------- /app/src/main/java/cn/sddman/bt/util/GsonUtil.java: -------------------------------------------------------------------------------- 1 | package cn.sddman.bt.util; 2 | 3 | import android.content.Context; 4 | 5 | import com.google.gson.Gson; 6 | import com.google.gson.JsonObject; 7 | import com.google.gson.JsonParser; 8 | import com.google.gson.reflect.TypeToken; 9 | 10 | import org.json.JSONObject; 11 | 12 | import java.io.IOException; 13 | import java.io.InputStream; 14 | import java.io.InputStreamReader; 15 | import java.io.UnsupportedEncodingException; 16 | import java.util.LinkedHashMap; 17 | import java.util.List; 18 | import java.util.Map; 19 | 20 | import cn.sddman.bt.mvp.e.MagnetRule; 21 | 22 | 23 | /** 24 | * author dengyuhan 25 | * created 2018/3/7 14:00 26 | */ 27 | public class GsonUtil { 28 | private static Gson gson = new Gson(); 29 | 30 | public static Map getMagnetRule(Context context, String path) { 31 | Map magnetRuleMap = new LinkedHashMap(); 32 | List rules = getRule(context,path); 33 | for (MagnetRule rule : rules) { 34 | magnetRuleMap.put(rule.getSite(), rule); 35 | } 36 | return magnetRuleMap; 37 | } 38 | public static Map getMagnetRule(List rules) { 39 | Map magnetRuleMap = new LinkedHashMap(); 40 | for (MagnetRule rule : rules) { 41 | magnetRuleMap.put(rule.getSite(), rule); 42 | } 43 | return magnetRuleMap; 44 | } 45 | public static List getRule(Context context,String path){ 46 | InputStream inputStream = null; 47 | try { 48 | inputStream = context.getAssets().open(path); 49 | } catch (IOException e) { 50 | e.printStackTrace(); 51 | } 52 | List rules = GsonUtil.fromJson(inputStream, new TypeToken>() { 53 | }); 54 | return rules; 55 | } 56 | public static List getRule(String jsonText){ 57 | List rules = GsonUtil.fromJson(jsonText, new TypeToken>() { 58 | }); 59 | return rules; 60 | } 61 | 62 | public static T fromJson(InputStream inputStream, TypeToken token) { 63 | try { 64 | return gson.fromJson(new InputStreamReader(inputStream,"UTF-8"), token.getType()); 65 | } catch (UnsupportedEncodingException e) { 66 | e.printStackTrace(); 67 | } 68 | return null; 69 | } 70 | public static T fromJson(String jsonText, TypeToken token) { 71 | try { 72 | return gson.fromJson(jsonText, token.getType()); 73 | } catch (Exception e) { 74 | e.printStackTrace(); 75 | } 76 | return null; 77 | } 78 | } 79 | -------------------------------------------------------------------------------- /app/src/main/res/layout/activity_magnet_search.xml: -------------------------------------------------------------------------------- 1 | 2 | 9 | 12 | 13 | 18 | 24 | 31 | 35 | 36 | 42 | 49 | 53 | 54 | 55 | 59 | 64 | 65 | 66 | -------------------------------------------------------------------------------- /app/src/main/java/cn/sddman/bt/mvp/e/MagnetRule.java: -------------------------------------------------------------------------------- 1 | package cn.sddman.bt.mvp.e; 2 | 3 | 4 | public class MagnetRule{ 5 | private String source; 6 | private String site; 7 | private String waiting; 8 | 9 | private String group; 10 | private String magnet; 11 | private String name; 12 | private String size; 13 | private String url; 14 | private String count; 15 | private String hotsort; 16 | private String datesort; 17 | private String sizesort; 18 | private String hot; 19 | 20 | public String getUrl() { 21 | return url; 22 | } 23 | 24 | public void setUrl(String url) { 25 | this.url = url; 26 | } 27 | 28 | public String getGroup() { 29 | return group; 30 | } 31 | 32 | public void setGroup(String group) { 33 | this.group = group; 34 | } 35 | 36 | public String getMagnet() { 37 | return magnet; 38 | } 39 | 40 | public void setMagnet(String magnet) { 41 | this.magnet = magnet; 42 | } 43 | 44 | public String getName() { 45 | return name; 46 | } 47 | 48 | public void setName(String name) { 49 | this.name = name; 50 | } 51 | 52 | public String getSize() { 53 | return size; 54 | } 55 | 56 | public void setSize(String size) { 57 | this.size = size; 58 | } 59 | 60 | public String getCount() { 61 | return count; 62 | } 63 | 64 | public void setCount(String count) { 65 | this.count = count; 66 | } 67 | 68 | public String getSource() { 69 | return source; 70 | } 71 | 72 | public void setSource(String source) { 73 | this.source = source; 74 | } 75 | 76 | public String getSite() { 77 | return site; 78 | } 79 | 80 | public void setSite(String site) { 81 | this.site = site; 82 | } 83 | 84 | public String getWaiting() { 85 | return waiting; 86 | } 87 | 88 | public void setWaiting(String waiting) { 89 | this.waiting = waiting; 90 | } 91 | 92 | public String getHotsort() { 93 | return hotsort; 94 | } 95 | 96 | public void setHotsort(String hotsort) { 97 | this.hotsort = hotsort; 98 | } 99 | 100 | public String getDatesort() { 101 | return datesort; 102 | } 103 | 104 | public void setDatesort(String datesort) { 105 | this.datesort = datesort; 106 | } 107 | 108 | public String getSizesort() { 109 | return sizesort; 110 | } 111 | 112 | public void setSizesort(String sizesort) { 113 | this.sizesort = sizesort; 114 | } 115 | 116 | public String getHot() { 117 | return hot; 118 | } 119 | 120 | public void setHot(String hot) { 121 | this.hot = hot; 122 | } 123 | } 124 | -------------------------------------------------------------------------------- /app/src/main/java/cn/sddman/bt/adapter/MagnetSearchListAdapter.java: -------------------------------------------------------------------------------- 1 | package cn.sddman.bt.adapter; 2 | 3 | import android.content.Context; 4 | import android.support.annotation.NonNull; 5 | import android.support.v7.widget.RecyclerView; 6 | import android.view.LayoutInflater; 7 | import android.view.View; 8 | import android.view.ViewGroup; 9 | import android.widget.TextView; 10 | import java.util.List; 11 | import cn.sddman.bt.R; 12 | import cn.sddman.bt.mvp.e.MagnetInfo; 13 | import cn.sddman.bt.mvp.v.MagnetSearchView; 14 | 15 | public class MagnetSearchListAdapter extends RecyclerView.Adapter{ 16 | private List list; 17 | private Context context; 18 | private MagnetSearchView magnetSearchView; 19 | public MagnetSearchListAdapter(Context context, MagnetSearchView magnetSearchView, List list){ 20 | this.context=context; 21 | this.magnetSearchView=magnetSearchView; 22 | this.list=list; 23 | } 24 | @NonNull 25 | @Override 26 | public RecyclerView.ViewHolder onCreateViewHolder(@NonNull ViewGroup viewGroup, int i) { 27 | LayoutInflater inflater = LayoutInflater.from(context); 28 | View view = inflater.inflate(R.layout.item_search_magnet_result, viewGroup, false); 29 | return new MagnetHolder(view); 30 | } 31 | 32 | @Override 33 | public void onBindViewHolder(@NonNull RecyclerView.ViewHolder viewHolder, int i) { 34 | final MagnetInfo task=list.get(i); 35 | MagnetHolder holder=(MagnetHolder)viewHolder; 36 | holder.bind(task); 37 | } 38 | 39 | @Override 40 | public int getItemCount() { 41 | return list.size(); 42 | } 43 | 44 | class MagnetHolder extends RecyclerView.ViewHolder{ 45 | private TextView magnetNameText,magnetSize,magnetDate,magnetHot; 46 | 47 | public MagnetHolder(View itemView) { 48 | super(itemView); 49 | magnetNameText = (TextView) itemView.findViewById(R.id.magnet_name); 50 | magnetSize = (TextView) itemView.findViewById(R.id.magnet_size); 51 | magnetDate = (TextView) itemView.findViewById(R.id.magnet_date); 52 | magnetHot = (TextView) itemView.findViewById(R.id.magnet_hot); 53 | } 54 | public void bind(final MagnetInfo magnet){ 55 | magnetNameText.setText(magnet.getName()); 56 | magnetDate.setText(String.format(itemView.getResources().getString(R.string.magnet_date),magnet.getCount())); 57 | magnetSize.setText(String.format(itemView.getResources().getString(R.string.magnet_size),magnet.getFormatSize())); 58 | magnetHot.setText(String.format(itemView.getResources().getString(R.string.magnet_hot),magnet.getHot())); 59 | itemView.setOnClickListener(new View.OnClickListener() { 60 | @Override 61 | public void onClick(View view) { 62 | magnetSearchView.moreOption(magnet); 63 | } 64 | }); 65 | } 66 | 67 | } 68 | } 69 | -------------------------------------------------------------------------------- /app/src/main/res/drawable/ic_copy.xml: -------------------------------------------------------------------------------- 1 | 2 | 7 | 8 | 40 | 56 | -------------------------------------------------------------------------------- /app/src/main/res/drawable/ic_launcher_background.xml: -------------------------------------------------------------------------------- 1 | 2 | 7 | 10 | 15 | 20 | 25 | 30 | 35 | 40 | 45 | 50 | 55 | 60 | 65 | 70 | 75 | 80 | 85 | 90 | 95 | 100 | 105 | 110 | 115 | 120 | 125 | 130 | 135 | 140 | 145 | 150 | 155 | 160 | 165 | 170 | 171 | -------------------------------------------------------------------------------- /app/src/main/java/cn/sddman/bt/mvp/m/MagnetWServiceModelImp.java: -------------------------------------------------------------------------------- 1 | package cn.sddman.bt.mvp.m; 2 | 3 | import android.util.Log; 4 | 5 | import org.htmlcleaner.CleanerProperties; 6 | import org.htmlcleaner.DomSerializer; 7 | import org.htmlcleaner.HtmlCleaner; 8 | import org.htmlcleaner.TagNode; 9 | import org.htmlcleaner.XPatherException; 10 | import org.jsoup.Connection; 11 | import org.jsoup.Jsoup; 12 | import org.w3c.dom.Document; 13 | import org.w3c.dom.Node; 14 | import org.w3c.dom.NodeList; 15 | 16 | import java.io.IOException; 17 | import java.util.ArrayList; 18 | import java.util.List; 19 | import java.util.regex.Pattern; 20 | 21 | import javax.xml.parsers.ParserConfigurationException; 22 | import javax.xml.xpath.XPath; 23 | import javax.xml.xpath.XPathConstants; 24 | import javax.xml.xpath.XPathExpressionException; 25 | import javax.xml.xpath.XPathFactory; 26 | 27 | import cn.sddman.bt.common.Const; 28 | import cn.sddman.bt.mvp.e.MagnetInfo; 29 | import cn.sddman.bt.mvp.e.MagnetRule; 30 | import cn.sddman.bt.mvp.e.MagnetSearchBean; 31 | import cn.sddman.bt.util.StringUtil; 32 | 33 | 34 | public class MagnetWServiceModelImp implements MagnetWServiceModel{ 35 | private MagnetRule rule; 36 | 37 | @Override 38 | public List parser(MagnetSearchBean bean) throws IOException, XPathExpressionException, ParserConfigurationException, XPatherException{ 39 | return parser(bean.getRule(),bean.getKeyword(),bean.getSort(),transformPage(bean.getPage())); 40 | } 41 | public List parser(MagnetRule rule, String keyword,String sort, int page) throws IOException, XPathExpressionException, ParserConfigurationException, XPatherException { 42 | this.rule=rule; 43 | return parser(rule.getUrl(), rule.getSource(), keyword,sort, page, rule.getGroup(), rule.getMagnet(), rule.getName(), rule.getSize(), rule.getCount(),rule.getHot()); 44 | } 45 | 46 | public List parser(String rootUrl, String url, String keyword,String sort, int page, String group, String magnet, String name, String size, String count,String hot) throws IOException, XPathExpressionException, ParserConfigurationException, XPatherException { 47 | String newUrl = transformUrl(url, keyword, sort, page); 48 | 49 | Connection conn = Jsoup.connect(newUrl); 50 | conn.header("Cookie", "verification=true; searchLimited=0;"); 51 | String html = conn.get().body().html(); 52 | Log.e("MagnetWServiceModelImp", newUrl); 53 | 54 | XPath xPath = XPathFactory.newInstance().newXPath(); 55 | TagNode tagNode = new HtmlCleaner().clean(html); 56 | Document dom = new DomSerializer(new CleanerProperties()).createDOM(tagNode); 57 | 58 | NodeList result = (NodeList) xPath.evaluate(group, dom, XPathConstants.NODESET); 59 | List infos = new ArrayList(); 60 | for (int i = 0; i < result.getLength(); i++) { 61 | Node node = result.item(i); 62 | if (node != null) { 63 | if (StringUtil.isEmpty(node.getTextContent().trim())) { 64 | continue; 65 | } 66 | MagnetInfo info = new MagnetInfo(); 67 | Node magnetNote = (Node) xPath.evaluate(magnet, node, XPathConstants.NODE); 68 | //磁力链 69 | String magnetValue = magnetNote.getTextContent(); 70 | info.setMagnet(transformMagnet(magnetValue)); 71 | //名称 72 | Node nameNote = ((Node) xPath.evaluate(name, node, XPathConstants.NODE)); 73 | String nameValue = nameNote.getTextContent(); 74 | info.setName(nameValue); 75 | String nameHref = nameNote.getAttributes().getNamedItem("href").getTextContent(); 76 | info.setDetailUrl(transformDetailUrl(rootUrl, nameHref)); 77 | //大小 78 | Node sizeNote = ((Node) xPath.evaluate(size, node, XPathConstants.NODE)); 79 | if (sizeNote != null) { 80 | String sizeValue = StringUtil.getStringNoBlank(sizeNote.getTextContent()); 81 | info.setFormatSize(sizeValue); 82 | info.setSize(transformSize(sizeValue)); 83 | } 84 | //时间 85 | Node dateNote=((Node) xPath.evaluate(count, node, XPathConstants.NODE)); 86 | if(dateNote!=null){ 87 | String countValue = StringUtil.getStringNoBlank(dateNote.getTextContent()); 88 | info.setCount(countValue); 89 | } 90 | //数量 91 | Node hotNote=((Node) xPath.evaluate(hot, node, XPathConstants.NODE)); 92 | if(hotNote!=null){ 93 | String hotValue = StringUtil.getStringNoBlank(hotNote.getTextContent()); 94 | info.setHot(hotValue); 95 | } 96 | //一些加工的额外信息 97 | String resolution = transformResolution(nameValue); 98 | info.setResolution(resolution); 99 | 100 | infos.add(info); 101 | } 102 | } 103 | return infos; 104 | } 105 | 106 | 107 | public int transformPage(Integer page) { 108 | return page == null || page <= 0 ? 1 : page; 109 | } 110 | 111 | 112 | /** 113 | * 数据源链接转换 114 | * 115 | * @param url 116 | * @param keyword 117 | * @param page 118 | * @return 119 | */ 120 | private String transformUrl(String url, String keyword, int page) { 121 | return url.replaceFirst("XXX", keyword) 122 | .replaceFirst("PPP", String.valueOf(page)); 123 | } 124 | 125 | private String transformUrl(String url, String keyword,String sort, int page) { 126 | if(Const.SEARCH_SORT_DATE.equals(sort)){ 127 | return String.format(url,keyword,rule.getDatesort(),page); 128 | }else if(Const.SEARCH_SORT_HOT.equals(sort)){ 129 | return String.format(url,keyword,rule.getHotsort(),page); 130 | } else { 131 | return String.format(url,keyword,rule.getSizesort(),page); 132 | } 133 | } 134 | 135 | 136 | private String transformDetailUrl(String url, String magnetValue) { 137 | return magnetValue.startsWith("http") ? magnetValue : url + magnetValue; 138 | } 139 | 140 | /** 141 | * 磁力链转换 142 | * 143 | * @param url 144 | * @return 145 | */ 146 | private String transformMagnet(String url) { 147 | String regex = "magnet:?[^\\\"]+"; 148 | boolean matches = Pattern.matches(regex, url); 149 | if (matches) { 150 | return url; 151 | } else { 152 | String newMagnet; 153 | try { 154 | StringBuffer sb = new StringBuffer(url); 155 | int htmlIndex = url.lastIndexOf(".html"); 156 | if (htmlIndex != -1) { 157 | sb.delete(htmlIndex, sb.length()); 158 | } 159 | int paramIndex = url.indexOf("&"); 160 | if (paramIndex != -1) { 161 | sb.delete(paramIndex, sb.length()); 162 | } 163 | int paramIndex2 = url.indexOf("?r"); 164 | if (paramIndex2 != -1) { 165 | sb.delete(paramIndex2, sb.length()); 166 | } 167 | if (sb.length() >= 40) { 168 | newMagnet = sb.substring(sb.length() - 40, sb.length()); 169 | } else { 170 | newMagnet = url; 171 | } 172 | } catch (Exception e) { 173 | e.printStackTrace(); 174 | newMagnet = url; 175 | } 176 | return String.format("magnet:?xt=urn:btih:%s", newMagnet); 177 | } 178 | } 179 | 180 | 181 | private String transformResolution(String name) { 182 | String regex720 = ".*(1280|720p|720P).*"; 183 | String regex1080 = ".*(1920|1080p|1080P).*"; 184 | boolean matches720 = Pattern.matches(regex720, name); 185 | if (matches720) { 186 | return "720P"; 187 | } 188 | boolean matches1080 = Pattern.matches(regex1080, name); 189 | if (matches1080) { 190 | return "1080P"; 191 | } 192 | return ""; 193 | } 194 | 195 | 196 | private long transformSize(String formatSize) { 197 | long newSize = 0; 198 | try { 199 | long baseNumber = 0; 200 | String newFormatSize = formatSize.toUpperCase().replace(" ", "").replace(" ", ""); 201 | if (newFormatSize.endsWith("GB")) { 202 | baseNumber = 1024 * 1024 * 1024; 203 | newFormatSize = newFormatSize.replace("GB", ""); 204 | } else if (newFormatSize.endsWith("MB")) { 205 | baseNumber = 1024 * 1024; 206 | newFormatSize = newFormatSize.replace("MB", ""); 207 | } else if (newFormatSize.endsWith("KB")) { 208 | baseNumber = 1024; 209 | newFormatSize = newFormatSize.replace("KB", ""); 210 | } 211 | float size = Float.parseFloat(newFormatSize); 212 | newSize = (long) (size * baseNumber); 213 | } catch (Exception e) { 214 | e.printStackTrace(); 215 | } 216 | return newSize; 217 | } 218 | 219 | } 220 | -------------------------------------------------------------------------------- /app/src/main/res/values/colors.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | #1bbc9b 4 | #1bbc9b 5 | #FF4081 6 | 7 | #252A34 8 | #313745 9 | #4D1bbc9b 10 | #444 11 | 12 | 13 | 14 | #00bcd4 15 | #009688 16 | #03a9f4 17 | #ffc107 18 | #e51c23 19 | #ffffff 20 | #4Dffffff 21 | #b0f1ffff 22 | #ded2d2 23 | #801a1a1a 24 | #00000000 25 | #FFFFF0 26 | #FFFFE0 27 | #FFFF00 28 | #FFFAFA 29 | #FFFAF0 30 | #FFFACD 31 | #FFF8DC 32 | #FFF5EE 33 | #FFF0F5 34 | #FFEFD5 35 | #FFEBCD 36 | #FFE4E1 37 | #FFE4C4 38 | #FFE4B5 39 | #FFDEAD 40 | #FFDAB9 41 | #FFD700 42 | #FFC0CB 43 | #FFB6C1 44 | #FFA500 45 | #FFA07A 46 | #FF8C00 47 | #FF7F50 48 | #FF69B4 49 | #FF6347 50 | #FF4500 51 | #FF1493 52 | #FF00FF 53 | #FF0000 54 | #FDF5E6 55 | #FAFAD2 56 | #FAF0E6 57 | #FAEBD7 58 | #FA8072 59 | #F8F8FF 60 | #F5FFFA 61 | #F5F5F5 62 | #F5F5DC 63 | #F5DEB3 64 | #F4A460 65 | #F0FFFF 66 | #F0FFF0 67 | #F0F8FF 68 | #F0E68C 69 | #F08080 70 | #EEE8AA 71 | #EE82EE 72 | #E9967A 73 | #E6E6FA 74 | #E0FFFF 75 | #DEB887 76 | #DDA0DD 77 | #DCDCDC 78 | #DC143C 79 | #DB7093 80 | #DAA520 81 | #DA70D6 82 | #D8BFD8 83 | #D3D3D3 84 | #D2B48C 85 | #D2691E 86 | #CD853F 87 | #CD5C5C 88 | #C71585 89 | #C0C0C0 90 | #BDB76B 91 | #BC8F8F 92 | #BA55D3 93 | #B8860B 94 | #B22222 95 | #B0E0E6 96 | #B0C4DE 97 | #AFEEEE 98 | #ADFF2F 99 | #ADD8E6 100 | #A9A9A9 101 | #A52A2A 102 | #A0522D 103 | #9932CC 104 | #98FB98 105 | #9400D3 106 | #9370DB 107 | #90EE90 108 | #8FBC8F 109 | #8B4513 110 | #8B008B 111 | #8B0000 112 | #8A2BE2 113 | #87CEFA 114 | #87CEEB 115 | #808080 116 | #808000 117 | #800080 118 | #800000 119 | #7FFFD4 120 | #7FFF00 121 | #7CFC00 122 | #7B68EE 123 | #778899 124 | #708090 125 | #6B8E23 126 | #6A5ACD 127 | #696969 128 | #66CDAA 129 | #6495ED 130 | #5F9EA0 131 | #556B2F 132 | #4B0082 133 | #48D1CC 134 | #483D8B 135 | #4682B4 136 | #4169E1 137 | #40E0D0 138 | #3CB371 139 | #32CD32 140 | #2F4F4F 141 | #2E8B57 142 | #228B22 143 | #20B2AA 144 | #1E90FF 145 | #191970 146 | #00FFFF 147 | #00FFFF 148 | #00FF7F 149 | #00FF00 150 | #00FA9A 151 | #00CED1 152 | #00BFFF 153 | #008B8B 154 | #008080 155 | #008000 156 | #006400 157 | #0000FF 158 | #0000CD 159 | #00008B 160 | #000080 161 | #000000 162 | #99cc33 163 | #cccccc 164 | #a0f3ecec 165 | #8f8f8f 166 | #2b8fff 167 | 168 | -------------------------------------------------------------------------------- /app/src/main/java/cn/sddman/bt/activity/MagnetSearchActivity.java: -------------------------------------------------------------------------------- 1 | package cn.sddman.bt.activity; 2 | 3 | import android.content.Context; 4 | import android.content.DialogInterface; 5 | import android.content.Intent; 6 | import android.net.Uri; 7 | import android.os.Bundle; 8 | import android.support.v7.widget.LinearLayoutManager; 9 | import android.support.v7.widget.RecyclerView; 10 | import android.view.KeyEvent; 11 | import android.view.View; 12 | import android.view.inputmethod.EditorInfo; 13 | import android.view.inputmethod.InputMethodManager; 14 | import android.widget.EditText; 15 | import android.widget.TextView; 16 | 17 | import com.cocosw.bottomsheet.BottomSheet; 18 | import com.coorchice.library.SuperTextView; 19 | import com.lcodecore.tkrefreshlayout.RefreshListenerAdapter; 20 | import com.lcodecore.tkrefreshlayout.TwinklingRefreshLayout; 21 | import com.lcodecore.tkrefreshlayout.header.progresslayout.ProgressLayout; 22 | import com.yarolegovich.lovelydialog.LovelyChoiceDialog; 23 | 24 | import org.xutils.view.annotation.ContentView; 25 | import org.xutils.view.annotation.Event; 26 | import org.xutils.view.annotation.ViewInject; 27 | 28 | import java.util.ArrayList; 29 | import java.util.Arrays; 30 | import java.util.List; 31 | 32 | import cn.sddman.bt.R; 33 | import cn.sddman.bt.adapter.MagnetSearchListAdapter; 34 | import cn.sddman.bt.common.BaseActivity; 35 | import cn.sddman.bt.common.Const; 36 | import cn.sddman.bt.mvp.e.MagnetInfo; 37 | import cn.sddman.bt.mvp.e.MagnetRule; 38 | import cn.sddman.bt.mvp.p.MagnetSearchPresenter; 39 | import cn.sddman.bt.mvp.p.MagnetSearchPresenterImp; 40 | 41 | import cn.sddman.bt.mvp.v.MagnetSearchView; 42 | 43 | import cn.sddman.bt.util.AlertUtil; 44 | 45 | import cn.sddman.bt.util.GsonUtil; 46 | import cn.sddman.bt.util.StringUtil; 47 | import cn.sddman.bt.util.Util; 48 | 49 | @ContentView(R.layout.activity_magnet_search) 50 | public class MagnetSearchActivity extends BaseActivity implements MagnetSearchView { 51 | @ViewInject(R.id.input_search) private EditText searchText; 52 | @ViewInject(R.id.recyclerview) private RecyclerView recyclerView; 53 | @ViewInject(R.id.search_source) private TextView searchSourceText; 54 | @ViewInject(R.id.search_sort) private TextView searchSortText; 55 | @ViewInject(R.id.refresh)private TwinklingRefreshLayout refreshLayout; 56 | 57 | private MagnetSearchPresenter magnetSearchPresenter; 58 | private MagnetSearchListAdapter searchListAdapter; 59 | private LovelyChoiceDialog sourceDialog=null,sortDialog=null; 60 | private List list=new ArrayList<>(); 61 | private List rules; 62 | private MagnetRule rule=new MagnetRule(); 63 | private List btSources=new ArrayList<>(); 64 | private String searchKeyWord; 65 | private int searchPage=1; 66 | private String searchSort= Const.SEARCH_SORT_DATE; 67 | @Override 68 | protected void onCreate(Bundle savedInstanceState) { 69 | super.onCreate(savedInstanceState); 70 | super.hideCloseView(); 71 | magnetSearchPresenter=new MagnetSearchPresenterImp(this); 72 | initData(); 73 | initView(); 74 | } 75 | private void initData(){ 76 | rules= GsonUtil.getRule(this,"rule.json"); 77 | if(rules==null){ 78 | AlertUtil.errorToast("获取种子来源网站失败,请重新打开本页面或者重启APP"); 79 | return; 80 | } 81 | for(MagnetRule rule:rules){ 82 | btSources.add(rule.getSite()); 83 | } 84 | rule=GsonUtil.getMagnetRule(rules).get(rules.get(0).getSite()); 85 | } 86 | private void initView(){ 87 | searchSourceText.setText(String.format(getString(R.string.search_source),rule.getSite())); 88 | searchSortText.setText(String.format(getString(R.string.search_sort), getString(R.string.date))); 89 | //recyclerView; 90 | recyclerView.setLayoutManager(new LinearLayoutManager(this, 91 | LinearLayoutManager.VERTICAL, false)); 92 | searchListAdapter=new MagnetSearchListAdapter(this,this,list); 93 | recyclerView.setAdapter(searchListAdapter); 94 | //refreshLayout 95 | ProgressLayout header = new ProgressLayout(this); 96 | header.setProgressBackgroundColorSchemeColor(getResources().getColor(R.color.colorMain)); 97 | header.setColorSchemeResources(R.color.white); 98 | refreshLayout.setHeaderView(header); 99 | refreshLayout.setFloatRefresh(true); 100 | refreshLayout.setOverScrollRefreshShow(false); 101 | refreshLayout.setOnRefreshListener(new RefreshListenerAdapter(){ 102 | @Override 103 | public void onRefresh(final TwinklingRefreshLayout refreshLayout) { 104 | searchPage=1; 105 | list.clear(); 106 | searchListAdapter.notifyDataSetChanged(); 107 | search(); 108 | } 109 | 110 | @Override 111 | public void onLoadMore(final TwinklingRefreshLayout refreshLayout) { 112 | searchPage+=1; 113 | search(); 114 | } 115 | }); 116 | // 117 | searchText.setOnEditorActionListener(new TextView.OnEditorActionListener() { 118 | @Override 119 | public boolean onEditorAction(TextView v, int actionId, KeyEvent event) { 120 | if (actionId == EditorInfo.IME_ACTION_SEND 121 | || actionId == EditorInfo.IME_ACTION_DONE 122 | || (event != null && KeyEvent.KEYCODE_ENTER == event.getKeyCode() && KeyEvent.ACTION_DOWN == event.getAction())) { 123 | refreshLayout.startRefresh(); 124 | } 125 | return false; 126 | } 127 | }); 128 | } 129 | 130 | @Override 131 | public void refreshData(List info) { 132 | refreshLayout.finishRefreshing(); 133 | refreshLayout.finishLoadmore(); 134 | if(null==info){ 135 | AlertUtil.errorToast("网络超时,请重试"); 136 | }else if(info.size()==0){ 137 | AlertUtil.errorToast("没有更多了"); 138 | }else { 139 | list.addAll(info); 140 | searchListAdapter.notifyDataSetChanged(); 141 | } 142 | } 143 | 144 | @Override 145 | public void moreOption(final MagnetInfo magnet) { 146 | Intent intent; 147 | new BottomSheet.Builder(this) 148 | .title(R.string.slest_option) 149 | .sheet(R.menu.magnet_option) 150 | .listener(new DialogInterface.OnClickListener() { 151 | @Override 152 | public void onClick(DialogInterface dialog, int which) { 153 | switch (which) { 154 | case R.id.copy: 155 | Util.putTextIntoClip(magnet.getMagnet()); 156 | AlertUtil.successToast("复制成功"); 157 | break; 158 | case R.id.xl: 159 | openXL(magnet); 160 | break; 161 | 162 | } 163 | } 164 | }).show(); 165 | } 166 | 167 | private void search(){ 168 | searchKeyWord=searchText.getText().toString().trim(); 169 | if(!StringUtil.isEmpty(searchKeyWord)) { 170 | magnetSearchPresenter.searchMagnet(rule, searchKeyWord,searchSort, searchPage); 171 | hintKeyBoard(); 172 | }else{ 173 | refreshLayout.finishRefreshing(); 174 | refreshLayout.finishLoadmore(); 175 | } 176 | } 177 | private void openXL(MagnetInfo magnet){ 178 | if(Util.checkApkExist(getString(R.string.xl_package_name))){ 179 | Intent intent = new Intent(Intent.ACTION_VIEW, Uri.parse(magnet.getMagnet())); 180 | intent.addCategory("android.intent.category.DEFAULT"); 181 | startActivity(intent); 182 | }else{ 183 | AlertUtil.errorToast("未安装迅雷"); 184 | } 185 | } 186 | private void hintKeyBoard() { 187 | InputMethodManager imm = (InputMethodManager) getSystemService(Context.INPUT_METHOD_SERVICE); 188 | if (imm.isActive() && getCurrentFocus() != null) { 189 | if (getCurrentFocus().getWindowToken() != null) { 190 | imm.hideSoftInputFromWindow(getCurrentFocus().getWindowToken(), InputMethodManager.HIDE_NOT_ALWAYS); 191 | } 192 | } 193 | } 194 | @Event(value = R.id.btn_search) 195 | private void magnetSearchClick(View view) { 196 | refreshLayout.startRefresh(); 197 | } 198 | @Event(value = R.id.bt_source) 199 | private void btSourceClick(View view) { 200 | if(sourceDialog==null) { 201 | sourceDialog=new LovelyChoiceDialog(this) 202 | .setTopColorRes(R.color.colorMain) 203 | .setIcon(R.drawable.ic_source) 204 | .setItems(btSources, new LovelyChoiceDialog.OnItemSelectedListener() { 205 | @Override 206 | public void onItemSelected(int position, String item) { 207 | rule = GsonUtil.getMagnetRule(rules).get(item); 208 | searchSourceText.setText(String.format(getString(R.string.search_source), item)); 209 | refreshLayout.startRefresh(); 210 | } 211 | }); 212 | } 213 | sourceDialog.show(); 214 | } 215 | 216 | @Event(value = R.id.bt_sort) 217 | private void btSortClick(View view) { 218 | if(sortDialog==null) { 219 | sortDialog= new LovelyChoiceDialog(this) 220 | .setTopColorRes(R.color.colorMain) 221 | .setIcon(R.drawable.ic_sort) 222 | .setItems(Arrays.asList(getString(R.string.date),getString(R.string.hot),getString(R.string.size)), new LovelyChoiceDialog.OnItemSelectedListener() { 223 | @Override 224 | public void onItemSelected(int position, String item) { 225 | if(item.equals(getString(R.string.date))){ 226 | searchSort=Const.SEARCH_SORT_DATE; 227 | }else if(item.equals(getString(R.string.hot))){ 228 | searchSort=Const.SEARCH_SORT_HOT; 229 | } else { 230 | searchSort=Const.SEARCH_SORT_SIZE; 231 | } 232 | searchSortText.setText(String.format(getString(R.string.search_sort), item)); 233 | refreshLayout.startRefresh(); 234 | } 235 | }); 236 | } 237 | sortDialog.show(); 238 | } 239 | 240 | } 241 | --------------------------------------------------------------------------------