├── .gitignore ├── README.md ├── app ├── .gitignore ├── build.gradle ├── proguard-rules.pro └── src │ └── main │ ├── AndroidManifest.xml │ ├── java │ └── com │ │ └── xigua │ │ └── test │ │ └── MainActivity.java │ └── res │ ├── drawable │ └── ic_launcher_web.png │ ├── layout │ └── activity_main.xml │ └── values │ └── strings.xml ├── build.gradle ├── gradle.properties ├── gradlew ├── gradlew.bat ├── settings.gradle └── xigua ├── .gitignore ├── build.gradle ├── proguard-rules.pro └── src └── main ├── AndroidManifest.xml ├── java └── com │ └── xigua │ └── p2p │ ├── InfoThread.java │ ├── NetWorkChangeReceiver.java │ ├── P2PClass.java │ ├── P2PManager.java │ ├── P2PMessageWhat.java │ ├── P2PService.java │ ├── StorageUtils.java │ ├── TaskVideoInfo.java │ ├── TaskVideoList.java │ └── XiguaProvider.java └── jniLibs └── armeabi └── libp2p.so /.gitignore: -------------------------------------------------------------------------------- 1 | *.iml 2 | .gradle 3 | /local.properties 4 | /.idea/workspace.xml 5 | /.idea/libraries 6 | .DS_Store 7 | /build 8 | /gradle 9 | /captures 10 | .externalNativeBuild 11 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # XiguaP2p 2 | 【次元番】使用的西瓜视频P2P库,用来缓存 xg:// xgadd:// xgplay:// 开头的ftp视频文件连接。并提供本地播放链接代理,实现边下边播的功能 3 | 4 | 基本使用 5 | ======== 6 | XiguaProvider已经完成过库的初始化,所以无需重复调用P2PManager.getInstance().init(getContext());
7 | P2PManager.getInstance().play("");//开始下载/播放西瓜ftp视频文件
8 | P2PManager.getInstance().stop("");//停止
9 | P2PManager.getInstance().remove("");删除
10 | 11 | 注意 12 | ====== 13 | 因为[P2PService](https://github.com/fanchen001/XiguaP2p/blob/master/xigua/src/main/java/com/xigua/p2p/P2PService.java)运行于单独的进程, 14 | 所以所有对[P2PManager](https://github.com/fanchen001/XiguaP2p/blob/master/xigua/src/main/java/com/xigua/p2p/P2PManager.java)的操作结果, 15 | 均以广播的形式反馈给调用者。具体广播参数,请看[P2PMessageWhat](https://github.com/fanchen001/XiguaP2p/blob/master/xigua/src/main/java/com/xigua/p2p/P2PMessageWhat.java), 16 | 其他使用方法,请查看[次元番](https://github.com/fanchen001/Bangumi) 17 | 18 | 添加依赖 19 | ======== 20 | 21 | 22 | Add it in your root build.gradle at the end of repositories: 23 | 24 | allprojects { 25 | repositories { 26 | ... 27 | maven { url 'https://www.jitpack.io' } 28 | } 29 | } 30 | Step 2. Add the dependency 31 | 32 | dependencies { 33 | implementation 'com.github.fanchen001:XiguaP2p:1.10.25' 34 | } 35 | -------------------------------------------------------------------------------- /app/.gitignore: -------------------------------------------------------------------------------- 1 | /build 2 | /test 3 | -------------------------------------------------------------------------------- /app/build.gradle: -------------------------------------------------------------------------------- 1 | apply plugin: 'com.android.application' 2 | 3 | android { 4 | compileSdkVersion 26 5 | defaultConfig { 6 | applicationId "com.xigua.test" 7 | minSdkVersion 19 8 | targetSdkVersion 26 9 | versionCode 20181022 10 | versionName "0.10.22" 11 | } 12 | buildTypes { 13 | release { 14 | minifyEnabled false 15 | proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro' 16 | } 17 | } 18 | } 19 | 20 | dependencies { 21 | compile project(path: ':xigua') 22 | } 23 | -------------------------------------------------------------------------------- /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/main/AndroidManifest.xml: -------------------------------------------------------------------------------- 1 | 2 | 4 | 5 | 6 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | -------------------------------------------------------------------------------- /app/src/main/java/com/xigua/test/MainActivity.java: -------------------------------------------------------------------------------- 1 | package com.xigua.test; 2 | 3 | import android.app.Activity; 4 | import android.content.BroadcastReceiver; 5 | import android.content.Context; 6 | import android.content.Intent; 7 | import android.content.IntentFilter; 8 | import android.os.Bundle; 9 | import android.os.SystemClock; 10 | import android.widget.TextView; 11 | 12 | import com.xigua.p2p.P2PManager; 13 | import com.xigua.p2p.P2PMessageWhat; 14 | 15 | public class MainActivity extends Activity { 16 | 17 | @Override 18 | protected void onCreate(Bundle savedInstanceState) { 19 | super.onCreate(savedInstanceState); 20 | setContentView(R.layout.activity_main); 21 | //SDK 的初始化需要时间 22 | new Thread(){ 23 | 24 | @Override 25 | public void run() { 26 | SystemClock.sleep(3000); 27 | String url = "xg://a.gbl.114s.com:20320/9847/魔法禁书目录第三季03.mp4"; 28 | P2PManager.getInstance().play(url.replace("xg://","ftp://")); 29 | } 30 | }.start(); 31 | registerReceiver(receiver,new IntentFilter(P2PMessageWhat.P2P_CALLBACK)); 32 | } 33 | 34 | @Override 35 | protected void onDestroy() { 36 | super.onDestroy(); 37 | unregisterReceiver(receiver); 38 | } 39 | 40 | private BroadcastReceiver receiver = new BroadcastReceiver() { 41 | 42 | @Override 43 | public void onReceive(Context context, Intent intent) { 44 | int intExtra = intent.getIntExtra(P2PMessageWhat.WHAT, 0); 45 | if(P2PMessageWhat.MESSAGE_TASK_LIST == intExtra){ 46 | ((TextView)findViewById(R.id.text1)).setText("MESSAGE_TASK_LIST : " + intent.getParcelableArrayListExtra(P2PMessageWhat.DATA).toString()); 47 | }else if(P2PMessageWhat.MESSAGE_PLAY_URL == intExtra){ 48 | ((TextView)findViewById(R.id.text1)).setText("MESSAGE_PLAY_URL : " + intent.getStringExtra(P2PMessageWhat.PLAY_URL)); 49 | }else if(P2PMessageWhat.MESSAGE_SPEED == intExtra){ 50 | ((TextView)findViewById(R.id.text1)).setText( "MESSAGE_SPEED : " + intent.getParcelableExtra(P2PMessageWhat.DATA).toString()); 51 | } 52 | } 53 | 54 | }; 55 | } 56 | -------------------------------------------------------------------------------- /app/src/main/res/drawable/ic_launcher_web.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fanchen001/XiguaP2p/71cc4aacf4a9abc2f66ab089ec8ad0a27ceccf84/app/src/main/res/drawable/ic_launcher_web.png -------------------------------------------------------------------------------- /app/src/main/res/layout/activity_main.xml: -------------------------------------------------------------------------------- 1 | 2 | 5 | 6 | 10 | 11 | 12 | -------------------------------------------------------------------------------- /app/src/main/res/values/strings.xml: -------------------------------------------------------------------------------- 1 | 2 | -------------------------------------------------------------------------------- /build.gradle: -------------------------------------------------------------------------------- 1 | // Top-level build file where you can add configuration options common to all sub-projects/modules. 2 | 3 | buildscript { 4 | repositories { 5 | google() 6 | jcenter() 7 | maven { url 'https://www.jitpack.io' } 8 | } 9 | dependencies { 10 | classpath 'com.android.tools.build:gradle:3.0.0' 11 | classpath 'com.github.dcendents:android-maven-gradle-plugin:1.5' 12 | } 13 | } 14 | 15 | allprojects { 16 | repositories { 17 | google() 18 | jcenter() 19 | maven { url 'https://www.jitpack.io' } 20 | } 21 | } 22 | 23 | task clean(type: Delete) { 24 | delete rootProject.buildDir 25 | } 26 | -------------------------------------------------------------------------------- /gradle.properties: -------------------------------------------------------------------------------- 1 | # Project-wide Gradle settings. 2 | 3 | # IDE (e.g. Android Studio) users: 4 | # Gradle settings configured through the IDE *will override* 5 | # any settings specified in this file. 6 | 7 | # For more details on how to configure your build environment visit 8 | # http://www.gradle.org/docs/current/userguide/build_environment.html 9 | 10 | # Specifies the JVM arguments used for the daemon process. 11 | # The setting is particularly useful for tweaking memory settings. 12 | org.gradle.jvmargs=-Xmx1536m 13 | 14 | # When configured, Gradle will run in incubating parallel mode. 15 | # This option should only be used with decoupled projects. More details, visit 16 | # http://www.gradle.org/docs/current/userguide/multi_project_builds.html#sec:decoupled_projects 17 | # org.gradle.parallel=true 18 | -------------------------------------------------------------------------------- /gradlew: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | 3 | ############################################################################## 4 | ## 5 | ## Gradle start up script for UN*X 6 | ## 7 | ############################################################################## 8 | 9 | # Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. 10 | DEFAULT_JVM_OPTS="" 11 | 12 | APP_NAME="Gradle" 13 | APP_BASE_NAME=`basename "$0"` 14 | 15 | # Use the maximum available, or set MAX_FD != -1 to use that value. 16 | MAX_FD="maximum" 17 | 18 | warn ( ) { 19 | echo "$*" 20 | } 21 | 22 | die ( ) { 23 | echo 24 | echo "$*" 25 | echo 26 | exit 1 27 | } 28 | 29 | # OS specific support (must be 'true' or 'false'). 30 | cygwin=false 31 | msys=false 32 | darwin=false 33 | case "`uname`" in 34 | CYGWIN* ) 35 | cygwin=true 36 | ;; 37 | Darwin* ) 38 | darwin=true 39 | ;; 40 | MINGW* ) 41 | msys=true 42 | ;; 43 | esac 44 | 45 | # Attempt to set APP_HOME 46 | # Resolve links: $0 may be a link 47 | PRG="$0" 48 | # Need this for relative symlinks. 49 | while [ -h "$PRG" ] ; do 50 | ls=`ls -ld "$PRG"` 51 | link=`expr "$ls" : '.*-> \(.*\)$'` 52 | if expr "$link" : '/.*' > /dev/null; then 53 | PRG="$link" 54 | else 55 | PRG=`dirname "$PRG"`"/$link" 56 | fi 57 | done 58 | SAVED="`pwd`" 59 | cd "`dirname \"$PRG\"`/" >/dev/null 60 | APP_HOME="`pwd -P`" 61 | cd "$SAVED" >/dev/null 62 | 63 | CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar 64 | 65 | # Determine the Java command to use to start the JVM. 66 | if [ -n "$JAVA_HOME" ] ; then 67 | if [ -x "$JAVA_HOME/jre/sh/java" ] ; then 68 | # IBM's JDK on AIX uses strange locations for the executables 69 | JAVACMD="$JAVA_HOME/jre/sh/java" 70 | else 71 | JAVACMD="$JAVA_HOME/bin/java" 72 | fi 73 | if [ ! -x "$JAVACMD" ] ; then 74 | die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME 75 | 76 | Please set the JAVA_HOME variable in your environment to match the 77 | location of your Java installation." 78 | fi 79 | else 80 | JAVACMD="java" 81 | which java >/dev/null 2>&1 || die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. 82 | 83 | Please set the JAVA_HOME variable in your environment to match the 84 | location of your Java installation." 85 | fi 86 | 87 | # Increase the maximum file descriptors if we can. 88 | if [ "$cygwin" = "false" -a "$darwin" = "false" ] ; then 89 | MAX_FD_LIMIT=`ulimit -H -n` 90 | if [ $? -eq 0 ] ; then 91 | if [ "$MAX_FD" = "maximum" -o "$MAX_FD" = "max" ] ; then 92 | MAX_FD="$MAX_FD_LIMIT" 93 | fi 94 | ulimit -n $MAX_FD 95 | if [ $? -ne 0 ] ; then 96 | warn "Could not set maximum file descriptor limit: $MAX_FD" 97 | fi 98 | else 99 | warn "Could not query maximum file descriptor limit: $MAX_FD_LIMIT" 100 | fi 101 | fi 102 | 103 | # For Darwin, add options to specify how the application appears in the dock 104 | if $darwin; then 105 | GRADLE_OPTS="$GRADLE_OPTS \"-Xdock:name=$APP_NAME\" \"-Xdock:icon=$APP_HOME/media/gradle.icns\"" 106 | fi 107 | 108 | # For Cygwin, switch paths to Windows format before running java 109 | if $cygwin ; then 110 | APP_HOME=`cygpath --path --mixed "$APP_HOME"` 111 | CLASSPATH=`cygpath --path --mixed "$CLASSPATH"` 112 | JAVACMD=`cygpath --unix "$JAVACMD"` 113 | 114 | # We build the pattern for arguments to be converted via cygpath 115 | ROOTDIRSRAW=`find -L / -maxdepth 1 -mindepth 1 -type d 2>/dev/null` 116 | SEP="" 117 | for dir in $ROOTDIRSRAW ; do 118 | ROOTDIRS="$ROOTDIRS$SEP$dir" 119 | SEP="|" 120 | done 121 | OURCYGPATTERN="(^($ROOTDIRS))" 122 | # Add a user-defined pattern to the cygpath arguments 123 | if [ "$GRADLE_CYGPATTERN" != "" ] ; then 124 | OURCYGPATTERN="$OURCYGPATTERN|($GRADLE_CYGPATTERN)" 125 | fi 126 | # Now convert the arguments - kludge to limit ourselves to /bin/sh 127 | i=0 128 | for arg in "$@" ; do 129 | CHECK=`echo "$arg"|egrep -c "$OURCYGPATTERN" -` 130 | CHECK2=`echo "$arg"|egrep -c "^-"` ### Determine if an option 131 | 132 | if [ $CHECK -ne 0 ] && [ $CHECK2 -eq 0 ] ; then ### Added a condition 133 | eval `echo args$i`=`cygpath --path --ignore --mixed "$arg"` 134 | else 135 | eval `echo args$i`="\"$arg\"" 136 | fi 137 | i=$((i+1)) 138 | done 139 | case $i in 140 | (0) set -- ;; 141 | (1) set -- "$args0" ;; 142 | (2) set -- "$args0" "$args1" ;; 143 | (3) set -- "$args0" "$args1" "$args2" ;; 144 | (4) set -- "$args0" "$args1" "$args2" "$args3" ;; 145 | (5) set -- "$args0" "$args1" "$args2" "$args3" "$args4" ;; 146 | (6) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" ;; 147 | (7) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" ;; 148 | (8) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" ;; 149 | (9) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" "$args8" ;; 150 | esac 151 | fi 152 | 153 | # Split up the JVM_OPTS And GRADLE_OPTS values into an array, following the shell quoting and substitution rules 154 | function splitJvmOpts() { 155 | JVM_OPTS=("$@") 156 | } 157 | eval splitJvmOpts $DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS 158 | JVM_OPTS[${#JVM_OPTS[*]}]="-Dorg.gradle.appname=$APP_BASE_NAME" 159 | 160 | exec "$JAVACMD" "${JVM_OPTS[@]}" -classpath "$CLASSPATH" org.gradle.wrapper.GradleWrapperMain "$@" 161 | -------------------------------------------------------------------------------- /gradlew.bat: -------------------------------------------------------------------------------- 1 | @if "%DEBUG%" == "" @echo off 2 | @rem ########################################################################## 3 | @rem 4 | @rem Gradle startup script for Windows 5 | @rem 6 | @rem ########################################################################## 7 | 8 | @rem Set local scope for the variables with windows NT shell 9 | if "%OS%"=="Windows_NT" setlocal 10 | 11 | @rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. 12 | set DEFAULT_JVM_OPTS= 13 | 14 | set DIRNAME=%~dp0 15 | if "%DIRNAME%" == "" set DIRNAME=. 16 | set APP_BASE_NAME=%~n0 17 | set APP_HOME=%DIRNAME% 18 | 19 | @rem Find java.exe 20 | if defined JAVA_HOME goto findJavaFromJavaHome 21 | 22 | set JAVA_EXE=java.exe 23 | %JAVA_EXE% -version >NUL 2>&1 24 | if "%ERRORLEVEL%" == "0" goto init 25 | 26 | echo. 27 | echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. 28 | echo. 29 | echo Please set the JAVA_HOME variable in your environment to match the 30 | echo location of your Java installation. 31 | 32 | goto fail 33 | 34 | :findJavaFromJavaHome 35 | set JAVA_HOME=%JAVA_HOME:"=% 36 | set JAVA_EXE=%JAVA_HOME%/bin/java.exe 37 | 38 | if exist "%JAVA_EXE%" goto init 39 | 40 | echo. 41 | echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME% 42 | echo. 43 | echo Please set the JAVA_HOME variable in your environment to match the 44 | echo location of your Java installation. 45 | 46 | goto fail 47 | 48 | :init 49 | @rem Get command-line arguments, handling Windowz variants 50 | 51 | if not "%OS%" == "Windows_NT" goto win9xME_args 52 | if "%@eval[2+2]" == "4" goto 4NT_args 53 | 54 | :win9xME_args 55 | @rem Slurp the command line arguments. 56 | set CMD_LINE_ARGS= 57 | set _SKIP=2 58 | 59 | :win9xME_args_slurp 60 | if "x%~1" == "x" goto execute 61 | 62 | set CMD_LINE_ARGS=%* 63 | goto execute 64 | 65 | :4NT_args 66 | @rem Get arguments from the 4NT Shell from JP Software 67 | set CMD_LINE_ARGS=%$ 68 | 69 | :execute 70 | @rem Setup the command line 71 | 72 | set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar 73 | 74 | @rem Execute Gradle 75 | "%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %CMD_LINE_ARGS% 76 | 77 | :end 78 | @rem End local scope for the variables with windows NT shell 79 | if "%ERRORLEVEL%"=="0" goto mainEnd 80 | 81 | :fail 82 | rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of 83 | rem the _cmd.exe /c_ return code! 84 | if not "" == "%GRADLE_EXIT_CONSOLE%" exit 1 85 | exit /b 1 86 | 87 | :mainEnd 88 | if "%OS%"=="Windows_NT" endlocal 89 | 90 | :omega 91 | -------------------------------------------------------------------------------- /settings.gradle: -------------------------------------------------------------------------------- 1 | include ':app', ':xigua' 2 | -------------------------------------------------------------------------------- /xigua/.gitignore: -------------------------------------------------------------------------------- 1 | /build 2 | /src/androidTest 3 | /src/test 4 | -------------------------------------------------------------------------------- /xigua/build.gradle: -------------------------------------------------------------------------------- 1 | apply plugin: 'com.android.library' 2 | apply plugin: 'com.github.dcendents.android-maven' 3 | group = 'com.github.fanchen001' 4 | 5 | android { 6 | compileSdkVersion 26 7 | 8 | defaultConfig { 9 | minSdkVersion 14 10 | targetSdkVersion 26 11 | versionCode 1 12 | versionName "1.10.24" 13 | 14 | testInstrumentationRunner "android.support.test.runner.AndroidJUnitRunner" 15 | 16 | } 17 | 18 | buildTypes { 19 | release { 20 | minifyEnabled false 21 | proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro' 22 | } 23 | } 24 | 25 | } 26 | 27 | dependencies { 28 | } 29 | -------------------------------------------------------------------------------- /xigua/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 | -------------------------------------------------------------------------------- /xigua/src/main/AndroidManifest.xml: -------------------------------------------------------------------------------- 1 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 14 | 15 | 20 | 21 | 22 | 23 | -------------------------------------------------------------------------------- /xigua/src/main/java/com/xigua/p2p/InfoThread.java: -------------------------------------------------------------------------------- 1 | package com.xigua.p2p; 2 | 3 | import android.content.Context; 4 | import android.content.Intent; 5 | import android.os.Handler; 6 | import android.os.Message; 7 | import android.os.SystemClock; 8 | 9 | import java.util.concurrent.atomic.AtomicBoolean; 10 | 11 | /** 12 | * InfoThread 13 | * Created by fanchen on 2018/10/19. 14 | */ 15 | public class InfoThread { 16 | private MainThreadHandler handler = new MainThreadHandler(); 17 | private AtomicBoolean isRun = new AtomicBoolean(true); 18 | private Context mContext; 19 | 20 | public InfoThread(Context context) { 21 | mContext = context.getApplicationContext(); 22 | } 23 | 24 | /** 25 | *发送下载速度广播 26 | */ 27 | private void sendBroadcastSpeed() { 28 | if (mContext == null) return; 29 | P2PClass p2PClass = P2PClass.getInstance(); 30 | long speed = p2PClass.P2Pgetspeed(-1); 31 | long percent = p2PClass.P2Pgetpercent(); 32 | TaskVideoInfo localTaskVideoInfo = new TaskVideoInfo(); 33 | localTaskVideoInfo.setSpeed(speed); 34 | localTaskVideoInfo.setPercent(percent); 35 | Intent intent = new Intent(); 36 | intent.setAction(P2PMessageWhat.P2P_CALLBACK); 37 | intent.putExtra(P2PMessageWhat.WHAT, P2PMessageWhat.MESSAGE_SPEED); 38 | intent.putExtra(P2PMessageWhat.DATA, localTaskVideoInfo); 39 | mContext.sendBroadcast(intent); 40 | } 41 | 42 | public void start() { 43 | new Thread(new MainRunnable()).start(); 44 | } 45 | 46 | public void stop() { 47 | isRun.set(false); 48 | } 49 | 50 | private class MainRunnable implements Runnable { 51 | 52 | @Override 53 | public void run() { 54 | try { 55 | int position = 0; 56 | while (isRun.get()) { 57 | SystemClock.sleep(2000L);//每2秒发送一次下载速度 58 | handler.sendEmptyMessage(P2PMessageWhat.MESSAGE_SPEED); 59 | if (position % 2 == 0) {//每4秒发送一次下载列表 60 | handler.sendEmptyMessage(P2PMessageWhat.MESSAGE_TASK_LIST); 61 | } else if (position % 5 == 0) { 62 | position = 0;//每10秒发送一次 63 | handler.sendEmptyMessage(P2PMessageWhat.MESSAGE_TASK_MSG); 64 | } 65 | position++; 66 | } 67 | } catch (Throwable e) { 68 | e.printStackTrace(); 69 | } 70 | } 71 | 72 | } 73 | 74 | private class MainThreadHandler extends Handler { 75 | 76 | @Override 77 | public void handleMessage(Message message) { 78 | if (message.what == P2PMessageWhat.MESSAGE_TASK_LIST) { 79 | TaskVideoList.getInstance().sendBroadcastTaskList(); 80 | } else if (message.what == P2PMessageWhat.MESSAGE_SPEED) { 81 | InfoThread.this.sendBroadcastSpeed(); 82 | } else if (message.what == P2PMessageWhat.MESSAGE_TASK_MSG) { 83 | TaskVideoList.getInstance().checkFreeSize(); 84 | } 85 | } 86 | 87 | } 88 | } 89 | -------------------------------------------------------------------------------- /xigua/src/main/java/com/xigua/p2p/NetWorkChangeReceiver.java: -------------------------------------------------------------------------------- 1 | package com.xigua.p2p; 2 | 3 | import android.content.BroadcastReceiver; 4 | import android.content.Context; 5 | import android.content.Intent; 6 | 7 | /** 8 | * NetWorkChangeReceiver 9 | * Created by fanchen on 2018/10/19. 10 | */ 11 | public class NetWorkChangeReceiver extends BroadcastReceiver { 12 | 13 | @Override 14 | public void onReceive(Context context, Intent intent) { 15 | Intent broadcast = new Intent(); 16 | broadcast.setAction(P2PMessageWhat.P2P_CALLBACK); 17 | broadcast.putExtra(P2PMessageWhat.WHAT, P2PMessageWhat.MESSAGE_NETWORK_CHANGE); 18 | context.sendBroadcast(broadcast); 19 | } 20 | 21 | } 22 | -------------------------------------------------------------------------------- /xigua/src/main/java/com/xigua/p2p/P2PClass.java: -------------------------------------------------------------------------------- 1 | package com.xigua.p2p; 2 | 3 | import android.os.StatFs; 4 | 5 | import java.io.File; 6 | 7 | /** 8 | * P2PClass 9 | * Created by fanchen on 2018/10/19. 10 | */ 11 | public class P2PClass { 12 | 13 | private static P2PClass instance; 14 | 15 | static { 16 | try { 17 | System.loadLibrary("p2p"); 18 | } catch (Throwable e) { 19 | e.printStackTrace(); 20 | } 21 | } 22 | 23 | private P2PClass() { 24 | } 25 | 26 | public static P2PClass getInstance() { 27 | if (instance == null) { 28 | synchronized (P2PClass.class) { 29 | if (instance == null) { 30 | instance = new P2PClass(); 31 | } 32 | } 33 | } 34 | return instance; 35 | } 36 | 37 | private final native int dosetupload(int var1); 38 | 39 | private final native int doxadd(byte[] var1); 40 | 41 | private final native int doxcheck(byte[] var1); 42 | 43 | private final native int doxdel(byte[] var1); 44 | 45 | private final native int doxdownload(byte[] var1); 46 | 47 | private final native int doxendhttpd(); 48 | 49 | private final native int doxpause(byte[] var1); 50 | 51 | private final native int doxsave(); 52 | 53 | private final native int doxsetduration(int var1); 54 | 55 | private final native int doxstart(byte[] var1); 56 | 57 | private final native int doxstarthttpd(byte[] var1); 58 | 59 | private final native int doxterminate(); 60 | 61 | private final native long getdownsize(int var1); 62 | 63 | private final native long getfilesize(int var1); 64 | 65 | private final native long getlocalfilesize(byte[] var1); 66 | 67 | private final native int getpercent(); 68 | 69 | private final native long getspeed(int var1); 70 | 71 | public long P2PGetFree() { 72 | StatFs var1 = new StatFs(StorageUtils.getCachePath()); 73 | return var1.getAvailableBlocks() * var1.getBlockSize(); 74 | } 75 | 76 | public long P2Pdosetduration(int var1) { 77 | return (long) this.doxsetduration(var1); 78 | } 79 | 80 | public int P2Pdosetupload(int var1) { 81 | return this.dosetupload(var1); 82 | } 83 | 84 | public int P2Pdoxadd(byte[] var1) { 85 | return this.doxadd(var1); 86 | } 87 | 88 | public int P2Pdoxcheck(byte[] var1) { 89 | return this.doxcheck(var1); 90 | } 91 | 92 | public int P2Pdoxdel(byte[] var1) { 93 | return this.doxdel(var1); 94 | } 95 | 96 | public int P2Pdoxdownload(byte[] var1) { 97 | return this.doxdownload(var1); 98 | } 99 | 100 | public int P2Pdoxpause(byte[] var1) { 101 | return this.doxpause(var1); 102 | } 103 | 104 | public int P2Pdoxstart(byte[] var1) { 105 | return this.doxstart(var1); 106 | } 107 | 108 | public int P2Pdoxstarthttpd(byte[] var1) { 109 | return this.doxstarthttpd(var1); 110 | } 111 | 112 | public int P2Pdoxterminate() { 113 | return doxterminate(); 114 | } 115 | 116 | public long P2Pgetdownsize(int var1) { 117 | return getdownsize(var1); 118 | } 119 | 120 | public long P2Pgetfilesize(int var1) { 121 | return getfilesize(var1); 122 | } 123 | 124 | public long P2Pgetlocalfilesize(byte[] var1) { 125 | return getlocalfilesize(var1); 126 | } 127 | 128 | public int P2Pgetpercent() { 129 | return getpercent(); 130 | } 131 | 132 | public long P2Pgetspeed(int var1) { 133 | return getspeed(var1); 134 | } 135 | 136 | public int getVersion() { 137 | return 18; 138 | } 139 | 140 | public void init() { 141 | try { 142 | String path = StorageUtils.getCachePath(); 143 | File file = new File(path + "/xigua"); 144 | if(!file.exists())file.mkdirs(); 145 | P2Pdoxstarthttpd(path.getBytes()); 146 | } catch (Exception e) { 147 | e.printStackTrace(); 148 | } 149 | } 150 | 151 | public void reinit() { 152 | try { 153 | String exec = StorageUtils.getCachePath(); 154 | exec = "rm -r " + exec + "/xigua"; 155 | Runtime.getRuntime().exec(exec).waitFor(); 156 | } catch (Exception e) { 157 | e.printStackTrace(); 158 | } 159 | } 160 | } 161 | -------------------------------------------------------------------------------- /xigua/src/main/java/com/xigua/p2p/P2PManager.java: -------------------------------------------------------------------------------- 1 | package com.xigua.p2p; 2 | 3 | import android.content.ComponentName; 4 | import android.content.Context; 5 | import android.content.Intent; 6 | import android.content.ServiceConnection; 7 | import android.os.Bundle; 8 | import android.os.IBinder; 9 | import android.os.Message; 10 | import android.os.Messenger; 11 | 12 | /** 13 | * P2PManager 14 | * Created by fanchen on 2018/10/19. 15 | */ 16 | public class P2PManager { 17 | public static final String PREFERENCES = "xigua_preferences"; 18 | public static final String TASK_3G = "task_3g"; 19 | public static final String TASK_LIST = "task_list"; 20 | public static final String TASK_LAST = "task_last"; 21 | public static final String TASK_PATH = "task_path"; 22 | public static final String SO_VERSION = "so_version"; 23 | 24 | private static P2PManager instance; 25 | 26 | private ConnectionCallback connectionCallback; 27 | private Context mContext; 28 | private boolean mIsConnect = false; 29 | private Messenger messenger; 30 | 31 | private ServiceConnection connection = new ServiceConnection() { 32 | 33 | @Override 34 | public void onServiceConnected(ComponentName name, IBinder binder) { 35 | messenger = new Messenger(binder); 36 | mIsConnect = true; 37 | if (connectionCallback != null) { 38 | connectionCallback.onServiceConnected(); 39 | } 40 | } 41 | 42 | @Override 43 | public void onServiceDisconnected(ComponentName var1) { 44 | mIsConnect = false; 45 | bindP2PService(); 46 | } 47 | }; 48 | 49 | /** 50 | * getInstance 51 | * 52 | * @return 53 | */ 54 | public static P2PManager getInstance() { 55 | if (instance == null) { 56 | synchronized (P2PManager.class) { 57 | if (instance == null) { 58 | instance = new P2PManager(); 59 | } 60 | } 61 | } 62 | return instance; 63 | } 64 | 65 | /** 66 | * 67 | * @param uri 68 | * @return 69 | */ 70 | public static boolean isXiguaUrl(Uri uri){ 71 | String scheme = uri.getScheme(); 72 | return scheme != null && (scheme.equalsIgnoreCase("xg") || scheme.equalsIgnoreCase("xgadd") || scheme.equalsIgnoreCase("xgplay")); 73 | } 74 | 75 | /** 76 | * 77 | * @param url 78 | * @return 79 | */ 80 | public static boolean isXiguaUrl(String url){ 81 | if(TextUtils.isEmpty(url))return false; 82 | return isXiguaUrl(Uri.parse(url)); 83 | } 84 | 85 | /** 86 | * 解绑服务 87 | */ 88 | private void unbindP2PService() { 89 | if (mContext == null) return; 90 | mContext.unbindService(connection); 91 | Intent var1 = new Intent(mContext, P2PService.class); 92 | mContext.stopService(var1); 93 | } 94 | 95 | /** 96 | * 绑定服务 97 | */ 98 | private void bindP2PService() { 99 | if (mContext == null) return; 100 | Intent var1 = new Intent(mContext, P2PService.class); 101 | mContext.startService(var1); 102 | mContext.bindService(var1, connection, Context.BIND_AUTO_CREATE); 103 | } 104 | 105 | /** 106 | * 清空缓存 107 | * 108 | * @return 109 | */ 110 | public boolean cleanCache() { 111 | if (!isConnect()) { 112 | return false; 113 | } else { 114 | try { 115 | messenger.send(Message.obtain(null, P2PMessageWhat.CLEAN_CACHE)); 116 | } catch (Exception e) { 117 | e.printStackTrace(); 118 | } 119 | } 120 | return true; 121 | } 122 | 123 | /** 124 | * 获取是否使用流量下载 125 | * 126 | * @return 127 | */ 128 | public boolean getAllow3G() { 129 | if (mContext == null) return true; 130 | return mContext.getSharedPreferences(P2PManager.PREFERENCES, Context.MODE_PRIVATE).getBoolean(P2PManager.TASK_3G, true); 131 | } 132 | 133 | /** 134 | * 初始化 135 | * 136 | * @param context 137 | */ 138 | public void init(Context context) { 139 | mContext = context.getApplicationContext(); 140 | StorageUtils.init(context.getApplicationContext()); 141 | bindP2PService(); 142 | } 143 | 144 | /** 145 | * 是否连接服务 146 | * 147 | * @return 148 | */ 149 | public boolean isConnect() { 150 | if (!mIsConnect) { 151 | bindP2PService(); 152 | } 153 | return this.mIsConnect; 154 | } 155 | 156 | /** 157 | * 暂停一个任务 158 | * 159 | * @param var1 160 | * @return 161 | */ 162 | public boolean pause(String var1) { 163 | if (!isConnect()) { 164 | return false; 165 | } else { 166 | try { 167 | Bundle bundle = new Bundle(); 168 | bundle.putString("url", var1); 169 | messenger.send(Message.obtain(null, P2PMessageWhat.PAUSE, bundle)); 170 | } catch (Exception e) { 171 | e.printStackTrace(); 172 | } 173 | } 174 | return true; 175 | } 176 | 177 | /** 178 | * 开始一个任务 179 | * 180 | * @param var1 181 | * @return 182 | */ 183 | public boolean play(String var1) { 184 | if (!isConnect()) { 185 | return false; 186 | } else { 187 | try { 188 | Bundle bundle = new Bundle(); 189 | bundle.putString("url", var1); 190 | messenger.send(Message.obtain(null, P2PMessageWhat.START, bundle)); 191 | } catch (Exception e) { 192 | e.printStackTrace(); 193 | } 194 | } 195 | return true; 196 | } 197 | 198 | /** 199 | * 释放资源 200 | */ 201 | public void release() { 202 | unbindP2PService(); 203 | } 204 | 205 | /** 206 | * 移除一个任务 207 | * 208 | * @param url 209 | * @return 210 | */ 211 | public boolean remove(String url) { 212 | if (!isConnect()) { 213 | return false; 214 | } else { 215 | try { 216 | Bundle bundle = new Bundle(); 217 | bundle.putString("url", url); 218 | messenger.send(Message.obtain(null, P2PMessageWhat.REMOVE, bundle)); 219 | } catch (Exception e) { 220 | e.printStackTrace(); 221 | } 222 | } 223 | return true; 224 | } 225 | 226 | /** 227 | * 重启服务 228 | * 229 | * @return 230 | */ 231 | public boolean restartService() { 232 | if (!isConnect()) { 233 | return false; 234 | } else { 235 | try { 236 | messenger.send(Message.obtain(null, P2PMessageWhat.RESTART_SERVICE)); 237 | } catch (Exception e) { 238 | e.printStackTrace(); 239 | } 240 | } 241 | return true; 242 | } 243 | 244 | /** 245 | * 设置是否允许流量下载 246 | * 247 | * @param allow3G 248 | */ 249 | public void setAllow3G(boolean allow3G) { 250 | if (mContext == null) return; 251 | mContext.getSharedPreferences(P2PManager.PREFERENCES, Context.MODE_PRIVATE).edit().putBoolean(P2PManager.TASK_3G, allow3G).apply(); 252 | } 253 | 254 | /** 255 | * @param connectionCallback 256 | */ 257 | public void setConnectionCallback(ConnectionCallback connectionCallback) { 258 | this.connectionCallback = connectionCallback; 259 | } 260 | 261 | /** 262 | * ConnectionCallback 263 | */ 264 | public interface ConnectionCallback { 265 | void onServiceConnected(); 266 | } 267 | } 268 | -------------------------------------------------------------------------------- /xigua/src/main/java/com/xigua/p2p/P2PMessageWhat.java: -------------------------------------------------------------------------------- 1 | package com.xigua.p2p; 2 | 3 | /** 4 | * P2PMessageWhat 5 | * Created by fanchen on 2018/10/19. 6 | */ 7 | public class P2PMessageWhat { 8 | public static final int CLEAN_CACHE = 257; 9 | public static final int DOWNLOAD = 2; 10 | public static final int PAUSE = 3; 11 | public static final int REMOVE = 4; 12 | public static final int RESTART_SERVICE = 256; 13 | public static final int START = 1; 14 | public static final int MESSAGE_FREE_SIZE_NOT = 4; 15 | public static final int MESSAGE_INIT_FINISHED = 257; 16 | public static final int MESSAGE_NETWORK_CHANGE = 5; 17 | public static final int MESSAGE_PLAY_URL = 258; 18 | public static final int MESSAGE_SPEED = 1; 19 | public static final int MESSAGE_TASK_LIST = 2; 20 | public static final int MESSAGE_TASK_MSG = 3; 21 | public static final String WHAT = "what"; 22 | public static final String DATA = "data"; 23 | public static final String PLAY_URL = "url"; 24 | public static final String LOCAL_FILE = "local"; 25 | public static final String P2P_CALLBACK = "com.broadcast.message.p2p"; 26 | 27 | private P2PMessageWhat(){ 28 | } 29 | } 30 | -------------------------------------------------------------------------------- /xigua/src/main/java/com/xigua/p2p/P2PService.java: -------------------------------------------------------------------------------- 1 | package com.xigua.p2p; 2 | 3 | import android.app.Service; 4 | import android.content.Context; 5 | import android.content.Intent; 6 | import android.os.Bundle; 7 | import android.os.Handler; 8 | import android.os.IBinder; 9 | import android.os.Message; 10 | import android.os.Messenger; 11 | import android.util.Log; 12 | 13 | /** 14 | * P2PService 15 | * Created by fanchen on 2018/10/19. 16 | */ 17 | public class P2PService extends Service { 18 | public static final String TAG = "xigua_sdk"; 19 | private Messenger messenger; 20 | 21 | public IBinder onBind(Intent var1) { 22 | return messenger.getBinder(); 23 | } 24 | 25 | public void onCreate() { 26 | super.onCreate(); 27 | TaskVideoList.getInstance().init(this); 28 | messenger = new Messenger(new IncomingHandler(this)); 29 | } 30 | 31 | public void onDestroy() { 32 | super.onDestroy(); 33 | TaskVideoList.getInstance().terminate(); 34 | } 35 | 36 | private static class IncomingHandler extends Handler implements Runnable { 37 | 38 | private String lastUrl = ""; 39 | private Context mContext; 40 | 41 | public IncomingHandler(Context context) { 42 | mContext = context; 43 | lastUrl = loadLastUrl(); 44 | if (lastUrl.length() > 0) TaskVideoList.getInstance().start(lastUrl); 45 | } 46 | 47 | private String loadLastUrl() { 48 | if (mContext == null) return ""; 49 | return mContext.getSharedPreferences(P2PManager.PREFERENCES, Context.MODE_PRIVATE).getString(P2PManager.TASK_LAST, ""); 50 | } 51 | 52 | private void saveLastUrl() { 53 | if (mContext == null) return; 54 | mContext.getSharedPreferences(P2PManager.PREFERENCES, Context.MODE_PRIVATE).edit().putString(P2PManager.TASK_LAST, lastUrl).apply(); 55 | } 56 | 57 | @Override 58 | public void handleMessage(Message msg) { 59 | if (msg.obj == null || !(msg.obj instanceof Bundle)) return; 60 | Bundle bundle = (Bundle) msg.obj; 61 | String url = bundle.getString("url", ""); 62 | if (msg.what == P2PMessageWhat.START) { 63 | TaskVideoList.getInstance().start(lastUrl = url); 64 | saveLastUrl(); 65 | Log.e(TAG, "PLAY " + url); 66 | } else if (msg.what == P2PMessageWhat.PAUSE) { 67 | TaskVideoList.getInstance().pause(url); 68 | if (lastUrl.equalsIgnoreCase(url)) { 69 | lastUrl = ""; 70 | saveLastUrl(); 71 | } 72 | Log.e(TAG, "PAUSE " + url); 73 | } else if (msg.what == P2PMessageWhat.MESSAGE_INIT_FINISHED) { 74 | lastUrl = ""; 75 | saveLastUrl(); 76 | new Thread(this).start(); 77 | Log.e(TAG, "CLEAN_CACHE"); 78 | } else if (msg.what == P2PMessageWhat.REMOVE) { 79 | TaskVideoList.getInstance().remove(url); 80 | if (lastUrl.equalsIgnoreCase(url)) { 81 | lastUrl = ""; 82 | saveLastUrl(); 83 | } 84 | Log.e(TAG, "REMOVE " + url); 85 | } else if (msg.what == P2PMessageWhat.RESTART_SERVICE) { 86 | Log.e(TAG, "RESTART_SERVICE"); 87 | System.exit(0); 88 | } 89 | } 90 | 91 | @Override 92 | public void run() { 93 | synchronized (IncomingHandler.class) { 94 | TaskVideoList.getInstance().cleanCache(); 95 | } 96 | } 97 | 98 | } 99 | } 100 | -------------------------------------------------------------------------------- /xigua/src/main/java/com/xigua/p2p/StorageUtils.java: -------------------------------------------------------------------------------- 1 | package com.xigua.p2p; 2 | 3 | import android.content.Context; 4 | import android.content.SharedPreferences; 5 | import android.os.Build; 6 | import android.os.Environment; 7 | import android.os.StatFs; 8 | import android.os.storage.StorageManager; 9 | 10 | import java.io.File; 11 | import java.lang.reflect.Method; 12 | import java.util.ArrayList; 13 | import java.util.Iterator; 14 | import java.util.List; 15 | 16 | /** 17 | * StorageUtils 18 | * Created by fanchen on 2018/10/19. 19 | */ 20 | public class StorageUtils { 21 | private static Context appContext = null; 22 | private static String defMaxPath = ""; 23 | 24 | /** 25 | * 初始化 26 | * @param paramContext 27 | */ 28 | public static void init(Context paramContext) { 29 | appContext = paramContext.getApplicationContext(); 30 | defMaxPath = autoSelectMaxCache(); 31 | } 32 | 33 | private static String autoSelectMaxCache() { 34 | String cachePath = Environment.getExternalStorageDirectory().getPath(); 35 | try { 36 | cachePath = cachePath + File.separator + "Android" + File.separator + "data" + File.separator + appContext.getPackageName() + File.separator + "video"; 37 | Iterator localIterator = getStorageList().iterator(); 38 | while (localIterator.hasNext()) { 39 | String str = localIterator.next(); 40 | if (getFreeSize(cachePath) >= getFreeSize(str)) continue; 41 | cachePath = str; 42 | } 43 | } catch (Exception e) { 44 | e.printStackTrace(); 45 | } 46 | return cachePath; 47 | } 48 | 49 | /** 50 | * 清除缓存 51 | */ 52 | public static void clearCache() { 53 | try { 54 | List storageList = getStorageList(); 55 | storageList.add(defMaxPath); 56 | Iterator localIterator = storageList.iterator(); 57 | while (localIterator.hasNext()) { 58 | File dir = new File(localIterator.next()); 59 | if (!dir.exists())continue; 60 | File[] files = dir.listFiles(); 61 | int i = 0; 62 | if (files != null && files.length > 0) 63 | while (i < files.length) { 64 | if (files[i].isFile()) 65 | files[i].delete(); 66 | i++; 67 | } 68 | } 69 | } catch (Exception e) { 70 | e.printStackTrace(); 71 | } 72 | } 73 | 74 | /** 75 | * 获取下载缓存目录 76 | * @return 77 | */ 78 | public static String getCachePath() { 79 | String cachePath = Environment.getExternalStorageDirectory().getPath(); 80 | try { 81 | cachePath = cachePath + File.separator + "Android" + File.separator + "data" + File.separator + appContext.getPackageName() + File.separator + "video"; 82 | if (defMaxPath.length() > 0)cachePath = defMaxPath; 83 | cachePath = appContext.getSharedPreferences(P2PManager.PREFERENCES, Context.MODE_PRIVATE).getString(P2PManager.TASK_PATH, cachePath); 84 | } catch (Exception e) { 85 | e.printStackTrace(); 86 | } 87 | return cachePath; 88 | } 89 | 90 | /** 91 | * 设置缓存目录 92 | * @param path 93 | * @return 94 | */ 95 | public static boolean setCachePath(String path) { 96 | try { 97 | SharedPreferences preferences = appContext.getSharedPreferences(P2PManager.PREFERENCES, Context.MODE_PRIVATE); 98 | SharedPreferences.Editor editor = preferences.edit(); 99 | editor.putString(P2PManager.TASK_PATH, path); 100 | return editor.commit(); 101 | } catch (Exception e) { 102 | e.printStackTrace(); 103 | } 104 | return false; 105 | } 106 | 107 | @SuppressWarnings("deprecation") 108 | public static long getFreeSize(String path) { 109 | long l2 = 10240L; 110 | long l1 = 10240L; 111 | try { 112 | StatFs statFs = new StatFs(path); 113 | if (Build.VERSION.SDK_INT >= 18) { 114 | l2 = statFs.getBlockSizeLong(); 115 | l1 = statFs.getAvailableBlocksLong(); 116 | } else { 117 | l2 = statFs.getBlockSize(); 118 | l1 = statFs.getAvailableBlocks(); 119 | } 120 | } catch (Throwable e) { 121 | e.printStackTrace(); 122 | } 123 | return l2 * l1; 124 | } 125 | 126 | public static List getStorageList() { 127 | List storageList = new ArrayList(); 128 | try { 129 | String str = "Android" + File.separator + "data" + File.separator + appContext.getPackageName() + File.separator + "video"; 130 | StorageManager storageManager = (StorageManager) appContext.getSystemService(Context.STORAGE_SERVICE); 131 | Class clazz = storageManager.getClass(); 132 | Method getVolumePaths = clazz.getMethod("getVolumePaths",new Class[0]); 133 | String[] volumePaths = (String[]) getVolumePaths.invoke(storageManager, new Object[0]); 134 | for (String s : volumePaths) { 135 | Method getVolumeState = clazz.getMethod("getVolumeState",new Class[] { String.class }); 136 | String state = (String) getVolumeState.invoke(storageManager,new Object[] { s }); 137 | if ("mounted".equals(state)) { 138 | String path = s + File.separator + str; 139 | getExternalFilesDirs(appContext, null); 140 | File localFile = new File(path); 141 | if(!localFile.exists())localFile.mkdirs(); 142 | if (localFile.exists())storageList.add(path); 143 | } 144 | } 145 | } catch (Exception e) { 146 | e.printStackTrace(); 147 | } 148 | return storageList; 149 | } 150 | 151 | public static File[] getExternalFilesDirs(Context context, String type) { 152 | if (Build.VERSION.SDK_INT >= 19) { 153 | return context.getExternalFilesDirs(type); 154 | } else { 155 | return new File[] { context.getExternalFilesDir(type) }; 156 | } 157 | } 158 | } -------------------------------------------------------------------------------- /xigua/src/main/java/com/xigua/p2p/TaskVideoInfo.java: -------------------------------------------------------------------------------- 1 | package com.xigua.p2p; 2 | 3 | import android.os.Parcel; 4 | import android.os.Parcelable; 5 | 6 | /** 7 | * TaskVideoInfo 8 | * Created by fanchen on 2018/10/19. 9 | */ 10 | public class TaskVideoInfo implements Parcelable { 11 | public static final int PAUSE = 1; 12 | public static final int START = 2; 13 | private long downSize;//已下载大小 14 | private long localSize;//本地文件大小 15 | private long percent; 16 | private long speed;//下载速度 17 | private int state;//状态 18 | private long totalSize;//总大小 19 | private String url;//url地址 20 | 21 | public TaskVideoInfo() { 22 | } 23 | 24 | protected TaskVideoInfo(Parcel in) { 25 | downSize = in.readLong(); 26 | localSize = in.readLong(); 27 | percent = in.readLong(); 28 | speed = in.readLong(); 29 | state = in.readInt(); 30 | totalSize = in.readLong(); 31 | url = in.readString(); 32 | } 33 | 34 | public long getDownSize() { 35 | return downSize; 36 | } 37 | 38 | public void setDownSize(long downSize) { 39 | this.downSize = downSize; 40 | } 41 | 42 | public long getLocalSize() { 43 | return localSize; 44 | } 45 | 46 | public void setLocalSize(long localSize) { 47 | this.localSize = localSize; 48 | } 49 | 50 | public long getPercent() { 51 | return percent; 52 | } 53 | 54 | public void setPercent(long percent) { 55 | this.percent = percent; 56 | } 57 | 58 | public long getSpeed() { 59 | return speed; 60 | } 61 | 62 | public void setSpeed(long speed) { 63 | this.speed = speed; 64 | } 65 | 66 | public int getState() { 67 | return state; 68 | } 69 | 70 | public void setState(int state) { 71 | this.state = state; 72 | } 73 | 74 | public long getTotalSize() { 75 | return totalSize; 76 | } 77 | 78 | public void setTotalSize(long totalSize) { 79 | this.totalSize = totalSize; 80 | } 81 | 82 | public String getUrl() { 83 | return url; 84 | } 85 | 86 | public void setUrl(String url) { 87 | this.url = url; 88 | } 89 | 90 | @Override 91 | public void writeToParcel(Parcel dest, int flags) { 92 | dest.writeLong(downSize); 93 | dest.writeLong(localSize); 94 | dest.writeLong(percent); 95 | dest.writeLong(speed); 96 | dest.writeInt(state); 97 | dest.writeLong(totalSize); 98 | dest.writeString(url); 99 | } 100 | 101 | @Override 102 | public int describeContents() { 103 | return 0; 104 | } 105 | 106 | public static final Creator CREATOR = new Creator() { 107 | @Override 108 | public TaskVideoInfo createFromParcel(Parcel in) { 109 | return new TaskVideoInfo(in); 110 | } 111 | 112 | @Override 113 | public TaskVideoInfo[] newArray(int size) { 114 | return new TaskVideoInfo[size]; 115 | } 116 | }; 117 | 118 | @Override 119 | public String toString() { 120 | return "[url -> " + url + ", state -> " + state +",speed -> " + speed + "]"; 121 | } 122 | 123 | @Override 124 | public boolean equals(Object object) { 125 | if (this == object) return true; 126 | if (object != null && object instanceof TaskVideoInfo) { 127 | return url.equals(((TaskVideoInfo) object).url); 128 | } 129 | return false; 130 | } 131 | } 132 | -------------------------------------------------------------------------------- /xigua/src/main/java/com/xigua/p2p/TaskVideoList.java: -------------------------------------------------------------------------------- 1 | package com.xigua.p2p; 2 | 3 | import android.annotation.SuppressLint; 4 | import android.content.Context; 5 | import android.content.Intent; 6 | import android.content.SharedPreferences; 7 | import android.net.ConnectivityManager; 8 | import android.net.NetworkInfo; 9 | import android.net.Uri; 10 | import android.os.Parcelable; 11 | import android.util.Log; 12 | 13 | import org.json.JSONArray; 14 | 15 | import java.io.File; 16 | import java.net.URLEncoder; 17 | import java.util.ArrayList; 18 | import java.util.Collections; 19 | import java.util.Comparator; 20 | import java.util.Set; 21 | import java.util.concurrent.CopyOnWriteArraySet; 22 | 23 | /** 24 | * TaskVideoList 25 | * Created by fanchen on 2018/10/19. 26 | */ 27 | public class TaskVideoList { 28 | public static final String TAG = "xigua_sdk"; 29 | private static TaskVideoList instance; 30 | private InfoThread infoThread; 31 | private Context mContext; 32 | private Set tastList = new CopyOnWriteArraySet<>(); 33 | 34 | /** 35 | * getInstance 36 | * @return 37 | */ 38 | public static TaskVideoList getInstance() { 39 | if (instance == null) { 40 | synchronized (TaskVideoList.class) { 41 | if (instance == null) { 42 | instance = new TaskVideoList(); 43 | } 44 | } 45 | } 46 | return instance; 47 | } 48 | 49 | /** 50 | * 构建下载信息 51 | * @param url 52 | * @return 53 | */ 54 | private TaskVideoInfo buildTaskInfo(String url) { 55 | try { 56 | P2PClass p2PClass = P2PClass.getInstance(); 57 | byte[] gbks = url.getBytes("GBK"); 58 | int doxadd = p2PClass.P2Pdoxadd(gbks); 59 | TaskVideoInfo task = new TaskVideoInfo(); 60 | task.setUrl(url); 61 | task.setState(1); 62 | task.setDownSize(p2PClass.P2Pgetdownsize(doxadd)); 63 | task.setTotalSize(p2PClass.P2Pgetfilesize(doxadd)); 64 | task.setLocalSize(p2PClass.P2Pgetlocalfilesize(gbks)); 65 | task.setSpeed(p2PClass.P2Pgetspeed(-1)); 66 | task.setPercent(p2PClass.P2Pgetpercent()); 67 | return task; 68 | } catch (Exception e) { 69 | e.printStackTrace(); 70 | } 71 | return null; 72 | } 73 | 74 | /** 75 | * 流量情况下,是否允许下载 76 | * @return 77 | */ 78 | private boolean is3G() { 79 | if (mContext == null) return false; 80 | return mContext.getSharedPreferences(P2PManager.PREFERENCES, Context.MODE_PRIVATE).getBoolean(P2PManager.TASK_3G, true); 81 | } 82 | 83 | /** 84 | * 暂停任务,不保存 85 | * 86 | * @param url 87 | */ 88 | private void pauseNoSave(String url) { 89 | try { 90 | byte[] gbks = url.getBytes("GBK"); 91 | P2PClass.getInstance().P2Pdoxpause(gbks); 92 | if (tastList == null) return; 93 | for (TaskVideoInfo info : tastList) { 94 | if (url.equals(info.getUrl())) { 95 | info.setState(1); 96 | } 97 | } 98 | } catch (Exception e) { 99 | e.printStackTrace(); 100 | } 101 | } 102 | 103 | /** 104 | * 移除任务,但不保存到本地 105 | * @param url 106 | */ 107 | private void removeNoSave(String url) { 108 | try { 109 | byte[] gbks = url.getBytes("GBK"); 110 | P2PClass p2PClass = P2PClass.getInstance(); 111 | p2PClass.P2Pdoxpause(gbks); 112 | p2PClass.P2Pdoxdel(gbks); 113 | TaskVideoInfo task = new TaskVideoInfo(); 114 | task.setUrl(url); 115 | if (tastList == null) return; 116 | tastList.remove(task); 117 | String segment = Uri.parse(url).getLastPathSegment(); 118 | String path = StorageUtils.getCachePath() + "/xigua/Downloads/" + segment; 119 | File file = new File(path); 120 | if (file.exists()) file.delete(); 121 | } catch (Exception e) { 122 | e.printStackTrace(); 123 | } 124 | } 125 | 126 | /** 127 | * 发送本地代理播放地址 128 | * @param info 129 | */ 130 | private void sendPlayUrl(TaskVideoInfo info) { 131 | try { 132 | String localFileName = Uri.parse(info.getUrl()).getLastPathSegment(); 133 | String localFile = StorageUtils.getCachePath() + "/xigua/Downloads/" + localFileName; 134 | File file = new File(localFile); 135 | boolean isLocal = info.getLocalSize() > 0 && file.exists(); 136 | String play_url = isLocal ? "file://" + localFile : "http://127.0.0.1:8083/" + URLEncoder.encode(localFileName, "GBK"); 137 | sendMessage(P2PMessageWhat.MESSAGE_PLAY_URL,new Object[]{play_url,isLocal}); 138 | Log.i(TAG, "sendPlayUrl:" + play_url); 139 | } catch(Exception e) { 140 | e.printStackTrace(); 141 | } 142 | } 143 | 144 | public void checkFreeSize() { 145 | try { 146 | if (mContext == null) return; 147 | if (StorageUtils.getFreeSize(StorageUtils.getCachePath()) >= 524288000L) return; 148 | getInstance().stopAll(); 149 | sendMessage(P2PMessageWhat.MESSAGE_FREE_SIZE_NOT); 150 | sendBroadcastTaskList(); 151 | } catch (Exception e) { 152 | e.printStackTrace(); 153 | } 154 | } 155 | 156 | /** 157 | * 清空缓存 158 | */ 159 | public void cleanCache() { 160 | StorageUtils.clearCache(); 161 | P2PClass.getInstance().reinit(); 162 | if (tastList == null) return; 163 | for (TaskVideoInfo info : tastList) { 164 | removeNoSave(info.getUrl()); 165 | } 166 | saveTaskList(); 167 | } 168 | 169 | /** 170 | * 初始化 171 | * @param context 172 | */ 173 | public void init(Context context) { 174 | mContext = context.getApplicationContext(); 175 | upgradeLibrary(); 176 | StorageUtils.init(mContext); 177 | P2PClass.getInstance().init(); 178 | sendBroadcastInitFinished(); 179 | infoThread = new InfoThread(mContext); 180 | infoThread.start(); 181 | loadTaskList(); 182 | } 183 | 184 | /** 185 | * 是否是wifi环境,是否可以下载 186 | * @return 187 | */ 188 | @SuppressLint("MissingPermission") 189 | public boolean isWifi() { 190 | try { 191 | ConnectivityManager cm = (ConnectivityManager) mContext.getSystemService(Context.CONNECTIVITY_SERVICE); 192 | NetworkInfo networkInfo = cm.getActiveNetworkInfo(); 193 | if (networkInfo != null && networkInfo.getType() == 1) { 194 | return true; 195 | } else { 196 | return is3G(); 197 | } 198 | } catch (Exception e) { 199 | e.printStackTrace(); 200 | } 201 | return true; 202 | } 203 | 204 | /** 205 | * 加载下载列表 206 | */ 207 | public void loadTaskList() { 208 | try{ 209 | String json = mContext.getSharedPreferences(P2PManager.PREFERENCES, Context.MODE_PRIVATE).getString(P2PManager.TASK_LIST, "[]"); 210 | JSONArray array = new JSONArray(json); 211 | for (int i = 0 ; i < array.length() ; i ++){ 212 | String url = array.optString(i); 213 | TaskVideoInfo info = buildTaskInfo(url); 214 | tastList.add(info); 215 | } 216 | sendBroadcastTaskList(); 217 | Log.i(TAG, "load task = :" + json); 218 | }catch (Exception e){ 219 | e.printStackTrace(); 220 | } 221 | } 222 | 223 | /** 224 | * 暂停任务 225 | * 226 | * @param paramString 227 | */ 228 | public void pause(String paramString) { 229 | pauseNoSave(paramString); 230 | saveTaskList(); 231 | } 232 | 233 | /** 234 | * 删除任务 235 | * 236 | * @param paramString 237 | */ 238 | public void remove(String paramString) { 239 | removeNoSave(paramString); 240 | saveTaskList(); 241 | } 242 | 243 | /** 244 | * 保存任务信息到本地 245 | */ 246 | public void saveTaskList() { 247 | if (tastList == null || mContext == null) return; 248 | try { 249 | JSONArray jsonArray = new JSONArray(); 250 | for (TaskVideoInfo info : tastList) { 251 | jsonArray.put(info.getUrl()); 252 | } 253 | String s = jsonArray.toString(); 254 | mContext.getSharedPreferences(P2PManager.PREFERENCES, Context.MODE_PRIVATE).edit().putString(P2PManager.TASK_LIST, s).apply(); 255 | Log.i(TAG, "save task = " + s); 256 | sendBroadcastTaskList(); 257 | } catch (Exception e) { 258 | e.printStackTrace(); 259 | } 260 | } 261 | 262 | /** 263 | * 发送下载完成消息 264 | */ 265 | public void sendBroadcastInitFinished() { 266 | try { 267 | sendMessage(P2PMessageWhat.MESSAGE_INIT_FINISHED); 268 | } catch (Exception e) { 269 | e.printStackTrace(); 270 | } 271 | } 272 | 273 | /** 274 | * 发送下载失败消息 275 | * @param data 276 | */ 277 | public void sendBroadcastTaskError(String data) { 278 | try { 279 | sendMessage(P2PMessageWhat.MESSAGE_TASK_MSG, data); 280 | Log.e(TAG, "sendBroadcastTaskError -> " + data); 281 | } catch (Exception e) { 282 | e.printStackTrace(); 283 | } 284 | } 285 | 286 | /** 287 | * 发送下载列表消息 288 | */ 289 | public void sendBroadcastTaskList() { 290 | try { 291 | if (tastList == null) return; 292 | ArrayList list = new ArrayList<>(); 293 | for (TaskVideoInfo info : tastList) { 294 | TaskVideoInfo task = buildTaskInfo(info.getUrl()); 295 | if (task != null) { 296 | task.setState(info.getState()); 297 | list.add(task); 298 | } 299 | } 300 | Collections.sort(list, comparator); 301 | sendMessage(P2PMessageWhat.MESSAGE_TASK_LIST, list); 302 | } catch (Exception e) { 303 | e.printStackTrace(); 304 | } 305 | } 306 | 307 | /** 308 | * 开始任务 309 | * @param url 310 | * @return 311 | */ 312 | public boolean start(String url) { 313 | try { 314 | byte[] bytes = url.getBytes("GBK"); 315 | P2PClass p2p = P2PClass.getInstance(); 316 | TaskVideoInfo info = buildTaskInfo(url); 317 | sendPlayUrl(info); 318 | if(info != null && info.getLocalSize() > 0) { 319 | return false; 320 | }else if(!isWifi()) { 321 | sendBroadcastTaskError("当前非wifi环境,不能下载"); 322 | return false; 323 | }else if(info != null) { 324 | stopAll(); 325 | p2p.P2Pdoxstart(bytes); 326 | info.setState(2); 327 | tastList.remove(info); 328 | tastList.add(info); 329 | } else { 330 | Log.i(TAG, "添加任务出错"); 331 | return false; 332 | } 333 | } catch(Exception e) { 334 | e.printStackTrace(); 335 | } 336 | saveTaskList(); 337 | checkFreeSize(); 338 | return true; 339 | } 340 | 341 | /** 342 | * 停止所有任务 343 | */ 344 | public void stopAll() { 345 | if (tastList == null) return; 346 | for (TaskVideoInfo info : tastList) { 347 | pauseNoSave(info.getUrl()); 348 | } 349 | saveTaskList(); 350 | } 351 | 352 | /** 353 | * 释放资源 354 | */ 355 | public void terminate() { 356 | stopAll(); 357 | infoThread.stop(); 358 | P2PClass.getInstance().P2Pdoxterminate(); 359 | saveTaskList(); 360 | } 361 | 362 | /** 363 | * 更新Library version 364 | */ 365 | public void upgradeLibrary() { 366 | if (mContext == null) return; 367 | SharedPreferences preferences = mContext.getSharedPreferences(P2PManager.PREFERENCES, Context.MODE_PRIVATE); 368 | int version = preferences.getInt(P2PManager.SO_VERSION, 0); 369 | int pVersion = P2PClass.getInstance().getVersion(); 370 | if (version != pVersion) { 371 | P2PClass.getInstance().reinit(); 372 | preferences.edit().putInt(P2PManager.SO_VERSION, pVersion).apply(); 373 | } 374 | Log.e(TAG, String.format("oldVersion = %d newVersion = %d", version, pVersion)); 375 | } 376 | 377 | /** 378 | * 发送消息 379 | * @param what 380 | */ 381 | private void sendMessage(int what) { 382 | sendMessage(P2PMessageWhat.P2P_CALLBACK, what, null); 383 | } 384 | 385 | /** 386 | * 发送消息 387 | * @param what 388 | * @param data 389 | */ 390 | private void sendMessage(int what, Object data) { 391 | sendMessage(P2PMessageWhat.P2P_CALLBACK, what, data); 392 | } 393 | 394 | /** 395 | * 发送消息 396 | * @param action 397 | * @param what 398 | * @param data 399 | */ 400 | private void sendMessage(String action, int what, Object data) { 401 | if (mContext == null) return; 402 | Intent intent = new Intent(action); 403 | intent.putExtra(P2PMessageWhat.WHAT, what); 404 | if (data != null && data instanceof String) { 405 | intent.putExtra(P2PMessageWhat.DATA, data.toString()); 406 | } else if (data != null && data instanceof ArrayList) { 407 | intent.putParcelableArrayListExtra(P2PMessageWhat.DATA, (ArrayList) data); 408 | } else if (data != null && data instanceof Object[]) { 409 | Object[] objects = (Object[]) data; 410 | intent.putExtra(P2PMessageWhat.PLAY_URL, objects[0].toString()); 411 | intent.putExtra(P2PMessageWhat.LOCAL_FILE, (boolean) objects[1]); 412 | } 413 | mContext.sendBroadcast(intent); 414 | } 415 | 416 | private Comparator comparator = new Comparator() { 417 | 418 | @Override 419 | public int compare(TaskVideoInfo o1, TaskVideoInfo o2) { 420 | return o1.getUrl().compareToIgnoreCase(o2.getUrl()); 421 | } 422 | 423 | }; 424 | } 425 | -------------------------------------------------------------------------------- /xigua/src/main/java/com/xigua/p2p/XiguaProvider.java: -------------------------------------------------------------------------------- 1 | package com.xigua.p2p; 2 | 3 | import android.content.ContentProvider; 4 | import android.content.ContentValues; 5 | import android.database.Cursor; 6 | import android.net.Uri; 7 | 8 | /** 9 | * XiguaProvider 10 | * 通过声明 {@link ContentProvider} 自动完成初始化 11 | * Created by fanchen on 2018/10/24. 12 | */ 13 | public class XiguaProvider extends ContentProvider { 14 | 15 | @Override 16 | public boolean onCreate() { 17 | P2PManager.getInstance().init(getContext()); 18 | P2PManager.getInstance().setAllow3G(true); 19 | P2PManager.getInstance().isConnect(); 20 | return true; 21 | } 22 | 23 | @Override 24 | public Cursor query(Uri uri, String[] projection, String selection, String[] selectionArgs, String sortOrder) { 25 | return null; 26 | } 27 | 28 | @Override 29 | public String getType(Uri uri) { 30 | return null; 31 | } 32 | 33 | @Override 34 | public Uri insert(Uri uri, ContentValues values) { 35 | return null; 36 | } 37 | 38 | @Override 39 | public int delete(Uri uri, String selection, String[] selectionArgs) { 40 | return 0; 41 | } 42 | 43 | @Override 44 | public int update(Uri uri, ContentValues values, String selection, String[] selectionArgs) { 45 | return 0; 46 | } 47 | } -------------------------------------------------------------------------------- /xigua/src/main/jniLibs/armeabi/libp2p.so: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fanchen001/XiguaP2p/71cc4aacf4a9abc2f66ab089ec8ad0a27ceccf84/xigua/src/main/jniLibs/armeabi/libp2p.so --------------------------------------------------------------------------------