Commit 9e579db1 by 王文龙

Initial commit

parents
*.iml
.gradle
/local.properties
/.idea/libraries
/.idea/modules.xml
/.idea/workspace.xml
.DS_Store
/build
/captures
.externalNativeBuild
<component name="ProjectCodeStyleConfiguration">
<code_scheme name="Project" version="173">
<Objective-C-extensions>
<file>
<option name="com.jetbrains.cidr.lang.util.OCDeclarationKind" value="Import" />
<option name="com.jetbrains.cidr.lang.util.OCDeclarationKind" value="Macro" />
<option name="com.jetbrains.cidr.lang.util.OCDeclarationKind" value="Typedef" />
<option name="com.jetbrains.cidr.lang.util.OCDeclarationKind" value="Enum" />
<option name="com.jetbrains.cidr.lang.util.OCDeclarationKind" value="Constant" />
<option name="com.jetbrains.cidr.lang.util.OCDeclarationKind" value="Global" />
<option name="com.jetbrains.cidr.lang.util.OCDeclarationKind" value="Struct" />
<option name="com.jetbrains.cidr.lang.util.OCDeclarationKind" value="FunctionPredecl" />
<option name="com.jetbrains.cidr.lang.util.OCDeclarationKind" value="Function" />
</file>
<class>
<option name="com.jetbrains.cidr.lang.util.OCDeclarationKind" value="Property" />
<option name="com.jetbrains.cidr.lang.util.OCDeclarationKind" value="Synthesize" />
<option name="com.jetbrains.cidr.lang.util.OCDeclarationKind" value="InitMethod" />
<option name="com.jetbrains.cidr.lang.util.OCDeclarationKind" value="StaticMethod" />
<option name="com.jetbrains.cidr.lang.util.OCDeclarationKind" value="InstanceMethod" />
<option name="com.jetbrains.cidr.lang.util.OCDeclarationKind" value="DeallocMethod" />
</class>
<extensions>
<pair source="cpp" header="h" fileNamingConvention="NONE" />
<pair source="c" header="h" fileNamingConvention="NONE" />
</extensions>
</Objective-C-extensions>
</code_scheme>
</component>
\ No newline at end of file
<?xml version="1.0" encoding="UTF-8"?>
<project version="4">
<component name="GradleSettings">
<option name="linkedExternalProjectsSettings">
<GradleProjectSettings>
<option name="distributionType" value="DEFAULT_WRAPPED" />
<option name="externalProjectPath" value="$PROJECT_DIR$" />
<option name="modules">
<set>
<option value="$PROJECT_DIR$" />
<option value="$PROJECT_DIR$/app" />
</set>
</option>
<option name="resolveModulePerSourceSet" value="false" />
</GradleProjectSettings>
</option>
</component>
</project>
\ No newline at end of file
<?xml version="1.0" encoding="UTF-8"?>
<project version="4">
<component name="NullableNotNullManager">
<option name="myDefaultNullable" value="android.support.annotation.Nullable" />
<option name="myDefaultNotNull" value="android.support.annotation.NonNull" />
<option name="myNullables">
<value>
<list size="5">
<item index="0" class="java.lang.String" itemvalue="org.jetbrains.annotations.Nullable" />
<item index="1" class="java.lang.String" itemvalue="javax.annotation.Nullable" />
<item index="2" class="java.lang.String" itemvalue="javax.annotation.CheckForNull" />
<item index="3" class="java.lang.String" itemvalue="edu.umd.cs.findbugs.annotations.Nullable" />
<item index="4" class="java.lang.String" itemvalue="android.support.annotation.Nullable" />
</list>
</value>
</option>
<option name="myNotNulls">
<value>
<list size="4">
<item index="0" class="java.lang.String" itemvalue="org.jetbrains.annotations.NotNull" />
<item index="1" class="java.lang.String" itemvalue="javax.annotation.Nonnull" />
<item index="2" class="java.lang.String" itemvalue="edu.umd.cs.findbugs.annotations.NonNull" />
<item index="3" class="java.lang.String" itemvalue="android.support.annotation.NonNull" />
</list>
</value>
</option>
</component>
<component name="ProjectRootManager" version="2" languageLevel="JDK_1_8" project-jdk-name="1.8" project-jdk-type="JavaSDK">
<output url="file://$PROJECT_DIR$/build/classes" />
</component>
<component name="ProjectType">
<option name="id" value="Android" />
</component>
</project>
\ No newline at end of file
<?xml version="1.0" encoding="UTF-8"?>
<project version="4">
<component name="RunConfigurationProducerService">
<option name="ignoredProducers">
<set>
<option value="org.jetbrains.plugins.gradle.execution.test.runner.AllInPackageGradleConfigurationProducer" />
<option value="org.jetbrains.plugins.gradle.execution.test.runner.TestClassGradleConfigurationProducer" />
<option value="org.jetbrains.plugins.gradle.execution.test.runner.TestMethodGradleConfigurationProducer" />
</set>
</option>
</component>
</project>
\ No newline at end of file
<?xml version="1.0" encoding="UTF-8"?>
<project version="4">
<component name="VcsDirectoryMappings">
<mapping directory="$PROJECT_DIR$" vcs="Git" />
</component>
</project>
\ No newline at end of file
apply plugin: 'com.android.application'
apply plugin: 'org.greenrobot.greendao'
android {
compileSdkVersion 28
defaultConfig {
applicationId "com.offcn.live.tsdm"
minSdkVersion 19
targetSdkVersion 28
versionCode 1
versionName "1.0"
testInstrumentationRunner "android.support.test.runner.AndroidJUnitRunner"
}
buildTypes {
release {
minifyEnabled false
proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
}
}
// GreenDao
/**
* schemaVersion--> 指定数据库schema版本号,迁移等操作会用到;
* daoPackage --> dao的包名,包名默认是entity所在的包;
* targetGenDir --> 生成数据库文件的目录;
*/
greendao {
schemaVersion 1
daoPackage 'com.offcn.live.tsdm.greendao.gen'
targetGenDir 'src/main/java'
}
compileOptions {
sourceCompatibility JavaVersion.VERSION_1_8
targetCompatibility JavaVersion.VERSION_1_8
}
}
dependencies {
implementation fileTree(dir: 'libs', include: ['*.jar'])
implementation 'com.android.support:appcompat-v7:28.0.0'
testImplementation 'junit:junit:4.12'
androidTestImplementation 'com.android.support.test:runner:1.0.2'
androidTestImplementation 'com.android.support.test.espresso:espresso-core:3.0.2'
compile("com.offcn.live:base:1.1.0-snapshot-1@aar") { transitive = true }
// Retrofit/Rx
compile 'com.google.code.gson:gson:2.8.1'
compile 'com.squareup.retrofit2:retrofit:2.3.0'
compile 'com.squareup.retrofit2:adapter-rxjava2:2.3.0'
compile 'com.squareup.retrofit2:converter-gson:2.3.0'
compile 'com.squareup.retrofit2:converter-scalars:2.3.0'
compile 'com.squareup.okhttp3:okhttp:3.11.0'
compile 'io.reactivex.rxjava2:rxjava:2.1.0'
compile 'io.reactivex.rxjava2:rxandroid:2.0.1'
compile 'com.squareup.okhttp3:logging-interceptor:3.11.0'
// 添加数据库
implementation 'org.greenrobot:greendao:3.2.2'
}
# Add project specific ProGuard rules here.
# You can control the set of applied configuration files using the
# proguardFiles setting in build.gradle.
#
# For more details, see
# http://developer.android.com/guide/developing/tools/proguard.html
# If your project uses WebView with JS, uncomment the following
# and specify the fully qualified class name to the JavaScript interface
# class:
#-keepclassmembers class fqcn.of.javascript.interface.for.webview {
# public *;
#}
# Uncomment this to preserve the line number information for
# debugging stack traces.
#-keepattributes SourceFile,LineNumberTable
# If you keep the line number information, uncomment this to
# hide the original source file name.
#-renamesourcefileattribute SourceFile
package tsdm.live.offcn.com.tsdownloadermanager;
import android.content.Context;
import android.support.test.InstrumentationRegistry;
import android.support.test.runner.AndroidJUnit4;
import org.junit.Test;
import org.junit.runner.RunWith;
import static org.junit.Assert.*;
/**
* Instrumented test, which will execute on an Android device.
*
* @see <a href="http://d.android.com/tools/testing">Testing documentation</a>
*/
@RunWith(AndroidJUnit4.class)
public class ExampleInstrumentedTest {
@Test
public void useAppContext() {
// Context of the app under test.
Context appContext = InstrumentationRegistry.getTargetContext();
assertEquals("tsdm.live.offcn.com.tsdownloadermanager", appContext.getPackageName());
}
}
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="tsdm.live.offcn.com.tsdownloadermanager">
<application
android:allowBackup="true"
android:icon="@mipmap/ic_launcher"
android:label="@string/app_name"
android:roundIcon="@mipmap/ic_launcher_round"
android:supportsRtl="true"
android:theme="@style/AppTheme" />
</manifest>
package com.maning.updatelibrary;
import android.app.Activity;
import android.content.Context;
import android.content.Intent;
import android.net.Uri;
import android.os.Build;
import android.provider.Settings;
import android.text.TextUtils;
import android.util.Log;
import com.maning.updatelibrary.http.AbsFileProgressCallback;
import com.maning.updatelibrary.http.DownloadFileUtils;
import com.maning.updatelibrary.utils.ActForResultCallback;
import com.maning.updatelibrary.utils.ActResultRequest;
import com.maning.updatelibrary.utils.MNUtils;
import java.io.File;
import static android.app.Activity.RESULT_OK;
/**
* Created by maning on 16/8/15.
* 安装APK的工具
*/
public class InstallUtils {
private static final String TAG = InstallUtils.class.getSimpleName();
private static InstallUtils mInstance;
private static Context mContext;
//------------------下载相关---------------------
private String httpUrl;
private String filePath;
private static DownloadCallBack mDownloadCallBack;
/**
* 是不是在正在下载
*/
private static boolean isDownloading = false;
/**
* 下载回调监听
*/
public interface DownloadCallBack {
void onStart();
void onComplete(String path);
void onLoading(long total, long current);
void onFail(Exception e);
void cancle();
}
/**
* 私有构造函数
*/
private InstallUtils() {
}
/**
* 是否正在下载
*/
public static boolean isDownloading() {
return isDownloading;
}
/**
* 设置监听
*
* @param downloadCallBack
*/
public static void setDownloadCallBack(DownloadCallBack downloadCallBack) {
//判断有没有开始
if (isDownloading) {
mDownloadCallBack = downloadCallBack;
}
}
/**
* 初始化对象
*
* @param context 上下文
* @return
*/
public static InstallUtils with(Context context) {
mContext = context.getApplicationContext();
if (mInstance == null) {
mInstance = new InstallUtils();
}
return mInstance;
}
/**
* 设置下载地址
*
* @param apkUrl
* @return
*/
public InstallUtils setApkUrl(String apkUrl) {
this.httpUrl = apkUrl;
return mInstance;
}
/**
* 设置下载后保存的地址,带后缀
*
* @param apkPath
* @return
*/
public InstallUtils setApkPath(String apkPath) {
this.filePath = apkPath;
return mInstance;
}
/**
* 设置回调监听
*
* @param downloadCallBack
* @return
*/
public InstallUtils setCallBack(DownloadCallBack downloadCallBack) {
mDownloadCallBack = downloadCallBack;
return mInstance;
}
/**
* 开始下载
*/
public void startDownload() {
//先取消之前的下载
if (isDownloading) {
cancleDownload();
}
//判断下载保存路径是不是空
if (TextUtils.isEmpty(filePath)) {
filePath = MNUtils.getCachePath(mContext) + "/update.apk";
}
//文件权限处理
MNUtils.changeApkFileMode(new File(filePath));
//下载
DownloadFileUtils.with()
.downloadPath(filePath)
.url(httpUrl)
.tag(InstallUtils.class)
.execute(new AbsFileProgressCallback() {
int currentProgress = 0;
@Override
public void onSuccess(String result) {
isDownloading = false;
if (mDownloadCallBack != null) {
mDownloadCallBack.onComplete(filePath);
}
}
@Override
public void onProgress(long bytesRead, long contentLength, boolean done) {
isDownloading = true;
if (mDownloadCallBack != null) {
//计算进度
int progress = (int) (bytesRead * 100 / contentLength);
//只有进度+1才回调,防止过快
if (progress - currentProgress >= 1) {
mDownloadCallBack.onLoading(contentLength, bytesRead);
}
currentProgress = progress;
}
}
@Override
public void onFailed(String errorMsg) {
isDownloading = false;
if (mDownloadCallBack != null) {
mDownloadCallBack.onFail(new Exception(errorMsg));
}
}
@Override
public void onStart() {
isDownloading = true;
if (mDownloadCallBack != null) {
mDownloadCallBack.onStart();
}
}
@Override
public void onCancle() {
isDownloading = false;
if (mDownloadCallBack != null) {
mDownloadCallBack.cancle();
}
}
});
}
public static void cancleDownload() {
DownloadFileUtils.cancle(InstallUtils.class);
}
//------------------安装相关---------------------
/**
* 安装回调监听
*/
public interface InstallCallBack {
void onSuccess();
void onFail(Exception e);
}
/**
* 安装APK工具类
*
* @param context 上下文
* @param filePath 文件路径
* @param callBack 安装界面成功调起的回调
*/
public static void installAPK(Activity context, String filePath, final InstallCallBack callBack) {
try {
MNUtils.changeApkFileMode(new File(filePath));
Intent intent = new Intent();
intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
intent.setAction(Intent.ACTION_VIEW);
File apkFile = new File(filePath);
Uri apkUri;
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) {
// 授予目录临时共享权限
intent.addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION);
String authority = context.getPackageName() + ".updateFileProvider";
apkUri = MNUpdateApkFileProvider.getUriForFile(context, authority, apkFile);
} else {
apkUri = Uri.fromFile(apkFile);
}
intent.setDataAndType(apkUri, "application/vnd.android.package-archive");
new ActResultRequest(context).startForResult(intent, new ActForResultCallback() {
@Override
public void onActivityResult(int resultCode, Intent data) {
Log.i(TAG, "onActivityResult:" + resultCode);
//调起了系统安装页面
if (callBack != null) {
callBack.onSuccess();
}
}
});
} catch (Exception e) {
if (callBack != null) {
callBack.onFail(e);
}
}
}
/**
* 通过浏览器下载APK更新安装
*
* @param context 上下文
* @param httpUrlApk APK下载地址
*/
public static void installAPKWithBrower(Context context, String httpUrlApk) {
Uri uri = Uri.parse(httpUrlApk);
Intent viewIntent = new Intent(Intent.ACTION_VIEW, uri);
context.startActivity(viewIntent);
}
/**
* 8.0权限检查回调监听
*/
public interface InstallPermissionCallBack {
void onGranted();
void onDenied();
}
/**
* 检查有没有安装权限
*
* @param activity
* @param installPermissionCallBack
*/
public static void checkInstallPermission(Activity activity, InstallPermissionCallBack installPermissionCallBack) {
if (hasInstallPermission(activity)) {
if (installPermissionCallBack != null) {
installPermissionCallBack.onGranted();
}
} else {
openInstallPermissionSetting(activity, installPermissionCallBack);
}
}
/**
* 判断有没有安装权限
*
* @param context
* @return
*/
public static boolean hasInstallPermission(Context context) {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
//先获取是否有安装未知来源应用的权限
return context.getPackageManager().canRequestPackageInstalls();
}
return true;
}
/**
* 去打开安装权限的页面
*
* @param activity
* @param installPermissionCallBack
*/
public static void openInstallPermissionSetting(Activity activity, final InstallPermissionCallBack installPermissionCallBack) {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
Uri packageURI = Uri.parse("package:" + activity.getPackageName());
Intent intent = new Intent(Settings.ACTION_MANAGE_UNKNOWN_APP_SOURCES, packageURI);
new ActResultRequest(activity).startForResult(intent, new ActForResultCallback() {
@Override
public void onActivityResult(int resultCode, Intent data) {
Log.i(TAG, "onActivityResult:" + resultCode);
if (resultCode == RESULT_OK) {
//用户授权了
if (installPermissionCallBack != null) {
installPermissionCallBack.onGranted();
}
} else {
//用户没有授权
if (installPermissionCallBack != null) {
installPermissionCallBack.onDenied();
}
}
}
});
} else {
//用户授权了
if (installPermissionCallBack != null) {
installPermissionCallBack.onGranted();
}
}
}
}
package com.maning.updatelibrary;
import android.support.v4.content.FileProvider;
/**
* <pre>
* author : maning
* e-mail : xxx@xx
* time : 2018/01/08
* desc : 更新专用FileProvider
* version: 1.0
* </pre>
*/
public class MNUpdateApkFileProvider extends FileProvider {
}
package com.maning.updatelibrary.http;
/**
* @author : maning
* @desc : 文件下载监听
*/
public abstract class AbsFileProgressCallback {
/**
* 下载成功
*/
public abstract void onSuccess(String result);
/**
*/
public abstract void onProgress(long bytesRead, long contentLength, boolean done);
/**
* 下载失败
*/
public abstract void onFailed(String errorMsg);
/**
* 下载开始
*/
public abstract void onStart();
/**
* 下载取消
*/
public abstract void onCancle();
}
package com.maning.updatelibrary.http;
import android.os.Handler;
import android.os.Looper;
import android.support.annotation.NonNull;
import android.util.Log;
import com.jyall.base.log.LogUtils;
import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.net.SocketTimeoutException;
import java.security.KeyManagementException;
import java.security.NoSuchAlgorithmException;
import java.security.SecureRandom;
import java.security.cert.X509Certificate;
import java.util.HashMap;
import java.util.Map;
import java.util.Set;
import java.util.concurrent.TimeUnit;
import javax.net.ssl.HostnameVerifier;
import javax.net.ssl.SSLContext;
import javax.net.ssl.SSLSession;
import javax.net.ssl.SSLSocketFactory;
import javax.net.ssl.TrustManager;
import javax.net.ssl.X509TrustManager;
import okhttp3.Call;
import okhttp3.Callback;
import okhttp3.Interceptor;
import okhttp3.OkHttpClient;
import okhttp3.Request;
import okhttp3.Response;
/**
* @author : maning
* @desc : 文件上传下载相关的工具
*/
public class DownloadFileUtils {
private static final String TAG = DownloadFileUtils.class.getSimpleName();
/**
* 请求的集合
*/
private static HashMap<Object, Call> mCallHashMap = new HashMap<>();
/**
* 当前实例
*/
private static DownloadFileUtils instance;
/**
* UI线程
*/
private static Handler mUIHandler = new Handler(Looper.getMainLooper());
/**
* 默认AbsFileProgressCallback
*/
private AbsFileProgressCallback defaultFileProgressCallback = new AbsFileProgressCallback() {
@Override
public void onSuccess(String result) {
}
@Override
public void onProgress(long bytesRead, long contentLength, boolean done) {
}
@Override
public void onFailed(String errorMsg) {
}
@Override
public void onStart() {
}
@Override
public void onCancle() {
}
};
/**
* 请求相关参数
*/
private DownloadModel downloadModel;
private DownloadFileUtils() {
downloadModel = new DownloadModel();
}
/**
* 回去当前实例
*
* @return
*/
public static DownloadFileUtils with() {
instance = new DownloadFileUtils();
return instance;
}
/**
* 设置请求Url
*
* @param url
* @return
*/
public DownloadFileUtils url(String url) {
downloadModel.setHttpUrl(url);
return instance;
}
/**
* 下载文件保存的路径
*
* @param filePath
* @return
*/
public DownloadFileUtils downloadPath(String filePath) {
downloadModel.setDownloadPath(filePath);
return instance;
}
/**
* 下载文件保存的路径
*
* @param tag
* @return
*/
public DownloadFileUtils tag(Object tag) {
downloadModel.setTag(tag);
return instance;
}
/**
* 设置请求头
*
* @param headersMap
* @return
*/
public DownloadFileUtils headers(Map<String, String> headersMap) {
downloadModel.setHeadersMap(headersMap);
return instance;
}
/**
* 设置单个请求头
*
* @param headerKey
* @param headerValue
* @return
*/
public DownloadFileUtils addHeader(String headerKey, String headerValue) {
downloadModel.getHeadersMap().put(headerKey, headerValue);
return instance;
}
/**
* 上传下载进度回调
*
* @param fileProgressCallback
*/
public void execute(AbsFileProgressCallback fileProgressCallback) {
if (fileProgressCallback == null) {
fileProgressCallback = defaultFileProgressCallback;
}
downloadModel.setFileProgressCallback(fileProgressCallback);
//开始请求
startDonwload();
}
private OkHttpClient.Builder okhttpBuilder;
private Request.Builder requestBuild;
private int retryCount = 0;
private void startDonwload() {
if (downloadModel == null) {
throw new NullPointerException("OkhttpRequestModel初始化失败");
}
//获取参数
//请求地址
String httpUrl = downloadModel.getHttpUrl();
//请求Tag
Object tag = downloadModel.getTag();
if (tag == null) {
tag = httpUrl;
}
//请求头
Map<String, String> headersMap = downloadModel.getHeadersMap();
//下载保存的路径
final String downloadPath = downloadModel.getDownloadPath();
//文件回调
final AbsFileProgressCallback fileProgressCallback = downloadModel.getFileProgressCallback();
//
// //获取OkHttpClient
// if (okhttpBuilder == null) {
//
// okhttpBuilder = getOkhttpDefaultBuilder();
// //初始化请求
// requestBuild = new Request.Builder();
// //添加请求地址
// requestBuild.url(httpUrl);
// //添加请求头
// if (headersMap != null && headersMap.size() > 0) {
// for (String key : headersMap.keySet()) {
// requestBuild.addHeader(key, headersMap.get(key));
// }
// }
// okhttpBuilder.addNetworkInterceptor(new Interceptor() {
// @Override
// public Response intercept(Chain chain) throws IOException {
// Response originalResponse = chain.proceed(chain.request());
// return originalResponse.newBuilder()
// .body(new ProgressResponseBody(originalResponse.body(), fileProgressCallback))
// .build();
// }
// });
//
// }
//初始化请求
Request.Builder requestBuild = new Request.Builder();
//添加请求地址
requestBuild.url(httpUrl);
//添加请求头
if (headersMap != null && headersMap.size() > 0) {
for (String key : headersMap.keySet()) {
requestBuild.addHeader(key, headersMap.get(key));
}
}
mUIHandler.post(new Runnable() {
@Override
public void run() {
fileProgressCallback.onStart();
}
});
Call call = OkHttpClientSingleton.getSingleton(httpUrl, headersMap, fileProgressCallback).newCall(requestBuild.get().build());
//添加请求到集合
mCallHashMap.put(tag, call);
call.enqueue(new Callback() {
@Override
public void onFailure(final Call call, final IOException e) {
Log.e(TAG, "onFailure:" + e.toString());
mUIHandler.postDelayed(new Runnable() {
@Override
public void run() {
if (call.isCanceled()) {
// 下载取消
fileProgressCallback.onCancle();
} else {
// 下载失败
// 超过5次再回调失败,否则重试
if (++retryCount >= 5) {
fileProgressCallback.onFailed(e.toString());
} else {
startDonwload();
}
}
}
}, 1000 * 5);
}
@Override
public void onResponse(Call call, Response response) throws IOException {
InputStream is = null;
byte[] buf = new byte[2048];
int len = 0;
FileOutputStream fos = null;
// 储存下载文件的目录
checkDownloadFilePath(downloadPath);
try {
is = response.body().byteStream();
long total = response.body().contentLength();
File file = new File(downloadPath);
fos = new FileOutputStream(file);
long sum = 0;
while ((len = is.read(buf)) != -1) {
fos.write(buf, 0, len);
sum += len;
}
fos.flush();
mUIHandler.post(new Runnable() {
@Override
public void run() {
// 下载完成
fileProgressCallback.onSuccess("");
// 重置重试次数
retryCount = 0;
}
});
} catch (final Exception e) {
mUIHandler.postDelayed(new Runnable() {
@Override
public void run() {
Log.e(TAG, "onFailure:" + e.getMessage());
if ("Socket closed".equals(e.getMessage())) {
// 下载失败
fileProgressCallback.onCancle();
} else {
// 下载失败
// 超过5次再回调失败,否则重试
if (++retryCount >= 5) {
fileProgressCallback.onFailed(e.toString());
} else {
startDonwload();
}
}
}
}, 1000 * 5);
} finally {
try {
if (is != null) {
is.close();
}
} catch (IOException e) {
}
try {
if (fos != null) {
fos.close();
}
} catch (IOException e) {
}
}
}
});
}
private static void checkDownloadFilePath(String localFilePath) {
File path = new File(localFilePath.substring(0,
localFilePath.lastIndexOf("/") + 1));
File file = new File(localFilePath);
if (!path.exists()) {
path.mkdirs();
}
if (!file.exists()) {
try {
file.createNewFile();
} catch (IOException e) {
e.printStackTrace();
}
}
}
/**
* 获取默认OkHttpClient.Builder
*
* @return
*/
@NonNull
public static OkHttpClient.Builder getOkhttpDefaultBuilder() {
//默认信任所有的证书
X509TrustManager trustManager = new X509TrustManager() {
@Override
public void checkClientTrusted(X509Certificate[] chain, String authType) {
}
@Override
public void checkServerTrusted(X509Certificate[] chain, String authType) {
}
@Override
public X509Certificate[] getAcceptedIssuers() {
X509Certificate[] x509Certificates = new X509Certificate[0];
return x509Certificates;
}
};
SSLContext sslContext = null;
try {
sslContext = SSLContext.getInstance("SSL");
sslContext.init(null, new TrustManager[]{trustManager}, new SecureRandom());
} catch (NoSuchAlgorithmException e) {
e.printStackTrace();
} catch (KeyManagementException e) {
e.printStackTrace();
}
SSLSocketFactory sslSocketFactory = sslContext.getSocketFactory();
HostnameVerifier DO_NOT_VERIFY = new HostnameVerifier() {
@Override
public boolean verify(String hostname, SSLSession session) {
return true;
}
};
OkHttpClient.Builder builder = new OkHttpClient.Builder();
builder.connectTimeout(30000, TimeUnit.MILLISECONDS);
builder.readTimeout(30000, TimeUnit.MILLISECONDS);
builder.writeTimeout(30000, TimeUnit.MILLISECONDS);
builder.sslSocketFactory(sslSocketFactory, trustManager);
builder.hostnameVerifier(DO_NOT_VERIFY);
return builder;
}
/**
* 取消一个请求
*
* @param tag
*/
public static void cancle(Object tag) {
LogUtils.e(TAG, "取消 === " + tag);
try {
if (mCallHashMap != null && mCallHashMap.size() > 0) {
if (mCallHashMap.containsKey(tag)) {
//获取对应的Call
Call call = mCallHashMap.get(tag);
if (call != null) {
//如果没有被取消 执行取消的方法
if (!call.isCanceled()) {
LogUtils.e(TAG, "取消 = 调用 cancel()");
call.cancel();
}
//移除对应的KEY
mCallHashMap.remove(tag);
}
}
}
} catch (Exception e) {
}
}
/**
* 取消所有请求
*/
public static void cancleAll() {
try {
if (mCallHashMap != null && mCallHashMap.size() > 0) {
//获取KEY的集合
Set<Map.Entry<Object, Call>> keyEntries = mCallHashMap.entrySet();
for (Map.Entry<Object, Call> entry : keyEntries) {
//key
Object key = entry.getKey();
//获取对应的Call
Call call = entry.getValue();
if (call != null) {
//如果没有被取消 执行取消的方法
if (!call.isCanceled()) {
call.cancel();
}
//移除对应的KEY
mCallHashMap.remove(key);
}
}
}
} catch (Exception e) {
}
}
static class OkHttpClientSingleton {
private static OkHttpClient.Builder sOkHttpClientBuilder;
private OkHttpClientSingleton() {
}
public static OkHttpClient getSingleton(String httpUrl, Map<String, String> headersMap, final AbsFileProgressCallback fileProgressCallback) {
if (sOkHttpClientBuilder == null) {
synchronized (OkHttpClientSingleton.class) {
if (sOkHttpClientBuilder == null) {
sOkHttpClientBuilder = getOkhttpDefaultBuilder();
sOkHttpClientBuilder.addNetworkInterceptor(new Interceptor() {
@Override
public Response intercept(Chain chain) throws IOException {
Response originalResponse = chain.proceed(chain.request());
return originalResponse.newBuilder()
.body(new ProgressResponseBody(originalResponse.body(), fileProgressCallback))
.build();
}
});
}
}
}
return sOkHttpClientBuilder.build();
}
}
}
package com.maning.updatelibrary.http;
import java.util.Map;
/**
* @author : maning
* @desc : 请求参数相关
*/
public class DownloadModel {
/**
* 请求地址
*/
private String httpUrl;
/**
* 请求头
*/
private Map<String, String> headersMap;
/**
* 请求Tag
*/
private Object tag;
/**
* 下载文件保存的路径
*/
private String downloadPath;
/**
* 文件下载进度
*/
private AbsFileProgressCallback fileProgressCallback;
public String getHttpUrl() {
return httpUrl;
}
public void setHttpUrl(String httpUrl) {
this.httpUrl = httpUrl;
}
public Map<String, String> getHeadersMap() {
return headersMap;
}
public void setHeadersMap(Map<String, String> headersMap) {
this.headersMap = headersMap;
}
public Object getTag() {
return tag;
}
public void setTag(Object tag) {
this.tag = tag;
}
public String getDownloadPath() {
return downloadPath;
}
public void setDownloadPath(String downloadPath) {
this.downloadPath = downloadPath;
}
public AbsFileProgressCallback getFileProgressCallback() {
return fileProgressCallback;
}
public void setFileProgressCallback(AbsFileProgressCallback fileProgressCallback) {
this.fileProgressCallback = fileProgressCallback;
}
}
package com.maning.updatelibrary.http;
import android.os.Handler;
import android.os.Looper;
import java.io.IOException;
import okhttp3.MediaType;
import okhttp3.ResponseBody;
import okio.Buffer;
import okio.BufferedSource;
import okio.ForwardingSource;
import okio.Okio;
import okio.Source;
/**
* @author : maning
* @desc : 下载监听所用
*/
public class ProgressResponseBody extends ResponseBody {
private final ResponseBody responseBody;
private final AbsFileProgressCallback progressListener;
private BufferedSource bufferedSource;
private Handler mUIHandler = new Handler(Looper.getMainLooper());
public ProgressResponseBody(ResponseBody mResponseBody, AbsFileProgressCallback mProgressListener) {
responseBody = mResponseBody;
progressListener = mProgressListener;
}
@Override
public MediaType contentType() {
return responseBody.contentType();
}
@Override
public long contentLength() {
return responseBody.contentLength();
}
@Override
public BufferedSource source() {
if (bufferedSource == null) {
bufferedSource = Okio.buffer(source(responseBody.source()));
}
return bufferedSource;
}
private Source source(Source source) {
return new ForwardingSource(source) {
long totalBytesRead = 0L;
@Override
public long read(Buffer sink, long byteCount) throws IOException {
long bytesRead = super.read(sink, byteCount);
// read() returns the number of bytes read, or -1 if this source is exhausted.
totalBytesRead += bytesRead != -1 ? bytesRead : 0;
final long finalBytesRead = bytesRead;
mUIHandler.post(new Runnable() {
@Override
public void run() {
progressListener.onProgress(totalBytesRead, responseBody.contentLength(), finalBytesRead == -1);
}
});
return bytesRead;
}
};
}
public interface ProgressListener {
void update(long bytesRead, long contentLength, boolean done);
}
}
package com.maning.updatelibrary.utils;
import android.content.Intent;
/**
* author : maning
* time : 2018/06/04
* desc :
* version: 1.0
*/
public interface ActForResultCallback {
void onActivityResult(int resultCode, Intent data);
}
package com.maning.updatelibrary.utils;
import android.app.Activity;
import android.app.FragmentManager;
import android.content.Intent;
/**
* author : maning
* time : 2018/06/04
* desc :
* version: 1.0
*/
public class ActResultRequest {
private OnActResultEventDispatcherFragment fragment;
public ActResultRequest(Activity activity) {
fragment = getEventDispatchFragment(activity);
}
private OnActResultEventDispatcherFragment getEventDispatchFragment(Activity activity) {
final FragmentManager fragmentManager = activity.getFragmentManager();
OnActResultEventDispatcherFragment fragment = findEventDispatchFragment(fragmentManager);
if (fragment == null) {
fragment = new OnActResultEventDispatcherFragment();
fragmentManager
.beginTransaction()
.add(fragment, OnActResultEventDispatcherFragment.TAG)
.commitAllowingStateLoss();
fragmentManager.executePendingTransactions();
}
return fragment;
}
private OnActResultEventDispatcherFragment findEventDispatchFragment(FragmentManager manager) {
return (OnActResultEventDispatcherFragment) manager.findFragmentByTag(OnActResultEventDispatcherFragment.TAG);
}
public void startForResult(Intent intent, ActForResultCallback callback) {
fragment.startForResult(intent, callback);
}
}
package com.maning.updatelibrary.utils;
import android.content.Context;
import android.os.Environment;
import java.io.File;
import java.io.IOException;
/**
* author : maning
* time : 2018/03/30
* desc : 工具类
* version: 1.0
*/
public class MNUtils {
/**
* 获取app缓存路径 SDCard/Android/data/你的应用的包名/cache
*
* @param context
* @return
*/
public static String getCachePath(Context context) {
String cachePath;
if (Environment.MEDIA_MOUNTED.equals(Environment.getExternalStorageState())
|| !Environment.isExternalStorageRemovable()) {
//外部存储可用
cachePath = context.getExternalCacheDir().getPath();
} else {
//外部存储不可用
cachePath = context.getCacheDir().getPath();
}
return cachePath;
}
//参照:APK放到data/data/下面提示解析失败 (http://blog.csdn.net/lonely_fireworks/article/details/27693073)
public static void changeApkFileMode(File file) {
try {
//apk放在缓存目录时,低版本安装提示权限错误,需要对父级目录和apk文件添加权限
String cmd1 = "chmod 777 " + file.getParent();
Runtime.getRuntime().exec(cmd1);
String cmd = "chmod 777 " + file.getAbsolutePath();
Runtime.getRuntime().exec(cmd);
} catch (IOException e) {
e.printStackTrace();
}
}
}
package com.maning.updatelibrary.utils;
import android.app.Fragment;
import android.content.Intent;
import android.os.Bundle;
import android.util.SparseArray;
/**
* author : maning
* time : 2018/06/04
* desc :
* version: 1.0
*/
public class OnActResultEventDispatcherFragment extends Fragment {
public static final String TAG = "on_act_result_event_dispatcher";
private SparseArray<ActForResultCallback> mCallbacks = new SparseArray<>();
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setRetainInstance(true);
}
public void startForResult(Intent intent, ActForResultCallback callback) {
mCallbacks.put(callback.hashCode(), callback);
startActivityForResult(intent, callback.hashCode());
}
@Override
public void onActivityResult(int requestCode, int resultCode, Intent data) {
super.onActivityResult(requestCode, resultCode, data);
ActForResultCallback callback = mCallbacks.get(requestCode);
mCallbacks.remove(requestCode);
if (callback != null) {
callback.onActivityResult(resultCode, data);
}
}
}
package com.offcn.live.tsdm;
import android.content.Context;
import android.database.sqlite.SQLiteDatabase;
import android.os.Environment;
import android.support.annotation.NonNull;
import android.text.TextUtils;
import com.jyall.base.log.LogUtils;
import com.jyall.base.util.CommonUtils;
import com.jyall.base.util.ThreadPoolUtils;
import com.jyall.base.util.ValidateUtils;
import com.maning.updatelibrary.http.AbsFileProgressCallback;
import com.maning.updatelibrary.http.DownloadFileUtils;
import com.offcn.live.tsdm.api.RetrofitManager;
import com.offcn.live.tsdm.api.network.ProgressSubscriber;
import com.offcn.live.tsdm.bean.ZGLDownLoaderBean;
import com.offcn.live.tsdm.bean.ZGLDownLoaderFileBean;
import com.offcn.live.tsdm.bean.ZGLDownloadStatusEnum;
import com.offcn.live.tsdm.bean.ZGLPlayBackVideoBean;
import com.offcn.live.tsdm.bean.ZGLPlaybackBean;
import com.offcn.live.tsdm.bean.ZGLSignInBean;
import com.offcn.live.tsdm.greendao.gen.DaoMaster;
import com.offcn.live.tsdm.greendao.gen.DaoSession;
import com.offcn.live.tsdm.greendao.gen.ZGLDownLoaderBeanDao;
import com.offcn.live.tsdm.greendao.gen.ZGLDownLoaderFileBeanDao;
import com.offcn.live.tsdm.util.ZGLUtils;
import java.io.BufferedReader;
import java.io.File;
import java.io.FileReader;
import java.io.IOException;
import java.security.InvalidParameterException;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import io.reactivex.android.schedulers.AndroidSchedulers;
import io.reactivex.schedulers.Schedulers;
import okhttp3.ResponseBody;
import retrofit2.Call;
import retrofit2.Callback;
import retrofit2.Response;
import tsdm.live.offcn.com.tsdownloadermanager.R;
public class ZGLDownLoaderManager {
private static final String TAG = ZGLDownLoaderManager.class.getSimpleName();
private static final String DOWNLOAD_PATH = Environment.getExternalStorageDirectory() + "/zgl/";
public static final String NAME_DB = "local.db";
public static final String NAME_M3U8 = "local.m3u8";
private static volatile ZGLDownLoaderManager instance;
private Context mContext;
// 来自外部的扩展参数,通过接口透传到服务器
private Map<String, String> mIntentExtensionsMap;
// 正在下载的队列
private List<String> mCurDownloadingList = new ArrayList<>();
// 暂停中队列
private List<String> mCurPausedList = new ArrayList<>();
// 等待中队列
private List<String> mCurWaitingList = new ArrayList<>();
// 准备中队列
private List<String> mCurPreparedList = new ArrayList<>();
// 集合存储code与对应信息:对应的文件列表集合,对应的文件总数
private Map<String, List<ZGLDownLoaderFileBean>> mDownloadInfoMap = new HashMap<>();
// 外部传递来的下载存储目录
private String mIntentDirPath = DOWNLOAD_PATH;
// 从外部传递来的hook参数, 自己的服务器用
private String mIntentHook;
// 获取当前code对应的目录
private String getDownloadPath(String code) {
return mIntentDirPath + code + "/";
}
// 获取当前code对应的本地m3u8文件地址
public String getDownloadFilePath(String code) {
return getDownloadPath(code) + NAME_M3U8;
}
// 获取当前code对应的本地m3u8文件地址
public String getDownloadDBPath(String code) {
return getDownloadPath(code) + NAME_DB;
}
// 最大同时下载数,默认为1个
private int mMaxDownloadNum = 1;
public ZGLDownLoaderManager maxNum(int maxDownloadNum) {
if (maxDownloadNum > 0) {
this.mMaxDownloadNum = maxDownloadNum;
}
return this;
}
private Context getActivity() {
return mContext;
}
private ZGLDownLoaderManager(Context context) {
this.mContext = context.getApplicationContext();
this.mIntentHook = "";
initDB();
}
public static ZGLDownLoaderManager getInstance(Context context) {
if (instance == null) {
synchronized (ZGLDownLoaderManager.class) {
if (instance == null) {
instance = new ZGLDownLoaderManager(context);
}
}
}
return instance;
}
// DB相关
private DaoMaster.DevOpenHelper mHelper;
private SQLiteDatabase db;
private DaoMaster mDaoMaster;
private DaoSession mDaoSession;
private ZGLDownLoaderBeanDao mBeanDao;
private ZGLDownLoaderFileBeanDao mFileBeanDao;
private void initDB() {
// 通过 DaoMaster 的内部类 DevOpenHelper,你可以得到一个便利的 SQLiteOpenHelper 对象。
// 可能你已经注意到了,你并不需要去编写「CREATE TABLE」这样的 SQL 语句,因为 greenDAO 已经帮你做了。
// 注意:默认的 DaoMaster.DevOpenHelper 会在数据库升级时,删除所有的表,意味着这将导致数据的丢失。
// 所以,在正式的项目中,你还应该做一层封装,来实现数据库的安全升级。
mHelper = new DaoMaster.DevOpenHelper(getActivity(), "zgl-video-db", null);
db = mHelper.getWritableDatabase();
// 注意:该数据库连接属于 DaoMaster,所以多个 Session 指的是相同的数据库连接。
mDaoMaster = new DaoMaster(db);
mDaoSession = mDaoMaster.newSession();
mBeanDao = mDaoSession.getZGLDownLoaderBeanDao();
mFileBeanDao = mDaoSession.getZGLDownLoaderFileBeanDao();
File file = new File(DOWNLOAD_PATH);
if (!file.exists()) {
file.mkdirs();
}
}
/**
* 扩展参数,用于第三方平台的验证
*
* @param map
* @return
*/
public ZGLDownLoaderManager extensions(Map<String, String> map) {
this.mIntentExtensionsMap = map;
return this;
}
/**
* hook回调
*
* @param hook
* @return
*/
public ZGLDownLoaderManager hook(String hook) {
this.mIntentHook = hook;
return this;
}
/**
* 存储目录
*
* @param downloadPath
*/
public ZGLDownLoaderManager downloadPath(String downloadPath) {
if (TextUtils.isEmpty(downloadPath)) {
downloadPath = DOWNLOAD_PATH;
}
File dirFile = new File(downloadPath);
// 若路径为文件,则抛异常
if (dirFile.isFile()) {
throw new InvalidParameterException("downloadPath must be dir NOT file");
}
if (dirFile.isDirectory() && !dirFile.exists()) {
// 若创建目录失败,则抛异常
if (!dirFile.mkdirs()) {
throw new InvalidParameterException("downloadPath check if valid");
}
}
// 若传递来的目录不以 / 结尾,则手动添加上
if (!downloadPath.endsWith("/")) {
downloadPath += "/";
}
mIntentDirPath = downloadPath;
return this;
}
/**
* 开启下载。不需要 额外id
*
* @param code
*/
public void start(@NonNull final String code) {
start(code, "");
}
/**
* 开始下载的对外暴露方法
*
* @param code 房间口令
* @param extensionId 外部的id,目前主要兼容在线课堂的特殊要求
*/
public void start(@NonNull final String code, final String extensionId) {
LogUtils.e(TAG, "调用 start 方法");
if (getActivity() == null) {
return;
}
if (TextUtils.isEmpty(code)) {
CommonUtils.showToast(getActivity(), "请设置房间口令");
return;
}
if (mOnTsProgressListener == null) {
CommonUtils.showToast(getActivity(), "请设置回调监听");
return;
}
checkMaxNum(code, extensionId);
// 如果正在下载集合中存在此code,则停止
if (mCurDownloadingList.contains(code) || mCurDownloadingList.contains(extensionId)) {
return;
}
if (!CommonUtils.isNetConnected(getActivity())) {
CommonUtils.showToast(getActivity(), R.string.net_off);
return;
}
// 开启内部下载方法
startInner(code, extensionId);
}
/**
* 开始下载的内部逻辑
*
* @param roomCode
*/
private void startInner(@NonNull final String roomCode, final String extensionId) {
LogUtils.e(TAG, "调用 startInner 方法");
// 回调方法:准备中-接口获取文件和写入
if (mOnTsProgressListener != null) {
mOnTsProgressListener.onPrepared(roomCode, extensionId);
}
// 检查当前下载是否超过最大
checkMaxNum(roomCode, extensionId);
// 若DB中存在此code,则直接开始下载
List<ZGLDownLoaderBean> tmpList = mBeanDao.loadAll();
if (tmpList != null && tmpList.size() != 0) {
for (ZGLDownLoaderBean bean : tmpList) {
if (roomCode.equals(bean.getCode())) {
// 存在此code
if (bean.isDownloaded()) {
LogUtils.e(TAG, "DB存储为下载完成");
// 已下载完毕
if (bean.isLocalM3u8Exist()) {
LogUtils.e(TAG, "SD卡存在此口令文件");
// 存在对应文件 local.m3u8
if (mOnTsProgressListener != null) {
mOnTsProgressListener.onSuccess(roomCode, extensionId);
}
} else {
LogUtils.e(TAG, "SD卡不存在此口令文件");
// 若文件不存在 local.m3u8
delete(roomCode, null);
delete(extensionId, null);
start(roomCode, extensionId);
}
} else {
LogUtils.e(TAG, "DB存储为未下载完成");
resumeTask(roomCode, extensionId);
}
return;
}
}
}
// 先判断有没有回放视频资源,有再去下载,没有则提示
RetrofitManager.getInstance(getActivity())
.getPlaybackExist(roomCode)
.subscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread())
.subscribe(new ProgressSubscriber<ZGLSignInBean>(getActivity()) {
@Override
public void onResponse(ZGLSignInBean result) {
if (!result.active) {
// 没有回放视频,则直接报错提示
LogUtils.e(TAG, "没有可下载资源 " + roomCode);
if (null != mOnTsProgressListener) {
mOnTsProgressListener.onFailed(roomCode, extensionId, "当前没有可下载资源");
}
} else {
// 有回放视频资源,则开启下载
Map<String, String> paramsMap = new HashMap<>();
paramsMap.put("account", roomCode);
if (!ValidateUtils.isEmpty(mIntentHook)) {
paramsMap.put("hook", mIntentHook);
}
if (mIntentExtensionsMap != null && mIntentExtensionsMap.size() > 0) {
paramsMap.putAll(mIntentExtensionsMap);
}
RetrofitManager.getInstance(getActivity())
.getPlaybackInfo(paramsMap)
.subscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread())
.subscribe(new ProgressSubscriber<ZGLPlaybackBean>(getActivity()) {
@Override
public void onResponse(final ZGLPlaybackBean result) {
final String url = result.getServerUrl();
// final String url = "http://service-xiaoyu.oss-cn-beijing.aliyuncs.com/vod/8916738ed64ff4bd1159e41ba5e39aec/index.m3u8";
// local.m3u8
Call<ResponseBody> callLocal = RetrofitManager.getInstance(getActivity())
.getPlaybackInfoFile(result.getLocalUrl());
callLocal.enqueue(new Callback<ResponseBody>() {
@Override
public void onResponse(Call<ResponseBody> call, Response<ResponseBody> response) {
String dirPath = getDownloadPath(roomCode);
File dirFile = new File(dirPath);
if (!dirFile.exists()) {
dirFile.mkdirs();
}
// 分别声明ts带与不带http的文件地址
String fileLocalPath = dirPath + NAME_M3U8;
File localFile = new File(fileLocalPath);
if (!localFile.exists()) {
try {
localFile.createNewFile();
} catch (IOException e) {
e.printStackTrace();
}
}
// 保存ts地址带与不带http的文件,只有两个都保存成功,才能下载
if (ZGLUtils.writeResponseBodyToDisk(localFile, response.body())) {
// 文件保存成功
// 读取文件内容,进行下载
} else {
// 文件保存失败
if (mOnTsProgressListener != null) {
mOnTsProgressListener.onFailed(roomCode, extensionId, "文件保存失败");
}
}
}
@Override
public void onFailure(Call<ResponseBody> call, Throwable t) {
// 接口请求失败
if (mOnTsProgressListener != null) {
mOnTsProgressListener.onFailed(roomCode, extensionId, "接口请求失败");
}
}
});
// index.m3u8
Call<ResponseBody> call = RetrofitManager.getInstance(getActivity())
.getPlaybackInfoFile(url);
call.enqueue(new Callback<ResponseBody>() {
@Override
public void onResponse(Call<ResponseBody> call, Response<ResponseBody> response) {
String dirPath = getDownloadPath(roomCode);
File dirFile = new File(dirPath);
if (!dirFile.exists()) {
dirFile.mkdirs();
}
// 分别声明ts带与不带http的文件地址
String fileServerPath = dirPath + "index.m3u8";
File serverFile = new File(fileServerPath);
if (!serverFile.exists()) {
try {
serverFile.createNewFile();
} catch (IOException e) {
e.printStackTrace();
}
}
// 保存ts地址带与不带http的文件,只有两个都保存成功,才能下载
if (ZGLUtils.writeResponseBodyToDisk(serverFile, response.body())) {
// 文件保存成功
// 读取文件内容,进行下载
addNewVideo(result.room_name, extensionId, processBody(roomCode, url, serverFile));
} else {
// 文件保存失败
if (mOnTsProgressListener != null) {
mOnTsProgressListener.onFailed(roomCode, extensionId, "文件保存失败");
}
}
}
@Override
public void onFailure(Call<ResponseBody> call, Throwable t) {
// 接口请求失败
if (mOnTsProgressListener != null) {
mOnTsProgressListener.onFailed(roomCode, extensionId, "接口请求失败");
}
}
});
// sqlite.db
Call<ResponseBody> callSqlite = RetrofitManager.getInstance(getActivity())
.getPlaybackInfoFile(result.getSqliteUrl());
callSqlite.enqueue(new Callback<ResponseBody>() {
@Override
public void onResponse(Call<ResponseBody> call, Response<ResponseBody> response) {
String dirPath = getDownloadPath(roomCode);
File dirFile = new File(dirPath);
if (!dirFile.exists()) {
dirFile.mkdirs();
}
// 声明DB存储路径
String fileSqlitePath = dirPath + NAME_DB;
File sqliteFile = new File(fileSqlitePath);
if (!sqliteFile.exists()) {
try {
sqliteFile.createNewFile();
} catch (IOException e) {
e.printStackTrace();
}
}
// 保存DB文件
if (ZGLUtils.writeResponseBodyToDisk(sqliteFile, response.body())) {
// 文件保存成功
} else {
// 文件保存失败
// mPreparedQueue.remove(roomCode);
// mPreparedQueue.remove(extensionId);
// if (mOnTsProgressListener != null) {
// mOnTsProgressListener.onFailed(roomCode, extensionId, "文件保存失败");
// }
}
}
@Override
public void onFailure(Call<ResponseBody> call, Throwable t) {
// 接口请求失败
// mPreparedQueue.remove(roomCode);
// mPreparedQueue.remove(extensionId);
// if (mOnTsProgressListener != null) {
// mOnTsProgressListener.onFailed(roomCode, extensionId, "接口请求失败");
// }
}
});
}
@Override
public boolean onError(int code, String error) {
// 接口请求失败
if (mOnTsProgressListener != null) {
mOnTsProgressListener.onFailed(roomCode, extensionId, "接口请求失败");
}
return true;
}
});
}
}
@Override
public boolean onError(int code, String error) {
if (null != mOnTsProgressListener) {
mOnTsProgressListener.onFailed(roomCode, extensionId, error);
}
return true;
}
});
}
private void checkMaxNum(@NonNull String roomCode, String extensionId) {
mCurPausedList.remove(roomCode);
mCurPausedList.remove(extensionId);
// 若发现当前下载数量超过最大下载数量,则暂停正在下载的
if (mCurDownloadingList != null && mCurDownloadingList.size() >= mMaxDownloadNum) {
if (mCurDownloadingList.contains(roomCode) || mCurDownloadingList.contains(extensionId)) {
} else {
try {
// 停止第一个,将现在的添加到最后
pause(mCurDownloadingList.get(0));
} catch (Exception e) {
e.printStackTrace();
} finally {
}
}
}
}
/**
* 解析服务器文件内容
*/
private ZGLPlayBackVideoBean processBody(String code, String url, File file) {
ZGLPlayBackVideoBean newBean = new ZGLPlayBackVideoBean();
newBean.code = code;
// String destUrl = url.substring(0, url.lastIndexOf("/"));
List<String> tmpUrls = new ArrayList<>();
try {
BufferedReader bufferedReader = new BufferedReader(new FileReader(file));
String line = null;
while ((line = bufferedReader.readLine()) != null) {
if (!ValidateUtils.isEmpty(line) && line.contains(".ts")) {
// tmpUrls.add(destUrl + "/" + line);
// 不用再手动拼接下载地址
tmpUrls.add(line);
}
}
} catch (Exception e) {
e.printStackTrace();
}
newBean.urls = tmpUrls;
return newBean;
}
/**
* 添加新视频的下载
*
* @param videoBean
*/
private synchronized void addNewVideo(String roomName, final String extensionId, final ZGLPlayBackVideoBean videoBean) {
// 如果已暂停该任务,则不再继续
final boolean isPaused = mCurPausedList.contains(videoBean.code)
|| mCurPausedList.contains(extensionId);
if (isPaused) {
mCurPausedList.remove(videoBean.code);
mCurPausedList.remove(extensionId);
return;
}
ZGLDownLoaderBean downLoaderBean = new ZGLDownLoaderBean();
downLoaderBean.setCode(videoBean.code);
downLoaderBean.setExtensionId(extensionId);
downLoaderBean.setRoomName(roomName);
downLoaderBean.setDownloadPath(getDownloadPath(videoBean.code));
mBeanDao.insertOrReplace(downLoaderBean);
ThreadPoolUtils.execute(new Runnable() {
@Override
public void run() {
for (int i = 0; i < videoBean.urls.size(); i++) {
ZGLDownLoaderFileBean downLoaderFileBean = new ZGLDownLoaderFileBean();
downLoaderFileBean.setCode(videoBean.code);
downLoaderFileBean.setExtensionId(extensionId);
downLoaderFileBean.setDownloadUrl(videoBean.urls.get(i));
mFileBeanDao.insertOrReplace(downLoaderFileBean);
}
if (mDownloadInfoMap == null) {
mDownloadInfoMap = new HashMap<>();
}
// 开启下载
List<ZGLDownLoaderFileBean> tmpList = mFileBeanDao.queryRaw("where CODE=?", videoBean.code);
if (!(mCurPausedList.contains(videoBean.code)
|| mCurPausedList.contains(extensionId))) {
addTask(videoBean.code, extensionId, tmpList.get(0));
}
// 将多个code与对应信息存储到集合中
mDownloadInfoMap.put(videoBean.code, tmpList);
}
});
}
private synchronized void addTask(@NonNull final String code, final String extensionId,
final ZGLDownLoaderFileBean fileBean) {
// 从暂停队列中清除
mCurPausedList.remove(code);
mCurPausedList.remove(extensionId);
// 若发现当前下载数量超过最大下载数量,则暂停正在下载的,并开启新的下载
checkMaxNum(code, extensionId);
// 添加到下载队列
if (TextUtils.isEmpty(extensionId)) {
if (!mCurDownloadingList.contains(code))
mCurDownloadingList.add(code);
} else {
if (!mCurDownloadingList.contains(extensionId))
mCurDownloadingList.add(extensionId);
}
// 更新DB状态为:下载中
List<ZGLDownLoaderBean> totalList = mBeanDao.queryRaw("where code=? or EXTENSION_ID=?", code, code);
if (!ValidateUtils.isEmpty(totalList)) {
ZGLDownLoaderBean downLoaderBean = totalList.get(0);
downLoaderBean.setStatus(2);
mBeanDao.update(downLoaderBean);
}
String destPath = getDownloadPath(code) + fileBean.getName();
File destFile = new File(destPath);
if (!destFile.exists()) {
try {
destFile.createNewFile();
} catch (IOException e) {
e.printStackTrace();
}
}
DownloadFileUtils.with()
.downloadPath(destPath)
.url(fileBean.getDownloadUrl())
.tag(fileBean.getDownloadUrl())
.execute(new AbsFileProgressCallback() {
@Override
public void onSuccess(String s) {
LogUtils.e(TAG, "下载成功 " + s);
fileBean.setStatus(1);
mFileBeanDao.update(fileBean);
// 判断当前下载完的是否为最后一条。是 则下载完毕,否 则继续下载下一条
// 是否为最后一条
boolean isLastOne = false;
int curPosition = mDownloadInfoMap.get(fileBean.getCode()).indexOf(fileBean);
isLastOne = curPosition == mDownloadInfoMap.get(fileBean.getCode()).size() - 1;
LogUtils.e(TAG, "totalCount " + mDownloadInfoMap.get(fileBean.getCode()).size() + ", curPosition " + curPosition);
if (isLastOne) {
// 全部下载完毕
LogUtils.e(TAG, fileBean.getCode() + " 全部下载完成");
// 更新正在下载的个数
if (mCurDownloadingList != null && mCurDownloadingList.size() > 0) {
mCurDownloadingList.remove(0);
}
if (mOnTsProgressListener != null) {
mOnTsProgressListener.onSuccess(fileBean.getCode(), extensionId);
}
// 更新DB状态为:已下载完成
List<ZGLDownLoaderBean> queryRaw = mBeanDao.queryRaw("where code=?", fileBean.getCode());
if (queryRaw != null && queryRaw.size() != 0) {
ZGLDownLoaderBean downLoaderBean = queryRaw.get(0);
downLoaderBean.setStatus(1);
downLoaderBean.setProgress("100");
mBeanDao.update(downLoaderBean);
}
} else {
// 继续下载下一个
// 保存当前进度
List<ZGLDownLoaderBean> queryRaw = mBeanDao.queryRaw("where code=?", fileBean.getCode());
String progress = String.format("%1.2f", (((float) curPosition) / mDownloadInfoMap.get(fileBean.getCode()).size()) * 100);
if (queryRaw != null && queryRaw.size() != 0) {
ZGLDownLoaderBean downLoaderBean = queryRaw.get(0);
if (!TextUtils.isEmpty(progress)
&& !TextUtils.isEmpty(downLoaderBean.getProgress())
&& Float.parseFloat(progress) < Float.parseFloat(downLoaderBean.getProgress())) {
progress = downLoaderBean.getProgress();
}
progress = String.valueOf(Math.abs(Float.parseFloat(progress)));
downLoaderBean.setProgress(progress);
mBeanDao.update(downLoaderBean);
LogUtils.e(TAG, "下载进度 " + progress);
}
// 如果指定房间已经暂停,则不再进行下一个的下载,不再回调进度
if (mCurPausedList.contains(fileBean.getCode()) || mCurPausedList.contains(extensionId)) {
return;
}
// 开启下一个的下载
List<ZGLDownLoaderFileBean> tmpList = mFileBeanDao.queryRaw("where code=?", fileBean.getCode());
if (tmpList != null && tmpList.size() > 0 && curPosition < tmpList.size() - 1) {
ZGLDownLoaderFileBean nextBean = tmpList.get(++curPosition);
addTask(fileBean.getCode(), extensionId, nextBean);
}
// 回调进度
if (mOnTsProgressListener != null) {
mOnTsProgressListener.onProgress(fileBean.getCode(), extensionId, String.valueOf(Math.abs(Float.parseFloat(progress))));
}
}
}
@Override
public void onProgress(long l, long l1, boolean b) {
LogUtils.e(TAG, "下载进度 " + l + "," + l1);
}
@Override
public void onFailed(String s) {
LogUtils.e(TAG, "下载失败 " + s);
pause(TextUtils.isEmpty(extensionId) ? code : extensionId);
if (mOnTsProgressListener != null) {
mOnTsProgressListener.onFailed(fileBean.getCode(), extensionId, "下载失败");
}
}
@Override
public void onStart() {
LogUtils.e(TAG, "下载开始 ");
if (mOnTsProgressListener != null) {
mOnTsProgressListener.onStart(fileBean.getCode(), extensionId);
}
}
@Override
public void onCancle() {
LogUtils.e(TAG, "下载取消 ");
pause(TextUtils.isEmpty(extensionId) ? code : extensionId);
}
});
}
/**
* 暂停下载
*
* @param code 房间口令
*/
public void pause(@NonNull String code) {
// 根据房间口令查询到当前正在下载的url ,并取消掉此请求
if (TextUtils.isEmpty(code)) {
return;
}
// 添加到已暂停队列
if (!mCurPausedList.contains(code)) {
mCurPausedList.add(code);
}
// 从下载队列中删除
mCurDownloadingList.remove(code);
if (mOnTsProgressListener != null) {
mOnTsProgressListener.onPaused(code, code);
}
List<ZGLDownLoaderFileBean> totalFileList = mFileBeanDao.queryRaw("where code=? or EXTENSION_ID=?", code, code);
List<ZGLDownLoaderBean> totalList = mBeanDao.queryRaw("where code=? or EXTENSION_ID=?", code, code);
if (!ValidateUtils.isEmpty(totalList)) {
ZGLDownLoaderBean downLoaderBean = totalList.get(0);
if (!downLoaderBean.isDownloaded()) {
downLoaderBean.setStatus(3);
}
mBeanDao.update(downLoaderBean);
}
if (!ValidateUtils.isEmpty(totalFileList)) {
int curPos = 0;
for (int i = 0; i < totalList.size(); i++) {
if (totalFileList.get(i).getStatus() == 0) {
// 第一个未下载的即是当前正在下载的
curPos = i;
break;
}
}
LogUtils.e(TAG, "暂停 ============= " + curPos);
DownloadFileUtils.cancle(totalFileList.get(curPos).getDownloadUrl());
}
}
/**
* 继续下载
*
* @param code 房间口令
*/
private void resumeTask(@NonNull String code, String extensionId) {
// 根据房间口令查询到当前未下载的第一条开始下载
List<ZGLDownLoaderFileBean> totalList = mFileBeanDao.queryRaw("where code=? or EXTENSION_ID=?", code, code);
if (ValidateUtils.isEmpty(totalList)) {
// 未插入到DB中,从新开始
// 清除掉主表数据,防止主表中有数据而子表中没数据造成死循环
mDaoSession.getDatabase().execSQL("delete from ZGLDOWN_LOADER_BEAN where CODE=? or EXTENSION_ID=?", new String[]{"code", "code"});
mDaoSession.clear();
start(code);
} else {
mDownloadInfoMap.put(code, totalList);
int curPos = -1;
for (int i = 0; i < totalList.size(); i++) {
if (totalList.get(i).getStatus() == 0) {
// 第一个未下载的即是当前需要继续下载的
curPos = i;
break;
}
}
if (curPos == -1) {
LogUtils.e(TAG, "resumeTask " + code + " 已下载完成");
if (mOnTsProgressListener != null) {
mOnTsProgressListener.onSuccess(code, extensionId);
}
} else {
addTask(code, extensionId, totalList.get(curPos));
}
}
}
/**
* 删除全部
*/
public void deleteAll(final OnZGLDeleteListener onZGLDeleteListener) {
mBeanDao.deleteAll();
mFileBeanDao.deleteAll();
if (onZGLDeleteListener != null) {
onZGLDeleteListener.onDelete("", true);
}
final File dirFile = new File(DOWNLOAD_PATH);
if (dirFile.exists()) {
ThreadPoolUtils.execute(new Runnable() {
@Override
public void run() {
final boolean isDelete = deleteDir(dirFile);
LogUtils.e(TAG, "删除SD卡全部内容成功: ");
}
});
}
}
/**
* 删除指定房间的视频文件和DB信息
*
* @param code
*/
public void delete(@NonNull final String code,
final OnZGLDeleteListener onZGLDeleteListener) {
if (TextUtils.isEmpty(code)) {
return;
}
// 先暂停下载,再作删除操作
pause(code);
// 删除表数据。因数据量大,采用原生SQL语句
try {
mDaoSession.getDatabase().execSQL("delete from ZGLDOWN_LOADER_BEAN where CODE=? or EXTENSION_ID=?", new String[]{"code", "code"});
mDaoSession.getDatabase().execSQL("delete from ZGLDOWN_LOADER_FILE_BEAN where CODE=? or EXTENSION_ID=?", new String[]{"code", "code"});
} catch (Exception e) {
e.printStackTrace();
}
// 删除完DB数据,直接回调。避免SD卡内容过多,回调过慢
if (onZGLDeleteListener != null) {
onZGLDeleteListener.onDelete(code, true);
}
// 删除SD卡内容
final List<ZGLDownLoaderBean> totalList = mBeanDao.queryRaw("where code=? or EXTENSION_ID=?", code, code);
if (totalList != null && totalList.size() > 0) {
for (ZGLDownLoaderBean bean : totalList) {
mBeanDao.delete(bean);
}
}
try {
final File dirFile = new File(totalList.get(0).getDownloadPath());
if (dirFile.exists()) {
ThreadPoolUtils.execute(new Runnable() {
@Override
public void run() {
final boolean isDelete = deleteDir(dirFile);
LogUtils.e(TAG, "删除SD卡内容成功: " + code);
}
});
}
} catch (Exception e) {
e.printStackTrace();
}
// 清除DB缓存。否则 mBeanDao mFileBeanDao 查询都会有缓存
mDaoSession.clear();
}
private boolean deleteDir(File dir) {
if (dir.isDirectory()) {
String[] children = dir.list();
//递归删除目录中的子目录下
for (int i = 0; i < children.length; i++) {
boolean success = deleteDir(new File(dir, children[i]));
if (!success) {
return false;
}
}
}
// 目录此时为空,可以删除
return dir.delete();
}
/**
* 获取当前下载进度
*
* @param code
* @return
*/
public String getProgress(@NonNull String code) {
String progress = "0";
List<ZGLDownLoaderBean> totalList = mBeanDao.queryRaw("where code=? or EXTENSION_ID=?", code, code);
if (totalList != null && totalList.size() > 0) {
ZGLDownLoaderBean bean = totalList.get(0);
progress = bean.getProgress();
}
return progress;
}
/**
* 获取全部的下载信息
*
* @return
*/
public List<ZGLDownLoaderBean> getAll() {
List<ZGLDownLoaderBean> totalList = mBeanDao.loadAll();
return totalList;
}
/**
* 根据code获取对应视频下载状态
*
* @param code
* @return
*/
public ZGLDownloadStatusEnum getStatusEnum(@NonNull String code) {
ZGLDownloadStatusEnum statusEnum = ZGLDownloadStatusEnum.NOT;
List<ZGLDownLoaderBean> totalList = mBeanDao.queryRaw("where code=? or EXTENSION_ID=?", code, code);
if (ValidateUtils.isEmpty(totalList)) {
// DB中不存在此code,说明未下载过
statusEnum = ZGLDownloadStatusEnum.NOT;
} else {
ZGLDownLoaderBean tmpBean = totalList.get(0);
statusEnum = tmpBean.getStatusEnum();
if (statusEnum == ZGLDownloadStatusEnum.DOWNLOADED) {
if (!tmpBean.isLocalM3u8Exist()) {
// 此时没有loca.m3u8文件了,则清除DB数据
delete(code, null);
statusEnum = ZGLDownloadStatusEnum.NOT;
}
}
}
return statusEnum;
}
/**
* 获取对应视频大小
*
* @param code
* @return
*/
public long getSize(@NonNull String code) {
return Long.parseLong(getSize(code, false));
}
/**
* 获取对应视频大小
*
* @param code
* @param format 是否要求格式化,转成 KB MB GB
* @return
*/
public String getSize(@NonNull String code, boolean format) {
String formatSize = "";
List<ZGLDownLoaderBean> totalList = mBeanDao.queryRaw("where code=? or EXTENSION_ID=?", code, code);
if (!ValidateUtils.isEmpty(totalList)) {
ZGLDownLoaderBean tmpBean = totalList.get(0);
File file = new File(tmpBean.getDownloadPath());
if (file.exists()) {
if (!format) {
return String.valueOf(ZGLUtils.getFolderSize(file));
} else {
formatSize = ZGLUtils.getFormattedSize(ZGLUtils.getFolderSize(file));
}
}
}
return formatSize;
}
/**
* 是否已下载成功
*
* @param code
* @return
*/
public boolean isDownloaded(@NonNull String code) {
return ZGLDownloadStatusEnum.DOWNLOADED == getStatusEnum(code);
}
/**
* 是否在下载中
*
* @param code
* @return
*/
public boolean isDownloading(@NonNull String code) {
return ZGLDownloadStatusEnum.DOWNLOADING == getStatusEnum(code);
}
/**
* 是否在暂停中
*
* @param code
* @return
*/
public boolean isPaused(@NonNull String code) {
return ZGLDownloadStatusEnum.PAUSED == getStatusEnum(code);
}
/**
* 是否未下载过
*
* @param code
* @return
*/
public boolean isDownloadNot(String code) {
return ZGLDownloadStatusEnum.NOT == getStatusEnum(code);
}
/**
* 将所有下载中的状态重置为已暂停
*/
public void resetStatusOfDownloading2Paused() {
List<ZGLDownLoaderBean> list = mBeanDao.loadAll();
if (list != null && list.size() > 0) {
for (ZGLDownLoaderBean loaderBean : list) {
if (loaderBean.isDownloading()) {
loaderBean.setStatus(3);
mBeanDao.update(loaderBean);
}
}
}
}
/**
* 获取指定房间code的信息
*
* @param code
* @return
*/
public ZGLDownLoaderBean getBean(@NonNull String code) {
List<ZGLDownLoaderBean> tmpList = mBeanDao.queryRaw("where CODE=? or EXTENSION_ID=?", code, code);
if (tmpList != null && tmpList.size() != 0) {
return tmpList.get(0);
}
return null;
}
// 下载进度及状态回调监听
public interface OnZGLProgressListener {
void onPrepared(String code, String extensionId);
void onStart(String code, String extensionId);
void onProgress(String code, String extensionId, String percent);
void onPaused(String code, String extensionId);
void onSuccess(String code, String extensionId);
void onFailed(String code, String extensionId, String message);
}
public ZGLDownLoaderManager listener(OnZGLProgressListener onTsProgressListener) {
mOnTsProgressListener = onTsProgressListener;
return this;
}
private OnZGLProgressListener mOnTsProgressListener;
// 删除监听
public interface OnZGLDeleteListener {
void onDelete(String code, boolean hasDelete);
}
public void setOnZGLDeleteListener(OnZGLDeleteListener onZGLDeleteListener) {
mOnZGLDeleteListener = onZGLDeleteListener;
}
private OnZGLDeleteListener mOnZGLDeleteListener;
}
package com.offcn.live.tsdm.api;
/**
* 接口地址
*
* @author wangwenlong
* @date 2018/7/26
*/
class APIConstants {
/**
* 正式地址
*/
private static final String HOST = "https://api.live.offcncloud.com/";
// private static final String HOST = "https://live.offcncloud.com/api/";
/**
* 协议版本号
*/
private static final String VERSION = "v1/";
/**
* 请求地址
*/
public static final String BASE = HOST;
/**
* 用户相关接口
*/
interface LiveUser {
// 登录
String userLogin = VERSION + "users";
}
/**
* 播放相关接口
*/
interface LiveVideo {
// 获取播放地址
String videoUrl = VERSION + "pull";
// 获取是否有在推流
String stream = VERSION + "room_stream";
// 获取播放信息
String roomInfo = VERSION + "room_info";
// 获取单个用户信息
String userInfo = VERSION + "users/{uuid}";
// 获取老师信息
String teacherInfo = VERSION + "get_teacher_info";
// 获取文件列表
String files = VERSION + "room_files";
// 获取提问列表
String questions = VERSION + "questions";
// 获取公告列表
String announce = VERSION + "announce?title=te";
// 发言间隔时间
String bannedTime = VERSION + "banned_time";
// 房间状态
String roomState = VERSION + "room_status";
// 运营信息
String operationInfo = VERSION + "operation_info";
// 意见反馈
String suggest = VERSION + "suggest";
// 意见反馈
String suggest_type = VERSION + "suggest_type";
// 配置:服务器地址
String config = VERSION + "get_config";
// 回放:回放信息
String playback = "user/on_demand";
// 答题弹窗:是否答过此题
String answerOrNot = VERSION + "answer/{qNo}";
// 答题弹窗:获取当前房间正在进行的答题
String answerCur = VERSION + "topic";
// 评价:提交
String evaluatePost = VERSION + "appraise_add";
// 评价:是否评价过
String evaluateOrNot = VERSION + "appraise_check";
// 评价:是否放弃过
String evaluateGiveUp = VERSION + "appraise_giveup";
// 评价:点击评价按钮
String evaluateClick = VERSION + "appraise_click";
// 签到:是否签到过
String signInCheck = VERSION + "sign_check";
// 签到:上报签到
String signIn = VERSION + "sign";
// 签到:当前房间是否正在签到
String signInNow = VERSION + "sign_in";
// 抽奖:当前房间是否正在抽奖
String lotteryNow = VERSION + "lottery";
// 聊天记录:历史聊天记录
String chatHistory = "user/getChats";
// 回放:是否存在回放视频
String playbackExist = VERSION + "record_check/{account}";
// 连麦:是否允许举手
String handsEnabled = VERSION + "check_handsup";
// 连麦:是否正在连麦
String handsGoing = VERSION + "check_calling";
// 连麦:告知服务器连麦失败
String handsFailed2Server = VERSION + "set_call";
}
}
package com.offcn.live.tsdm.api;
import android.content.Context;
import android.os.Build;
import com.jyall.base.util.DeviceUtils;
import com.offcn.live.tsdm.util.ZGLCacheManager;
import com.offcn.live.tsdm.util.ZGLUserInfoHelper;
import java.util.HashMap;
import tsdm.live.offcn.com.tsdownloadermanager.BuildConfig;
/**
* 通用网络请求头部信息
*
* @author wangwenlong
* @date 2018/8/2
*/
class CommonHeaders {
/**
* 通用的http请求头 header
*/
private static HashMap<String, String> sCommonHeaders = new HashMap<>();
/**
* 设置通用的http请求的header
*/
public static HashMap<String, String> getCommonHeaders(Context context) {
//此处可以添加通用头
sCommonHeaders.put("zgl-clienttype", "ZgLiveStudent");
sCommonHeaders.put("zgl-clientversion", BuildConfig.VERSION_NAME);
sCommonHeaders.put("zgl-systemtype", "Android");
sCommonHeaders.put("zgl-systemversion", String.valueOf(Build.VERSION.RELEASE));
sCommonHeaders.put("zgl-phonekey", DeviceUtils.getUniqueId(context));
sCommonHeaders.put("zgl-phonetype", DeviceUtils.getPhoneType());
sCommonHeaders.put("zgl-resolution", DeviceUtils.getScreenWidth(context) + "x" + DeviceUtils.getScreenHeight(context));
HashMap<String, String> signMap = new HashMap<>(sCommonHeaders);
// 目前只存在 access_token
if (ZGLUserInfoHelper.getInstance().isLogin()) {
signMap.put("Authorization", "Bearer " + ZGLCacheManager.getInstance(context).getUserInfo().access_token);
} else {
signMap.remove("Authorization");
}
return signMap;
}
}
package com.offcn.live.api;
import java.io.IOException;
import java.io.InputStream;
import java.security.KeyManagementException;
import java.security.KeyStore;
import java.security.KeyStoreException;
import java.security.NoSuchAlgorithmException;
import java.security.UnrecoverableKeyException;
import java.security.cert.CertificateException;
import java.security.cert.CertificateFactory;
import java.security.cert.X509Certificate;
import javax.net.ssl.HostnameVerifier;
import javax.net.ssl.KeyManager;
import javax.net.ssl.KeyManagerFactory;
import javax.net.ssl.SSLContext;
import javax.net.ssl.SSLSession;
import javax.net.ssl.SSLSocketFactory;
import javax.net.ssl.TrustManager;
import javax.net.ssl.TrustManagerFactory;
import javax.net.ssl.X509TrustManager;
/**
* https证书 <b>出处不详</b>
* @author wangwenlong
* @date 2018/8/2
*/
public class HttpsCert {
public static class SSLParams {
public SSLSocketFactory sSLSocketFactory;
public X509TrustManager trustManager;
}
public static SSLParams getSslSocketFactory(InputStream[] certificates, InputStream bksFile, String password) {
SSLParams sslParams = new SSLParams();
try {
TrustManager[] trustManagers = prepareTrustManager(certificates);
KeyManager[] keyManagers = prepareKeyManager(bksFile, password);
SSLContext sslContext = SSLContext.getInstance("TLS");
X509TrustManager trustManager = null;
if (trustManagers != null) {
trustManager = new MyTrustManager(chooseTrustManager(trustManagers));
} else {
trustManager = new UnSafeTrustManager();
}
sslContext.init(keyManagers, new TrustManager[]{trustManager}, null);
sslParams.sSLSocketFactory = sslContext.getSocketFactory();
sslParams.trustManager = trustManager;
return sslParams;
} catch (NoSuchAlgorithmException e) {
throw new AssertionError(e);
} catch (KeyManagementException e) {
throw new AssertionError(e);
} catch (KeyStoreException e) {
throw new AssertionError(e);
}
}
private class UnSafeHostnameVerifier implements HostnameVerifier {
@Override
public boolean verify(String hostname, SSLSession session) {
return true;
}
}
private static class UnSafeTrustManager implements X509TrustManager {
@Override
public void checkClientTrusted(X509Certificate[] chain, String authType)
throws CertificateException {
}
@Override
public void checkServerTrusted(X509Certificate[] chain, String authType)
throws CertificateException {
}
@Override
public X509Certificate[] getAcceptedIssuers() {
return new X509Certificate[]{};
}
}
private static TrustManager[] prepareTrustManager(InputStream... certificates) {
if (certificates == null || certificates.length <= 0) return null;
try {
CertificateFactory certificateFactory = CertificateFactory.getInstance("X.509");
KeyStore keyStore = KeyStore.getInstance(KeyStore.getDefaultType());
keyStore.load(null);
int index = 0;
for (InputStream certificate : certificates) {
String certificateAlias = Integer.toString(index++);
keyStore.setCertificateEntry(certificateAlias, certificateFactory.generateCertificate(certificate));
try {
if (certificate != null)
certificate.close();
} catch (IOException e)
{
}
}
TrustManagerFactory trustManagerFactory = null;
trustManagerFactory = TrustManagerFactory.
getInstance(TrustManagerFactory.getDefaultAlgorithm());
trustManagerFactory.init(keyStore);
TrustManager[] trustManagers = trustManagerFactory.getTrustManagers();
return trustManagers;
} catch (NoSuchAlgorithmException e) {
e.printStackTrace();
} catch (CertificateException e) {
e.printStackTrace();
} catch (KeyStoreException e) {
e.printStackTrace();
} catch (Exception e) {
e.printStackTrace();
}
return null;
}
private static KeyManager[] prepareKeyManager(InputStream bksFile, String password) {
try {
if (bksFile == null || password == null) return null;
KeyStore clientKeyStore = KeyStore.getInstance("BKS");
clientKeyStore.load(bksFile, password.toCharArray());
KeyManagerFactory keyManagerFactory = KeyManagerFactory.getInstance(KeyManagerFactory.getDefaultAlgorithm());
keyManagerFactory.init(clientKeyStore, password.toCharArray());
return keyManagerFactory.getKeyManagers();
} catch (KeyStoreException e) {
e.printStackTrace();
} catch (NoSuchAlgorithmException e) {
e.printStackTrace();
} catch (UnrecoverableKeyException e) {
e.printStackTrace();
} catch (CertificateException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
} catch (Exception e) {
e.printStackTrace();
}
return null;
}
private static X509TrustManager chooseTrustManager(TrustManager[] trustManagers) {
for (TrustManager trustManager : trustManagers) {
if (trustManager instanceof X509TrustManager) {
return (X509TrustManager) trustManager;
}
}
return null;
}
private static class MyTrustManager implements X509TrustManager {
private X509TrustManager defaultTrustManager;
private X509TrustManager localTrustManager;
public MyTrustManager(X509TrustManager localTrustManager) throws NoSuchAlgorithmException, KeyStoreException {
TrustManagerFactory var4 = TrustManagerFactory.getInstance(TrustManagerFactory.getDefaultAlgorithm());
var4.init((KeyStore) null);
defaultTrustManager = chooseTrustManager(var4.getTrustManagers());
this.localTrustManager = localTrustManager;
}
@Override
public void checkClientTrusted(X509Certificate[] chain, String authType) throws CertificateException {
}
@Override
public void checkServerTrusted(X509Certificate[] chain, String authType) throws CertificateException {
try {
defaultTrustManager.checkServerTrusted(chain, authType);
} catch (CertificateException ce) {
localTrustManager.checkServerTrusted(chain, authType);
}
}
@Override
public X509Certificate[] getAcceptedIssuers() {
return new X509Certificate[0];
}
}
}
package com.offcn.live.tsdm.api;
import com.offcn.live.tsdm.api.network.ResponseBean;
import com.offcn.live.tsdm.bean.ZGLPlaybackBean;
import com.offcn.live.tsdm.bean.ZGLSignInBean;
import java.util.List;
import java.util.Map;
import io.reactivex.Observable;
import okhttp3.RequestBody;
import okhttp3.ResponseBody;
import retrofit2.Call;
import retrofit2.Response;
import retrofit2.http.Body;
import retrofit2.http.DELETE;
import retrofit2.http.Field;
import retrofit2.http.FieldMap;
import retrofit2.http.FormUrlEncoded;
import retrofit2.http.GET;
import retrofit2.http.POST;
import retrofit2.http.Path;
import retrofit2.http.Query;
import retrofit2.http.Url;
/**
* 接口实现
*
* @author wangwenlong
* @date 2018/8/2
*/
public interface LiveApi {
/**
* 获取回放信息
*
* @return
*/
@FormUrlEncoded
@POST(APIConstants.LiveVideo.playback)
Observable<Response<ResponseBean<ZGLPlaybackBean>>> getPlaybackInfo(@FieldMap Map<String, String> map);
/**
* 获取回放视频位于服务器的文件
*
* @param url
* @return
*/
@GET
Call<ResponseBody> getPlaybackInfoFile(@Url String url);
/**
* 检测是否在回放视频
*
* @return
*/
@GET(APIConstants.LiveVideo.playbackExist)
Observable<Response<ResponseBean<ZGLSignInBean>>> getPlaybackExist(@Path("account") String account);
}
package com.offcn.live.tsdm.api;
import android.content.Context;
import com.google.gson.Gson;
import com.jyall.base.log.LogUtils;
import com.offcn.live.tsdm.api.LiveApi;
import java.io.IOException;
import java.util.concurrent.TimeUnit;
import io.reactivex.schedulers.Schedulers;
import okhttp3.Headers;
import okhttp3.Interceptor;
import okhttp3.OkHttpClient;
import okhttp3.Request;
import okhttp3.Response;
import okhttp3.logging.HttpLoggingInterceptor;
import retrofit2.Retrofit;
import retrofit2.adapter.rxjava2.RxJava2CallAdapterFactory;
import retrofit2.converter.gson.GsonConverterFactory;
import retrofit2.converter.scalars.ScalarsConverterFactory;
/**
* http 工具类
*
* @author wangwenlong
* @date 2018/7/27
*/
public class RetrofitManager {
private static final String TAG = RetrofitManager.class.getSimpleName();
// 超时时间
private static final long TIME_OUT = 1000 * 30;
private static LiveApi liveApi;
private static volatile Retrofit retrofit;
private static OkHttpClient okHttpClient;
private static Context mContext;
public static LiveApi getInstance(Context context) {
if (retrofit == null) {
synchronized (RetrofitManager.class) {
if (retrofit == null) {
retrofit = new Retrofit.Builder()
.baseUrl(APIConstants.BASE)
.addConverterFactory(GsonConverterFactory.create(new Gson()))
.addConverterFactory(ScalarsConverterFactory.create())
.addCallAdapterFactory(RxJava2CallAdapterFactory.createWithScheduler(Schedulers.io()))
.client(getClient())
.build();
liveApi = retrofit.create(LiveApi.class);
mContext = context;
}
}
}
return liveApi;
}
private static OkHttpClient getClient() {
if (okHttpClient == null) {
OkHttpClient.Builder okHttpClientBuilder = new OkHttpClient.Builder()
.connectTimeout(TIME_OUT, TimeUnit.MILLISECONDS)
.readTimeout(TIME_OUT, TimeUnit.MILLISECONDS)
.addInterceptor(getCommonHeaderInterceptor())
.addInterceptor(getLogInterceptor());
// 添加 https 信任
com.offcn.live.api.HttpsCert.SSLParams sslParams = com.offcn.live.api.HttpsCert.getSslSocketFactory(null, null, null);
okHttpClientBuilder.sslSocketFactory(sslParams.sSLSocketFactory, sslParams.trustManager);
okHttpClient = okHttpClientBuilder.build();
}
return okHttpClient;
}
/**
* 打印 retrofit 日志
*/
private static HttpLoggingInterceptor getLogInterceptor() {
return new HttpLoggingInterceptor(new HttpLoggingInterceptor.Logger() {
@Override
public void log(String message) {
LogUtils.e(TAG, "retrofit = " + message);
}
}).setLevel(HttpLoggingInterceptor.Level.BODY);
}
/**
* 增加通用的请求头
*/
private static Interceptor getCommonHeaderInterceptor() {
return new Interceptor() {
@Override
public Response intercept(Chain chain) throws IOException {
// 增加拦截器,用以加入 http 头部请求信息
Request.Builder builder = chain.request().newBuilder();
builder.headers(Headers.of(CommonHeaders.getCommonHeaders(mContext)));
Request request = builder.build();
return chain.proceed(request);
}
};
}
}
package com.offcn.live.tsdm.api.network;
import android.support.annotation.NonNull;
/**
* 通用接口返回处理
*
* @author wangwenlong
* @date 2018/7/27
*/
class CommonCodeHelper {
static <T> boolean processCode(@NonNull ResponseBean<T> errorResponseBean) {
switch (errorResponseBean.code) {
// 登录失效
case ResponseCode.ACCESS_TOKEN_INVALID:
return true;
// 强制升级
case ResponseCode.UPDATE_FORCE:
return true;
case ResponseCode.NOT_FOUND:
break;
}
return false;
}
}
package com.offcn.live.tsdm.api.network;
import android.content.Context;
import android.text.TextUtils;
import com.jyall.base.util.CommonUtils;
import io.reactivex.Observer;
import io.reactivex.disposables.Disposable;
import retrofit2.Response;
import tsdm.live.offcn.com.tsdownloadermanager.R;
/**
* RxJava 请求回调类
*
* @author wangwenlong
* @date 2018/7/27
*/
public abstract class ProgressSubscriber<T> implements Observer<Response<ResponseBean<T>>> {
private Context mContext;
private ProgressSubscriber() {
}
public ProgressSubscriber(Context context) {
this.mContext = context;
}
/**
* 不要重写此方法,这个是给Rx调用
*/
@Override
@Deprecated
public void onError(Throwable e) {
try {
// 若不重写 onError(int, string) 方法,则直接 toast 提示
if (!onError(999, e.getMessage())) {
CommonUtils.showToast(mContext, e.getMessage());
}
} catch (Exception exception) {
CommonUtils.showToast(mContext, R.string.server_error);
}
}
@Override
public void onComplete() {
}
@Override
public void onSubscribe(Disposable d) {
}
@Override
public void onNext(Response<ResponseBean<T>> response) {
if (response.errorBody() != null) {
// 外部错误。若不重写 onError 则直接 toast 错误提示
if (!onError(response.code(), response.message())) {
CommonUtils.showToast(mContext, response.message());
}
} else {
// 内部处理
ResponseBean responseBean = response.body();
if (responseBean != null) {
if (responseBean.code == 200) {
onResponse((T) responseBean.data);
} else {
// 是否有通用接口返回处理
if (!CommonCodeHelper.processCode(responseBean)) {
if (!onError(responseBean.code, responseBean.msg)) {
if (!TextUtils.isEmpty(responseBean.msg)) {
CommonUtils.showToast(mContext, responseBean.msg);
}
}
}
}
} else {
// 返回为空
onResponse(null);
}
}
}
/**
* 请求返回错误
*
* @return 如果处理了这个错误信息,返回true, 如果不处理这个错误,而是直接走统一处理(弹Toast),那么就返回false
*/
public boolean onError(int code, String error) {
return false;
}
public abstract void onResponse(T result);
}
package com.offcn.live.tsdm.api.network;
/**
* 请求返回实体
*
* @author wangwenlong
* @date 2018/7/26
*/
public class ResponseBean<T> {
public int code;
public String msg;
public T data;
@Override
public String toString() {
return "code: " + code + ", msg: " + msg;
}
}
package com.offcn.live.tsdm.api.network;
/**
* 请求返回码
*
* @author wangwenlong
* @date 2018/7/26
*/
class ResponseCode {
/**
* 业务正确
*/
public static final int SUCCESS = 0;
/**
* 业务错误
*/
public static final int BAD_REQUEST = 400;
/**
* 404
*/
public static final int NOT_FOUND = 404;
/**
* 强制更新
*/
public static final int UPDATE_FORCE = 400009000;
/**
* token过期 需要强制登录
*/
public static final int ACCESS_TOKEN_INVALID = 400001045;
/**
* 其它错误
*/
public static final int OTHER = 9999;
}
package com.offcn.live.tsdm.bean;
import com.offcn.live.tsdm.ZGLDownLoaderManager;
import org.greenrobot.greendao.annotation.Entity;
import org.greenrobot.greendao.annotation.Generated;
import org.greenrobot.greendao.annotation.Id;
import org.greenrobot.greendao.annotation.Unique;
import java.io.File;
/**
* 视频下载实体
*
* @author wangwenlong
* @date 2019/1/21
*/
@Entity
public class ZGLDownLoaderBean {
@Id
private Long id;
@Unique
private String code; // 下载文件的唯一标识,房间口令
private String roomName; // 房间名称
private String downloadPath; // 下载地址
private int status; // 下载文件的当前状态,默认为0未下载成功 1已下载 2下载中 3暂停中
private String progress; // 下载进度
private String extensionId; // 扩展参数:外部提供的id,与code一一对应
@Generated(hash = 1512665455)
public ZGLDownLoaderBean(Long id, String code, String roomName,
String downloadPath, int status, String progress, String extensionId) {
this.id = id;
this.code = code;
this.roomName = roomName;
this.downloadPath = downloadPath;
this.status = status;
this.progress = progress;
this.extensionId = extensionId;
}
@Generated(hash = 304727487)
public ZGLDownLoaderBean() {
}
public Long getId() {
return this.id;
}
public void setId(Long id) {
this.id = id;
}
public String getCode() {
return this.code;
}
public void setCode(String code) {
this.code = code;
}
public String getRoomName() {
return this.roomName;
}
public void setRoomName(String roomName) {
this.roomName = roomName;
}
public String getDownloadPath() {
return this.downloadPath;
}
public void setDownloadPath(String downloadPath) {
this.downloadPath = downloadPath;
}
public int getStatus() {
return this.status;
}
public ZGLDownloadStatusEnum getStatusEnum() {
ZGLDownloadStatusEnum statusEnum = ZGLDownloadStatusEnum.NOT;
switch (status) {
case 0:
statusEnum = ZGLDownloadStatusEnum.NOT;
break;
case 1:
statusEnum = ZGLDownloadStatusEnum.DOWNLOADED;
break;
case 2:
statusEnum = ZGLDownloadStatusEnum.DOWNLOADING;
break;
case 3:
statusEnum = ZGLDownloadStatusEnum.PAUSED;
break;
}
return statusEnum;
}
public void setStatus(int status) {
this.status = status;
}
public boolean isLocalM3u8Exist() {
String localM3u8Path = downloadPath + ZGLDownLoaderManager.NAME_M3U8;
File localM3u8File = new File(localM3u8Path);
if (localM3u8File.isFile() && localM3u8File.exists()) {
return true;
}
return false;
}
public boolean isDownloaded() {
return 1 == status;
}
public boolean isDownloading() {
return 2 == status;
}
public boolean isPaused() {
return 3 == status;
}
@Override
public boolean equals(Object obj) {
if (obj instanceof ZGLDownLoaderBean) {
return ((ZGLDownLoaderBean) obj).code.equals(this.code);
}
return false;
}
public String getProgress() {
return this.progress;
}
public void setProgress(String progress) {
this.progress = progress;
}
public String getExtensionId() {
return this.extensionId;
}
public void setExtensionId(String extensionId) {
this.extensionId = extensionId;
}
}
package com.offcn.live.tsdm.bean;
import org.greenrobot.greendao.annotation.Entity;
import org.greenrobot.greendao.annotation.Generated;
import org.greenrobot.greendao.annotation.Id;
/**
* 视频下载的ts片实体
*
* @author wangwenlong
* @date 2019/1/21
*/
@Entity
public class ZGLDownLoaderFileBean {
@Id
private Long id;
private String code; // 房间口令标识
private String downloadUrl; // 下载地址
private int status; // 0未下载 1已下载
private String extensionId; // 扩展id
@Generated(hash = 1619330277)
public ZGLDownLoaderFileBean(Long id, String code, String downloadUrl, int status,
String extensionId) {
this.id = id;
this.code = code;
this.downloadUrl = downloadUrl;
this.status = status;
this.extensionId = extensionId;
}
@Generated(hash = 226575781)
public ZGLDownLoaderFileBean() {
}
public Long getId() {
return this.id;
}
public void setId(Long id) {
this.id = id;
}
public String getCode() {
return this.code;
}
public void setCode(String code) {
this.code = code;
}
public String getDownloadUrl() {
return this.downloadUrl;
}
public void setDownloadUrl(String downloadUrl) {
this.downloadUrl = downloadUrl;
}
public int getStatus() {
return this.status;
}
public void setStatus(int status) {
this.status = status;
}
public String getName() {
return downloadUrl.substring(downloadUrl.lastIndexOf("/"), downloadUrl.length());
}
public String getExtensionId() {
return this.extensionId;
}
public void setExtensionId(String extensionId) {
this.extensionId = extensionId;
}
}
package com.offcn.live.tsdm.bean;
public enum ZGLDownloadStatusEnum {
NOT("不存在"),
DOWNLOADED("下载完成"),
DOWNLOADING("下载中"),
PAUSED("暂停中");
ZGLDownloadStatusEnum(String s) {
}
}
package com.offcn.live.tsdm.bean;
import java.util.List;
/**
* 回放视频实体
*
* @author wangwenlong
* @date 2019/1/21
*/
public class ZGLPlayBackVideoBean {
public String code; // 房间口令
public List<String> urls; // 各个ts片的下载地址
}
package com.offcn.live.tsdm.bean;
import android.text.TextUtils;
/**
* 回放信息
*
* @author wangwenlong
* @date 2018/11/7
*/
public class ZGLPlaybackBean {
public String room_name;
public String vod; // 文件内容带有 http 的 ts 片的文件地址,用来下载
public String vodName; // 文件内容不带 http 的 ts 片的文件地址,用来本地播放
public String sqlite; // 聊天历史记录sqlite文件下载地址
/**
* 获取回放地址,ts地址里面带有http
*
* @return
*/
public String getServerUrl() {
if (TextUtils.isEmpty(vod)) {
return "";
}
if (vod.startsWith("http:") || vod.startsWith("https:")) {
return vod;
}
return "http:" + vod;
}
/**
* 获取回放地址,ts地址不带http
*
* @return
*/
public String getLocalUrl() {
return vodName;
}
/**
* 获取历史聊天记录sqlite文件
*
* @return
*/
public String getSqliteUrl() {
return sqlite;
}
}
package com.offcn.live.tsdm.bean;
/**
* 签到实体
*
* @author wangwenlong
* @date 2019/1/14
*/
public class ZGLSignInBean {
public boolean active; // true 已经签到过
}
package com.offcn.live.tsdm.bean;
/**
* 用户信息实体
*
* @author wangwenlong
* @date 2018/7/27
*/
public class ZGLUserBean {
public String userName;
public String userPassword;
public String access_token;
public String uuid;
public ZGLUserBean(String access_token) {
this.access_token = access_token;
}
public ZGLUserBean(String access_token, String uuid) {
this.access_token = access_token;
this.uuid = uuid;
}
public ZGLUserBean(String access_token, String uuid, String userPassword) {
this.userPassword = userPassword;
this.access_token = access_token;
this.uuid = uuid;
}
}
package com.offcn.live.tsdm.greendao.gen;
import android.content.Context;
import android.database.sqlite.SQLiteDatabase;
import android.database.sqlite.SQLiteDatabase.CursorFactory;
import android.util.Log;
import org.greenrobot.greendao.AbstractDaoMaster;
import org.greenrobot.greendao.database.StandardDatabase;
import org.greenrobot.greendao.database.Database;
import org.greenrobot.greendao.database.DatabaseOpenHelper;
import org.greenrobot.greendao.identityscope.IdentityScopeType;
// THIS CODE IS GENERATED BY greenDAO, DO NOT EDIT.
/**
* Master of DAO (schema version 1): knows all DAOs.
*/
public class DaoMaster extends AbstractDaoMaster {
public static final int SCHEMA_VERSION = 1;
/** Creates underlying database table using DAOs. */
public static void createAllTables(Database db, boolean ifNotExists) {
ZGLDownLoaderBeanDao.createTable(db, ifNotExists);
ZGLDownLoaderFileBeanDao.createTable(db, ifNotExists);
}
/** Drops underlying database table using DAOs. */
public static void dropAllTables(Database db, boolean ifExists) {
ZGLDownLoaderBeanDao.dropTable(db, ifExists);
ZGLDownLoaderFileBeanDao.dropTable(db, ifExists);
}
/**
* WARNING: Drops all table on Upgrade! Use only during development.
* Convenience method using a {@link DevOpenHelper}.
*/
public static DaoSession newDevSession(Context context, String name) {
Database db = new DevOpenHelper(context, name).getWritableDb();
DaoMaster daoMaster = new DaoMaster(db);
return daoMaster.newSession();
}
public DaoMaster(SQLiteDatabase db) {
this(new StandardDatabase(db));
}
public DaoMaster(Database db) {
super(db, SCHEMA_VERSION);
registerDaoClass(ZGLDownLoaderBeanDao.class);
registerDaoClass(ZGLDownLoaderFileBeanDao.class);
}
public DaoSession newSession() {
return new DaoSession(db, IdentityScopeType.Session, daoConfigMap);
}
public DaoSession newSession(IdentityScopeType type) {
return new DaoSession(db, type, daoConfigMap);
}
/**
* Calls {@link #createAllTables(Database, boolean)} in {@link #onCreate(Database)} -
*/
public static abstract class OpenHelper extends DatabaseOpenHelper {
public OpenHelper(Context context, String name) {
super(context, name, SCHEMA_VERSION);
}
public OpenHelper(Context context, String name, CursorFactory factory) {
super(context, name, factory, SCHEMA_VERSION);
}
@Override
public void onCreate(Database db) {
Log.i("greenDAO", "Creating tables for schema version " + SCHEMA_VERSION);
createAllTables(db, false);
}
}
/** WARNING: Drops all table on Upgrade! Use only during development. */
public static class DevOpenHelper extends OpenHelper {
public DevOpenHelper(Context context, String name) {
super(context, name);
}
public DevOpenHelper(Context context, String name, CursorFactory factory) {
super(context, name, factory);
}
@Override
public void onUpgrade(Database db, int oldVersion, int newVersion) {
Log.i("greenDAO", "Upgrading schema from version " + oldVersion + " to " + newVersion + " by dropping all tables");
dropAllTables(db, true);
onCreate(db);
}
}
}
package com.offcn.live.tsdm.greendao.gen;
import java.util.Map;
import org.greenrobot.greendao.AbstractDao;
import org.greenrobot.greendao.AbstractDaoSession;
import org.greenrobot.greendao.database.Database;
import org.greenrobot.greendao.identityscope.IdentityScopeType;
import org.greenrobot.greendao.internal.DaoConfig;
import com.offcn.live.tsdm.bean.ZGLDownLoaderBean;
import com.offcn.live.tsdm.bean.ZGLDownLoaderFileBean;
import com.offcn.live.tsdm.greendao.gen.ZGLDownLoaderBeanDao;
import com.offcn.live.tsdm.greendao.gen.ZGLDownLoaderFileBeanDao;
// THIS CODE IS GENERATED BY greenDAO, DO NOT EDIT.
/**
* {@inheritDoc}
*
* @see org.greenrobot.greendao.AbstractDaoSession
*/
public class DaoSession extends AbstractDaoSession {
private final DaoConfig zGLDownLoaderBeanDaoConfig;
private final DaoConfig zGLDownLoaderFileBeanDaoConfig;
private final ZGLDownLoaderBeanDao zGLDownLoaderBeanDao;
private final ZGLDownLoaderFileBeanDao zGLDownLoaderFileBeanDao;
public DaoSession(Database db, IdentityScopeType type, Map<Class<? extends AbstractDao<?, ?>>, DaoConfig>
daoConfigMap) {
super(db);
zGLDownLoaderBeanDaoConfig = daoConfigMap.get(ZGLDownLoaderBeanDao.class).clone();
zGLDownLoaderBeanDaoConfig.initIdentityScope(type);
zGLDownLoaderFileBeanDaoConfig = daoConfigMap.get(ZGLDownLoaderFileBeanDao.class).clone();
zGLDownLoaderFileBeanDaoConfig.initIdentityScope(type);
zGLDownLoaderBeanDao = new ZGLDownLoaderBeanDao(zGLDownLoaderBeanDaoConfig, this);
zGLDownLoaderFileBeanDao = new ZGLDownLoaderFileBeanDao(zGLDownLoaderFileBeanDaoConfig, this);
registerDao(ZGLDownLoaderBean.class, zGLDownLoaderBeanDao);
registerDao(ZGLDownLoaderFileBean.class, zGLDownLoaderFileBeanDao);
}
public void clear() {
zGLDownLoaderBeanDaoConfig.clearIdentityScope();
zGLDownLoaderFileBeanDaoConfig.clearIdentityScope();
}
public ZGLDownLoaderBeanDao getZGLDownLoaderBeanDao() {
return zGLDownLoaderBeanDao;
}
public ZGLDownLoaderFileBeanDao getZGLDownLoaderFileBeanDao() {
return zGLDownLoaderFileBeanDao;
}
}
package com.offcn.live.tsdm.greendao.gen;
import android.database.Cursor;
import android.database.sqlite.SQLiteStatement;
import org.greenrobot.greendao.AbstractDao;
import org.greenrobot.greendao.Property;
import org.greenrobot.greendao.internal.DaoConfig;
import org.greenrobot.greendao.database.Database;
import org.greenrobot.greendao.database.DatabaseStatement;
import com.offcn.live.tsdm.bean.ZGLDownLoaderBean;
// THIS CODE IS GENERATED BY greenDAO, DO NOT EDIT.
/**
* DAO for table "ZGLDOWN_LOADER_BEAN".
*/
public class ZGLDownLoaderBeanDao extends AbstractDao<ZGLDownLoaderBean, Long> {
public static final String TABLENAME = "ZGLDOWN_LOADER_BEAN";
/**
* Properties of entity ZGLDownLoaderBean.<br/>
* Can be used for QueryBuilder and for referencing column names.
*/
public static class Properties {
public final static Property Id = new Property(0, Long.class, "id", true, "_id");
public final static Property Code = new Property(1, String.class, "code", false, "CODE");
public final static Property RoomName = new Property(2, String.class, "roomName", false, "ROOM_NAME");
public final static Property DownloadPath = new Property(3, String.class, "downloadPath", false, "DOWNLOAD_PATH");
public final static Property Status = new Property(4, int.class, "status", false, "STATUS");
public final static Property Progress = new Property(5, String.class, "progress", false, "PROGRESS");
public final static Property ExtensionId = new Property(6, String.class, "extensionId", false, "EXTENSION_ID");
}
public ZGLDownLoaderBeanDao(DaoConfig config) {
super(config);
}
public ZGLDownLoaderBeanDao(DaoConfig config, DaoSession daoSession) {
super(config, daoSession);
}
/** Creates the underlying database table. */
public static void createTable(Database db, boolean ifNotExists) {
String constraint = ifNotExists? "IF NOT EXISTS ": "";
db.execSQL("CREATE TABLE " + constraint + "\"ZGLDOWN_LOADER_BEAN\" (" + //
"\"_id\" INTEGER PRIMARY KEY ," + // 0: id
"\"CODE\" TEXT UNIQUE ," + // 1: code
"\"ROOM_NAME\" TEXT," + // 2: roomName
"\"DOWNLOAD_PATH\" TEXT," + // 3: downloadPath
"\"STATUS\" INTEGER NOT NULL ," + // 4: status
"\"PROGRESS\" TEXT," + // 5: progress
"\"EXTENSION_ID\" TEXT);"); // 6: extensionId
}
/** Drops the underlying database table. */
public static void dropTable(Database db, boolean ifExists) {
String sql = "DROP TABLE " + (ifExists ? "IF EXISTS " : "") + "\"ZGLDOWN_LOADER_BEAN\"";
db.execSQL(sql);
}
@Override
protected final void bindValues(DatabaseStatement stmt, ZGLDownLoaderBean entity) {
stmt.clearBindings();
Long id = entity.getId();
if (id != null) {
stmt.bindLong(1, id);
}
String code = entity.getCode();
if (code != null) {
stmt.bindString(2, code);
}
String roomName = entity.getRoomName();
if (roomName != null) {
stmt.bindString(3, roomName);
}
String downloadPath = entity.getDownloadPath();
if (downloadPath != null) {
stmt.bindString(4, downloadPath);
}
stmt.bindLong(5, entity.getStatus());
String progress = entity.getProgress();
if (progress != null) {
stmt.bindString(6, progress);
}
String extensionId = entity.getExtensionId();
if (extensionId != null) {
stmt.bindString(7, extensionId);
}
}
@Override
protected final void bindValues(SQLiteStatement stmt, ZGLDownLoaderBean entity) {
stmt.clearBindings();
Long id = entity.getId();
if (id != null) {
stmt.bindLong(1, id);
}
String code = entity.getCode();
if (code != null) {
stmt.bindString(2, code);
}
String roomName = entity.getRoomName();
if (roomName != null) {
stmt.bindString(3, roomName);
}
String downloadPath = entity.getDownloadPath();
if (downloadPath != null) {
stmt.bindString(4, downloadPath);
}
stmt.bindLong(5, entity.getStatus());
String progress = entity.getProgress();
if (progress != null) {
stmt.bindString(6, progress);
}
String extensionId = entity.getExtensionId();
if (extensionId != null) {
stmt.bindString(7, extensionId);
}
}
@Override
public Long readKey(Cursor cursor, int offset) {
return cursor.isNull(offset + 0) ? null : cursor.getLong(offset + 0);
}
@Override
public ZGLDownLoaderBean readEntity(Cursor cursor, int offset) {
ZGLDownLoaderBean entity = new ZGLDownLoaderBean( //
cursor.isNull(offset + 0) ? null : cursor.getLong(offset + 0), // id
cursor.isNull(offset + 1) ? null : cursor.getString(offset + 1), // code
cursor.isNull(offset + 2) ? null : cursor.getString(offset + 2), // roomName
cursor.isNull(offset + 3) ? null : cursor.getString(offset + 3), // downloadPath
cursor.getInt(offset + 4), // status
cursor.isNull(offset + 5) ? null : cursor.getString(offset + 5), // progress
cursor.isNull(offset + 6) ? null : cursor.getString(offset + 6) // extensionId
);
return entity;
}
@Override
public void readEntity(Cursor cursor, ZGLDownLoaderBean entity, int offset) {
entity.setId(cursor.isNull(offset + 0) ? null : cursor.getLong(offset + 0));
entity.setCode(cursor.isNull(offset + 1) ? null : cursor.getString(offset + 1));
entity.setRoomName(cursor.isNull(offset + 2) ? null : cursor.getString(offset + 2));
entity.setDownloadPath(cursor.isNull(offset + 3) ? null : cursor.getString(offset + 3));
entity.setStatus(cursor.getInt(offset + 4));
entity.setProgress(cursor.isNull(offset + 5) ? null : cursor.getString(offset + 5));
entity.setExtensionId(cursor.isNull(offset + 6) ? null : cursor.getString(offset + 6));
}
@Override
protected final Long updateKeyAfterInsert(ZGLDownLoaderBean entity, long rowId) {
entity.setId(rowId);
return rowId;
}
@Override
public Long getKey(ZGLDownLoaderBean entity) {
if(entity != null) {
return entity.getId();
} else {
return null;
}
}
@Override
public boolean hasKey(ZGLDownLoaderBean entity) {
return entity.getId() != null;
}
@Override
protected final boolean isEntityUpdateable() {
return true;
}
}
package com.offcn.live.tsdm.greendao.gen;
import android.database.Cursor;
import android.database.sqlite.SQLiteStatement;
import org.greenrobot.greendao.AbstractDao;
import org.greenrobot.greendao.Property;
import org.greenrobot.greendao.internal.DaoConfig;
import org.greenrobot.greendao.database.Database;
import org.greenrobot.greendao.database.DatabaseStatement;
import com.offcn.live.tsdm.bean.ZGLDownLoaderFileBean;
// THIS CODE IS GENERATED BY greenDAO, DO NOT EDIT.
/**
* DAO for table "ZGLDOWN_LOADER_FILE_BEAN".
*/
public class ZGLDownLoaderFileBeanDao extends AbstractDao<ZGLDownLoaderFileBean, Long> {
public static final String TABLENAME = "ZGLDOWN_LOADER_FILE_BEAN";
/**
* Properties of entity ZGLDownLoaderFileBean.<br/>
* Can be used for QueryBuilder and for referencing column names.
*/
public static class Properties {
public final static Property Id = new Property(0, Long.class, "id", true, "_id");
public final static Property Code = new Property(1, String.class, "code", false, "CODE");
public final static Property DownloadUrl = new Property(2, String.class, "downloadUrl", false, "DOWNLOAD_URL");
public final static Property Status = new Property(3, int.class, "status", false, "STATUS");
public final static Property ExtensionId = new Property(4, String.class, "extensionId", false, "EXTENSION_ID");
}
public ZGLDownLoaderFileBeanDao(DaoConfig config) {
super(config);
}
public ZGLDownLoaderFileBeanDao(DaoConfig config, DaoSession daoSession) {
super(config, daoSession);
}
/** Creates the underlying database table. */
public static void createTable(Database db, boolean ifNotExists) {
String constraint = ifNotExists? "IF NOT EXISTS ": "";
db.execSQL("CREATE TABLE " + constraint + "\"ZGLDOWN_LOADER_FILE_BEAN\" (" + //
"\"_id\" INTEGER PRIMARY KEY ," + // 0: id
"\"CODE\" TEXT," + // 1: code
"\"DOWNLOAD_URL\" TEXT," + // 2: downloadUrl
"\"STATUS\" INTEGER NOT NULL ," + // 3: status
"\"EXTENSION_ID\" TEXT);"); // 4: extensionId
}
/** Drops the underlying database table. */
public static void dropTable(Database db, boolean ifExists) {
String sql = "DROP TABLE " + (ifExists ? "IF EXISTS " : "") + "\"ZGLDOWN_LOADER_FILE_BEAN\"";
db.execSQL(sql);
}
@Override
protected final void bindValues(DatabaseStatement stmt, ZGLDownLoaderFileBean entity) {
stmt.clearBindings();
Long id = entity.getId();
if (id != null) {
stmt.bindLong(1, id);
}
String code = entity.getCode();
if (code != null) {
stmt.bindString(2, code);
}
String downloadUrl = entity.getDownloadUrl();
if (downloadUrl != null) {
stmt.bindString(3, downloadUrl);
}
stmt.bindLong(4, entity.getStatus());
String extensionId = entity.getExtensionId();
if (extensionId != null) {
stmt.bindString(5, extensionId);
}
}
@Override
protected final void bindValues(SQLiteStatement stmt, ZGLDownLoaderFileBean entity) {
stmt.clearBindings();
Long id = entity.getId();
if (id != null) {
stmt.bindLong(1, id);
}
String code = entity.getCode();
if (code != null) {
stmt.bindString(2, code);
}
String downloadUrl = entity.getDownloadUrl();
if (downloadUrl != null) {
stmt.bindString(3, downloadUrl);
}
stmt.bindLong(4, entity.getStatus());
String extensionId = entity.getExtensionId();
if (extensionId != null) {
stmt.bindString(5, extensionId);
}
}
@Override
public Long readKey(Cursor cursor, int offset) {
return cursor.isNull(offset + 0) ? null : cursor.getLong(offset + 0);
}
@Override
public ZGLDownLoaderFileBean readEntity(Cursor cursor, int offset) {
ZGLDownLoaderFileBean entity = new ZGLDownLoaderFileBean( //
cursor.isNull(offset + 0) ? null : cursor.getLong(offset + 0), // id
cursor.isNull(offset + 1) ? null : cursor.getString(offset + 1), // code
cursor.isNull(offset + 2) ? null : cursor.getString(offset + 2), // downloadUrl
cursor.getInt(offset + 3), // status
cursor.isNull(offset + 4) ? null : cursor.getString(offset + 4) // extensionId
);
return entity;
}
@Override
public void readEntity(Cursor cursor, ZGLDownLoaderFileBean entity, int offset) {
entity.setId(cursor.isNull(offset + 0) ? null : cursor.getLong(offset + 0));
entity.setCode(cursor.isNull(offset + 1) ? null : cursor.getString(offset + 1));
entity.setDownloadUrl(cursor.isNull(offset + 2) ? null : cursor.getString(offset + 2));
entity.setStatus(cursor.getInt(offset + 3));
entity.setExtensionId(cursor.isNull(offset + 4) ? null : cursor.getString(offset + 4));
}
@Override
protected final Long updateKeyAfterInsert(ZGLDownLoaderFileBean entity, long rowId) {
entity.setId(rowId);
return rowId;
}
@Override
public Long getKey(ZGLDownLoaderFileBean entity) {
if(entity != null) {
return entity.getId();
} else {
return null;
}
}
@Override
public boolean hasKey(ZGLDownLoaderFileBean entity) {
return entity.getId() != null;
}
@Override
protected final boolean isEntityUpdateable() {
return true;
}
}
package com.offcn.live.tsdm.util;
import android.content.Context;
import com.offcn.live.tsdm.bean.ZGLUserBean;
/**
* 缓存类,比如用户信息,或其它配置
*
* @author wangwenlong
* @date 2018/10/17
*/
public class ZGLCacheManager {
private static volatile ZGLCacheManager INSTANCE;
private Context mContext;
private ZGLCacheManager(Context context) {
this.mContext = context.getApplicationContext();
}
public static ZGLCacheManager getInstance(Context context) {
if (INSTANCE == null) {
synchronized (ZGLCacheManager.class) {
if (INSTANCE == null) {
INSTANCE = new ZGLCacheManager(context);
}
}
}
return INSTANCE;
}
// 用户信息
private ZGLUserBean mUserInfo;
/**
* 统一清除缓存方法,退出登录时调用
*/
public void clear() {
setUserInfo(null);
ZGLSPUtils.clear(mContext);
}
/**
* 设置用户信息
*/
public void setUserInfo(ZGLUserBean userInfo) {
this.mUserInfo = userInfo;
ZGLUserInfoHelper.getInstance().saveUserInfo(mContext, userInfo);
}
public ZGLUserBean getUserInfo() {
if (null == mUserInfo) {
mUserInfo = ZGLUserInfoHelper.getInstance().getUserInfo(mContext);
}
return mUserInfo;
}
}
package com.offcn.live.tsdm.util;
import com.google.gson.Gson;
import java.util.List;
/**
* 解析工具类
*
* @author wangwenlong
* @date 2018/7/27
*/
public class ZGLParseUtils {
/**
* 解析成对象
*
* @param source
* @param clazz
* @param <T>
* @return
*/
public static <T> T parseObjectByGson(String source, Class<T> clazz) {
return new Gson().fromJson(source, clazz);
}
/**
* 解析成数组
*
* @param source
* @param clazz
* @param <T>
* @return
*/
public static <T> List<T> parseArrayByGson(String source, Class<T> clazz) {
try {
return (List<T>) new Gson().fromJson(source, clazz);
} catch (Exception e) {
return null;
}
}
/**
* 解析成 json 字符串
*
* @param object
* @return
*/
public static String parseToJson(Object object) {
return new Gson().toJson(object);
}
}
package com.offcn.live.tsdm.util;
import android.content.Context;
import android.text.TextUtils;
import com.jyall.base.util.SharedPrefUtils;
import com.offcn.live.tsdm.bean.ZGLUserBean;
/**
* SP工具类
*
* @author wangwenlong
* @date 2018/7/27
*/
public class ZGLSPUtils {
// 用户信息
private static final String USER_INFO = "user_info";
// 清空,注意有需要保留的要提前取出来再清空,再重新赋值
public static void clear(Context context) {
setUserInfo(context, "");
}
// 用户信息
public static void setUserInfo(Context context, ZGLUserBean userInfo) {
SharedPrefUtils.setParam(context, USER_INFO, userInfo);
}
public static void setUserInfo(Context context, String userInfo) {
SharedPrefUtils.setParam(context, USER_INFO, userInfo);
}
public static ZGLUserBean getUserInfo(Context context) {
String result = String.valueOf(SharedPrefUtils.getParam(context, USER_INFO, ""));
if (TextUtils.isEmpty(result)) {
return null;
} else {
return ZGLParseUtils.parseObjectByGson(result, ZGLUserBean.class);
}
}
}
package com.offcn.live.tsdm.util;
import android.content.Context;
import android.text.TextUtils;
import com.offcn.live.tsdm.bean.ZGLUserBean;
/**
* 用户信息工具类
*
* @author wangwenlong
* @date 2018/8/2
*/
public class ZGLUserInfoHelper {
private static final ZGLUserInfoHelper userInfoHelper = new ZGLUserInfoHelper();
private ZGLUserBean mUserInfo;
private boolean isLogin;
public static ZGLUserInfoHelper getInstance() {
return userInfoHelper;
}
/**
* 是否已经登录
*
* @return
*/
public boolean isLogin() {
return isLogin;
}
/**
* 获取token
*
* @return
*/
public String getToken() {
return mUserInfo.access_token;
}
private boolean isNull() {
return mUserInfo == null || TextUtils.isEmpty(mUserInfo.access_token);
}
/**
* 保存用户信息
*/
public void saveUserInfo(Context context, ZGLUserBean mUserInfo) {
this.mUserInfo = mUserInfo;
ZGLSPUtils.setUserInfo(context, mUserInfo);
isLogin = !isNull();
}
public ZGLUserBean getUserInfo(Context context) {
if (null == mUserInfo) {
mUserInfo = ZGLSPUtils.getUserInfo(context);
}
return mUserInfo;
}
public void deleteUserInfo(Context context) {
this.mUserInfo = null;
isLogin = false;
ZGLSPUtils.setUserInfo(context, "");
}
}
package com.offcn.live.tsdm.util;
import android.animation.Keyframe;
import android.animation.ObjectAnimator;
import android.animation.PropertyValuesHolder;
import android.app.Activity;
import android.content.ComponentName;
import android.content.Context;
import android.content.Intent;
import android.content.pm.PackageInfo;
import android.content.pm.PackageManager;
import android.net.Uri;
import android.os.Build;
import android.text.TextUtils;
import android.view.View;
import com.jyall.base.log.LogUtils;
import com.jyall.base.util.ValidateUtils;
import java.io.File;
import java.io.FileOutputStream;
import java.io.InputStream;
import java.io.OutputStream;
import java.net.InetAddress;
import java.net.UnknownHostException;
import java.text.DecimalFormat;
import java.util.ArrayList;
import java.util.Date;
import java.util.List;
import okhttp3.ResponseBody;
/**
* 本项目中用到的工具类
*
* @author wangwenlong
* @date 2018/8/18
*/
public class ZGLUtils {
/**
* 获取文件夹大小
*
* @param file File实例
* @return long
*/
public static long getFolderSize(File file) {
long size = 0;
try {
File[] fileList = file.listFiles();
for (int i = 0; i < fileList.length; i++) {
if (fileList[i].isFile() && fileList[i].getName().endsWith(".ts")) {
size = size + fileList[i].length();
}
}
} catch (Exception e) {
e.printStackTrace();
}
return size;
}
/**
* 格式化文件大小
*
* @param size
* @return
*/
public static String getFormattedSize(long size) {
if (size <= 0) return "0";
final String[] units = new String[]{"B", "KB", "MB", "GB", "TB"};
int digitGroups = (int) (Math.log10(size) / Math.log10(1024));
return new DecimalFormat("###0.##").format(size / Math.pow(1024, digitGroups)) + units[digitGroups];
}
/**
* 写文件入SD卡
*
* @param futureStudioIconFile
* @param body
* @return
*/
public static boolean writeResponseBodyToDisk(File futureStudioIconFile, ResponseBody body) {
try {
InputStream inputStream = null;
OutputStream outputStream = null;
try {
byte[] fileReader = new byte[4096];
long fileSize = body.contentLength();
long fileSizeDownloaded = 0;
inputStream = body.byteStream();
outputStream = new FileOutputStream(futureStudioIconFile);
while (true) {
int read = inputStream.read(fileReader);
if (read == -1) {
break;
}
outputStream.write(fileReader, 0, read);
fileSizeDownloaded += read;
LogUtils.e("", "file download: " + fileSizeDownloaded + " of " + fileSize);
}
outputStream.flush();
return true;
} catch (Exception e) {
return false;
} finally {
if (inputStream != null) {
inputStream.close();
}
if (outputStream != null) {
outputStream.close();
}
}
} catch (Exception e) {
return false;
}
}
}
<vector xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:aapt="http://schemas.android.com/aapt"
android:width="108dp"
android:height="108dp"
android:viewportHeight="108"
android:viewportWidth="108">
<path
android:fillType="evenOdd"
android:pathData="M32,64C32,64 38.39,52.99 44.13,50.95C51.37,48.37 70.14,49.57 70.14,49.57L108.26,87.69L108,109.01L75.97,107.97L32,64Z"
android:strokeColor="#00000000"
android:strokeWidth="1">
<aapt:attr name="android:fillColor">
<gradient
android:endX="78.5885"
android:endY="90.9159"
android:startX="48.7653"
android:startY="61.0927"
android:type="linear">
<item
android:color="#44000000"
android:offset="0.0" />
<item
android:color="#00000000"
android:offset="1.0" />
</gradient>
</aapt:attr>
</path>
<path
android:fillColor="#FFFFFF"
android:fillType="nonZero"
android:pathData="M66.94,46.02L66.94,46.02C72.44,50.07 76,56.61 76,64L32,64C32,56.61 35.56,50.11 40.98,46.06L36.18,41.19C35.45,40.45 35.45,39.3 36.18,38.56C36.91,37.81 38.05,37.81 38.78,38.56L44.25,44.05C47.18,42.57 50.48,41.71 54,41.71C57.48,41.71 60.78,42.57 63.68,44.05L69.11,38.56C69.84,37.81 70.98,37.81 71.71,38.56C72.44,39.3 72.44,40.45 71.71,41.19L66.94,46.02ZM62.94,56.92C64.08,56.92 65,56.01 65,54.88C65,53.76 64.08,52.85 62.94,52.85C61.8,52.85 60.88,53.76 60.88,54.88C60.88,56.01 61.8,56.92 62.94,56.92ZM45.06,56.92C46.2,56.92 47.13,56.01 47.13,54.88C47.13,53.76 46.2,52.85 45.06,52.85C43.92,52.85 43,53.76 43,54.88C43,56.01 43.92,56.92 45.06,56.92Z"
android:strokeColor="#00000000"
android:strokeWidth="1" />
</vector>
<?xml version="1.0" encoding="utf-8"?>
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="108dp"
android:height="108dp"
android:viewportHeight="108"
android:viewportWidth="108">
<path
android:fillColor="#26A69A"
android:pathData="M0,0h108v108h-108z" />
<path
android:fillColor="#00000000"
android:pathData="M9,0L9,108"
android:strokeColor="#33FFFFFF"
android:strokeWidth="0.8" />
<path
android:fillColor="#00000000"
android:pathData="M19,0L19,108"
android:strokeColor="#33FFFFFF"
android:strokeWidth="0.8" />
<path
android:fillColor="#00000000"
android:pathData="M29,0L29,108"
android:strokeColor="#33FFFFFF"
android:strokeWidth="0.8" />
<path
android:fillColor="#00000000"
android:pathData="M39,0L39,108"
android:strokeColor="#33FFFFFF"
android:strokeWidth="0.8" />
<path
android:fillColor="#00000000"
android:pathData="M49,0L49,108"
android:strokeColor="#33FFFFFF"
android:strokeWidth="0.8" />
<path
android:fillColor="#00000000"
android:pathData="M59,0L59,108"
android:strokeColor="#33FFFFFF"
android:strokeWidth="0.8" />
<path
android:fillColor="#00000000"
android:pathData="M69,0L69,108"
android:strokeColor="#33FFFFFF"
android:strokeWidth="0.8" />
<path
android:fillColor="#00000000"
android:pathData="M79,0L79,108"
android:strokeColor="#33FFFFFF"
android:strokeWidth="0.8" />
<path
android:fillColor="#00000000"
android:pathData="M89,0L89,108"
android:strokeColor="#33FFFFFF"
android:strokeWidth="0.8" />
<path
android:fillColor="#00000000"
android:pathData="M99,0L99,108"
android:strokeColor="#33FFFFFF"
android:strokeWidth="0.8" />
<path
android:fillColor="#00000000"
android:pathData="M0,9L108,9"
android:strokeColor="#33FFFFFF"
android:strokeWidth="0.8" />
<path
android:fillColor="#00000000"
android:pathData="M0,19L108,19"
android:strokeColor="#33FFFFFF"
android:strokeWidth="0.8" />
<path
android:fillColor="#00000000"
android:pathData="M0,29L108,29"
android:strokeColor="#33FFFFFF"
android:strokeWidth="0.8" />
<path
android:fillColor="#00000000"
android:pathData="M0,39L108,39"
android:strokeColor="#33FFFFFF"
android:strokeWidth="0.8" />
<path
android:fillColor="#00000000"
android:pathData="M0,49L108,49"
android:strokeColor="#33FFFFFF"
android:strokeWidth="0.8" />
<path
android:fillColor="#00000000"
android:pathData="M0,59L108,59"
android:strokeColor="#33FFFFFF"
android:strokeWidth="0.8" />
<path
android:fillColor="#00000000"
android:pathData="M0,69L108,69"
android:strokeColor="#33FFFFFF"
android:strokeWidth="0.8" />
<path
android:fillColor="#00000000"
android:pathData="M0,79L108,79"
android:strokeColor="#33FFFFFF"
android:strokeWidth="0.8" />
<path
android:fillColor="#00000000"
android:pathData="M0,89L108,89"
android:strokeColor="#33FFFFFF"
android:strokeWidth="0.8" />
<path
android:fillColor="#00000000"
android:pathData="M0,99L108,99"
android:strokeColor="#33FFFFFF"
android:strokeWidth="0.8" />
<path
android:fillColor="#00000000"
android:pathData="M19,29L89,29"
android:strokeColor="#33FFFFFF"
android:strokeWidth="0.8" />
<path
android:fillColor="#00000000"
android:pathData="M19,39L89,39"
android:strokeColor="#33FFFFFF"
android:strokeWidth="0.8" />
<path
android:fillColor="#00000000"
android:pathData="M19,49L89,49"
android:strokeColor="#33FFFFFF"
android:strokeWidth="0.8" />
<path
android:fillColor="#00000000"
android:pathData="M19,59L89,59"
android:strokeColor="#33FFFFFF"
android:strokeWidth="0.8" />
<path
android:fillColor="#00000000"
android:pathData="M19,69L89,69"
android:strokeColor="#33FFFFFF"
android:strokeWidth="0.8" />
<path
android:fillColor="#00000000"
android:pathData="M19,79L89,79"
android:strokeColor="#33FFFFFF"
android:strokeWidth="0.8" />
<path
android:fillColor="#00000000"
android:pathData="M29,19L29,89"
android:strokeColor="#33FFFFFF"
android:strokeWidth="0.8" />
<path
android:fillColor="#00000000"
android:pathData="M39,19L39,89"
android:strokeColor="#33FFFFFF"
android:strokeWidth="0.8" />
<path
android:fillColor="#00000000"
android:pathData="M49,19L49,89"
android:strokeColor="#33FFFFFF"
android:strokeWidth="0.8" />
<path
android:fillColor="#00000000"
android:pathData="M59,19L59,89"
android:strokeColor="#33FFFFFF"
android:strokeWidth="0.8" />
<path
android:fillColor="#00000000"
android:pathData="M69,19L69,89"
android:strokeColor="#33FFFFFF"
android:strokeWidth="0.8" />
<path
android:fillColor="#00000000"
android:pathData="M79,19L79,89"
android:strokeColor="#33FFFFFF"
android:strokeWidth="0.8" />
</vector>
<?xml version="1.0" encoding="utf-8"?>
<adaptive-icon xmlns:android="http://schemas.android.com/apk/res/android">
<background android:drawable="@drawable/ic_launcher_background" />
<foreground android:drawable="@drawable/ic_launcher_foreground" />
</adaptive-icon>
\ No newline at end of file
<?xml version="1.0" encoding="utf-8"?>
<adaptive-icon xmlns:android="http://schemas.android.com/apk/res/android">
<background android:drawable="@drawable/ic_launcher_background" />
<foreground android:drawable="@drawable/ic_launcher_foreground" />
</adaptive-icon>
\ No newline at end of file
<?xml version="1.0" encoding="utf-8"?>
<resources>
<color name="colorPrimary">#3F51B5</color>
<color name="colorPrimaryDark">#303F9F</color>
<color name="colorAccent">#FF4081</color>
</resources>
<resources>
<string name="app_name">TsDownloaderManager</string>
<string name="net_off" >暂无网络</string>
<string name="server_error">服务器异常</string>
</resources>
<resources>
<!-- Base application theme. -->
<style name="AppTheme" parent="Theme.AppCompat.Light.DarkActionBar">
<!-- Customize your theme here. -->
<item name="colorPrimary">@color/colorPrimary</item>
<item name="colorPrimaryDark">@color/colorPrimaryDark</item>
<item name="colorAccent">@color/colorAccent</item>
</style>
</resources>
package tsdm.live.offcn.com.tsdownloadermanager;
import org.junit.Test;
import static org.junit.Assert.*;
/**
* Example local unit test, which will execute on the development machine (host).
*
* @see <a href="http://d.android.com/tools/testing">Testing documentation</a>
*/
public class ExampleUnitTest {
@Test
public void addition_isCorrect() {
assertEquals(4, 2 + 2);
}
}
\ No newline at end of file
// Top-level build file where you can add configuration options common to all sub-projects/modules.
buildscript {
repositories {
google()
jcenter()
maven {
url 'http://s2.wawaps.com:8081/nexus/content/repositories/thirdparty/'
}
}
dependencies {
classpath 'com.android.tools.build:gradle:3.1.2'
classpath 'org.greenrobot:greendao-gradle-plugin:3.2.2'
// NOTE: Do not place your application dependencies here; they belong
// in the individual module build.gradle files
}
}
allprojects {
repositories {
google()
jcenter()
maven {
url 'http://s2.wawaps.com:8081/nexus/content/repositories/thirdparty/'
}
}
}
task clean(type: Delete) {
delete rootProject.buildDir
}
# Project-wide Gradle settings.
# IDE (e.g. Android Studio) users:
# Gradle settings configured through the IDE *will override*
# any settings specified in this file.
# For more details on how to configure your build environment visit
# http://www.gradle.org/docs/current/userguide/build_environment.html
# Specifies the JVM arguments used for the daemon process.
# The setting is particularly useful for tweaking memory settings.
org.gradle.jvmargs=-Xmx1536m
# When configured, Gradle will run in incubating parallel mode.
# This option should only be used with decoupled projects. More details, visit
# http://www.gradle.org/docs/current/userguide/multi_project_builds.html#sec:decoupled_projects
# org.gradle.parallel=true
#Wed Jun 26 09:38:45 CST 2019
distributionBase=GRADLE_USER_HOME
distributionPath=wrapper/dists
zipStoreBase=GRADLE_USER_HOME
zipStorePath=wrapper/dists
distributionUrl=https\://services.gradle.org/distributions/gradle-4.4-all.zip
#!/usr/bin/env sh
##############################################################################
##
## Gradle start up script for UN*X
##
##############################################################################
# Attempt to set APP_HOME
# Resolve links: $0 may be a link
PRG="$0"
# Need this for relative symlinks.
while [ -h "$PRG" ] ; do
ls=`ls -ld "$PRG"`
link=`expr "$ls" : '.*-> \(.*\)$'`
if expr "$link" : '/.*' > /dev/null; then
PRG="$link"
else
PRG=`dirname "$PRG"`"/$link"
fi
done
SAVED="`pwd`"
cd "`dirname \"$PRG\"`/" >/dev/null
APP_HOME="`pwd -P`"
cd "$SAVED" >/dev/null
APP_NAME="Gradle"
APP_BASE_NAME=`basename "$0"`
# Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
DEFAULT_JVM_OPTS=""
# Use the maximum available, or set MAX_FD != -1 to use that value.
MAX_FD="maximum"
warn () {
echo "$*"
}
die () {
echo
echo "$*"
echo
exit 1
}
# OS specific support (must be 'true' or 'false').
cygwin=false
msys=false
darwin=false
nonstop=false
case "`uname`" in
CYGWIN* )
cygwin=true
;;
Darwin* )
darwin=true
;;
MINGW* )
msys=true
;;
NONSTOP* )
nonstop=true
;;
esac
CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar
# Determine the Java command to use to start the JVM.
if [ -n "$JAVA_HOME" ] ; then
if [ -x "$JAVA_HOME/jre/sh/java" ] ; then
# IBM's JDK on AIX uses strange locations for the executables
JAVACMD="$JAVA_HOME/jre/sh/java"
else
JAVACMD="$JAVA_HOME/bin/java"
fi
if [ ! -x "$JAVACMD" ] ; then
die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME
Please set the JAVA_HOME variable in your environment to match the
location of your Java installation."
fi
else
JAVACMD="java"
which java >/dev/null 2>&1 || die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
Please set the JAVA_HOME variable in your environment to match the
location of your Java installation."
fi
# Increase the maximum file descriptors if we can.
if [ "$cygwin" = "false" -a "$darwin" = "false" -a "$nonstop" = "false" ] ; then
MAX_FD_LIMIT=`ulimit -H -n`
if [ $? -eq 0 ] ; then
if [ "$MAX_FD" = "maximum" -o "$MAX_FD" = "max" ] ; then
MAX_FD="$MAX_FD_LIMIT"
fi
ulimit -n $MAX_FD
if [ $? -ne 0 ] ; then
warn "Could not set maximum file descriptor limit: $MAX_FD"
fi
else
warn "Could not query maximum file descriptor limit: $MAX_FD_LIMIT"
fi
fi
# For Darwin, add options to specify how the application appears in the dock
if $darwin; then
GRADLE_OPTS="$GRADLE_OPTS \"-Xdock:name=$APP_NAME\" \"-Xdock:icon=$APP_HOME/media/gradle.icns\""
fi
# For Cygwin, switch paths to Windows format before running java
if $cygwin ; then
APP_HOME=`cygpath --path --mixed "$APP_HOME"`
CLASSPATH=`cygpath --path --mixed "$CLASSPATH"`
JAVACMD=`cygpath --unix "$JAVACMD"`
# We build the pattern for arguments to be converted via cygpath
ROOTDIRSRAW=`find -L / -maxdepth 1 -mindepth 1 -type d 2>/dev/null`
SEP=""
for dir in $ROOTDIRSRAW ; do
ROOTDIRS="$ROOTDIRS$SEP$dir"
SEP="|"
done
OURCYGPATTERN="(^($ROOTDIRS))"
# Add a user-defined pattern to the cygpath arguments
if [ "$GRADLE_CYGPATTERN" != "" ] ; then
OURCYGPATTERN="$OURCYGPATTERN|($GRADLE_CYGPATTERN)"
fi
# Now convert the arguments - kludge to limit ourselves to /bin/sh
i=0
for arg in "$@" ; do
CHECK=`echo "$arg"|egrep -c "$OURCYGPATTERN" -`
CHECK2=`echo "$arg"|egrep -c "^-"` ### Determine if an option
if [ $CHECK -ne 0 ] && [ $CHECK2 -eq 0 ] ; then ### Added a condition
eval `echo args$i`=`cygpath --path --ignore --mixed "$arg"`
else
eval `echo args$i`="\"$arg\""
fi
i=$((i+1))
done
case $i in
(0) set -- ;;
(1) set -- "$args0" ;;
(2) set -- "$args0" "$args1" ;;
(3) set -- "$args0" "$args1" "$args2" ;;
(4) set -- "$args0" "$args1" "$args2" "$args3" ;;
(5) set -- "$args0" "$args1" "$args2" "$args3" "$args4" ;;
(6) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" ;;
(7) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" ;;
(8) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" ;;
(9) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" "$args8" ;;
esac
fi
# Escape application args
save () {
for i do printf %s\\n "$i" | sed "s/'/'\\\\''/g;1s/^/'/;\$s/\$/' \\\\/" ; done
echo " "
}
APP_ARGS=$(save "$@")
# Collect all arguments for the java command, following the shell quoting and substitution rules
eval set -- $DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS "\"-Dorg.gradle.appname=$APP_BASE_NAME\"" -classpath "\"$CLASSPATH\"" org.gradle.wrapper.GradleWrapperMain "$APP_ARGS"
# by default we should be in the correct project dir, but when run from Finder on Mac, the cwd is wrong
if [ "$(uname)" = "Darwin" ] && [ "$HOME" = "$PWD" ]; then
cd "$(dirname "$0")"
fi
exec "$JAVACMD" "$@"
@if "%DEBUG%" == "" @echo off
@rem ##########################################################################
@rem
@rem Gradle startup script for Windows
@rem
@rem ##########################################################################
@rem Set local scope for the variables with windows NT shell
if "%OS%"=="Windows_NT" setlocal
set DIRNAME=%~dp0
if "%DIRNAME%" == "" set DIRNAME=.
set APP_BASE_NAME=%~n0
set APP_HOME=%DIRNAME%
@rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
set DEFAULT_JVM_OPTS=
@rem Find java.exe
if defined JAVA_HOME goto findJavaFromJavaHome
set JAVA_EXE=java.exe
%JAVA_EXE% -version >NUL 2>&1
if "%ERRORLEVEL%" == "0" goto init
echo.
echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
echo.
echo Please set the JAVA_HOME variable in your environment to match the
echo location of your Java installation.
goto fail
:findJavaFromJavaHome
set JAVA_HOME=%JAVA_HOME:"=%
set JAVA_EXE=%JAVA_HOME%/bin/java.exe
if exist "%JAVA_EXE%" goto init
echo.
echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME%
echo.
echo Please set the JAVA_HOME variable in your environment to match the
echo location of your Java installation.
goto fail
:init
@rem Get command-line arguments, handling Windows variants
if not "%OS%" == "Windows_NT" goto win9xME_args
:win9xME_args
@rem Slurp the command line arguments.
set CMD_LINE_ARGS=
set _SKIP=2
:win9xME_args_slurp
if "x%~1" == "x" goto execute
set CMD_LINE_ARGS=%*
:execute
@rem Setup the command line
set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar
@rem Execute Gradle
"%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %CMD_LINE_ARGS%
:end
@rem End local scope for the variables with windows NT shell
if "%ERRORLEVEL%"=="0" goto mainEnd
:fail
rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of
rem the _cmd.exe /c_ return code!
if not "" == "%GRADLE_EXIT_CONSOLE%" exit 1
exit /b 1
:mainEnd
if "%OS%"=="Windows_NT" endlocal
:omega
include ':app'
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment