parent
ce43e48097
commit
7b15d4c052
@ -0,0 +1,13 @@ |
|||||||
|
package com.lyy.frame; |
||||||
|
|
||||||
|
import android.app.Application; |
||||||
|
import android.test.ApplicationTestCase; |
||||||
|
|
||||||
|
/** |
||||||
|
* <a href="http://d.android.com/tools/testing/testing_android.html">Testing Fundamentals</a> |
||||||
|
*/ |
||||||
|
public class ApplicationTest extends ApplicationTestCase<Application> { |
||||||
|
public ApplicationTest() { |
||||||
|
super(Application.class); |
||||||
|
} |
||||||
|
} |
@ -0,0 +1,13 @@ |
|||||||
|
package com.arialyy.frame.base; |
||||||
|
|
||||||
|
import android.app.Application; |
||||||
|
import android.content.Context; |
||||||
|
|
||||||
|
/** |
||||||
|
* Created by AriaL on 2017/11/26. |
||||||
|
*/ |
||||||
|
|
||||||
|
public class BaseApp { |
||||||
|
public static Context context; |
||||||
|
public static Application app; |
||||||
|
} |
@ -0,0 +1,108 @@ |
|||||||
|
package com.arialyy.frame.base; |
||||||
|
|
||||||
|
import android.animation.Animator; |
||||||
|
import android.animation.AnimatorListenerAdapter; |
||||||
|
import android.animation.AnimatorSet; |
||||||
|
import android.animation.IntEvaluator; |
||||||
|
import android.animation.ObjectAnimator; |
||||||
|
import android.animation.ValueAnimator; |
||||||
|
import android.app.Dialog; |
||||||
|
import android.databinding.ViewDataBinding; |
||||||
|
import android.graphics.Color; |
||||||
|
import android.graphics.drawable.ColorDrawable; |
||||||
|
import android.os.Bundle; |
||||||
|
import android.util.Log; |
||||||
|
import android.view.View; |
||||||
|
import android.view.Window; |
||||||
|
import android.view.WindowManager; |
||||||
|
import android.view.animation.BounceInterpolator; |
||||||
|
import com.arialyy.frame.core.AbsDialogFragment; |
||||||
|
import com.arialyy.frame.util.AndroidUtils; |
||||||
|
|
||||||
|
/** |
||||||
|
* Created by Aria.Lao on 2017/12/4. |
||||||
|
*/ |
||||||
|
|
||||||
|
public abstract class BaseDialog<VB extends ViewDataBinding> extends AbsDialogFragment<VB> { |
||||||
|
private WindowManager.LayoutParams mWpm; |
||||||
|
private Window mWindow; |
||||||
|
protected boolean useDefaultAnim = true; |
||||||
|
|
||||||
|
@Override protected void init(Bundle savedInstanceState) { |
||||||
|
mWindow = getDialog().getWindow(); |
||||||
|
if (mWindow != null) { |
||||||
|
mWpm = mWindow.getAttributes(); |
||||||
|
} |
||||||
|
if (mWpm != null && mWindow != null) { |
||||||
|
//mView = mWindow.getDecorView();
|
||||||
|
mRootView.setBackgroundColor(Color.WHITE); |
||||||
|
mWindow.setBackgroundDrawable(new ColorDrawable(Color.TRANSPARENT)); |
||||||
|
//in();
|
||||||
|
if (useDefaultAnim) { |
||||||
|
in1(); |
||||||
|
} |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
@Override public void dismiss() { |
||||||
|
if (mWpm != null && mWindow != null) { |
||||||
|
if (useDefaultAnim) { |
||||||
|
out(); |
||||||
|
} |
||||||
|
} else { |
||||||
|
super.dismiss(); |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
@Override protected void dataCallback(int result, Object data) { |
||||||
|
|
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* 进场动画 |
||||||
|
*/ |
||||||
|
private void in() { |
||||||
|
int height = AndroidUtils.getScreenParams(getContext())[1]; |
||||||
|
ValueAnimator animator = ValueAnimator.ofObject(new IntEvaluator(), -height / 2, 0); |
||||||
|
animator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() { |
||||||
|
@Override public void onAnimationUpdate(ValueAnimator animation) { |
||||||
|
mWpm.y = (int) animation.getAnimatedValue(); |
||||||
|
mWindow.setAttributes(mWpm); |
||||||
|
} |
||||||
|
}); |
||||||
|
animator.setInterpolator(new BounceInterpolator()); //弹跳
|
||||||
|
Animator alpha = ObjectAnimator.ofFloat(mRootView, "alpha", 0f, 1f); |
||||||
|
AnimatorSet set = new AnimatorSet(); |
||||||
|
set.play(animator).with(alpha); |
||||||
|
set.setDuration(2000).start(); |
||||||
|
} |
||||||
|
|
||||||
|
private void in1() { |
||||||
|
Animator alpha = ObjectAnimator.ofFloat(mRootView, "alpha", 0f, 1f); |
||||||
|
alpha.setDuration(800); |
||||||
|
alpha.start(); |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* 重力动画 |
||||||
|
*/ |
||||||
|
private void out() { |
||||||
|
int height = AndroidUtils.getScreenParams(getContext())[1]; |
||||||
|
ValueAnimator animator = ValueAnimator.ofObject(new IntEvaluator(), 0, height / 3); |
||||||
|
animator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() { |
||||||
|
@Override public void onAnimationUpdate(ValueAnimator animation) { |
||||||
|
mWpm.y = (int) animation.getAnimatedValue(); |
||||||
|
mWindow.setAttributes(mWpm); |
||||||
|
} |
||||||
|
}); |
||||||
|
Animator alpha = ObjectAnimator.ofFloat(mRootView, "alpha", 1f, 0f); |
||||||
|
AnimatorSet set = new AnimatorSet(); |
||||||
|
set.play(animator).with(alpha); |
||||||
|
set.addListener(new AnimatorListenerAdapter() { |
||||||
|
@Override public void onAnimationEnd(Animator animation) { |
||||||
|
BaseDialog.super.dismiss(); |
||||||
|
} |
||||||
|
}); |
||||||
|
set.setDuration(600).start(); |
||||||
|
} |
||||||
|
} |
@ -0,0 +1,19 @@ |
|||||||
|
package com.arialyy.frame.base; |
||||||
|
|
||||||
|
import android.databinding.ViewDataBinding; |
||||||
|
import com.arialyy.frame.core.AbsFragment; |
||||||
|
|
||||||
|
/** |
||||||
|
* Created by Aria.Lao on 2017/12/1. |
||||||
|
*/ |
||||||
|
public abstract class BaseFragment<VB extends ViewDataBinding> extends AbsFragment<VB> { |
||||||
|
public int color; |
||||||
|
|
||||||
|
@Override protected void dataCallback(int result, Object obj) { |
||||||
|
|
||||||
|
} |
||||||
|
|
||||||
|
@Override protected void onDelayLoad() { |
||||||
|
|
||||||
|
} |
||||||
|
} |
@ -0,0 +1,20 @@ |
|||||||
|
package com.arialyy.frame.base; |
||||||
|
|
||||||
|
import android.arch.lifecycle.ViewModel; |
||||||
|
import com.arialyy.frame.base.net.NetManager; |
||||||
|
import com.arialyy.frame.util.StringUtil; |
||||||
|
|
||||||
|
/** |
||||||
|
* Created by AriaL on 2017/11/26. |
||||||
|
* ViewModule只能是public |
||||||
|
*/ |
||||||
|
|
||||||
|
public class BaseViewModule extends ViewModel { |
||||||
|
protected NetManager mNetManager; |
||||||
|
protected String TAG = ""; |
||||||
|
|
||||||
|
public BaseViewModule() { |
||||||
|
mNetManager = NetManager.getInstance(); |
||||||
|
TAG = StringUtil.getClassName(this); |
||||||
|
} |
||||||
|
} |
@ -0,0 +1,52 @@ |
|||||||
|
package com.arialyy.frame.base; |
||||||
|
|
||||||
|
import android.annotation.TargetApi; |
||||||
|
import android.app.Activity; |
||||||
|
import android.content.Context; |
||||||
|
import android.graphics.Color; |
||||||
|
import android.os.Build; |
||||||
|
import android.view.View; |
||||||
|
import android.view.ViewGroup; |
||||||
|
|
||||||
|
public class StatusBarCompat { |
||||||
|
private static final int INVALID_VAL = -1; |
||||||
|
private static final int COLOR_DEFAULT = Color.parseColor("#20000000"); |
||||||
|
|
||||||
|
@TargetApi(Build.VERSION_CODES.LOLLIPOP) |
||||||
|
public static void compat(Activity activity, int statusColor) { |
||||||
|
|
||||||
|
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) { |
||||||
|
if (statusColor != INVALID_VAL) { |
||||||
|
activity.getWindow().setStatusBarColor(statusColor); |
||||||
|
} |
||||||
|
return; |
||||||
|
} |
||||||
|
|
||||||
|
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT |
||||||
|
&& Build.VERSION.SDK_INT < Build.VERSION_CODES.LOLLIPOP) { |
||||||
|
int color = COLOR_DEFAULT; |
||||||
|
ViewGroup contentView = activity.findViewById(android.R.id.content); |
||||||
|
if (statusColor != INVALID_VAL) { |
||||||
|
color = statusColor; |
||||||
|
} |
||||||
|
View statusBarView = new View(activity); |
||||||
|
ViewGroup.LayoutParams lp = new ViewGroup.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT, |
||||||
|
getStatusBarHeight(activity)); |
||||||
|
statusBarView.setBackgroundColor(color); |
||||||
|
contentView.addView(statusBarView, lp); |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
public static void compat(Activity activity) { |
||||||
|
compat(activity, INVALID_VAL); |
||||||
|
} |
||||||
|
|
||||||
|
public static int getStatusBarHeight(Context context) { |
||||||
|
int result = 0; |
||||||
|
int resourceId = context.getResources().getIdentifier("status_bar_height", "dimen", "android"); |
||||||
|
if (resourceId > 0) { |
||||||
|
result = context.getResources().getDimensionPixelSize(resourceId); |
||||||
|
} |
||||||
|
return result; |
||||||
|
} |
||||||
|
} |
@ -0,0 +1,28 @@ |
|||||||
|
package com.arialyy.frame.base.net; |
||||||
|
|
||||||
|
import com.google.gson.Gson; |
||||||
|
import com.google.gson.JsonDeserializationContext; |
||||||
|
import com.google.gson.JsonDeserializer; |
||||||
|
import com.google.gson.JsonElement; |
||||||
|
import com.google.gson.JsonObject; |
||||||
|
import com.google.gson.JsonParseException; |
||||||
|
import java.lang.reflect.Type; |
||||||
|
|
||||||
|
/** |
||||||
|
* 自定义Gson描述 |
||||||
|
* Created by “Aria.Lao” on 2016/10/26. |
||||||
|
* |
||||||
|
* @param <T> 服务器数据实体 |
||||||
|
*/ |
||||||
|
public class BasicDeserializer<T> implements JsonDeserializer<T> { |
||||||
|
@Override |
||||||
|
public T deserialize(JsonElement element, Type typeOfT, JsonDeserializationContext context) |
||||||
|
throws JsonParseException { |
||||||
|
JsonObject root = element.getAsJsonObject(); |
||||||
|
if (JsonCodeAnalysisUtil.isSuccess(root)) { |
||||||
|
return new Gson().fromJson(root.get("object"), typeOfT); |
||||||
|
} else { |
||||||
|
throw new IllegalStateException(root.get("rltmsg").getAsString()); |
||||||
|
} |
||||||
|
} |
||||||
|
} |
@ -0,0 +1,39 @@ |
|||||||
|
package com.arialyy.frame.base.net; |
||||||
|
|
||||||
|
import com.arialyy.frame.util.show.FL; |
||||||
|
import com.arialyy.frame.util.show.L; |
||||||
|
import rx.Observable; |
||||||
|
import rx.android.schedulers.AndroidSchedulers; |
||||||
|
import rx.functions.Func1; |
||||||
|
import rx.schedulers.Schedulers; |
||||||
|
|
||||||
|
/** |
||||||
|
* Created by “Aria.Lao” on 2016/10/26. |
||||||
|
* HTTP数据回调 |
||||||
|
*/ |
||||||
|
public abstract class HttpCallback<T> implements INetResponse<T>, Observable.Transformer<T, T> { |
||||||
|
|
||||||
|
@Override public void onFailure(Throwable e) { |
||||||
|
L.e("HttpCallback", FL.getExceptionString(e)); |
||||||
|
} |
||||||
|
|
||||||
|
@Override public Observable<T> call(Observable<T> observable) { |
||||||
|
Observable<T> tObservable = observable.subscribeOn(Schedulers.io()) |
||||||
|
.unsubscribeOn(Schedulers.io()) |
||||||
|
.observeOn(AndroidSchedulers.mainThread()) |
||||||
|
.map(new Func1<T, T>() { |
||||||
|
@Override public T call(T t) { |
||||||
|
onResponse(t); |
||||||
|
return t; |
||||||
|
} |
||||||
|
}) |
||||||
|
.onErrorReturn(new Func1<Throwable, T>() { |
||||||
|
@Override public T call(Throwable throwable) { |
||||||
|
onFailure(throwable); |
||||||
|
return null; |
||||||
|
} |
||||||
|
}); |
||||||
|
tObservable.subscribe(); |
||||||
|
return tObservable; |
||||||
|
} |
||||||
|
} |
@ -0,0 +1,20 @@ |
|||||||
|
package com.arialyy.frame.base.net; |
||||||
|
|
||||||
|
/** |
||||||
|
* Created by “Aria.Lao” on 2016/10/25. |
||||||
|
* 网络响应接口,所有的网络回调都要继承该接口 |
||||||
|
* |
||||||
|
* @param <T> 数据实体结构 |
||||||
|
*/ |
||||||
|
public interface INetResponse<T> { |
||||||
|
|
||||||
|
/** |
||||||
|
* 网络请求成功 |
||||||
|
*/ |
||||||
|
public void onResponse(T response); |
||||||
|
|
||||||
|
/** |
||||||
|
* 请求失败 |
||||||
|
*/ |
||||||
|
public void onFailure(Throwable e); |
||||||
|
} |
@ -0,0 +1,23 @@ |
|||||||
|
package com.arialyy.frame.base.net; |
||||||
|
|
||||||
|
import com.google.gson.JsonObject; |
||||||
|
import org.json.JSONException; |
||||||
|
import org.json.JSONObject; |
||||||
|
|
||||||
|
/** |
||||||
|
* Created by AriaL on 2017/11/26. |
||||||
|
*/ |
||||||
|
|
||||||
|
public class JsonCodeAnalysisUtil { |
||||||
|
|
||||||
|
public static boolean isSuccess(JsonObject obj) { |
||||||
|
JSONObject object = null; |
||||||
|
try { |
||||||
|
object = new JSONObject(obj.toString()); |
||||||
|
return object.optBoolean("success"); |
||||||
|
} catch (JSONException e) { |
||||||
|
e.printStackTrace(); |
||||||
|
} |
||||||
|
return false; |
||||||
|
} |
||||||
|
} |
@ -0,0 +1,112 @@ |
|||||||
|
package com.arialyy.frame.base.net; |
||||||
|
|
||||||
|
import android.util.SparseArray; |
||||||
|
import com.arialyy.frame.base.BaseApp; |
||||||
|
import com.arialyy.frame.config.CommonConstant; |
||||||
|
import com.arialyy.frame.config.NetConstant; |
||||||
|
import com.franmontiel.persistentcookiejar.ClearableCookieJar; |
||||||
|
import com.franmontiel.persistentcookiejar.PersistentCookieJar; |
||||||
|
import com.franmontiel.persistentcookiejar.cache.SetCookieCache; |
||||||
|
import com.franmontiel.persistentcookiejar.persistence.SharedPrefsCookiePersistor; |
||||||
|
import com.google.gson.Gson; |
||||||
|
import java.util.concurrent.TimeUnit; |
||||||
|
import okhttp3.OkHttpClient; |
||||||
|
import retrofit2.Retrofit; |
||||||
|
import retrofit2.adapter.rxjava.RxJavaCallAdapterFactory; |
||||||
|
import retrofit2.converter.gson.GsonConverterFactory; |
||||||
|
|
||||||
|
/** |
||||||
|
* Created by “Aria.Lao” on 2016/10/25. |
||||||
|
* 网络管理器 |
||||||
|
*/ |
||||||
|
public class NetManager { |
||||||
|
private static final Object LOCK = new Object(); |
||||||
|
private static volatile NetManager INSTANCE = null; |
||||||
|
private static final long TIME_OUT = 8 * 1000; |
||||||
|
private Retrofit mRetrofit; |
||||||
|
private Retrofit.Builder mBuilder; |
||||||
|
private SparseArray<GsonConverterFactory> mConverterFactorys = new SparseArray<>(); |
||||||
|
private ClearableCookieJar mCookieJar; |
||||||
|
|
||||||
|
private NetManager() { |
||||||
|
init(); |
||||||
|
} |
||||||
|
|
||||||
|
public static NetManager getInstance() { |
||||||
|
if (INSTANCE == null) { |
||||||
|
synchronized (LOCK) { |
||||||
|
INSTANCE = new NetManager(); |
||||||
|
} |
||||||
|
} |
||||||
|
return INSTANCE; |
||||||
|
} |
||||||
|
|
||||||
|
OkHttpClient okHttpClient; |
||||||
|
|
||||||
|
private void init() { |
||||||
|
mCookieJar = new PersistentCookieJar(new SetCookieCache(), |
||||||
|
new SharedPrefsCookiePersistor(BaseApp.context)); |
||||||
|
//OkHttpClient okHttpClient = provideOkHttpClient();
|
||||||
|
okHttpClient = provideOkHttpClient(); |
||||||
|
} |
||||||
|
|
||||||
|
public ClearableCookieJar getCookieJar() { |
||||||
|
return mCookieJar; |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* 执行网络请求 |
||||||
|
* |
||||||
|
* @param service 服务器返回的实体类型 |
||||||
|
* @param gson gson 为传入的数据解析器,ENTITY 为 网络实体 |
||||||
|
* <pre><code> |
||||||
|
* Gson gson = new GsonBuilder().registerTypeAdapter(new TypeToken<ENTITY>() { |
||||||
|
* }.getType(), new BasicDeserializer<ENTITY>()).create(); |
||||||
|
* |
||||||
|
* //如启动图,需要将‘ENTITY’替换为启动图实体‘LauncherImgEntity’
|
||||||
|
* Gson gson = new GsonBuilder().registerTypeAdapter(new TypeToken<LauncherImgEntity>() { |
||||||
|
* }.getType(), new BasicDeserializer<LauncherImgEntity>()).create(); |
||||||
|
* |
||||||
|
* </code></pre> |
||||||
|
*/ |
||||||
|
public <SERVICE> SERVICE request(Class<SERVICE> service, Gson gson) { |
||||||
|
GsonConverterFactory f = null; |
||||||
|
if (gson == null) { |
||||||
|
f = GsonConverterFactory.create(); |
||||||
|
} else { |
||||||
|
f = GsonConverterFactory.create(gson); |
||||||
|
} |
||||||
|
; |
||||||
|
final Retrofit.Builder builder = new Retrofit.Builder().client(okHttpClient) |
||||||
|
.baseUrl(NetConstant.BASE_URL) |
||||||
|
.addCallAdapterFactory(RxJavaCallAdapterFactory.create()); |
||||||
|
builder.addConverterFactory(f); |
||||||
|
return builder.build().create(service); |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* 创建OKHTTP |
||||||
|
*/ |
||||||
|
private OkHttpClient provideOkHttpClient() { |
||||||
|
final OkHttpClient.Builder builder = new OkHttpClient.Builder(); |
||||||
|
if (CommonConstant.DEBUG) { |
||||||
|
//HttpLoggingInterceptor logging = new HttpLoggingInterceptor();
|
||||||
|
//logging.setLevel(HttpLoggingInterceptor.Level.BODY);
|
||||||
|
//builder.addInterceptor(logging);
|
||||||
|
builder.addInterceptor(new OkHttpLogger()); |
||||||
|
} |
||||||
|
builder.connectTimeout(TIME_OUT, TimeUnit.MILLISECONDS) |
||||||
|
.readTimeout(TIME_OUT, TimeUnit.MILLISECONDS); |
||||||
|
builder.cookieJar(mCookieJar); |
||||||
|
//builder.addInterceptor(chain -> {
|
||||||
|
// //String cookies = CookieUtil.getCookies();
|
||||||
|
// Request request = chain.request().newBuilder()
|
||||||
|
// //.addHeader("Content-Type", "application/x-www-form-urlencoded")
|
||||||
|
// //.addHeader("Content-Type", "application/x-www-form-urlencoded; charset=utf-8")
|
||||||
|
// //.addHeader("Cookie", cookies)
|
||||||
|
// .build();
|
||||||
|
// return chain.proceed(request);
|
||||||
|
//});
|
||||||
|
return builder.build(); |
||||||
|
} |
||||||
|
} |
@ -0,0 +1,65 @@ |
|||||||
|
package com.arialyy.frame.base.net; |
||||||
|
|
||||||
|
import com.arialyy.frame.util.show.FL; |
||||||
|
import com.arialyy.frame.util.show.L; |
||||||
|
import java.io.IOException; |
||||||
|
import java.nio.charset.Charset; |
||||||
|
import java.util.concurrent.TimeUnit; |
||||||
|
import okhttp3.Headers; |
||||||
|
import okhttp3.Interceptor; |
||||||
|
import okhttp3.MediaType; |
||||||
|
import okhttp3.Request; |
||||||
|
import okhttp3.Response; |
||||||
|
import okhttp3.ResponseBody; |
||||||
|
import okio.Buffer; |
||||||
|
import okio.BufferedSource; |
||||||
|
|
||||||
|
/** |
||||||
|
* Created by Lyy on 2016/9/19. |
||||||
|
* 自定义的 OKHTTP 日志 |
||||||
|
*/ |
||||||
|
public class OkHttpLogger implements Interceptor { |
||||||
|
final static String TAG = "OKHTTP"; |
||||||
|
|
||||||
|
@Override public Response intercept(Chain chain) throws IOException { |
||||||
|
Request request = chain.request(); |
||||||
|
long startNs = System.nanoTime(); |
||||||
|
Response response = chain.proceed(request); |
||||||
|
long tookMs = TimeUnit.NANOSECONDS.toMillis(System.nanoTime() - startNs); |
||||||
|
ResponseBody responseBody = response.body(); |
||||||
|
long contentLength = responseBody.contentLength(); |
||||||
|
String bodySize = contentLength != -1 ? contentLength + "-byte" : "unknown-length"; |
||||||
|
L.d(TAG, "<-- " |
||||||
|
+ response.code() |
||||||
|
+ ' ' |
||||||
|
+ response.message() |
||||||
|
+ ' ' |
||||||
|
+ response.request().url() |
||||||
|
+ " (" |
||||||
|
+ tookMs |
||||||
|
+ "ms" |
||||||
|
+ (", " + bodySize + " body") |
||||||
|
+ ')'); |
||||||
|
//Headers headers = response.headers();
|
||||||
|
//for (int i = 0, count = headers.size(); i < count; i++) {
|
||||||
|
// FL.d(TAG, headers.name(i) + ": " + headers.value(i));
|
||||||
|
//}
|
||||||
|
BufferedSource source = responseBody.source(); |
||||||
|
source.request(Long.MAX_VALUE); // Buffer the entire body.
|
||||||
|
Buffer buffer = source.buffer(); |
||||||
|
Charset UTF8 = Charset.forName("UTF-8"); |
||||||
|
Charset charset = UTF8; |
||||||
|
MediaType contentType = responseBody.contentType(); |
||||||
|
if (contentType != null) { |
||||||
|
charset = contentType.charset(UTF8); |
||||||
|
} |
||||||
|
if (contentLength != 0) { |
||||||
|
//FL.j(TAG, buffer.clone().readString(charset));
|
||||||
|
L.j(buffer.clone().readString(charset)); |
||||||
|
} |
||||||
|
|
||||||
|
L.d(TAG, "<-- END HTTP (" + buffer.size() + "-byte body)"); |
||||||
|
|
||||||
|
return response; |
||||||
|
} |
||||||
|
} |
@ -0,0 +1,968 @@ |
|||||||
|
/* |
||||||
|
* Copyright (C) 2011 The Android Open Source Project |
||||||
|
* |
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License"); |
||||||
|
* you may not use this file except in compliance with the License. |
||||||
|
* You may obtain a copy of the License at |
||||||
|
* |
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
* |
||||||
|
* Unless required by applicable law or agreed to in writing, software |
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS, |
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
||||||
|
* See the License for the specific language governing permissions and |
||||||
|
* limitations under the License. |
||||||
|
*/ |
||||||
|
|
||||||
|
package com.arialyy.frame.cache; |
||||||
|
|
||||||
|
import java.io.BufferedInputStream; |
||||||
|
import java.io.BufferedWriter; |
||||||
|
import java.io.Closeable; |
||||||
|
import java.io.EOFException; |
||||||
|
import java.io.File; |
||||||
|
import java.io.FileInputStream; |
||||||
|
import java.io.FileNotFoundException; |
||||||
|
import java.io.FileOutputStream; |
||||||
|
import java.io.FileWriter; |
||||||
|
import java.io.FilterOutputStream; |
||||||
|
import java.io.IOException; |
||||||
|
import java.io.InputStream; |
||||||
|
import java.io.InputStreamReader; |
||||||
|
import java.io.OutputStream; |
||||||
|
import java.io.OutputStreamWriter; |
||||||
|
import java.io.Reader; |
||||||
|
import java.io.StringWriter; |
||||||
|
import java.io.Writer; |
||||||
|
import java.lang.reflect.Array; |
||||||
|
import java.nio.charset.Charset; |
||||||
|
import java.util.ArrayList; |
||||||
|
import java.util.Arrays; |
||||||
|
import java.util.Iterator; |
||||||
|
import java.util.LinkedHashMap; |
||||||
|
import java.util.Map; |
||||||
|
import java.util.concurrent.Callable; |
||||||
|
import java.util.concurrent.ExecutorService; |
||||||
|
import java.util.concurrent.LinkedBlockingQueue; |
||||||
|
import java.util.concurrent.ThreadPoolExecutor; |
||||||
|
import java.util.concurrent.TimeUnit; |
||||||
|
|
||||||
|
/** |
||||||
|
* ***************************************************************************** |
||||||
|
* Taken from the JB source code, can be found in: |
||||||
|
* libcore/luni/src/main/java/libcore/io/DiskLruCache.java |
||||||
|
* or direct link: |
||||||
|
* https://android.googlesource.com/platform/libcore/+/android-4.1.1_r1/luni/src/main/java/libcore/io/DiskLruCache.java
|
||||||
|
* ***************************************************************************** |
||||||
|
* |
||||||
|
* A cache that uses a bounded amount of space on a filesystem. Each cache |
||||||
|
* entry has a string key and a fixed number of values. Values are byte |
||||||
|
* sequences, accessible as streams or files. Each value must be between {@code |
||||||
|
* 0} and {@code Integer.MAX_VALUE} bytes in length. |
||||||
|
* |
||||||
|
* The cache stores its data in a directory on the filesystem. This |
||||||
|
* directory must be exclusive to the cache; the cache may delete or overwrite |
||||||
|
* files from its directory. It is an error for multiple processes to use the |
||||||
|
* same cache directory at the same time. |
||||||
|
* |
||||||
|
* This cache limits the number of bytes that it will store on the |
||||||
|
* filesystem. When the number of stored bytes exceeds the limit, the cache will |
||||||
|
* remove entries in the background until the limit is satisfied. The limit is |
||||||
|
* not strict: the cache may temporarily exceed it while waiting for files to be |
||||||
|
* deleted. The limit does not include filesystem overhead or the cache |
||||||
|
* journal so space-sensitive applications should set a conservative limit. |
||||||
|
* |
||||||
|
* Clients call {@link #edit} to create or update the values of an entry. An |
||||||
|
* entry may have only one editor at one time; if a value is not available to be |
||||||
|
* edited then {@link #edit} will return null. |
||||||
|
* |
||||||
|
* When an entry is being <strong>created</strong> it is necessary to |
||||||
|
* supply a full set of values; the empty value should be used as a |
||||||
|
* placeholder if necessary. |
||||||
|
* When an entry is being <strong>edited</strong>, it is not necessary |
||||||
|
* to supply data for every value; values default to their previous |
||||||
|
* value. |
||||||
|
* |
||||||
|
* Clients call {@link #get} to read a snapshot of an entry. The read will |
||||||
|
* observe the value at the time that {@link #get} was called. Updates and |
||||||
|
* removals after the call do not impact ongoing reads. |
||||||
|
* |
||||||
|
* This class is tolerant of some I/O errors. If files are missing from the |
||||||
|
* filesystem, the corresponding entries will be dropped from the cache. If |
||||||
|
* an error occurs while writing a cache value, the edit will fail silently. |
||||||
|
* Callers should handle other problems by catching {@code IOException} and |
||||||
|
* responding appropriately. |
||||||
|
*/ |
||||||
|
public final class DiskLruCache implements Closeable { |
||||||
|
static final String JOURNAL_FILE = "journal"; |
||||||
|
static final String JOURNAL_FILE_TMP = "journal.tmp"; |
||||||
|
static final String MAGIC = "libcore.io.DiskLruCache"; |
||||||
|
static final String VERSION_1 = "1"; |
||||||
|
static final long ANY_SEQUENCE_NUMBER = -1; |
||||||
|
private static final String CLEAN = "CLEAN"; |
||||||
|
private static final String DIRTY = "DIRTY"; |
||||||
|
private static final String REMOVE = "REMOVE"; |
||||||
|
private static final String READ = "READ"; |
||||||
|
|
||||||
|
private static final Charset UTF_8 = Charset.forName("UTF-8"); |
||||||
|
private static final int IO_BUFFER_SIZE = 8 * 1024; |
||||||
|
|
||||||
|
/* |
||||||
|
* This cache uses a journal file named "journal". A typical journal file |
||||||
|
* looks like this: |
||||||
|
* libcore.io.DiskLruCache |
||||||
|
* 1 |
||||||
|
* 100 |
||||||
|
* 2 |
||||||
|
* |
||||||
|
* CLEAN 3400330d1dfc7f3f7f4b8d4d803dfcf6 832 21054 |
||||||
|
* DIRTY 335c4c6028171cfddfbaae1a9c313c52 |
||||||
|
* CLEAN 335c4c6028171cfddfbaae1a9c313c52 3934 2342 |
||||||
|
* REMOVE 335c4c6028171cfddfbaae1a9c313c52 |
||||||
|
* DIRTY 1ab96a171faeeee38496d8b330771a7a |
||||||
|
* CLEAN 1ab96a171faeeee38496d8b330771a7a 1600 234 |
||||||
|
* READ 335c4c6028171cfddfbaae1a9c313c52 |
||||||
|
* READ 3400330d1dfc7f3f7f4b8d4d803dfcf6 |
||||||
|
* |
||||||
|
* The first five lines of the journal form its header. They are the |
||||||
|
* constant string "libcore.io.DiskLruCache", the disk cache's version, |
||||||
|
* the application's version, the value count, and a blank line. |
||||||
|
* |
||||||
|
* Each of the subsequent lines in the file is a record of the state of a |
||||||
|
* cache entry. Each line contains space-separated values: a state, a key, |
||||||
|
* and optional state-specific values. |
||||||
|
* o DIRTY lines track that an entry is actively being created or updated. |
||||||
|
* Every successful DIRTY action should be followed by a CLEAN or REMOVE |
||||||
|
* action. DIRTY lines without a matching CLEAN or REMOVE indicate that |
||||||
|
* temporary files may need to be deleted. |
||||||
|
* o CLEAN lines track a cache entry that has been successfully published |
||||||
|
* and may be read. A publish line is followed by the lengths of each of |
||||||
|
* its values. |
||||||
|
* o READ lines track accesses for LRU. |
||||||
|
* o REMOVE lines track entries that have been deleted. |
||||||
|
* |
||||||
|
* The journal file is appended to as cache operations occur. The journal may |
||||||
|
* occasionally be compacted by dropping redundant lines. A temporary file named |
||||||
|
* "journal.tmp" will be used during compaction; that file should be deleted if |
||||||
|
* it exists when the cache is opened. |
||||||
|
*/ |
||||||
|
|
||||||
|
private final File directory; |
||||||
|
private final File journalFile; |
||||||
|
private final File journalFileTmp; |
||||||
|
private final int appVersion; |
||||||
|
private final long maxSize; |
||||||
|
private final int valueCount; |
||||||
|
private long size = 0; |
||||||
|
private Writer journalWriter; |
||||||
|
private final LinkedHashMap<String, Entry> lruEntries = |
||||||
|
new LinkedHashMap<String, Entry>(0, 0.75f, true); |
||||||
|
private int redundantOpCount; |
||||||
|
|
||||||
|
/** |
||||||
|
* To differentiate between old and current snapshots, each entry is given |
||||||
|
* a sequence number each time an edit is committed. A snapshot is stale if |
||||||
|
* its sequence number is not equal to its entry's sequence number. |
||||||
|
*/ |
||||||
|
private long nextSequenceNumber = 0; |
||||||
|
|
||||||
|
/* From java.util.Arrays */ |
||||||
|
@SuppressWarnings("unchecked") |
||||||
|
private static <T> T[] copyOfRange(T[] original, int start, int end) { |
||||||
|
final int originalLength = original.length; // For exception priority compatibility.
|
||||||
|
if (start > end) { |
||||||
|
throw new IllegalArgumentException(); |
||||||
|
} |
||||||
|
if (start < 0 || start > originalLength) { |
||||||
|
throw new ArrayIndexOutOfBoundsException(); |
||||||
|
} |
||||||
|
final int resultLength = end - start; |
||||||
|
final int copyLength = Math.min(resultLength, originalLength - start); |
||||||
|
final T[] result = |
||||||
|
(T[]) Array.newInstance(original.getClass().getComponentType(), resultLength); |
||||||
|
System.arraycopy(original, start, result, 0, copyLength); |
||||||
|
return result; |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* Returns the remainder of 'reader' as a string, closing it when done. |
||||||
|
*/ |
||||||
|
public static String readFully(Reader reader) throws IOException { |
||||||
|
try { |
||||||
|
StringWriter writer = new StringWriter(); |
||||||
|
char[] buffer = new char[1024]; |
||||||
|
int count; |
||||||
|
while ((count = reader.read(buffer)) != -1) { |
||||||
|
writer.write(buffer, 0, count); |
||||||
|
} |
||||||
|
return writer.toString(); |
||||||
|
} finally { |
||||||
|
reader.close(); |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* Returns the ASCII characters up to but not including the next "\r\n", or |
||||||
|
* "\n". |
||||||
|
* |
||||||
|
* @throws EOFException if the stream is exhausted before the next newline |
||||||
|
* character. |
||||||
|
*/ |
||||||
|
public static String readAsciiLine(InputStream in) throws IOException { |
||||||
|
|
||||||
|
StringBuilder result = new StringBuilder(80); |
||||||
|
while (true) { |
||||||
|
int c = in.read(); |
||||||
|
if (c == -1) { |
||||||
|
throw new EOFException(); |
||||||
|
} else if (c == '\n') { |
||||||
|
break; |
||||||
|
} |
||||||
|
|
||||||
|
result.append((char) c); |
||||||
|
} |
||||||
|
int length = result.length(); |
||||||
|
if (length > 0 && result.charAt(length - 1) == '\r') { |
||||||
|
result.setLength(length - 1); |
||||||
|
} |
||||||
|
return result.toString(); |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* Closes 'closeable', ignoring any checked exceptions. Does nothing if 'closeable' is null. |
||||||
|
*/ |
||||||
|
public static void closeQuietly(Closeable closeable) { |
||||||
|
if (closeable != null) { |
||||||
|
try { |
||||||
|
closeable.close(); |
||||||
|
} catch (RuntimeException rethrown) { |
||||||
|
throw rethrown; |
||||||
|
} catch (Exception ignored) { |
||||||
|
} |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* Recursively delete everything in {@code dir}. |
||||||
|
*/ |
||||||
|
// TODO: this should specify paths as Strings rather than as Files
|
||||||
|
public static void deleteContents(File dir) throws IOException { |
||||||
|
File[] files = dir.listFiles(); |
||||||
|
if (files == null) { |
||||||
|
throw new IllegalArgumentException("not a directory: " + dir); |
||||||
|
} |
||||||
|
for (File file : files) { |
||||||
|
if (file.isDirectory()) { |
||||||
|
deleteContents(file); |
||||||
|
} |
||||||
|
if (!file.delete()) { |
||||||
|
throw new IOException("failed to delete file: " + file); |
||||||
|
} |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* This cache uses a single background thread to evict entries. |
||||||
|
*/ |
||||||
|
private final ExecutorService executorService = |
||||||
|
new ThreadPoolExecutor(0, 1, 60L, TimeUnit.SECONDS, new LinkedBlockingQueue<Runnable>()); |
||||||
|
private final Callable<Void> cleanupCallable = new Callable<Void>() { |
||||||
|
@Override |
||||||
|
public Void call() throws Exception { |
||||||
|
synchronized (DiskLruCache.this) { |
||||||
|
if (journalWriter == null) { |
||||||
|
return null; // closed
|
||||||
|
} |
||||||
|
trimToSize(); |
||||||
|
if (journalRebuildRequired()) { |
||||||
|
rebuildJournal(); |
||||||
|
redundantOpCount = 0; |
||||||
|
} |
||||||
|
} |
||||||
|
return null; |
||||||
|
} |
||||||
|
}; |
||||||
|
|
||||||
|
private DiskLruCache(File directory, int appVersion, int valueCount, long maxSize) { |
||||||
|
this.directory = directory; |
||||||
|
this.appVersion = appVersion; |
||||||
|
this.journalFile = new File(directory, JOURNAL_FILE); |
||||||
|
this.journalFileTmp = new File(directory, JOURNAL_FILE_TMP); |
||||||
|
this.valueCount = valueCount; |
||||||
|
this.maxSize = maxSize; |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* Opens the cache in {@code directory}, creating a cache if none exists |
||||||
|
* there. |
||||||
|
* |
||||||
|
* @param directory a writable directory |
||||||
|
* @param valueCount the number of values per cache entry. Must be positive. |
||||||
|
* @param maxSize the maximum number of bytes this cache should use to store |
||||||
|
* @throws IOException if reading or writing the cache directory fails |
||||||
|
*/ |
||||||
|
public static DiskLruCache open(File directory, int appVersion, int valueCount, long maxSize) |
||||||
|
throws IOException { |
||||||
|
if (maxSize <= 0) { |
||||||
|
throw new IllegalArgumentException("maxSize <= 0"); |
||||||
|
} |
||||||
|
if (valueCount <= 0) { |
||||||
|
throw new IllegalArgumentException("valueCount <= 0"); |
||||||
|
} |
||||||
|
|
||||||
|
// prefer to pick up where we left off
|
||||||
|
DiskLruCache cache = new DiskLruCache(directory, appVersion, valueCount, maxSize); |
||||||
|
if (cache.journalFile.exists()) { |
||||||
|
try { |
||||||
|
cache.readJournal(); |
||||||
|
cache.processJournal(); |
||||||
|
cache.journalWriter = |
||||||
|
new BufferedWriter(new FileWriter(cache.journalFile, true), IO_BUFFER_SIZE); |
||||||
|
return cache; |
||||||
|
} catch (IOException journalIsCorrupt) { |
||||||
|
// System.logW("DiskLruCache " + directory + " is corrupt: "
|
||||||
|
// + journalIsCorrupt.getMessage() + ", removing");
|
||||||
|
cache.delete(); |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
// create a new empty cache
|
||||||
|
directory.mkdirs(); |
||||||
|
cache = new DiskLruCache(directory, appVersion, valueCount, maxSize); |
||||||
|
cache.rebuildJournal(); |
||||||
|
return cache; |
||||||
|
} |
||||||
|
|
||||||
|
private void readJournal() throws IOException { |
||||||
|
InputStream in = new BufferedInputStream(new FileInputStream(journalFile), IO_BUFFER_SIZE); |
||||||
|
try { |
||||||
|
String magic = readAsciiLine(in); |
||||||
|
String version = readAsciiLine(in); |
||||||
|
String appVersionString = readAsciiLine(in); |
||||||
|
String valueCountString = readAsciiLine(in); |
||||||
|
String blank = readAsciiLine(in); |
||||||
|
if (!MAGIC.equals(magic) || !VERSION_1.equals(version) || !Integer.toString(appVersion) |
||||||
|
.equals(appVersionString) || !Integer.toString(valueCount).equals(valueCountString) || !"" |
||||||
|
.equals(blank)) { |
||||||
|
throw new IOException("unexpected journal header: [" |
||||||
|
+ magic |
||||||
|
+ ", " |
||||||
|
+ version |
||||||
|
+ ", " |
||||||
|
+ valueCountString |
||||||
|
+ ", " |
||||||
|
+ blank |
||||||
|
+ "]"); |
||||||
|
} |
||||||
|
|
||||||
|
while (true) { |
||||||
|
try { |
||||||
|
readJournalLine(readAsciiLine(in)); |
||||||
|
} catch (EOFException endOfJournal) { |
||||||
|
break; |
||||||
|
} |
||||||
|
} |
||||||
|
} finally { |
||||||
|
closeQuietly(in); |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
private void readJournalLine(String line) throws IOException { |
||||||
|
String[] parts = line.split(" "); |
||||||
|
if (parts.length < 2) { |
||||||
|
throw new IOException("unexpected journal line: " + line); |
||||||
|
} |
||||||
|
|
||||||
|
String key = parts[1]; |
||||||
|
if (parts[0].equals(REMOVE) && parts.length == 2) { |
||||||
|
lruEntries.remove(key); |
||||||
|
return; |
||||||
|
} |
||||||
|
|
||||||
|
Entry entry = lruEntries.get(key); |
||||||
|
if (entry == null) { |
||||||
|
entry = new Entry(key); |
||||||
|
lruEntries.put(key, entry); |
||||||
|
} |
||||||
|
|
||||||
|
if (parts[0].equals(CLEAN) && parts.length == 2 + valueCount) { |
||||||
|
entry.readable = true; |
||||||
|
entry.currentEditor = null; |
||||||
|
entry.setLengths(copyOfRange(parts, 2, parts.length)); |
||||||
|
} else if (parts[0].equals(DIRTY) && parts.length == 2) { |
||||||
|
entry.currentEditor = new Editor(entry); |
||||||
|
} else if (parts[0].equals(READ) && parts.length == 2) { |
||||||
|
// this work was already done by calling lruEntries.get()
|
||||||
|
} else { |
||||||
|
throw new IOException("unexpected journal line: " + line); |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* Computes the initial size and collects garbage as a part of opening the |
||||||
|
* cache. Dirty entries are assumed to be inconsistent and will be deleted. |
||||||
|
*/ |
||||||
|
private void processJournal() throws IOException { |
||||||
|
deleteIfExists(journalFileTmp); |
||||||
|
for (Iterator<Entry> i = lruEntries.values().iterator(); i.hasNext(); ) { |
||||||
|
Entry entry = i.next(); |
||||||
|
if (entry.currentEditor == null) { |
||||||
|
for (int t = 0; t < valueCount; t++) { |
||||||
|
size += entry.lengths[t]; |
||||||
|
} |
||||||
|
} else { |
||||||
|
entry.currentEditor = null; |
||||||
|
for (int t = 0; t < valueCount; t++) { |
||||||
|
deleteIfExists(entry.getCleanFile(t)); |
||||||
|
deleteIfExists(entry.getDirtyFile(t)); |
||||||
|
} |
||||||
|
i.remove(); |
||||||
|
} |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* Creates a new journal that omits redundant information. This replaces the |
||||||
|
* current journal if it exists. |
||||||
|
*/ |
||||||
|
private synchronized void rebuildJournal() throws IOException { |
||||||
|
if (journalWriter != null) { |
||||||
|
journalWriter.close(); |
||||||
|
} |
||||||
|
|
||||||
|
Writer writer = new BufferedWriter(new FileWriter(journalFileTmp), IO_BUFFER_SIZE); |
||||||
|
writer.write(MAGIC); |
||||||
|
writer.write("\n"); |
||||||
|
writer.write(VERSION_1); |
||||||
|
writer.write("\n"); |
||||||
|
writer.write(Integer.toString(appVersion)); |
||||||
|
writer.write("\n"); |
||||||
|
writer.write(Integer.toString(valueCount)); |
||||||
|
writer.write("\n"); |
||||||
|
writer.write("\n"); |
||||||
|
|
||||||
|
for (Entry entry : lruEntries.values()) { |
||||||
|
if (entry.currentEditor != null) { |
||||||
|
writer.write(DIRTY + ' ' + entry.key + '\n'); |
||||||
|
} else { |
||||||
|
writer.write(CLEAN + ' ' + entry.key + entry.getLengths() + '\n'); |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
writer.close(); |
||||||
|
journalFileTmp.renameTo(journalFile); |
||||||
|
journalWriter = new BufferedWriter(new FileWriter(journalFile, true), IO_BUFFER_SIZE); |
||||||
|
} |
||||||
|
|
||||||
|
private static void deleteIfExists(File file) throws IOException { |
||||||
|
// try {
|
||||||
|
// Libcore.os.remove(file.getPath());
|
||||||
|
// } catch (ErrnoException errnoException) {
|
||||||
|
// if (errnoException.errno != OsConstants.ENOENT) {
|
||||||
|
// throw errnoException.rethrowAsIOException();
|
||||||
|
// }
|
||||||
|
// }
|
||||||
|
if (file.exists() && !file.delete()) { |
||||||
|
throw new IOException(); |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* Returns a snapshot of the entry named {@code key}, or null if it doesn't |
||||||
|
* exist is not currently readable. If a value is returned, it is moved to |
||||||
|
* the head of the LRU queue. |
||||||
|
*/ |
||||||
|
public synchronized Snapshot get(String key) throws IOException { |
||||||
|
checkNotClosed(); |
||||||
|
validateKey(key); |
||||||
|
Entry entry = lruEntries.get(key); |
||||||
|
if (entry == null) { |
||||||
|
return null; |
||||||
|
} |
||||||
|
|
||||||
|
if (!entry.readable) { |
||||||
|
return null; |
||||||
|
} |
||||||
|
|
||||||
|
/* |
||||||
|
* Open all streams eagerly to guarantee that we see a single published |
||||||
|
* snapshot. If we opened streams lazily then the streams could come |
||||||
|
* from different edits. |
||||||
|
*/ |
||||||
|
InputStream[] ins = new InputStream[valueCount]; |
||||||
|
try { |
||||||
|
for (int i = 0; i < valueCount; i++) { |
||||||
|
ins[i] = new FileInputStream(entry.getCleanFile(i)); |
||||||
|
} |
||||||
|
} catch (FileNotFoundException e) { |
||||||
|
// a file must have been deleted manually!
|
||||||
|
return null; |
||||||
|
} |
||||||
|
|
||||||
|
redundantOpCount++; |
||||||
|
journalWriter.append(READ + ' ' + key + '\n'); |
||||||
|
if (journalRebuildRequired()) { |
||||||
|
executorService.submit(cleanupCallable); |
||||||
|
} |
||||||
|
|
||||||
|
return new Snapshot(key, entry.sequenceNumber, ins); |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* Returns an editor for the entry named {@code key}, or null if another |
||||||
|
* edit is in progress. |
||||||
|
*/ |
||||||
|
public Editor edit(String key) throws IOException { |
||||||
|
return edit(key, ANY_SEQUENCE_NUMBER); |
||||||
|
} |
||||||
|
|
||||||
|
private synchronized Editor edit(String key, long expectedSequenceNumber) throws IOException { |
||||||
|
checkNotClosed(); |
||||||
|
validateKey(key); |
||||||
|
Entry entry = lruEntries.get(key); |
||||||
|
if (expectedSequenceNumber != ANY_SEQUENCE_NUMBER && (entry == null |
||||||
|
|| entry.sequenceNumber != expectedSequenceNumber)) { |
||||||
|
return null; // snapshot is stale
|
||||||
|
} |
||||||
|
if (entry == null) { |
||||||
|
entry = new Entry(key); |
||||||
|
lruEntries.put(key, entry); |
||||||
|
} else if (entry.currentEditor != null) { |
||||||
|
return null; // another edit is in progress
|
||||||
|
} |
||||||
|
|
||||||
|
Editor editor = new Editor(entry); |
||||||
|
entry.currentEditor = editor; |
||||||
|
|
||||||
|
// flush the journal before creating files to prevent file leaks
|
||||||
|
journalWriter.write(DIRTY + ' ' + key + '\n'); |
||||||
|
journalWriter.flush(); |
||||||
|
return editor; |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* Returns the directory where this cache stores its data. |
||||||
|
*/ |
||||||
|
public File getDirectory() { |
||||||
|
return directory; |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* Returns the maximum number of bytes that this cache should use to store |
||||||
|
* its data. |
||||||
|
*/ |
||||||
|
public long maxSize() { |
||||||
|
return maxSize; |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* Returns the number of bytes currently being used to store the values in |
||||||
|
* this cache. This may be greater than the max size if a background |
||||||
|
* deletion is pending. |
||||||
|
*/ |
||||||
|
public synchronized long size() { |
||||||
|
return size; |
||||||
|
} |
||||||
|
|
||||||
|
private synchronized void completeEdit(Editor editor, boolean success) throws IOException { |
||||||
|
Entry entry = editor.entry; |
||||||
|
if (entry.currentEditor != editor) { |
||||||
|
throw new IllegalStateException(); |
||||||
|
} |
||||||
|
|
||||||
|
// if this edit is creating the entry for the first time, every index must have a value
|
||||||
|
if (success && !entry.readable) { |
||||||
|
for (int i = 0; i < valueCount; i++) { |
||||||
|
if (!entry.getDirtyFile(i).exists()) { |
||||||
|
editor.abort(); |
||||||
|
throw new IllegalStateException("edit didn't create file " + i); |
||||||
|
} |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
for (int i = 0; i < valueCount; i++) { |
||||||
|
File dirty = entry.getDirtyFile(i); |
||||||
|
if (success) { |
||||||
|
if (dirty.exists()) { |
||||||
|
File clean = entry.getCleanFile(i); |
||||||
|
dirty.renameTo(clean); |
||||||
|
long oldLength = entry.lengths[i]; |
||||||
|
long newLength = clean.length(); |
||||||
|
entry.lengths[i] = newLength; |
||||||
|
size = size - oldLength + newLength; |
||||||
|
} |
||||||
|
} else { |
||||||
|
deleteIfExists(dirty); |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
redundantOpCount++; |
||||||
|
entry.currentEditor = null; |
||||||
|
if (entry.readable | success) { |
||||||
|
entry.readable = true; |
||||||
|
journalWriter.write(CLEAN + ' ' + entry.key + entry.getLengths() + '\n'); |
||||||
|
if (success) { |
||||||
|
entry.sequenceNumber = nextSequenceNumber++; |
||||||
|
} |
||||||
|
} else { |
||||||
|
lruEntries.remove(entry.key); |
||||||
|
journalWriter.write(REMOVE + ' ' + entry.key + '\n'); |
||||||
|
} |
||||||
|
|
||||||
|
if (size > maxSize || journalRebuildRequired()) { |
||||||
|
executorService.submit(cleanupCallable); |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* We only rebuild the journal when it will halve the size of the journal |
||||||
|
* and eliminate at least 2000 ops. |
||||||
|
*/ |
||||||
|
private boolean journalRebuildRequired() { |
||||||
|
final int REDUNDANT_OP_COMPACT_THRESHOLD = 2000; |
||||||
|
return redundantOpCount >= REDUNDANT_OP_COMPACT_THRESHOLD |
||||||
|
&& redundantOpCount >= lruEntries.size(); |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* Drops the entry for {@code key} if it exists and can be removed. Entries |
||||||
|
* actively being edited cannot be removed. |
||||||
|
* |
||||||
|
* @return true if an entry was removed. |
||||||
|
*/ |
||||||
|
public synchronized boolean remove(String key) throws IOException { |
||||||
|
checkNotClosed(); |
||||||
|
validateKey(key); |
||||||
|
Entry entry = lruEntries.get(key); |
||||||
|
if (entry == null || entry.currentEditor != null) { |
||||||
|
return false; |
||||||
|
} |
||||||
|
|
||||||
|
for (int i = 0; i < valueCount; i++) { |
||||||
|
File file = entry.getCleanFile(i); |
||||||
|
if (!file.delete()) { |
||||||
|
throw new IOException("failed to delete " + file); |
||||||
|
} |
||||||
|
size -= entry.lengths[i]; |
||||||
|
entry.lengths[i] = 0; |
||||||
|
} |
||||||
|
|
||||||
|
redundantOpCount++; |
||||||
|
journalWriter.append(REMOVE + ' ' + key + '\n'); |
||||||
|
lruEntries.remove(key); |
||||||
|
|
||||||
|
if (journalRebuildRequired()) { |
||||||
|
executorService.submit(cleanupCallable); |
||||||
|
} |
||||||
|
|
||||||
|
return true; |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* Returns true if this cache has been closed. |
||||||
|
*/ |
||||||
|
public boolean isClosed() { |
||||||
|
return journalWriter == null; |
||||||
|
} |
||||||
|
|
||||||
|
private void checkNotClosed() { |
||||||
|
if (journalWriter == null) { |
||||||
|
throw new IllegalStateException("cache is closed"); |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* Force buffered operations to the filesystem. |
||||||
|
*/ |
||||||
|
public synchronized void flush() throws IOException { |
||||||
|
checkNotClosed(); |
||||||
|
trimToSize(); |
||||||
|
journalWriter.flush(); |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* Closes this cache. Stored values will remain on the filesystem. |
||||||
|
*/ |
||||||
|
public synchronized void close() throws IOException { |
||||||
|
if (journalWriter == null) { |
||||||
|
return; // already closed
|
||||||
|
} |
||||||
|
for (Entry entry : new ArrayList<Entry>(lruEntries.values())) { |
||||||
|
if (entry.currentEditor != null) { |
||||||
|
entry.currentEditor.abort(); |
||||||
|
} |
||||||
|
} |
||||||
|
trimToSize(); |
||||||
|
journalWriter.close(); |
||||||
|
journalWriter = null; |
||||||
|
} |
||||||
|
|
||||||
|
private void trimToSize() throws IOException { |
||||||
|
while (size > maxSize) { |
||||||
|
// Map.Entry<String, Entry> toEvict = lruEntries.eldest();
|
||||||
|
final Map.Entry<String, Entry> toEvict = lruEntries.entrySet().iterator().next(); |
||||||
|
remove(toEvict.getKey()); |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* Closes the cache and deletes all of its stored values. This will delete |
||||||
|
* all files in the cache directory including files that weren't created by |
||||||
|
* the cache. |
||||||
|
*/ |
||||||
|
public void delete() throws IOException { |
||||||
|
close(); |
||||||
|
deleteContents(directory); |
||||||
|
} |
||||||
|
|
||||||
|
private void validateKey(String key) { |
||||||
|
if (key.contains(" ") || key.contains("\n") || key.contains("\r")) { |
||||||
|
throw new IllegalArgumentException( |
||||||
|
"keys must not contain spaces or newlines: \"" + key + "\""); |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
private static String inputStreamToString(InputStream in) throws IOException { |
||||||
|
return readFully(new InputStreamReader(in, UTF_8)); |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* A snapshot of the values for an entry. |
||||||
|
*/ |
||||||
|
public final class Snapshot implements Closeable { |
||||||
|
private final String key; |
||||||
|
private final long sequenceNumber; |
||||||
|
private final InputStream[] ins; |
||||||
|
|
||||||
|
private Snapshot(String key, long sequenceNumber, InputStream[] ins) { |
||||||
|
this.key = key; |
||||||
|
this.sequenceNumber = sequenceNumber; |
||||||
|
this.ins = ins; |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* Returns an editor for this snapshot's entry, or null if either the |
||||||
|
* entry has changed since this snapshot was created or if another edit |
||||||
|
* is in progress. |
||||||
|
*/ |
||||||
|
public Editor edit() throws IOException { |
||||||
|
return DiskLruCache.this.edit(key, sequenceNumber); |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* Returns the unbuffered stream with the value for {@code index}. |
||||||
|
*/ |
||||||
|
public InputStream getInputStream(int index) { |
||||||
|
return ins[index]; |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* Returns the string value for {@code index}. |
||||||
|
*/ |
||||||
|
public String getString(int index) throws IOException { |
||||||
|
return inputStreamToString(getInputStream(index)); |
||||||
|
} |
||||||
|
|
||||||
|
@Override |
||||||
|
public void close() { |
||||||
|
for (InputStream in : ins) { |
||||||
|
closeQuietly(in); |
||||||
|
} |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* Edits the values for an entry. |
||||||
|
*/ |
||||||
|
public final class Editor { |
||||||
|
private final Entry entry; |
||||||
|
private boolean hasErrors; |
||||||
|
|
||||||
|
private Editor(Entry entry) { |
||||||
|
this.entry = entry; |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* Returns an unbuffered input stream to read the last committed value, |
||||||
|
* or null if no value has been committed. |
||||||
|
*/ |
||||||
|
public InputStream newInputStream(int index) throws IOException { |
||||||
|
synchronized (DiskLruCache.this) { |
||||||
|
if (entry.currentEditor != this) { |
||||||
|
throw new IllegalStateException(); |
||||||
|
} |
||||||
|
if (!entry.readable) { |
||||||
|
return null; |
||||||
|
} |
||||||
|
return new FileInputStream(entry.getCleanFile(index)); |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* Returns the last committed value as a string, or null if no value |
||||||
|
* has been committed. |
||||||
|
*/ |
||||||
|
public String getString(int index) throws IOException { |
||||||
|
InputStream in = newInputStream(index); |
||||||
|
return in != null ? inputStreamToString(in) : null; |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* Returns a new unbuffered output stream to write the value at |
||||||
|
* {@code index}. If the underlying output stream encounters errors |
||||||
|
* when writing to the filesystem, this edit will be aborted when |
||||||
|
* {@link #commit} is called. The returned output stream does not throw |
||||||
|
* IOExceptions. |
||||||
|
*/ |
||||||
|
public OutputStream newOutputStream(int index) throws IOException { |
||||||
|
synchronized (DiskLruCache.this) { |
||||||
|
if (entry.currentEditor != this) { |
||||||
|
throw new IllegalStateException(); |
||||||
|
} |
||||||
|
return new FaultHidingOutputStream(new FileOutputStream(entry.getDirtyFile(index))); |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* Sets the value at {@code index} to {@code value}. |
||||||
|
*/ |
||||||
|
public void set(int index, String value) throws IOException { |
||||||
|
Writer writer = null; |
||||||
|
try { |
||||||
|
writer = new OutputStreamWriter(newOutputStream(index), UTF_8); |
||||||
|
writer.write(value); |
||||||
|
} finally { |
||||||
|
closeQuietly(writer); |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* Commits this edit so it is visible to readers. This releases the |
||||||
|
* edit lock so another edit may be started on the same key. |
||||||
|
*/ |
||||||
|
public void commit() throws IOException { |
||||||
|
if (hasErrors) { |
||||||
|
completeEdit(this, false); |
||||||
|
remove(entry.key); // the previous entry is stale
|
||||||
|
} else { |
||||||
|
completeEdit(this, true); |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* Aborts this edit. This releases the edit lock so another edit may be |
||||||
|
* started on the same key. |
||||||
|
*/ |
||||||
|
public void abort() throws IOException { |
||||||
|
completeEdit(this, false); |
||||||
|
} |
||||||
|
|
||||||
|
private class FaultHidingOutputStream extends FilterOutputStream { |
||||||
|
private FaultHidingOutputStream(OutputStream out) { |
||||||
|
super(out); |
||||||
|
} |
||||||
|
|
||||||
|
@Override |
||||||
|
public void write(int oneByte) { |
||||||
|
try { |
||||||
|
out.write(oneByte); |
||||||
|
} catch (IOException e) { |
||||||
|
hasErrors = true; |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
@Override |
||||||
|
public void write(byte[] buffer, int offset, int length) { |
||||||
|
try { |
||||||
|
out.write(buffer, offset, length); |
||||||
|
} catch (IOException e) { |
||||||
|
hasErrors = true; |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
@Override |
||||||
|
public void close() { |
||||||
|
try { |
||||||
|
out.close(); |
||||||
|
} catch (IOException e) { |
||||||
|
hasErrors = true; |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
@Override |
||||||
|
public void flush() { |
||||||
|
try { |
||||||
|
out.flush(); |
||||||
|
} catch (IOException e) { |
||||||
|
hasErrors = true; |
||||||
|
} |
||||||
|
} |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
private final class Entry { |
||||||
|
private final String key; |
||||||
|
|
||||||
|
/** |
||||||
|
* Lengths of this entry's files. |
||||||
|
*/ |
||||||
|
private final long[] lengths; |
||||||
|
|
||||||
|
/** |
||||||
|
* True if this entry has ever been published |
||||||
|
*/ |
||||||
|
private boolean readable; |
||||||
|
|
||||||
|
/** |
||||||
|
* The ongoing edit or null if this entry is not being edited. |
||||||
|
*/ |
||||||
|
private Editor currentEditor; |
||||||
|
|
||||||
|
/** |
||||||
|
* The sequence number of the most recently committed edit to this entry. |
||||||
|
*/ |
||||||
|
private long sequenceNumber; |
||||||
|
|
||||||
|
private Entry(String key) { |
||||||
|
this.key = key; |
||||||
|
this.lengths = new long[valueCount]; |
||||||
|
} |
||||||
|
|
||||||
|
public String getLengths() throws IOException { |
||||||
|
StringBuilder result = new StringBuilder(); |
||||||
|
for (long size : lengths) { |
||||||
|
result.append(' ').append(size); |
||||||
|
} |
||||||
|
return result.toString(); |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* Set lengths using decimal numbers like "10123". |
||||||
|
*/ |
||||||
|
private void setLengths(String[] strings) throws IOException { |
||||||
|
if (strings.length != valueCount) { |
||||||
|
throw invalidLengths(strings); |
||||||
|
} |
||||||
|
|
||||||
|
try { |
||||||
|
for (int i = 0; i < strings.length; i++) { |
||||||
|
lengths[i] = Long.parseLong(strings[i]); |
||||||
|
} |
||||||
|
} catch (NumberFormatException e) { |
||||||
|
throw invalidLengths(strings); |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
private IOException invalidLengths(String[] strings) throws IOException { |
||||||
|
throw new IOException("unexpected journal line: " + Arrays.toString(strings)); |
||||||
|
} |
||||||
|
|
||||||
|
public File getCleanFile(int i) { |
||||||
|
return new File(directory, key + "." + i); |
||||||
|
} |
||||||
|
|
||||||
|
public File getDirtyFile(int i) { |
||||||
|
return new File(directory, key + "." + i + ".tmp"); |
||||||
|
} |
||||||
|
} |
||||||
|
} |
@ -0,0 +1,21 @@ |
|||||||
|
package com.arialyy.frame.cache; |
||||||
|
|
||||||
|
import android.os.Environment; |
||||||
|
|
||||||
|
/** |
||||||
|
* Created by AriaL on 2017/11/26. |
||||||
|
*/ |
||||||
|
|
||||||
|
public class PathConstaant { |
||||||
|
private static final String WP_DIR = "windPath"; |
||||||
|
|
||||||
|
/** |
||||||
|
* 获取APK升级路径 |
||||||
|
*/ |
||||||
|
public static String getWpPath() { |
||||||
|
return Environment.getExternalStorageDirectory().getPath() |
||||||
|
+ "/" |
||||||
|
+ WP_DIR |
||||||
|
+ "/update/windPath.apk"; |
||||||
|
} |
||||||
|
} |
@ -0,0 +1,9 @@ |
|||||||
|
package com.arialyy.frame.config; |
||||||
|
|
||||||
|
/** |
||||||
|
* Created by AriaL on 2017/11/26. |
||||||
|
*/ |
||||||
|
|
||||||
|
public interface CommonConstant { |
||||||
|
boolean DEBUG = true; |
||||||
|
} |
@ -0,0 +1,9 @@ |
|||||||
|
package com.arialyy.frame.config; |
||||||
|
|
||||||
|
/** |
||||||
|
* Created by AriaL on 2017/11/26. |
||||||
|
*/ |
||||||
|
|
||||||
|
public interface NetConstant { |
||||||
|
String BASE_URL = "http://wwww.baidu.com/"; |
||||||
|
} |
@ -0,0 +1,83 @@ |
|||||||
|
package com.arialyy.frame.util; |
||||||
|
|
||||||
|
import android.content.Context; |
||||||
|
import android.graphics.Bitmap; |
||||||
|
import android.graphics.BitmapFactory; |
||||||
|
import android.media.MediaMetadataRetriever; |
||||||
|
import android.media.MediaPlayer; |
||||||
|
import android.net.Uri; |
||||||
|
import java.io.IOException; |
||||||
|
import java.util.Formatter; |
||||||
|
import java.util.Locale; |
||||||
|
|
||||||
|
/** |
||||||
|
* Created by Aria.Lao on 2018/1/4. |
||||||
|
* 多媒体工具 |
||||||
|
*/ |
||||||
|
public class MediaUtil { |
||||||
|
private MediaUtil() { |
||||||
|
throw new AssertionError(); |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* 获取音频、视频播放长度 |
||||||
|
*/ |
||||||
|
public static long getDuration(String path) { |
||||||
|
MediaPlayer mediaPlayer = new MediaPlayer(); |
||||||
|
try { |
||||||
|
mediaPlayer.setDataSource(path); |
||||||
|
} catch (IOException e) { |
||||||
|
e.printStackTrace(); |
||||||
|
return -1; |
||||||
|
} |
||||||
|
int duration = mediaPlayer.getDuration(); |
||||||
|
mediaPlayer.release(); |
||||||
|
return duration; |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* 格式化视频时间 |
||||||
|
*/ |
||||||
|
public static String convertViewTime(long timeMs) { |
||||||
|
int totalSeconds = (int) (timeMs / 1000); |
||||||
|
|
||||||
|
int seconds = totalSeconds % 60; |
||||||
|
int minutes = (totalSeconds / 60) % 60; |
||||||
|
int hours = totalSeconds / 3600; |
||||||
|
StringBuilder sFormatBuilder = new StringBuilder(); |
||||||
|
Formatter sFormatter = new Formatter(sFormatBuilder, Locale.getDefault()); |
||||||
|
sFormatBuilder.setLength(0); |
||||||
|
if (hours > 0) { |
||||||
|
return sFormatter.format("%02d:%02d:%02d", hours, minutes, seconds).toString(); |
||||||
|
} else { |
||||||
|
return sFormatter.format("%02d:%02d", minutes, seconds).toString(); |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* 获取音频封面 |
||||||
|
*/ |
||||||
|
public static Bitmap getArtwork(Context context, String url) { |
||||||
|
Uri selectedAudio = Uri.parse(url); |
||||||
|
MediaMetadataRetriever myRetriever = new MediaMetadataRetriever(); |
||||||
|
myRetriever.setDataSource(context, selectedAudio); // the URI of audio file
|
||||||
|
byte[] artwork; |
||||||
|
artwork = myRetriever.getEmbeddedPicture(); |
||||||
|
if (artwork != null) { |
||||||
|
return BitmapFactory.decodeByteArray(artwork, 0, artwork.length); |
||||||
|
} |
||||||
|
return null; |
||||||
|
} |
||||||
|
|
||||||
|
public static byte[] getArtworkAsByte(Context context, String url) { |
||||||
|
Uri selectedAudio = Uri.parse(url); |
||||||
|
MediaMetadataRetriever myRetriever = new MediaMetadataRetriever(); |
||||||
|
myRetriever.setDataSource(context, selectedAudio); // the URI of audio file
|
||||||
|
byte[] artwork; |
||||||
|
artwork = myRetriever.getEmbeddedPicture(); |
||||||
|
if (artwork != null) { |
||||||
|
return artwork; |
||||||
|
} |
||||||
|
return null; |
||||||
|
} |
||||||
|
} |
@ -0,0 +1,90 @@ |
|||||||
|
|
||||||
|
import android.os.Bundle; |
||||||
|
import android.util.Log; |
||||||
|
import android.view.View; |
||||||
|
import com.arialyy.annotations.Download; |
||||||
|
import com.arialyy.aria.core.Aria; |
||||||
|
import com.arialyy.aria.core.download.DownloadTask; |
||||||
|
import com.arialyy.aria.util.CommonUtil; |
||||||
|
import com.arialyy.simple.R; |
||||||
|
import com.arialyy.simple.base.BaseActivity; |
||||||
|
import com.arialyy.simple.databinding.ActivityTestBinding; |
||||||
|
import java.io.File; |
||||||
|
|
||||||
|
/** |
||||||
|
* Created by lyy on 2019/5/28. |
||||||
|
* Ftp 下载 |
||||||
|
* <a href="https://aria.laoyuyu.me/aria_doc/">文档</> |
||||||
|
*/ |
||||||
|
public class FtpDownload extends BaseActivity<ActivityTestBinding> { |
||||||
|
String TAG = "TestFTPActivity"; |
||||||
|
private final String URL = "ftp://192.168.1.3:21/download//AriaPrj.rar"; |
||||||
|
private final String FILE_PATH = "/mnt/sdcard/AriaPrj.rar"; |
||||||
|
|
||||||
|
@Override protected int setLayoutId() { |
||||||
|
return R.layout.activity_test; |
||||||
|
} |
||||||
|
|
||||||
|
@Override protected void init(Bundle savedInstanceState) { |
||||||
|
super.init(savedInstanceState); |
||||||
|
mBar.setVisibility(View.GONE); |
||||||
|
Aria.download(this).register(); |
||||||
|
} |
||||||
|
|
||||||
|
@Download.onWait void onWait(DownloadTask task) { |
||||||
|
Log.d(TAG, "wait ==> " + task.getEntity().getFileName()); |
||||||
|
} |
||||||
|
|
||||||
|
@Download.onPre protected void onPre(DownloadTask task) { |
||||||
|
Log.d(TAG, "onPre"); |
||||||
|
} |
||||||
|
|
||||||
|
@Download.onTaskStart void taskStart(DownloadTask task) { |
||||||
|
Log.d(TAG, "onStart"); |
||||||
|
} |
||||||
|
|
||||||
|
@Download.onTaskRunning protected void running(DownloadTask task) { |
||||||
|
Log.d(TAG, "running,speed=" + task.getConvertSpeed()); |
||||||
|
} |
||||||
|
|
||||||
|
@Download.onTaskResume void taskResume(DownloadTask task) { |
||||||
|
Log.d(TAG, "resume"); |
||||||
|
} |
||||||
|
|
||||||
|
@Download.onTaskStop void taskStop(DownloadTask task) { |
||||||
|
Log.d(TAG, "stop"); |
||||||
|
} |
||||||
|
|
||||||
|
@Download.onTaskCancel void taskCancel(DownloadTask task) { |
||||||
|
Log.d(TAG, "cancel"); |
||||||
|
} |
||||||
|
|
||||||
|
@Download.onTaskFail void taskFail(DownloadTask task) { |
||||||
|
Log.d(TAG, "fail"); |
||||||
|
} |
||||||
|
|
||||||
|
@Download.onTaskComplete void taskComplete(DownloadTask task) { |
||||||
|
Log.d(TAG, "complete, md5 => " + CommonUtil.getFileMD5(new File(task.getKey()))); |
||||||
|
} |
||||||
|
|
||||||
|
public void onClick(View view) { |
||||||
|
switch (view.getId()) { |
||||||
|
case R.id.start: |
||||||
|
Aria.download(this) |
||||||
|
.loadFtp(URL) |
||||||
|
.setFilePath(FILE_PATH) |
||||||
|
.login("lao", "123456") |
||||||
|
//.asFtps() // ftps 配置
|
||||||
|
//.setStorePath("/mnt/sdcard/Download/server.crt") //设置证书路径
|
||||||
|
// .setAlias("www.laoyuyu.me") // 设置证书别名
|
||||||
|
.start(); |
||||||
|
break; |
||||||
|
case R.id.stop: |
||||||
|
Aria.download(this).loadFtp(FILE_PATH).stop(); |
||||||
|
break; |
||||||
|
case R.id.cancel: |
||||||
|
Aria.download(this).loadFtp(FILE_PATH).cancel(); |
||||||
|
break; |
||||||
|
} |
||||||
|
} |
||||||
|
} |
@ -0,0 +1,127 @@ |
|||||||
|
/* |
||||||
|
* Copyright (C) 2016 AriaLyy(https://github.com/AriaLyy/Aria)
|
||||||
|
* |
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License"); |
||||||
|
* you may not use this file except in compliance with the License. |
||||||
|
* You may obtain a copy of the License at |
||||||
|
* |
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
* |
||||||
|
* Unless required by applicable law or agreed to in writing, software |
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS, |
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
||||||
|
* See the License for the specific language governing permissions and |
||||||
|
* limitations under the License. |
||||||
|
*/ |
||||||
|
import android.app.Activity; |
||||||
|
import android.os.Bundle; |
||||||
|
import android.os.Environment; |
||||||
|
import android.util.Log; |
||||||
|
import android.view.View; |
||||||
|
import com.arialyy.annotations.Upload; |
||||||
|
import com.arialyy.aria.core.Aria; |
||||||
|
import com.arialyy.aria.core.common.ftp.FtpInterceptHandler; |
||||||
|
import com.arialyy.aria.core.common.ftp.IFtpUploadInterceptor; |
||||||
|
import com.arialyy.aria.core.upload.UploadEntity; |
||||||
|
import com.arialyy.aria.core.upload.UploadTask; |
||||||
|
import com.arialyy.frame.util.FileUtil; |
||||||
|
import com.arialyy.frame.util.show.T; |
||||||
|
import com.arialyy.simple.R; |
||||||
|
import java.io.File; |
||||||
|
import java.util.List; |
||||||
|
|
||||||
|
/** |
||||||
|
* Created by lyy on 2019/5/28. Ftp 文件上传demo |
||||||
|
* <a href="https://aria.laoyuyu.me/aria_doc/">文档</> |
||||||
|
*/ |
||||||
|
public class FtpUpload extends Activity { |
||||||
|
private static final String TAG = "FtpUpload"; |
||||||
|
private String mFilePath = Environment.getExternalStorageDirectory().getPath() + "/AriaPrj.rar"; |
||||||
|
private String mUrl = "ftp://172.168.1.2:2121/aa/你好"; |
||||||
|
|
||||||
|
@Override protected void onCreate(Bundle savedInstanceState) { |
||||||
|
super.onCreate(savedInstanceState); |
||||||
|
Aria.upload(this).register(); |
||||||
|
// 读取历史记录信息
|
||||||
|
UploadEntity entity = Aria.upload(this).getUploadEntity(mFilePath); |
||||||
|
if (entity != null) { |
||||||
|
// 设置界面的进度、文件大小等信息
|
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
public void onClick(View view) { |
||||||
|
switch (view.getId()) { |
||||||
|
case R.id.start: |
||||||
|
if (Aria.upload(this).load(mFilePath).isRunning()) { |
||||||
|
Aria.upload(this).loadFtp(mFilePath).stop(); // 停止任务
|
||||||
|
} else { |
||||||
|
Aria.upload(this) |
||||||
|
.loadFtp(mFilePath) // 需要上传的文件
|
||||||
|
.setUploadUrl(mUrl) // 服务器地址
|
||||||
|
// 如果ftp服务器端有同名文件,可通过拦截器处理是覆盖服务器端文件,还是修改文件名
|
||||||
|
.setUploadInterceptor( |
||||||
|
new IFtpUploadInterceptor() { |
||||||
|
|
||||||
|
@Override |
||||||
|
public FtpInterceptHandler onIntercept(UploadEntity entity, |
||||||
|
List<String> fileList) { |
||||||
|
FtpInterceptHandler.Builder builder = new FtpInterceptHandler.Builder(); |
||||||
|
//builder.coverServerFile();
|
||||||
|
builder.resetFileName("test.zip"); |
||||||
|
return builder.build(); |
||||||
|
} |
||||||
|
}) |
||||||
|
.login("N0rI", "0qcK") |
||||||
|
.start(); |
||||||
|
} |
||||||
|
break; |
||||||
|
case R.id.cancel: |
||||||
|
Aria.upload(this).loadFtp(mFilePath).cancel(); |
||||||
|
break; |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
@Upload.onWait void onWait(UploadTask task) { |
||||||
|
Log.d(TAG, task.getTaskName() + "_wait"); |
||||||
|
} |
||||||
|
|
||||||
|
@Upload.onPre public void onPre(UploadTask task) { |
||||||
|
setFileSize(task.getConvertFileSize()); |
||||||
|
} |
||||||
|
|
||||||
|
@Upload.onTaskStart public void taskStart(UploadTask task) { |
||||||
|
Log.d(TAG, "开始上传,md5:" + FileUtil.getFileMD5(new File(task.getEntity().getFilePath()))); |
||||||
|
} |
||||||
|
|
||||||
|
@Upload.onTaskResume public void taskResume(UploadTask task) { |
||||||
|
Log.d(TAG, "恢复上传"); |
||||||
|
} |
||||||
|
|
||||||
|
@Upload.onTaskStop public void taskStop(UploadTask task) { |
||||||
|
setSpeed(""); |
||||||
|
Log.d(TAG, "停止上传"); |
||||||
|
} |
||||||
|
|
||||||
|
@Upload.onTaskCancel public void taskCancel(UploadTask task) { |
||||||
|
setSpeed(""); |
||||||
|
setFileSize(""); |
||||||
|
setProgress(0); |
||||||
|
Log.d(TAG, "删除任务"); |
||||||
|
} |
||||||
|
|
||||||
|
@Upload.onTaskFail public void taskFail(UploadTask task) { |
||||||
|
Log.d(TAG, "上传失败"); |
||||||
|
} |
||||||
|
|
||||||
|
@Upload.onTaskRunning public void taskRunning(UploadTask task) { |
||||||
|
Log.d(TAG, "PP = " + task.getPercent()); |
||||||
|
setProgress(task.getPercent()); |
||||||
|
setSpeed(task.getConvertSpeed()); |
||||||
|
} |
||||||
|
|
||||||
|
@Upload.onTaskComplete public void taskComplete(UploadTask task) { |
||||||
|
setProgress(100); |
||||||
|
setSpeed(""); |
||||||
|
T.showShort(this, "文件:" + task.getEntity().getFileName() + ",上传完成"); |
||||||
|
} |
||||||
|
} |
@ -0,0 +1,119 @@ |
|||||||
|
/* |
||||||
|
* Copyright (C) 2016 AriaLyy(https://github.com/AriaLyy/Aria)
|
||||||
|
* |
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License"); |
||||||
|
* you may not use this file except in compliance with the License. |
||||||
|
* You may obtain a copy of the License at |
||||||
|
* |
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
* |
||||||
|
* Unless required by applicable law or agreed to in writing, software |
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS, |
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
||||||
|
* See the License for the specific language governing permissions and |
||||||
|
* limitations under the License. |
||||||
|
*/ |
||||||
|
package com.arialyy.simple.core.upload; |
||||||
|
|
||||||
|
import android.app.Activity; |
||||||
|
import android.os.Bundle; |
||||||
|
import android.os.Environment; |
||||||
|
import android.util.Log; |
||||||
|
import android.view.View; |
||||||
|
import com.arialyy.annotations.Download; |
||||||
|
import com.arialyy.aria.core.Aria; |
||||||
|
import com.arialyy.aria.core.common.ftp.FtpInterceptHandler; |
||||||
|
import com.arialyy.aria.core.common.ftp.IFtpUploadInterceptor; |
||||||
|
import com.arialyy.aria.core.download.DownloadEntity; |
||||||
|
import com.arialyy.aria.core.upload.UploadEntity; |
||||||
|
import com.arialyy.aria.core.upload.UploadTask; |
||||||
|
import com.arialyy.frame.util.FileUtil; |
||||||
|
import com.arialyy.frame.util.show.T; |
||||||
|
import com.arialyy.simple.R; |
||||||
|
import java.io.File; |
||||||
|
import java.util.List; |
||||||
|
|
||||||
|
/** |
||||||
|
* Created by lyy on 2017/7/28. HTTP 文件下载demo |
||||||
|
* <a href="https://aria.laoyuyu.me/aria_doc/">文档</> |
||||||
|
*/ |
||||||
|
public class FtpUpload extends Activity { |
||||||
|
private static final String TAG = "FtpUpload"; |
||||||
|
private String mFilePath = Environment.getExternalStorageDirectory().getPath() + "/test.apk"; |
||||||
|
private String mUrl = "http://hzdown.muzhiwan.com/2017/05/08/nl.noio.kingdom_59104935e56f0.apk"; |
||||||
|
|
||||||
|
@Override protected void onCreate(Bundle savedInstanceState) { |
||||||
|
super.onCreate(savedInstanceState); |
||||||
|
Aria.download(this).register(); |
||||||
|
// 读取历史记录信息
|
||||||
|
DownloadEntity entity = Aria.download(this).getDownloadEntity(mFilePath); |
||||||
|
if (entity != null) { |
||||||
|
// 设置界面的进度、文件大小等信息
|
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
public void onClick(View view) { |
||||||
|
switch (view.getId()) { |
||||||
|
case R.id.start: |
||||||
|
if (Aria.download(this).load(mUrl).isRunning()) { |
||||||
|
Aria.download(this).load(mUrl).stop(); // 停止任务
|
||||||
|
} else { |
||||||
|
Aria.download(this) |
||||||
|
.load(mUrl) // 下载url
|
||||||
|
.setFilePath(mFilePath) // 文件保存路径
|
||||||
|
//.addHeader(key, value) // 添加头
|
||||||
|
//.asPost() //或 asGet()
|
||||||
|
//.setParam() // 设置参数
|
||||||
|
.start(); |
||||||
|
} |
||||||
|
break; |
||||||
|
case R.id.cancel: |
||||||
|
Aria.download(this).load(mUrl).cancel(); |
||||||
|
break; |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
@Download.onWait void onWait(DownloadTask task) { |
||||||
|
Log.d(TAG, task.getTaskName() + "_wait"); |
||||||
|
} |
||||||
|
|
||||||
|
@Download.onPre public void onPre(DownloadTask task) { |
||||||
|
setFileSize(task.getConvertFileSize()); |
||||||
|
} |
||||||
|
|
||||||
|
@Download.onTaskStart public void taskStart(DownloadTask task) { |
||||||
|
Log.d(TAG, "开始下载,md5:" + FileUtil.getFileMD5(new File(task.getEntity().getFilePath()))); |
||||||
|
} |
||||||
|
|
||||||
|
@Download.onTaskResume public void taskResume(DownloadTask task) { |
||||||
|
Log.d(TAG, "恢复下载"); |
||||||
|
} |
||||||
|
|
||||||
|
@Download.onTaskStop public void taskStop(DownloadTask task) { |
||||||
|
setSpeed(""); |
||||||
|
Log.d(TAG, "停止下载"); |
||||||
|
} |
||||||
|
|
||||||
|
@Download.onTaskCancel public void taskCancel(DownloadTask task) { |
||||||
|
setSpeed(""); |
||||||
|
setFileSize(""); |
||||||
|
setProgress(0); |
||||||
|
Log.d(TAG, "删除任务"); |
||||||
|
} |
||||||
|
|
||||||
|
@Download.onTaskFail public void taskFail(DownloadTask task) { |
||||||
|
Log.d(TAG, "下载失败"); |
||||||
|
} |
||||||
|
|
||||||
|
@Download.onTaskRunning public void taskRunning(DownloadTask task) { |
||||||
|
Log.d(TAG, "PP = " + task.getPercent()); |
||||||
|
setProgress(task.getPercent()); |
||||||
|
setSpeed(task.getConvertSpeed()); |
||||||
|
} |
||||||
|
|
||||||
|
@Download.onTaskComplete public void taskComplete(DownloadTask task) { |
||||||
|
setProgress(100); |
||||||
|
setSpeed(""); |
||||||
|
T.showShort(this, "文件:" + task.getEntity().getFileName() + ",下载完成"); |
||||||
|
} |
||||||
|
} |
@ -0,0 +1,170 @@ |
|||||||
|
/* |
||||||
|
* Copyright (C) 2016 AriaLyy(https://github.com/AriaLyy/Aria) |
||||||
|
* |
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License"); |
||||||
|
* you may not use this file except in compliance with the License. |
||||||
|
* You may obtain a copy of the License at |
||||||
|
* |
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0 |
||||||
|
* |
||||||
|
* Unless required by applicable law or agreed to in writing, software |
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS, |
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
||||||
|
* See the License for the specific language governing permissions and |
||||||
|
* limitations under the License. |
||||||
|
*/ |
||||||
|
import android.os.Bundle |
||||||
|
import android.os.Environment |
||||||
|
import android.util.Log |
||||||
|
import android.view.View |
||||||
|
import android.widget.Button |
||||||
|
import android.widget.Toast |
||||||
|
import com.arialyy.annotations.Download |
||||||
|
import com.arialyy.aria.core.Aria |
||||||
|
import com.arialyy.aria.core.download.DownloadTarget |
||||||
|
import com.arialyy.aria.core.download.DownloadTask |
||||||
|
import com.arialyy.aria.core.inf.IEntity |
||||||
|
import com.arialyy.simple.R |
||||||
|
import com.arialyy.simple.base.BaseActivity |
||||||
|
import com.arialyy.simple.databinding.ActivitySingleBinding |
||||||
|
|
||||||
|
/** |
||||||
|
* Created by lyy on 2017/10/23. |
||||||
|
*/ |
||||||
|
class KotlinDownloadActivity : BaseActivity<ActivitySingleBinding>() { |
||||||
|
|
||||||
|
private val DOWNLOAD_URL = |
||||||
|
"http://static.gaoshouyou.com/d/22/94/822260b849944492caadd2983f9bb624.apk" |
||||||
|
|
||||||
|
private lateinit var mStart: Button |
||||||
|
private lateinit var mStop: Button |
||||||
|
private lateinit var mCancel: Button |
||||||
|
private lateinit var target: DownloadTarget |
||||||
|
|
||||||
|
override fun setLayoutId(): Int { |
||||||
|
return R.layout.activity_single |
||||||
|
} |
||||||
|
|
||||||
|
override fun init(savedInstanceState: Bundle?) { |
||||||
|
title = "kotlin测试" |
||||||
|
Aria.get(this) |
||||||
|
.downloadConfig.maxTaskNum = 2 |
||||||
|
Aria.download(this) |
||||||
|
.register() |
||||||
|
mStart = findViewById(R.id.start) |
||||||
|
mStop = findViewById(R.id.stop) |
||||||
|
mCancel = findViewById(R.id.cancel) |
||||||
|
mStop.visibility = View.GONE |
||||||
|
|
||||||
|
target = Aria.download(this) |
||||||
|
.load(DOWNLOAD_URL) |
||||||
|
binding.progress = target.percent |
||||||
|
if (target.taskState == IEntity.STATE_STOP) { |
||||||
|
mStart.text = "恢复" |
||||||
|
} else if (target.isRunning) { |
||||||
|
mStart.text = "停止" |
||||||
|
} |
||||||
|
binding.fileSize = target.convertFileSize |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* 注解方法不能添加internal修饰符,否则会出现e: [kapt] An exception occurred: java.lang.IllegalArgumentException: index 1 for '$a' not in range (received 0 arguments)错误 |
||||||
|
*/ |
||||||
|
@Download.onTaskRunning |
||||||
|
fun running(task: DownloadTask) { |
||||||
|
Log.d(TAG, task.percent.toString()) |
||||||
|
val len = task.fileSize |
||||||
|
if (len == 0L) { |
||||||
|
binding.progress = 0 |
||||||
|
} else { |
||||||
|
binding.progress = task.percent |
||||||
|
} |
||||||
|
binding.speed = task.convertSpeed |
||||||
|
} |
||||||
|
|
||||||
|
@Download.onWait |
||||||
|
fun onWait(task: DownloadTask) { |
||||||
|
if (task.key == DOWNLOAD_URL) { |
||||||
|
Log.d(TAG, "wait ==> " + task.downloadEntity.fileName) |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
@Download.onPre |
||||||
|
fun onPre(task: DownloadTask) { |
||||||
|
if (task.key == DOWNLOAD_URL) { |
||||||
|
mStart.text = "停止" |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
@Download.onTaskStart |
||||||
|
fun taskStart(task: DownloadTask) { |
||||||
|
if (task.key == DOWNLOAD_URL) { |
||||||
|
binding.fileSize = task.convertFileSize |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
@Download.onTaskComplete |
||||||
|
fun complete(task: DownloadTask) { |
||||||
|
Log.d(TAG, "完成") |
||||||
|
} |
||||||
|
|
||||||
|
@Download.onTaskResume |
||||||
|
fun taskResume(task: DownloadTask) { |
||||||
|
if (task.key == DOWNLOAD_URL) { |
||||||
|
mStart.text = "停止" |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
@Download.onTaskStop |
||||||
|
fun taskStop(task: DownloadTask) { |
||||||
|
if (task.key == DOWNLOAD_URL) { |
||||||
|
mStart.text = "恢复" |
||||||
|
binding.speed = "" |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
@Download.onTaskCancel |
||||||
|
fun taskCancel(task: DownloadTask) { |
||||||
|
if (task.key == DOWNLOAD_URL) { |
||||||
|
binding.progress = 0 |
||||||
|
Toast.makeText(this@KotlinDownloadActivity, "取消下载", Toast.LENGTH_SHORT) |
||||||
|
.show() |
||||||
|
mStart.text = "开始" |
||||||
|
binding.speed = "" |
||||||
|
Log.d(TAG, "cancel") |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
@Download.onTaskFail |
||||||
|
fun taskFail(task: DownloadTask) { |
||||||
|
if (task.key == DOWNLOAD_URL) { |
||||||
|
Toast.makeText(this@KotlinDownloadActivity, "下载失败", Toast.LENGTH_SHORT) |
||||||
|
.show() |
||||||
|
mStart.text = "开始" |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
fun onClick(view: View) { |
||||||
|
when (view.id) { |
||||||
|
R.id.start -> { |
||||||
|
if (target.isRunning) { |
||||||
|
Aria.download(this) |
||||||
|
.load(DOWNLOAD_URL) |
||||||
|
.stop() |
||||||
|
} else { |
||||||
|
startD() |
||||||
|
} |
||||||
|
} |
||||||
|
R.id.stop -> Aria.download(this).load(DOWNLOAD_URL).stop() |
||||||
|
R.id.cancel -> Aria.download(this).load(DOWNLOAD_URL).cancel() |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
private fun startD() { |
||||||
|
Aria.download(this) |
||||||
|
.load(DOWNLOAD_URL) |
||||||
|
.addHeader("groupHash", "value") |
||||||
|
.setFilePath(Environment.getExternalStorageDirectory().path + "/kotlin.apk") |
||||||
|
.start() |
||||||
|
} |
||||||
|
} |
@ -0,0 +1,58 @@ |
|||||||
|
/* |
||||||
|
* Copyright (C) 2016 AriaLyy(https://github.com/AriaLyy/Aria)
|
||||||
|
* |
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License"); |
||||||
|
* you may not use this file except in compliance with the License. |
||||||
|
* You may obtain a copy of the License at |
||||||
|
* |
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
* |
||||||
|
* Unless required by applicable law or agreed to in writing, software |
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS, |
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
||||||
|
* See the License for the specific language governing permissions and |
||||||
|
* limitations under the License. |
||||||
|
*/ |
||||||
|
|
||||||
|
package com.arialyy.simple.common; |
||||||
|
|
||||||
|
import android.arch.lifecycle.LiveData; |
||||||
|
import android.arch.lifecycle.MutableLiveData; |
||||||
|
import android.text.TextUtils; |
||||||
|
import com.arialyy.aria.util.ALog; |
||||||
|
import com.arialyy.frame.base.BaseViewModule; |
||||||
|
import java.io.File; |
||||||
|
import java.util.ArrayList; |
||||||
|
import java.util.List; |
||||||
|
|
||||||
|
public class DialogModule extends BaseViewModule { |
||||||
|
|
||||||
|
private MutableLiveData<List<File>> mDirs = new MutableLiveData<>(); |
||||||
|
|
||||||
|
/** |
||||||
|
* 获取指定目录下的文件夹 |
||||||
|
* |
||||||
|
* @param path 指定路径 |
||||||
|
*/ |
||||||
|
LiveData<List<File>> getDirs(String path) { |
||||||
|
if (TextUtils.isEmpty(path)) { |
||||||
|
ALog.e(TAG, "路径为空"); |
||||||
|
return mDirs; |
||||||
|
} |
||||||
|
if (!path.startsWith("/")) { |
||||||
|
ALog.e(TAG, "路径错误"); |
||||||
|
return mDirs; |
||||||
|
} |
||||||
|
File file = new File(path); |
||||||
|
File[] files = file.listFiles(); |
||||||
|
List<File> data = new ArrayList<>(); |
||||||
|
for (File f : files) { |
||||||
|
if (f.isDirectory()) { |
||||||
|
data.add(f); |
||||||
|
} |
||||||
|
} |
||||||
|
mDirs.postValue(data); |
||||||
|
|
||||||
|
return mDirs; |
||||||
|
} |
||||||
|
} |
@ -0,0 +1,177 @@ |
|||||||
|
/* |
||||||
|
* Copyright (C) 2016 AriaLyy(https://github.com/AriaLyy/Aria)
|
||||||
|
* |
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License"); |
||||||
|
* you may not use this file except in compliance with the License. |
||||||
|
* You may obtain a copy of the License at |
||||||
|
* |
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
* |
||||||
|
* Unless required by applicable law or agreed to in writing, software |
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS, |
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
||||||
|
* See the License for the specific language governing permissions and |
||||||
|
* limitations under the License. |
||||||
|
*/ |
||||||
|
package com.arialyy.simple.common; |
||||||
|
|
||||||
|
import android.annotation.SuppressLint; |
||||||
|
import android.app.Dialog; |
||||||
|
import android.arch.lifecycle.Observer; |
||||||
|
import android.arch.lifecycle.ViewModelProviders; |
||||||
|
import android.content.Context; |
||||||
|
import android.content.DialogInterface; |
||||||
|
import android.os.Bundle; |
||||||
|
import android.os.Environment; |
||||||
|
import android.support.annotation.Nullable; |
||||||
|
import android.support.v7.widget.DividerItemDecoration; |
||||||
|
import android.support.v7.widget.LinearLayoutManager; |
||||||
|
import android.support.v7.widget.RecyclerView; |
||||||
|
import android.util.DisplayMetrics; |
||||||
|
import android.view.KeyEvent; |
||||||
|
import android.view.LayoutInflater; |
||||||
|
import android.view.View; |
||||||
|
import android.view.ViewGroup; |
||||||
|
import android.view.Window; |
||||||
|
import android.widget.TextView; |
||||||
|
import com.arialyy.aria.util.ALog; |
||||||
|
import com.arialyy.simple.R; |
||||||
|
import com.arialyy.simple.base.BaseDialog; |
||||||
|
import com.arialyy.simple.base.adapter.AbsHolder; |
||||||
|
import com.arialyy.simple.base.adapter.AbsRVAdapter; |
||||||
|
import com.arialyy.simple.base.adapter.RvItemClickSupport; |
||||||
|
import com.arialyy.simple.databinding.DialogChooseDirBinding; |
||||||
|
import com.arialyy.simple.databinding.DialogMsgBinding; |
||||||
|
import java.io.File; |
||||||
|
import java.util.ArrayList; |
||||||
|
import java.util.List; |
||||||
|
|
||||||
|
/** |
||||||
|
* Created by AriaL on 2019/5/28. |
||||||
|
*/ |
||||||
|
@SuppressLint("ValidFragment") public class DirChooseDialog |
||||||
|
extends BaseDialog<DialogChooseDirBinding> { |
||||||
|
public static final int DIR_CHOOSE_DIALOG_RESULT = 0xB2; |
||||||
|
private String mCurrentPath = Environment.getExternalStorageDirectory().getPath(); |
||||||
|
private List<File> mData = new ArrayList<>(); |
||||||
|
private DialogModule mModule; |
||||||
|
|
||||||
|
public DirChooseDialog(Object obj) { |
||||||
|
super(obj); |
||||||
|
} |
||||||
|
|
||||||
|
@Override protected void init(Bundle savedInstanceState) { |
||||||
|
super.init(savedInstanceState); |
||||||
|
getBinding().list.setLayoutManager(new LinearLayoutManager(getContext())); |
||||||
|
getBinding().list.addItemDecoration( |
||||||
|
new DividerItemDecoration(getContext(), DividerItemDecoration.VERTICAL)); |
||||||
|
final Adapter adapter = new Adapter(getContext(), mData); |
||||||
|
getBinding().list.setAdapter(adapter); |
||||||
|
getBinding().setCurrentPath(mCurrentPath); |
||||||
|
|
||||||
|
mModule = ViewModelProviders.of(this).get(DialogModule.class); |
||||||
|
mModule.getDirs(mCurrentPath).observe(this, new Observer<List<File>>() { |
||||||
|
@Override public void onChanged(@Nullable List<File> files) { |
||||||
|
mData.clear(); |
||||||
|
if (files != null && !files.isEmpty()) { |
||||||
|
mData.addAll(files); |
||||||
|
} |
||||||
|
adapter.notifyDataSetChanged(); |
||||||
|
if (mCurrentPath.equals(Environment.getExternalStorageDirectory().getPath())) { |
||||||
|
getBinding().up.setVisibility(View.GONE); |
||||||
|
} |
||||||
|
getBinding().setCurrentPath(mCurrentPath); |
||||||
|
getBinding().currentPath.setSelected(true); |
||||||
|
} |
||||||
|
}); |
||||||
|
|
||||||
|
getBinding().up.setOnClickListener(new View.OnClickListener() { |
||||||
|
@Override public void onClick(View v) { |
||||||
|
up(); |
||||||
|
} |
||||||
|
}); |
||||||
|
|
||||||
|
getBinding().enter.setOnClickListener(new View.OnClickListener() { |
||||||
|
@Override public void onClick(View v) { |
||||||
|
getSimplerModule().onDialog(DIR_CHOOSE_DIALOG_RESULT, mCurrentPath); |
||||||
|
dismiss(); |
||||||
|
} |
||||||
|
}); |
||||||
|
|
||||||
|
RvItemClickSupport.addTo(getBinding().list).setOnItemClickListener( |
||||||
|
new RvItemClickSupport.OnItemClickListener() { |
||||||
|
@Override public void onItemClicked(RecyclerView recyclerView, int position, View v) { |
||||||
|
getBinding().up.setVisibility(View.VISIBLE); |
||||||
|
mCurrentPath = mCurrentPath.concat("/").concat(mData.get(position).getName()); |
||||||
|
mModule.getDirs(mCurrentPath); |
||||||
|
} |
||||||
|
}); |
||||||
|
} |
||||||
|
|
||||||
|
private void up() { |
||||||
|
int endIndex = mCurrentPath.lastIndexOf("/"); |
||||||
|
mCurrentPath = mCurrentPath.substring(0, endIndex); |
||||||
|
mModule.getDirs(mCurrentPath); |
||||||
|
} |
||||||
|
|
||||||
|
@Override public void onStart() { |
||||||
|
super.onStart(); |
||||||
|
Dialog dialog = getDialog(); |
||||||
|
if (dialog != null) { |
||||||
|
DisplayMetrics dm = new DisplayMetrics(); |
||||||
|
getActivity().getWindowManager().getDefaultDisplay().getMetrics(dm); |
||||||
|
dialog.getWindow() |
||||||
|
.setLayout((dm.widthPixels), ViewGroup.LayoutParams.WRAP_CONTENT); |
||||||
|
// 拦截返回键
|
||||||
|
dialog.setOnKeyListener(new DialogInterface.OnKeyListener() { |
||||||
|
@Override public boolean onKey(DialogInterface dialog, int keyCode, KeyEvent event) { |
||||||
|
if (keyCode == KeyEvent.KEYCODE_BACK && event.getAction() == KeyEvent.ACTION_DOWN) { |
||||||
|
if (mCurrentPath.equals(Environment.getExternalStorageDirectory().getPath())) { |
||||||
|
dismiss(); |
||||||
|
} else { |
||||||
|
up(); |
||||||
|
} |
||||||
|
return true; |
||||||
|
} |
||||||
|
return false; |
||||||
|
} |
||||||
|
}); |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
@Override protected int setLayoutId() { |
||||||
|
return R.layout.dialog_choose_dir; |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* 适配器 |
||||||
|
*/ |
||||||
|
private class Adapter extends AbsRVAdapter<File, Adapter.Holder> { |
||||||
|
|
||||||
|
Adapter(Context context, List<File> data) { |
||||||
|
super(context, data); |
||||||
|
} |
||||||
|
|
||||||
|
@Override protected Holder getViewHolder(View convertView, int viewType) { |
||||||
|
return new Holder(convertView); |
||||||
|
} |
||||||
|
|
||||||
|
@Override protected int setLayoutId(int type) { |
||||||
|
return R.layout.item_choose_dir; |
||||||
|
} |
||||||
|
|
||||||
|
@Override protected void bindData(Holder holder, int position, File item) { |
||||||
|
holder.text.setSelected(true); |
||||||
|
holder.text.setText(item.getName()); |
||||||
|
} |
||||||
|
|
||||||
|
private class Holder extends AbsHolder { |
||||||
|
private TextView text; |
||||||
|
|
||||||
|
Holder(View itemView) { |
||||||
|
super(itemView); |
||||||
|
text = findViewById(R.id.text); |
||||||
|
} |
||||||
|
} |
||||||
|
} |
||||||
|
} |
@ -0,0 +1,93 @@ |
|||||||
|
/* |
||||||
|
* Copyright (C) 2016 AriaLyy(https://github.com/AriaLyy/Aria)
|
||||||
|
* |
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License"); |
||||||
|
* you may not use this file except in compliance with the License. |
||||||
|
* You may obtain a copy of the License at |
||||||
|
* |
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
* |
||||||
|
* Unless required by applicable law or agreed to in writing, software |
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS, |
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
||||||
|
* See the License for the specific language governing permissions and |
||||||
|
* limitations under the License. |
||||||
|
*/ |
||||||
|
package com.arialyy.simple.common; |
||||||
|
|
||||||
|
import android.annotation.SuppressLint; |
||||||
|
import android.os.Bundle; |
||||||
|
import android.text.TextUtils; |
||||||
|
import android.view.View; |
||||||
|
import android.widget.Toast; |
||||||
|
import com.arialyy.simple.R; |
||||||
|
import com.arialyy.simple.base.BaseDialog; |
||||||
|
import com.arialyy.simple.databinding.DialogModifyPathBinding; |
||||||
|
import com.arialyy.simple.databinding.DialogMsgBinding; |
||||||
|
import java.io.File; |
||||||
|
|
||||||
|
/** |
||||||
|
* Created by AriaL on 2019/5/28. |
||||||
|
* 修改文件路径 |
||||||
|
*/ |
||||||
|
@SuppressLint("ValidFragment") public class ModifyPathDialog |
||||||
|
extends BaseDialog<DialogModifyPathBinding> { |
||||||
|
public static final int MODIFY_PATH_RESULT = 0xB3; |
||||||
|
|
||||||
|
private String mTitle, mFilePath, mDir; |
||||||
|
|
||||||
|
public ModifyPathDialog(Object obj, String title, String filePath) { |
||||||
|
super(obj); |
||||||
|
mTitle = title; |
||||||
|
mFilePath = filePath; |
||||||
|
} |
||||||
|
|
||||||
|
@Override protected void init(Bundle savedInstanceState) { |
||||||
|
super.init(savedInstanceState); |
||||||
|
getBinding().setTitle(mTitle); |
||||||
|
getBinding().setViewModel(this); |
||||||
|
final File temp = new File(mFilePath); |
||||||
|
mDir = temp.getParent(); |
||||||
|
getBinding().setDir(mDir); |
||||||
|
getBinding().setName(temp.getName()); |
||||||
|
getBinding().edit.post(new Runnable() { |
||||||
|
@Override public void run() { |
||||||
|
getBinding().edit.setSelection(temp.getName().length()); |
||||||
|
} |
||||||
|
}); |
||||||
|
getBinding().enter.setOnClickListener(new View.OnClickListener() { |
||||||
|
@Override public void onClick(View v) { |
||||||
|
if (TextUtils.isEmpty(getBinding().getName())) { |
||||||
|
Toast.makeText(getContext(), getString(R.string.error_file_name_null), Toast.LENGTH_SHORT) |
||||||
|
.show(); |
||||||
|
return; |
||||||
|
} |
||||||
|
mFilePath = mDir + "/" + getBinding().getName(); |
||||||
|
getSimplerModule().onDialog(MODIFY_PATH_RESULT, mFilePath); |
||||||
|
dismiss(); |
||||||
|
} |
||||||
|
}); |
||||||
|
getBinding().cancel.setOnClickListener(new View.OnClickListener() { |
||||||
|
@Override public void onClick(View v) { |
||||||
|
dismiss(); |
||||||
|
} |
||||||
|
}); |
||||||
|
} |
||||||
|
|
||||||
|
public void chooseDir() { |
||||||
|
DirChooseDialog dirChooseDialog = new DirChooseDialog(this); |
||||||
|
dirChooseDialog.show(getChildFragmentManager(), "DirChooseDialog"); |
||||||
|
} |
||||||
|
|
||||||
|
@Override protected int setLayoutId() { |
||||||
|
return R.layout.dialog_modify_path; |
||||||
|
} |
||||||
|
|
||||||
|
@Override protected void dataCallback(int result, Object data) { |
||||||
|
super.dataCallback(result, data); |
||||||
|
if (result == DirChooseDialog.DIR_CHOOSE_DIALOG_RESULT) { |
||||||
|
mDir = String.valueOf(data); |
||||||
|
getBinding().setDir(mDir); |
||||||
|
} |
||||||
|
} |
||||||
|
} |
@ -0,0 +1,68 @@ |
|||||||
|
/* |
||||||
|
* Copyright (C) 2016 AriaLyy(https://github.com/AriaLyy/Aria)
|
||||||
|
* |
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License"); |
||||||
|
* you may not use this file except in compliance with the License. |
||||||
|
* You may obtain a copy of the License at |
||||||
|
* |
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
* |
||||||
|
* Unless required by applicable law or agreed to in writing, software |
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS, |
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
||||||
|
* See the License for the specific language governing permissions and |
||||||
|
* limitations under the License. |
||||||
|
*/ |
||||||
|
|
||||||
|
package com.arialyy.simple.core; |
||||||
|
|
||||||
|
import android.content.pm.ActivityInfo; |
||||||
|
import android.os.Bundle; |
||||||
|
import android.text.TextUtils; |
||||||
|
import android.view.Window; |
||||||
|
import android.view.WindowManager; |
||||||
|
import com.arialyy.aria.util.ALog; |
||||||
|
import com.arialyy.simple.R; |
||||||
|
import com.arialyy.simple.base.BaseActivity; |
||||||
|
import com.arialyy.simple.databinding.ActivityFullScreenCodeBinding; |
||||||
|
import com.pddstudio.highlightjs.models.Language; |
||||||
|
import com.pddstudio.highlightjs.models.Theme; |
||||||
|
import java.io.File; |
||||||
|
|
||||||
|
/** |
||||||
|
* 全是显示代码的actiivty |
||||||
|
*/ |
||||||
|
public class FullScreenCodeActivity extends BaseActivity<ActivityFullScreenCodeBinding> { |
||||||
|
private static final String TAG = "FullScreenCodeActivity"; |
||||||
|
public static final String KEY_FILE_PATH = "KEY_FILE_PATH"; |
||||||
|
|
||||||
|
@Override protected int setLayoutId() { |
||||||
|
return R.layout.activity_full_screen_code; |
||||||
|
} |
||||||
|
|
||||||
|
@Override protected void onCreate(Bundle savedInstanceState) { |
||||||
|
//去除标题栏
|
||||||
|
requestWindowFeature(Window.FEATURE_NO_TITLE); |
||||||
|
//去除状态栏
|
||||||
|
getWindow().setFlags(WindowManager.LayoutParams.FLAG_FULLSCREEN, |
||||||
|
WindowManager.LayoutParams.FLAG_FULLSCREEN); |
||||||
|
super.onCreate(savedInstanceState); |
||||||
|
} |
||||||
|
|
||||||
|
@Override protected void init(Bundle savedInstanceState) { |
||||||
|
super.init(savedInstanceState); |
||||||
|
String filePath = getIntent().getStringExtra(KEY_FILE_PATH); |
||||||
|
|
||||||
|
if (TextUtils.isEmpty(filePath)) { |
||||||
|
ALog.e(TAG, "代码的文件路径为空"); |
||||||
|
finish(); |
||||||
|
return; |
||||||
|
} |
||||||
|
getBinding().codeView.setZoomSupportEnabled(true); |
||||||
|
getBinding().codeView.setHighlightLanguage(Language.JAVA); |
||||||
|
getBinding().codeView.setTheme(Theme.ANDROID_STUDIO); |
||||||
|
getBinding().codeView.setSource(new File(filePath)); |
||||||
|
|
||||||
|
setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_LANDSCAPE); |
||||||
|
} |
||||||
|
} |
@ -0,0 +1,95 @@ |
|||||||
|
/* |
||||||
|
* Copyright (C) 2016 AriaLyy(https://github.com/AriaLyy/Aria)
|
||||||
|
* |
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License"); |
||||||
|
* you may not use this file except in compliance with the License. |
||||||
|
* You may obtain a copy of the License at |
||||||
|
* |
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
* |
||||||
|
* Unless required by applicable law or agreed to in writing, software |
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS, |
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
||||||
|
* See the License for the specific language governing permissions and |
||||||
|
* limitations under the License. |
||||||
|
*/ |
||||||
|
|
||||||
|
package com.arialyy.simple.core.download; |
||||||
|
|
||||||
|
import android.arch.lifecycle.LiveData; |
||||||
|
import android.arch.lifecycle.MutableLiveData; |
||||||
|
import android.content.Context; |
||||||
|
import android.os.Environment; |
||||||
|
import android.text.TextUtils; |
||||||
|
import com.arialyy.aria.core.Aria; |
||||||
|
import com.arialyy.aria.core.download.DownloadEntity; |
||||||
|
import com.arialyy.aria.util.ALog; |
||||||
|
import com.arialyy.frame.base.BaseViewModule; |
||||||
|
import com.arialyy.simple.util.AppUtil; |
||||||
|
import java.io.File; |
||||||
|
|
||||||
|
public class DownloadModule1 extends BaseViewModule { |
||||||
|
private final String DOWNLOAD_URL_KEY = "DOWNLOAD_URL_KEY"; |
||||||
|
private final String DOWNLOAD_PATH_KEY = "DOWNLOAD_PATH_KEY"; |
||||||
|
|
||||||
|
private final String defUrl = |
||||||
|
"http://hzdown.muzhiwan.com/2017/05/08/nl.noio.kingdom_59104935e56f0.apk"; |
||||||
|
private final String defFilePath = |
||||||
|
Environment.getExternalStoragePublicDirectory(Environment.DIRECTORY_DOWNLOADS).getPath(); |
||||||
|
|
||||||
|
private MutableLiveData<DownloadEntity> liveData = new MutableLiveData<>(); |
||||||
|
private DownloadEntity singDownloadInfo; |
||||||
|
|
||||||
|
/** |
||||||
|
* 单任务下载的信息 |
||||||
|
*/ |
||||||
|
LiveData<DownloadEntity> getSingDownloadInfo(Context context) { |
||||||
|
String url = AppUtil.getConfigValue(context, DOWNLOAD_URL_KEY, defUrl); |
||||||
|
String filePath = AppUtil.getConfigValue(context, DOWNLOAD_PATH_KEY, defFilePath); |
||||||
|
|
||||||
|
singDownloadInfo = Aria.download(context).getDownloadEntity(url); |
||||||
|
if (singDownloadInfo == null) { |
||||||
|
singDownloadInfo = new DownloadEntity(); |
||||||
|
singDownloadInfo.setUrl(url); |
||||||
|
File temp = new File(defFilePath); |
||||||
|
singDownloadInfo.setDownloadPath(filePath); |
||||||
|
singDownloadInfo.setFileName(temp.getName()); |
||||||
|
} else { |
||||||
|
AppUtil.setConfigValue(context, DOWNLOAD_PATH_KEY, singDownloadInfo.getDownloadPath()); |
||||||
|
AppUtil.setConfigValue(context, DOWNLOAD_URL_KEY, singDownloadInfo.getUrl()); |
||||||
|
} |
||||||
|
liveData.postValue(singDownloadInfo); |
||||||
|
|
||||||
|
return liveData; |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* 更新文件保存路径 |
||||||
|
* |
||||||
|
* @param filePath 文件保存路径 |
||||||
|
*/ |
||||||
|
void updateFilePath(Context context, String filePath) { |
||||||
|
if (TextUtils.isEmpty(filePath)) { |
||||||
|
ALog.e(TAG, "文件保存路径为空"); |
||||||
|
return; |
||||||
|
} |
||||||
|
File temp = new File(filePath); |
||||||
|
AppUtil.setConfigValue(context, DOWNLOAD_PATH_KEY, filePath); |
||||||
|
singDownloadInfo.setFileName(temp.getName()); |
||||||
|
singDownloadInfo.setDownloadPath(filePath); |
||||||
|
liveData.postValue(singDownloadInfo); |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* 更新url |
||||||
|
*/ |
||||||
|
void uploadUrl(Context context, String url) { |
||||||
|
if (TextUtils.isEmpty(url)) { |
||||||
|
ALog.e(TAG, "下载地址为空"); |
||||||
|
return; |
||||||
|
} |
||||||
|
AppUtil.setConfigValue(context, DOWNLOAD_URL_KEY, url); |
||||||
|
singDownloadInfo.setUrl(url); |
||||||
|
liveData.postValue(singDownloadInfo); |
||||||
|
} |
||||||
|
} |
@ -1,166 +1,317 @@ |
|||||||
|
/* |
||||||
|
* Copyright (C) 2016 AriaLyy(https://github.com/AriaLyy/Aria) |
||||||
|
* |
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License"); |
||||||
|
* you may not use this file except in compliance with the License. |
||||||
|
* You may obtain a copy of the License at |
||||||
|
* |
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0 |
||||||
|
* |
||||||
|
* Unless required by applicable law or agreed to in writing, software |
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS, |
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
||||||
|
* See the License for the specific language governing permissions and |
||||||
|
* limitations under the License. |
||||||
|
*/ |
||||||
|
|
||||||
package com.arialyy.simple.core.download |
package com.arialyy.simple.core.download |
||||||
|
|
||||||
|
import android.arch.lifecycle.Observer |
||||||
|
import android.arch.lifecycle.ViewModelProviders |
||||||
|
import android.content.BroadcastReceiver |
||||||
|
import android.content.Context |
||||||
|
import android.content.Intent |
||||||
import android.os.Bundle |
import android.os.Bundle |
||||||
import android.os.Environment |
|
||||||
import android.util.Log |
import android.util.Log |
||||||
|
import android.view.Menu |
||||||
|
import android.view.MenuItem |
||||||
import android.view.View |
import android.view.View |
||||||
import android.widget.Button |
|
||||||
import android.widget.Toast |
import android.widget.Toast |
||||||
|
|
||||||
import com.arialyy.annotations.Download |
import com.arialyy.annotations.Download |
||||||
import com.arialyy.aria.core.Aria |
import com.arialyy.aria.core.Aria |
||||||
|
import com.arialyy.aria.core.download.DownloadEntity |
||||||
import com.arialyy.aria.core.download.DownloadTarget |
import com.arialyy.aria.core.download.DownloadTarget |
||||||
import com.arialyy.aria.core.download.DownloadTask |
import com.arialyy.aria.core.download.DownloadTask |
||||||
import com.arialyy.aria.core.inf.IEntity |
import com.arialyy.aria.core.inf.IEntity |
||||||
|
import com.arialyy.aria.core.inf.IHttpFileLenAdapter |
||||||
|
import com.arialyy.aria.core.scheduler.ISchedulers |
||||||
|
import com.arialyy.aria.util.ALog |
||||||
|
import com.arialyy.aria.util.CommonUtil |
||||||
import com.arialyy.frame.util.show.T |
import com.arialyy.frame.util.show.T |
||||||
import com.arialyy.simple.R |
import com.arialyy.simple.R |
||||||
import com.arialyy.simple.base.BaseActivity |
import com.arialyy.simple.base.BaseActivity |
||||||
import com.arialyy.simple.databinding.ActivitySingleBinding |
import com.arialyy.simple.common.ModifyPathDialog |
||||||
|
import com.arialyy.simple.common.ModifyUrlDialog |
||||||
|
import com.arialyy.simple.databinding.ActivitySingleKotlinBinding |
||||||
|
import com.arialyy.simple.util.AppUtil |
||||||
|
import com.pddstudio.highlightjs.models.Language |
||||||
|
|
||||||
/** |
import java.io.IOException |
||||||
* Created by lyy on 2017/10/23. |
|
||||||
*/ |
|
||||||
class KotlinDownloadActivity : BaseActivity<ActivitySingleBinding>() { |
|
||||||
|
|
||||||
private val DOWNLOAD_URL = |
class KotlinDownloadActivity : BaseActivity<ActivitySingleKotlinBinding>() { |
||||||
"http://static.gaoshouyou.com/d/22/94/822260b849944492caadd2983f9bb624.apk" |
|
||||||
|
|
||||||
private lateinit var mStart: Button |
private var mUrl: String? = null |
||||||
private lateinit var mStop: Button |
private var mFilePath: String? = null |
||||||
private lateinit var mCancel: Button |
private var mModule: DownloadModule1? = null |
||||||
private lateinit var target: DownloadTarget |
private var mTarget: DownloadTarget? = null |
||||||
|
|
||||||
override fun setLayoutId(): Int { |
internal var receiver: BroadcastReceiver = object : BroadcastReceiver() { |
||||||
return R.layout.activity_single |
override fun onReceive( |
||||||
|
context: Context, |
||||||
|
intent: Intent |
||||||
|
) { |
||||||
|
if (intent.action == ISchedulers.ARIA_TASK_INFO_ACTION) { |
||||||
|
ALog.d(TAG, "state = " + intent.getIntExtra(ISchedulers.TASK_STATE, -1)) |
||||||
|
ALog.d(TAG, "type = " + intent.getIntExtra(ISchedulers.TASK_TYPE, -1)) |
||||||
|
ALog.d(TAG, "speed = " + intent.getLongExtra(ISchedulers.TASK_SPEED, -1)) |
||||||
|
ALog.d(TAG, "percent = " + intent.getIntExtra(ISchedulers.TASK_PERCENT, -1)) |
||||||
|
ALog.d( |
||||||
|
TAG, "entity = " + intent.getParcelableExtra<DownloadEntity>( |
||||||
|
ISchedulers.TASK_ENTITY |
||||||
|
).toString() |
||||||
|
) |
||||||
|
} |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
override fun onResume() { |
||||||
|
super.onResume() |
||||||
|
//registerReceiver(receiver, new IntentFilter(ISchedulers.ARIA_TASK_INFO_ACTION)); |
||||||
|
} |
||||||
|
|
||||||
|
override fun onDestroy() { |
||||||
|
super.onDestroy() |
||||||
|
//unregisterReceiver(receiver); |
||||||
|
Aria.download(this) |
||||||
|
.unRegister() |
||||||
} |
} |
||||||
|
|
||||||
override fun init(savedInstanceState: Bundle?) { |
override fun init(savedInstanceState: Bundle?) { |
||||||
title = "kotlin测试" |
super.init(savedInstanceState) |
||||||
Aria.get(this) |
title = "单任务下载" |
||||||
.downloadConfig.maxTaskNum = 2 |
|
||||||
Aria.download(this) |
Aria.download(this) |
||||||
.register() |
.register() |
||||||
mStart = findViewById(R.id.start) |
mModule = ViewModelProviders.of(this) |
||||||
mStop = findViewById(R.id.stop) |
.get(DownloadModule1::class.java) |
||||||
mCancel = findViewById(R.id.cancel) |
mModule!!.getSingDownloadInfo(this) |
||||||
mStop.visibility = View.GONE |
.observe(this, Observer { entity -> |
||||||
|
if (entity == null) { |
||||||
|
return@Observer |
||||||
|
} |
||||||
|
mTarget = Aria.download(this) |
||||||
|
.load(entity.url) |
||||||
|
if (mTarget!!.taskState == IEntity.STATE_STOP) { |
||||||
|
binding.stateStr = getString(R.string.resume) |
||||||
|
} else if (mTarget!!.isRunning) { |
||||||
|
binding.stateStr = getString(R.string.stop) |
||||||
|
} |
||||||
|
|
||||||
target = Aria.download(this) |
if (entity.fileSize != 0L) { |
||||||
.load(DOWNLOAD_URL) |
binding.fileSize = CommonUtil.formatFileSize(entity.fileSize.toDouble()) |
||||||
binding.progress = target.percent |
binding.progress = if (entity.isComplete) |
||||||
if (target.taskState == IEntity.STATE_STOP) { |
100 |
||||||
mStart.text = "恢复" |
else |
||||||
} else if (target.isRunning) { |
(entity.currentProgress * 100 / entity.fileSize).toInt() |
||||||
mStart.text = "停止" |
|
||||||
} |
} |
||||||
binding.fileSize = target.convertFileSize |
binding.url = entity.url |
||||||
|
binding.filePath = entity.filePath |
||||||
|
mUrl = entity.url |
||||||
|
mFilePath = entity.filePath |
||||||
|
}) |
||||||
|
binding.viewModel = this |
||||||
|
try { |
||||||
|
binding.codeView.setSource(AppUtil.getHelpCode(this, "KotlinHttpDownload.kt"), Language.JAVA) |
||||||
|
} catch (e: IOException) { |
||||||
|
e.printStackTrace() |
||||||
} |
} |
||||||
|
|
||||||
/** |
|
||||||
* 注解方法不能添加internal修饰符,否则会出现e: [kapt] An exception occurred: java.lang.IllegalArgumentException: index 1 for '$a' not in range (received 0 arguments)错误 |
|
||||||
*/ |
|
||||||
@Download.onTaskRunning |
|
||||||
fun running(task: DownloadTask) { |
|
||||||
Log.d(TAG, task.percent.toString()) |
|
||||||
val len = task.fileSize |
|
||||||
if (len == 0L) { |
|
||||||
binding.progress = 0 |
|
||||||
} else { |
|
||||||
binding.progress = task.percent |
|
||||||
} |
} |
||||||
binding.speed = task.convertSpeed |
|
||||||
|
fun chooseUrl() { |
||||||
|
val dialog = ModifyUrlDialog(this, getString(R.string.modify_url_dialog_title), mUrl) |
||||||
|
dialog.show(supportFragmentManager, "ModifyUrlDialog") |
||||||
|
} |
||||||
|
|
||||||
|
fun chooseFilePath() { |
||||||
|
val dialog = ModifyPathDialog(this, getString(R.string.modify_file_path), mFilePath) |
||||||
|
dialog.show(supportFragmentManager, "ModifyPathDialog") |
||||||
|
} |
||||||
|
|
||||||
|
override fun onCreateOptionsMenu(menu: Menu): Boolean { |
||||||
|
menuInflater.inflate(R.menu.menu_single_task_activity, menu) |
||||||
|
return super.onCreateOptionsMenu(menu) |
||||||
|
} |
||||||
|
|
||||||
|
override fun onMenuItemClick(item: MenuItem): Boolean { |
||||||
|
var speed = -1 |
||||||
|
var msg = "" |
||||||
|
when (item.itemId) { |
||||||
|
R.id.help -> { |
||||||
|
msg = ("一些小知识点:\n" |
||||||
|
+ "1、你可以在注解中增加链接,用于指定被注解的方法只能被特定的下载任务回调,以防止progress乱跳\n" |
||||||
|
+ "2、当遇到网络慢的情况时,你可以先使用onPre()更新UI界面,待连接成功时,再在onTaskPre()获取完整的task数据,然后给UI界面设置正确的数据\n" |
||||||
|
+ "3、你可以在界面初始化时通过Aria.download(this).load(URL).getPercent()等方法快速获取相关任务的一些数据") |
||||||
|
showMsgDialog("tip", msg) |
||||||
|
} |
||||||
|
R.id.speed_0 -> speed = 0 |
||||||
|
R.id.speed_128 -> speed = 128 |
||||||
|
R.id.speed_256 -> speed = 256 |
||||||
|
R.id.speed_512 -> speed = 512 |
||||||
|
R.id.speed_1m -> speed = 1024 |
||||||
|
} |
||||||
|
if (speed > -1) { |
||||||
|
msg = item.title.toString() |
||||||
|
Aria.download(this) |
||||||
|
.setMaxSpeed(speed) |
||||||
|
T.showShort(this, msg) |
||||||
|
} |
||||||
|
return true |
||||||
} |
} |
||||||
|
|
||||||
@Download.onWait |
@Download.onWait |
||||||
fun onWait(task: DownloadTask) { |
fun onWait(task: DownloadTask) { |
||||||
if (task.key == DOWNLOAD_URL) { |
if (task.key == mUrl) { |
||||||
Log.d(TAG, "wait ==> " + task.downloadEntity.fileName) |
Log.d(TAG, "wait ==> " + task.downloadEntity.fileName) |
||||||
} |
} |
||||||
} |
} |
||||||
|
|
||||||
@Download.onPre |
@Download.onPre |
||||||
fun onPre(task: DownloadTask) { |
fun onPre(task: DownloadTask) { |
||||||
if (task.key == DOWNLOAD_URL) { |
if (task.key == mUrl) { |
||||||
mStart.text = "停止" |
binding.stateStr = getString(R.string.stop) |
||||||
} |
} |
||||||
} |
} |
||||||
|
|
||||||
@Download.onTaskStart |
@Download.onTaskStart |
||||||
fun taskStart(task: DownloadTask) { |
fun taskStart(task: DownloadTask) { |
||||||
if (task.key == DOWNLOAD_URL) { |
if (task.key == mUrl) { |
||||||
binding.fileSize = task.convertFileSize |
binding.fileSize = task.convertFileSize |
||||||
} |
} |
||||||
} |
} |
||||||
|
|
||||||
@Download.onTaskComplete |
@Download.onTaskRunning |
||||||
fun complete(task: DownloadTask) { |
fun running(task: DownloadTask) { |
||||||
Log.d(TAG, "完成") |
if (task.key == mUrl) { |
||||||
|
//Log.d(TAG, task.getKey()); |
||||||
|
val len = task.fileSize |
||||||
|
if (len == 0L) { |
||||||
|
binding.progress = 0 |
||||||
|
} else { |
||||||
|
binding.progress = task.percent |
||||||
|
} |
||||||
|
binding.speed = task.convertSpeed |
||||||
|
} |
||||||
} |
} |
||||||
|
|
||||||
@Download.onTaskResume |
@Download.onTaskResume |
||||||
fun taskResume(task: DownloadTask) { |
fun taskResume(task: DownloadTask) { |
||||||
if (task.key == DOWNLOAD_URL) { |
if (task.key == mUrl) { |
||||||
mStart.text = "停止" |
binding.stateStr = getString(R.string.stop) |
||||||
} |
} |
||||||
} |
} |
||||||
|
|
||||||
@Download.onTaskStop |
@Download.onTaskStop |
||||||
fun taskStop(task: DownloadTask) { |
fun taskStop(task: DownloadTask) { |
||||||
if (task.key == DOWNLOAD_URL) { |
if (task.key == mUrl) { |
||||||
mStart.text = "恢复" |
binding.stateStr = getString(R.string.resume) |
||||||
binding.speed = "" |
binding.speed = "" |
||||||
} |
} |
||||||
} |
} |
||||||
|
|
||||||
@Download.onTaskCancel |
@Download.onTaskCancel |
||||||
fun taskCancel(task: DownloadTask) { |
fun taskCancel(task: DownloadTask) { |
||||||
if (task.key == DOWNLOAD_URL) { |
if (task.key == mUrl) { |
||||||
binding.progress = 0 |
binding.progress = 0 |
||||||
Toast.makeText(this@KotlinDownloadActivity, "取消下载", Toast.LENGTH_SHORT) |
binding.stateStr = getString(R.string.start) |
||||||
.show() |
|
||||||
mStart.text = "开始" |
|
||||||
binding.speed = "" |
binding.speed = "" |
||||||
Log.d(TAG, "cancel") |
Log.d(TAG, "cancel") |
||||||
} |
} |
||||||
} |
} |
||||||
|
|
||||||
|
/** |
||||||
|
* |
||||||
|
*/ |
||||||
@Download.onTaskFail |
@Download.onTaskFail |
||||||
fun taskFail(task: DownloadTask) { |
fun taskFail( |
||||||
if (task.key == DOWNLOAD_URL) { |
task: DownloadTask, |
||||||
Toast.makeText(this@KotlinDownloadActivity, "下载失败", Toast.LENGTH_SHORT) |
e: Exception |
||||||
|
) { |
||||||
|
if (task.key == mUrl) { |
||||||
|
Toast.makeText(this, getString(R.string.download_fail), Toast.LENGTH_SHORT) |
||||||
.show() |
.show() |
||||||
mStart.text = "开始" |
binding.stateStr = getString(R.string.start) |
||||||
} |
} |
||||||
} |
} |
||||||
|
|
||||||
@Download.onNoSupportBreakPoint |
@Download.onTaskComplete |
||||||
fun onNoSupportBreakPoint(task: DownloadTask) { |
fun taskComplete(task: DownloadTask) { |
||||||
Log.d(TAG, "该下载链接不支持断点") |
|
||||||
if (task.key == DOWNLOAD_URL) { |
if (task.key == mUrl) { |
||||||
T.showShort(this@KotlinDownloadActivity, "该下载链接不支持断点") |
binding.progress = 100 |
||||||
|
Toast.makeText( |
||||||
|
this, getString(R.string.download_success), |
||||||
|
Toast.LENGTH_SHORT |
||||||
|
) |
||||||
|
.show() |
||||||
|
binding.stateStr = getString(R.string.re_start) |
||||||
|
binding.speed = "" |
||||||
|
} |
||||||
} |
} |
||||||
|
|
||||||
|
override fun setLayoutId(): Int { |
||||||
|
return R.layout.activity_single_kotlin |
||||||
} |
} |
||||||
|
|
||||||
fun onClick(view: View) { |
fun onClick(view: View) { |
||||||
when (view.id) { |
when (view.id) { |
||||||
R.id.start -> { |
R.id.start -> if (mTarget!!.isRunning) { |
||||||
if (target.isRunning) { |
|
||||||
Aria.download(this) |
Aria.download(this) |
||||||
.load(DOWNLOAD_URL) |
.load(mUrl!!) |
||||||
.stop() |
.stop() |
||||||
} else { |
} else { |
||||||
startD() |
startD() |
||||||
} |
} |
||||||
} |
R.id.stop -> Aria.download(this).load(mUrl!!).stop() |
||||||
R.id.stop -> Aria.download(this).load(DOWNLOAD_URL).stop() |
R.id.cancel -> Aria.download(this).load(mUrl!!).cancel(true) |
||||||
R.id.cancel -> Aria.download(this).load(DOWNLOAD_URL).cancel() |
|
||||||
} |
} |
||||||
} |
} |
||||||
|
|
||||||
private fun startD() { |
private fun startD() { |
||||||
Aria.download(this) |
Aria.download(this) |
||||||
.load(DOWNLOAD_URL) |
.load(mUrl!!) |
||||||
.addHeader("groupHash", "value") |
//.addHeader("Accept", "text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,image/apng,*/*;q=0.8,application/signed-exchange;v=b3") |
||||||
.setFilePath(Environment.getExternalStorageDirectory().path + "/kotlin.apk") |
//.addHeader("Accept-Encoding", "gzip, deflate") |
||||||
|
//.addHeader("DNT", "1") |
||||||
|
//.addHeader("Cookie", "BAIDUID=648E5FF020CC69E8DD6F492D1068AAA9:FG=1; BIDUPSID=648E5FF020CC69E8DD6F492D1068AAA9; PSTM=1519099573; BD_UPN=12314753; locale=zh; BDSVRTM=0") |
||||||
|
.useServerFileName(true) |
||||||
|
.setFilePath(mFilePath!!, true) |
||||||
|
.setFileLenAdapter(IHttpFileLenAdapter { headers -> |
||||||
|
val sLength = headers["Content-Length"] |
||||||
|
if (sLength == null || sLength.isEmpty()) { |
||||||
|
return@IHttpFileLenAdapter -1 |
||||||
|
} |
||||||
|
val temp = sLength[0] |
||||||
|
|
||||||
|
java.lang.Long.parseLong(temp) |
||||||
|
}) |
||||||
.start() |
.start() |
||||||
} |
} |
||||||
|
|
||||||
|
override fun onStop() { |
||||||
|
super.onStop() |
||||||
|
//Aria.download(this).unRegister(); |
||||||
|
} |
||||||
|
|
||||||
|
override fun dataCallback( |
||||||
|
result: Int, |
||||||
|
data: Any |
||||||
|
) { |
||||||
|
super.dataCallback(result, data) |
||||||
|
if (result == ModifyUrlDialog.MODIFY_URL_DIALOG_RESULT) { |
||||||
|
mModule!!.uploadUrl(this, data.toString()) |
||||||
|
} else if (result == ModifyPathDialog.MODIFY_PATH_RESULT) { |
||||||
|
mModule!!.updateFilePath(this, data.toString()) |
||||||
|
} |
||||||
|
} |
||||||
} |
} |
@ -0,0 +1,74 @@ |
|||||||
|
/* |
||||||
|
* Copyright (C) 2016 AriaLyy(https://github.com/AriaLyy/Aria)
|
||||||
|
* |
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License"); |
||||||
|
* you may not use this file except in compliance with the License. |
||||||
|
* You may obtain a copy of the License at |
||||||
|
* |
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
* |
||||||
|
* Unless required by applicable law or agreed to in writing, software |
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS, |
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
||||||
|
* See the License for the specific language governing permissions and |
||||||
|
* limitations under the License. |
||||||
|
*/ |
||||||
|
|
||||||
|
package com.arialyy.simple.core.upload; |
||||||
|
|
||||||
|
import android.arch.lifecycle.LiveData; |
||||||
|
import android.arch.lifecycle.MutableLiveData; |
||||||
|
import android.content.Context; |
||||||
|
import android.os.Environment; |
||||||
|
import com.arialyy.aria.core.Aria; |
||||||
|
import com.arialyy.aria.core.upload.UploadEntity; |
||||||
|
import com.arialyy.frame.base.BaseViewModule; |
||||||
|
import com.arialyy.simple.util.AppUtil; |
||||||
|
|
||||||
|
public class UploadModule extends BaseViewModule { |
||||||
|
private final String FTP_URL_KEY = "FTP_URL_KEY"; |
||||||
|
private final String FTP_PATH_KEY = "FTP_PATH_KEY"; |
||||||
|
private MutableLiveData<UploadEntity> liveData = new MutableLiveData<>(); |
||||||
|
UploadEntity uploadInfo; |
||||||
|
|
||||||
|
/** |
||||||
|
* 获取Ftp上传信息 |
||||||
|
*/ |
||||||
|
LiveData<UploadEntity> getFtpInfo(Context context) { |
||||||
|
String url = AppUtil.getConfigValue(context, FTP_URL_KEY, "ftp://9.9.9.205:2121/aa/你好"); |
||||||
|
String filePath = AppUtil.getConfigValue(context, FTP_PATH_KEY, |
||||||
|
Environment.getExternalStorageDirectory().getPath() + "/AriaPrj.rar"); |
||||||
|
|
||||||
|
UploadEntity entity = Aria.upload(context).getUploadEntity(filePath); |
||||||
|
if (entity != null) { |
||||||
|
uploadInfo = entity; |
||||||
|
AppUtil.setConfigValue(context, FTP_URL_KEY, uploadInfo.getUrl()); |
||||||
|
AppUtil.setConfigValue(context, FTP_PATH_KEY, uploadInfo.getFilePath()); |
||||||
|
} else { |
||||||
|
uploadInfo = new UploadEntity(); |
||||||
|
uploadInfo.setUrl(url); |
||||||
|
uploadInfo.setFilePath(filePath); |
||||||
|
} |
||||||
|
|
||||||
|
liveData.postValue(uploadInfo); |
||||||
|
return liveData; |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* 更新Url |
||||||
|
*/ |
||||||
|
void updateFtpUrl(Context context, String url) { |
||||||
|
uploadInfo.setUrl(url); |
||||||
|
AppUtil.setConfigValue(context, FTP_URL_KEY, url); |
||||||
|
liveData.postValue(uploadInfo); |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* 更新文件路径 |
||||||
|
*/ |
||||||
|
void updateFtpFilePath(Context context, String filePath) { |
||||||
|
uploadInfo.setFilePath(filePath); |
||||||
|
AppUtil.setConfigValue(context, FTP_PATH_KEY, filePath); |
||||||
|
liveData.postValue(uploadInfo); |
||||||
|
} |
||||||
|
} |
@ -0,0 +1,106 @@ |
|||||||
|
/* |
||||||
|
* Copyright (C) 2016 AriaLyy(https://github.com/AriaLyy/Aria)
|
||||||
|
* |
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License"); |
||||||
|
* you may not use this file except in compliance with the License. |
||||||
|
* You may obtain a copy of the License at |
||||||
|
* |
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
* |
||||||
|
* Unless required by applicable law or agreed to in writing, software |
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS, |
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
||||||
|
* See the License for the specific language governing permissions and |
||||||
|
* limitations under the License. |
||||||
|
*/ |
||||||
|
|
||||||
|
package com.arialyy.simple.util; |
||||||
|
|
||||||
|
import android.app.Activity; |
||||||
|
import android.content.Context; |
||||||
|
import android.content.Intent; |
||||||
|
import android.content.SharedPreferences; |
||||||
|
import android.net.Uri; |
||||||
|
import android.os.Build; |
||||||
|
import android.support.v4.content.FileProvider; |
||||||
|
import android.text.TextUtils; |
||||||
|
import com.arialyy.aria.util.ALog; |
||||||
|
import com.arialyy.aria.util.CommonUtil; |
||||||
|
import com.arialyy.simple.BuildConfig; |
||||||
|
import java.io.File; |
||||||
|
import java.io.IOException; |
||||||
|
|
||||||
|
public class AppUtil { |
||||||
|
private static final String TAG = "AppUtil"; |
||||||
|
private static final String ARIA_SHARE_PRE_KEY = "ARIA_SHARE_PRE_KEY"; |
||||||
|
|
||||||
|
/** |
||||||
|
* http下载示例代码 |
||||||
|
*/ |
||||||
|
public static File getHelpCode(Context context, String fileName) throws IOException { |
||||||
|
String path = String.format("%s/code/%s", context.getFilesDir().getPath(), fileName); |
||||||
|
File ftpCode = new File(path); |
||||||
|
if (!ftpCode.exists()) { |
||||||
|
CommonUtil.createFile(path); |
||||||
|
CommonUtil.createFileFormInputStream(context.getAssets() |
||||||
|
.open(String.format("help_code/%s", fileName)), |
||||||
|
path); |
||||||
|
} |
||||||
|
return ftpCode; |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* 读取配置文件字段 |
||||||
|
* |
||||||
|
* @param key key |
||||||
|
* @param defStr 默认字符串 |
||||||
|
*/ |
||||||
|
public static String getConfigValue(Context context, String key, String defStr) { |
||||||
|
SharedPreferences preferences = |
||||||
|
context.getSharedPreferences(ARIA_SHARE_PRE_KEY, Context.MODE_PRIVATE); |
||||||
|
return preferences.getString(key, defStr); |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* set配置文件字段 |
||||||
|
* |
||||||
|
* @param key key |
||||||
|
* @param value 需要保存的字符串 |
||||||
|
*/ |
||||||
|
public static void setConfigValue(Context context, String key, String value) { |
||||||
|
SharedPreferences preferences = |
||||||
|
context.getSharedPreferences(ARIA_SHARE_PRE_KEY, Context.MODE_PRIVATE); |
||||||
|
SharedPreferences.Editor editor = preferences.edit(); |
||||||
|
editor.putString(key, value); |
||||||
|
editor.apply(); |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* 调用系统文件管理器选择文件 |
||||||
|
* |
||||||
|
* @param file 不能是文件夹 |
||||||
|
* @param mineType android 可用的minetype |
||||||
|
* @param requestCode 请求码 |
||||||
|
*/ |
||||||
|
public static void chooseFile(Activity activity, File file, String mineType, int requestCode) { |
||||||
|
if (file.isDirectory()) { |
||||||
|
ALog.e(TAG, "不能选择文件夹"); |
||||||
|
return; |
||||||
|
} |
||||||
|
Intent intent = new Intent(Intent.ACTION_GET_CONTENT); |
||||||
|
Uri uri; |
||||||
|
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) { |
||||||
|
uri = FileProvider.getUriForFile(activity.getApplicationContext(), |
||||||
|
BuildConfig.APPLICATION_ID + ".provider", |
||||||
|
file); |
||||||
|
} else { |
||||||
|
uri = Uri.fromFile(file); |
||||||
|
} |
||||||
|
|
||||||
|
intent.setDataAndType(uri, TextUtils.isEmpty(mineType) ? "*/*" : mineType); |
||||||
|
intent.addCategory(Intent.CATEGORY_OPENABLE); |
||||||
|
intent.addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION); |
||||||
|
|
||||||
|
activity.startActivityForResult(intent, requestCode); |
||||||
|
} |
||||||
|
} |
@ -0,0 +1,76 @@ |
|||||||
|
/* |
||||||
|
* Copyright (C) 2016 AriaLyy(https://github.com/AriaLyy/Aria)
|
||||||
|
* |
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License"); |
||||||
|
* you may not use this file except in compliance with the License. |
||||||
|
* You may obtain a copy of the License at |
||||||
|
* |
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
* |
||||||
|
* Unless required by applicable law or agreed to in writing, software |
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS, |
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
||||||
|
* See the License for the specific language governing permissions and |
||||||
|
* limitations under the License. |
||||||
|
*/ |
||||||
|
|
||||||
|
package com.arialyy.simple.widget; |
||||||
|
|
||||||
|
import android.content.Context; |
||||||
|
import android.content.Intent; |
||||||
|
import android.util.AttributeSet; |
||||||
|
import android.view.LayoutInflater; |
||||||
|
import android.view.View; |
||||||
|
import android.widget.RelativeLayout; |
||||||
|
import com.arialyy.simple.R; |
||||||
|
import com.arialyy.simple.core.FullScreenCodeActivity; |
||||||
|
import com.pddstudio.highlightjs.HighlightJsView; |
||||||
|
import com.pddstudio.highlightjs.models.Language; |
||||||
|
import com.pddstudio.highlightjs.models.Theme; |
||||||
|
import java.io.File; |
||||||
|
|
||||||
|
/** |
||||||
|
* 代码高亮控件 |
||||||
|
*/ |
||||||
|
public class CodeView extends RelativeLayout { |
||||||
|
|
||||||
|
private HighlightJsView mCodeView; |
||||||
|
private File mSourceFile; |
||||||
|
|
||||||
|
public CodeView(Context context) { |
||||||
|
super(context, null); |
||||||
|
} |
||||||
|
|
||||||
|
public CodeView(Context context, AttributeSet attrs) { |
||||||
|
super(context, attrs); |
||||||
|
init(context); |
||||||
|
} |
||||||
|
|
||||||
|
private void init(Context context) { |
||||||
|
LayoutInflater.from(context).inflate(R.layout.layout_code_demo, this, true); |
||||||
|
mCodeView = findViewById(R.id.js_view); |
||||||
|
mCodeView.setHighlightLanguage(Language.JAVA); |
||||||
|
mCodeView.setTheme(Theme.ANDROID_STUDIO); |
||||||
|
mCodeView.setZoomSupportEnabled(true); |
||||||
|
findViewById(R.id.full_screen).setOnClickListener(new OnClickListener() { |
||||||
|
@Override public void onClick(View v) { |
||||||
|
// 横屏显示代码
|
||||||
|
Intent intent = new Intent(getContext(), FullScreenCodeActivity.class); |
||||||
|
intent.putExtra(FullScreenCodeActivity.KEY_FILE_PATH, mSourceFile.getPath()); |
||||||
|
getContext().startActivity(intent); |
||||||
|
} |
||||||
|
}); |
||||||
|
} |
||||||
|
|
||||||
|
public void setSource(File sourceFile) { |
||||||
|
mSourceFile = sourceFile; |
||||||
|
mCodeView.setSource(sourceFile); |
||||||
|
} |
||||||
|
|
||||||
|
|
||||||
|
public void setSource(File sourceFile, Language language) { |
||||||
|
mSourceFile = sourceFile; |
||||||
|
mCodeView.setHighlightLanguage(language); |
||||||
|
mCodeView.setSource(sourceFile); |
||||||
|
} |
||||||
|
} |
@ -1,6 +1,15 @@ |
|||||||
<vector android:height="24dp" android:viewportHeight="1024" |
<vector xmlns:android="http://schemas.android.com/apk/res/android" |
||||||
android:viewportWidth="1024" android:width="24dp" xmlns:android="http://schemas.android.com/apk/res/android"> |
android:height="24dp" |
||||||
<path android:fillColor="#2b2b2b" android:pathData="M895.65,672.81c14.3,0 23.74,-9.54 23.74,-23.74L919.39,269.29a30.46,30.46 0,0 0,-8.66 -21.29L738.23,71.19A30.46,30.46 0,0 0,716.42 62L199.21,62c-52.29,0 -95.07,42.78 -95.07,95.07v654h-0.1v51.85h0.1c0,52.25 42.81,95.07 95.07,95.07h625.11c52.25,0 95.07,-42.81 95.07,-95.07v-71.31c0,-14.31 -9.54,-23.74 -23.74,-23.74s-23.74,9.54 -23.74,23.74v71.32a47.69,47.69 0,0 1,-47.48 47.58L199.21,910.51a47.72,47.72 0,0 1,-47.58 -47.58v-28.11h0.1L151.73,157.06a47.63,47.63 0,0 1,47.58 -47.48h482.47v123.57a71.32,71.32 0,0 0,71.32 71.32h118.8v344.6c-0.09,14.2 9.44,23.74 23.75,23.74zM847.19,256.81L753,256.81v0.1c-14.3,0 -23.74,-11.92 -23.74,-23.74v-98.75a2.4,2.4 0,0 1,4.13 -1.67l115.52,120a2.4,2.4 0,0 1,-1.73 4.04z"/> |
android:viewportHeight="1024" |
||||||
<path android:fillColor="#2b2b2b" android:pathData="M318.91,394.59c-14.78,0 -26.81,-12.25 -26.81,-27.3s12,-27.3 26.81,-27.3h300.47c14.78,0 26.81,12.25 26.81,27.3s-12,27.3 -26.81,27.3zM318.91,569.8c-14.78,0 -26.81,-12.25 -26.81,-27.3s12,-27.3 26.81,-27.3h384.85c14.78,0 26.81,12.25 26.81,27.3s-12,27.3 -26.81,27.3zM318.91,742c-14.78,0 -26.81,-12.25 -26.81,-27.3s12,-27.3 26.81,-27.3h261.3c14.78,0 26.81,12.25 26.81,27.3S595,742 580.21,742z"/> |
android:viewportWidth="1024" |
||||||
<path android:fillColor="#2b2b2b" android:pathData="M704.01,367.3m-27.3,0a27.3,27.3 0,1 0,54.6 0,27.3 27.3,0 1,0 -54.6,0Z"/> |
android:width="24dp"> |
||||||
|
<path |
||||||
|
android:fillColor="@color/icon_black" |
||||||
|
android:pathData="M895.65,672.81c14.3,0 23.74,-9.54 23.74,-23.74L919.39,269.29a30.46,30.46 0,0 0,-8.66 -21.29L738.23,71.19A30.46,30.46 0,0 0,716.42 62L199.21,62c-52.29,0 -95.07,42.78 -95.07,95.07v654h-0.1v51.85h0.1c0,52.25 42.81,95.07 95.07,95.07h625.11c52.25,0 95.07,-42.81 95.07,-95.07v-71.31c0,-14.31 -9.54,-23.74 -23.74,-23.74s-23.74,9.54 -23.74,23.74v71.32a47.69,47.69 0,0 1,-47.48 47.58L199.21,910.51a47.72,47.72 0,0 1,-47.58 -47.58v-28.11h0.1L151.73,157.06a47.63,47.63 0,0 1,47.58 -47.48h482.47v123.57a71.32,71.32 0,0 0,71.32 71.32h118.8v344.6c-0.09,14.2 9.44,23.74 23.75,23.74zM847.19,256.81L753,256.81v0.1c-14.3,0 -23.74,-11.92 -23.74,-23.74v-98.75a2.4,2.4 0,0 1,4.13 -1.67l115.52,120a2.4,2.4 0,0 1,-1.73 4.04z"/> |
||||||
|
<path |
||||||
|
android:fillColor="@color/icon_black" |
||||||
|
android:pathData="M318.91,394.59c-14.78,0 -26.81,-12.25 -26.81,-27.3s12,-27.3 26.81,-27.3h300.47c14.78,0 26.81,12.25 26.81,27.3s-12,27.3 -26.81,27.3zM318.91,569.8c-14.78,0 -26.81,-12.25 -26.81,-27.3s12,-27.3 26.81,-27.3h384.85c14.78,0 26.81,12.25 26.81,27.3s-12,27.3 -26.81,27.3zM318.91,742c-14.78,0 -26.81,-12.25 -26.81,-27.3s12,-27.3 26.81,-27.3h261.3c14.78,0 26.81,12.25 26.81,27.3S595,742 580.21,742z"/> |
||||||
|
<path |
||||||
|
android:fillColor="@color/icon_black" |
||||||
|
android:pathData="M704.01,367.3m-27.3,0a27.3,27.3 0,1 0,54.6 0,27.3 27.3,0 1,0 -54.6,0Z"/> |
||||||
</vector> |
</vector> |
||||||
|
@ -0,0 +1,6 @@ |
|||||||
|
<vector android:height="24dp" android:viewportHeight="1024" |
||||||
|
android:viewportWidth="1024" android:width="24dp" xmlns:android="http://schemas.android.com/apk/res/android"> |
||||||
|
<path android:fillColor="#CE9F06" android:pathData="M975,774.77H49V220.37h857.09c38.11,0 69.04,30.93 69.04,69.04v485.37h-0.14zM627.71,220.37c0,-63.93 -51.78,-115.71 -115.71,-115.71H49v115.71h578.71"/> |
||||||
|
<path android:fillColor="#FFFFFF" android:pathData="M164.72,620.26V273.11h694.56v347.14h-694.56z"/> |
||||||
|
<path android:fillColor="#FFCD2C" android:pathData="M975,848.1V398.49c0,-31.9 -25.96,-57.86 -57.86,-57.86H106.86c-31.9,0 -57.86,25.96 -57.86,57.86v449.46c0,39.35 31.9,71.39 71.39,71.39h783.35c39.35,0.14 71.25,-31.9 71.25,-71.25z"/> |
||||||
|
</vector> |
@ -0,0 +1,4 @@ |
|||||||
|
<vector android:height="24dp" android:viewportHeight="1024" |
||||||
|
android:viewportWidth="1024" android:width="24dp" xmlns:android="http://schemas.android.com/apk/res/android"> |
||||||
|
<path android:fillColor="@color/icon_black" android:pathData="M432,636.8l-41.6,-41.6 -150.4,150.4L128,633.6L128,896h259.2l-108.8,-108.8 153.6,-150.4zM595.2,387.2l41.6,41.6 150.4,-150.4 108.8,108.8L896,128h-259.2l108.8,108.8 -150.4,150.4zM128,128v259.2l108.8,-108.8 150.4,150.4 41.6,-41.6 -147.2,-150.4L390.4,128L128,128zM595.2,636.8l150.4,150.4 -108.8,108.8L896,896v-259.2l-108.8,108.8 -150.4,-150.4 -41.6,41.6zM595.2,636.8"/> |
||||||
|
</vector> |
@ -0,0 +1,17 @@ |
|||||||
|
<?xml version="1.0" encoding="utf-8"?> |
||||||
|
<layout xmlns:android="http://schemas.android.com/apk/res/android"> |
||||||
|
<LinearLayout |
||||||
|
android:layout_width="match_parent" |
||||||
|
android:layout_height="match_parent" |
||||||
|
android:orientation="vertical" |
||||||
|
> |
||||||
|
|
||||||
|
<com.pddstudio.highlightjs.HighlightJsView |
||||||
|
android:id="@+id/code_view" |
||||||
|
android:layout_width="wrap_content" |
||||||
|
android:layout_height="match_parent" |
||||||
|
/> |
||||||
|
|
||||||
|
</LinearLayout> |
||||||
|
|
||||||
|
</layout> |
@ -0,0 +1,86 @@ |
|||||||
|
<?xml version="1.0" encoding="utf-8"?> |
||||||
|
<layout xmlns:android="http://schemas.android.com/apk/res/android" |
||||||
|
xmlns:bind="http://schemas.android.com/apk/res-auto" |
||||||
|
> |
||||||
|
|
||||||
|
<data> |
||||||
|
<variable |
||||||
|
name="fileSize" |
||||||
|
type="String" |
||||||
|
/> |
||||||
|
<variable |
||||||
|
name="speed" |
||||||
|
type="String" |
||||||
|
/> |
||||||
|
<variable |
||||||
|
name="progress" |
||||||
|
type="int" |
||||||
|
/> |
||||||
|
<variable |
||||||
|
name="stateStr" |
||||||
|
type="String" |
||||||
|
/> |
||||||
|
|
||||||
|
<variable |
||||||
|
name="url" |
||||||
|
type="String" |
||||||
|
/> |
||||||
|
<variable |
||||||
|
name="filePath" |
||||||
|
type="String" |
||||||
|
/> |
||||||
|
<variable |
||||||
|
name="viewModel" |
||||||
|
type="com.arialyy.simple.core.download.KotlinDownloadActivity" |
||||||
|
/> |
||||||
|
</data> |
||||||
|
|
||||||
|
<LinearLayout |
||||||
|
xmlns:tools="http://schemas.android.com/tools" |
||||||
|
android:layout_width="match_parent" |
||||||
|
android:layout_height="match_parent" |
||||||
|
android:fitsSystemWindows="true" |
||||||
|
android:orientation="vertical" |
||||||
|
tools:context=".core.download.SingleTaskActivity" |
||||||
|
> |
||||||
|
|
||||||
|
<include layout="@layout/layout_bar"/> |
||||||
|
|
||||||
|
<com.arialyy.simple.widget.SvgTextView |
||||||
|
android:layout_width="match_parent" |
||||||
|
android:layout_height="wrap_content" |
||||||
|
android:layout_marginLeft="16dp" |
||||||
|
android:layout_marginRight="16dp" |
||||||
|
android:layout_marginTop="16dp" |
||||||
|
bind:iconClickListener="@{() -> viewModel.chooseUrl()}" |
||||||
|
bind:svg_text_view_icon="@drawable/ic_modify" |
||||||
|
bind:text="@{@string/url(url)}" |
||||||
|
/> |
||||||
|
|
||||||
|
<com.arialyy.simple.widget.SvgTextView |
||||||
|
android:layout_width="match_parent" |
||||||
|
android:layout_height="wrap_content" |
||||||
|
android:layout_marginLeft="16dp" |
||||||
|
android:layout_marginRight="16dp" |
||||||
|
android:layout_marginTop="8dp" |
||||||
|
bind:iconClickListener="@{() -> viewModel.chooseFilePath()}" |
||||||
|
bind:svg_text_view_icon="@drawable/ic_choose_file" |
||||||
|
bind:text="@{@string/file_path(filePath)}" |
||||||
|
/> |
||||||
|
|
||||||
|
<include |
||||||
|
layout="@layout/layout_content_single" |
||||||
|
bind:fileSize="@{fileSize}" |
||||||
|
bind:progress="@{progress}" |
||||||
|
bind:speed="@{speed}" |
||||||
|
bind:stateStr="@{stateStr}" |
||||||
|
/> |
||||||
|
|
||||||
|
<com.arialyy.simple.widget.CodeView |
||||||
|
android:id="@+id/code_view" |
||||||
|
android:layout_width="match_parent" |
||||||
|
android:layout_height="match_parent" |
||||||
|
/> |
||||||
|
|
||||||
|
</LinearLayout> |
||||||
|
</layout> |
@ -0,0 +1,81 @@ |
|||||||
|
<?xml version="1.0" encoding="utf-8"?> |
||||||
|
<layout xmlns:android="http://schemas.android.com/apk/res/android"> |
||||||
|
<data> |
||||||
|
<variable |
||||||
|
name="currentPath" |
||||||
|
type="String" |
||||||
|
/> |
||||||
|
<import type="android.text.Html"/> |
||||||
|
</data> |
||||||
|
<RelativeLayout |
||||||
|
android:layout_width="match_parent" |
||||||
|
android:layout_height="wrap_content" |
||||||
|
android:background="@color/white" |
||||||
|
android:orientation="vertical" |
||||||
|
> |
||||||
|
|
||||||
|
<TextView |
||||||
|
android:id="@+id/title" |
||||||
|
android:layout_width="match_parent" |
||||||
|
android:layout_height="wrap_content" |
||||||
|
android:background="@color/background_color" |
||||||
|
android:maxHeight="400dp" |
||||||
|
android:paddingBottom="8dp" |
||||||
|
android:paddingLeft="16dp" |
||||||
|
android:paddingRight="16dp" |
||||||
|
android:paddingTop="8dp" |
||||||
|
android:text="@string/choose_dir" |
||||||
|
android:textColor="@android:color/black" |
||||||
|
android:textSize="22sp" |
||||||
|
/> |
||||||
|
|
||||||
|
<TextView |
||||||
|
android:id="@+id/current_path" |
||||||
|
android:layout_width="match_parent" |
||||||
|
android:layout_height="wrap_content" |
||||||
|
android:layout_below="@+id/title" |
||||||
|
android:layout_marginLeft="16dp" |
||||||
|
android:layout_marginRight="16dp" |
||||||
|
android:layout_marginTop="4dp" |
||||||
|
android:ellipsize="marquee" |
||||||
|
android:singleLine="true" |
||||||
|
android:text="@{Html.fromHtml(@string/current_path(currentPath))}" |
||||||
|
/> |
||||||
|
|
||||||
|
<TextView |
||||||
|
android:id="@+id/up" |
||||||
|
android:layout_width="match_parent" |
||||||
|
android:layout_height="wrap_content" |
||||||
|
android:layout_below="@+id/current_path" |
||||||
|
android:layout_marginLeft="16dp" |
||||||
|
android:layout_marginRight="16dp" |
||||||
|
android:gravity="center_vertical" |
||||||
|
android:text=".." |
||||||
|
android:textColor="@color/text_black" |
||||||
|
android:textSize="18sp" |
||||||
|
android:textStyle="bold" |
||||||
|
/> |
||||||
|
|
||||||
|
<android.support.v7.widget.RecyclerView |
||||||
|
android:id="@+id/list" |
||||||
|
android:layout_width="match_parent" |
||||||
|
android:layout_height="wrap_content" |
||||||
|
android:layout_above="@+id/enter" |
||||||
|
android:layout_below="@+id/up" |
||||||
|
android:layout_marginLeft="16dp" |
||||||
|
android:layout_marginRight="16dp" |
||||||
|
android:layout_marginTop="4dp" |
||||||
|
/> |
||||||
|
|
||||||
|
<Button |
||||||
|
android:id="@+id/enter" |
||||||
|
android:layout_width="match_parent" |
||||||
|
android:layout_height="wrap_content" |
||||||
|
android:layout_alignParentBottom="true" |
||||||
|
android:text="@string/choose_current_dir" |
||||||
|
style="?buttonBarButtonStyle" |
||||||
|
/> |
||||||
|
|
||||||
|
|
||||||
|
</RelativeLayout> |
||||||
|
</layout> |
@ -0,0 +1,120 @@ |
|||||||
|
<?xml version="1.0" encoding="utf-8"?> |
||||||
|
<layout xmlns:android="http://schemas.android.com/apk/res/android" |
||||||
|
xmlns:bind="http://schemas.android.com/apk/res-auto" |
||||||
|
> |
||||||
|
<data> |
||||||
|
<variable |
||||||
|
name="title" |
||||||
|
type="String" |
||||||
|
/> |
||||||
|
<variable |
||||||
|
name="name" |
||||||
|
type="String" |
||||||
|
/> |
||||||
|
<variable |
||||||
|
name="dir" |
||||||
|
type="String" |
||||||
|
/> |
||||||
|
<variable |
||||||
|
name="viewModel" |
||||||
|
type="com.arialyy.simple.common.ModifyPathDialog" |
||||||
|
/> |
||||||
|
</data> |
||||||
|
<LinearLayout |
||||||
|
android:layout_width="match_parent" |
||||||
|
android:layout_height="wrap_content" |
||||||
|
android:background="@color/white" |
||||||
|
android:orientation="vertical" |
||||||
|
> |
||||||
|
|
||||||
|
<TextView |
||||||
|
android:layout_width="match_parent" |
||||||
|
android:layout_height="wrap_content" |
||||||
|
android:background="@color/background_color" |
||||||
|
android:gravity="center|left" |
||||||
|
android:maxHeight="400dp" |
||||||
|
android:paddingBottom="8dp" |
||||||
|
android:paddingLeft="16dp" |
||||||
|
android:paddingRight="16dp" |
||||||
|
android:paddingTop="8dp" |
||||||
|
android:text="@{title}" |
||||||
|
android:textColor="@android:color/black" |
||||||
|
android:textSize="22sp" |
||||||
|
/> |
||||||
|
|
||||||
|
<com.arialyy.simple.widget.SvgTextView |
||||||
|
android:layout_width="match_parent" |
||||||
|
android:layout_height="wrap_content" |
||||||
|
android:layout_marginLeft="16dp" |
||||||
|
android:layout_marginRight="16dp" |
||||||
|
android:layout_marginTop="16dp" |
||||||
|
bind:iconClickListener="@{() -> viewModel.chooseDir()}" |
||||||
|
bind:svg_text_view_icon="@drawable/ic_modify" |
||||||
|
bind:text="@{@string/dir_path(dir)}" |
||||||
|
/> |
||||||
|
|
||||||
|
<RelativeLayout |
||||||
|
android:layout_width="match_parent" |
||||||
|
android:layout_height="wrap_content" |
||||||
|
android:layout_marginLeft="16dp" |
||||||
|
android:layout_marginRight="16dp" |
||||||
|
android:layout_marginTop="8dp" |
||||||
|
> |
||||||
|
|
||||||
|
<TextView |
||||||
|
android:id="@+id/dir_hint" |
||||||
|
android:layout_width="wrap_content" |
||||||
|
android:layout_height="wrap_content" |
||||||
|
android:layout_centerVertical="true" |
||||||
|
android:text="@string/name" |
||||||
|
android:textColor="@color/text_black" |
||||||
|
android:textSize="@dimen/text_size_normal" |
||||||
|
android:textStyle="bold" |
||||||
|
/> |
||||||
|
|
||||||
|
<EditText |
||||||
|
android:id="@+id/edit" |
||||||
|
android:layout_width="match_parent" |
||||||
|
android:layout_height="wrap_content" |
||||||
|
android:layout_centerVertical="true" |
||||||
|
android:layout_marginBottom="15dp" |
||||||
|
android:layout_marginLeft="8dp" |
||||||
|
android:layout_toRightOf="@+id/dir_hint" |
||||||
|
android:background="@android:color/transparent" |
||||||
|
android:hint="@string/file_name_hint" |
||||||
|
android:lineSpacingMultiplier="1.2" |
||||||
|
android:text="@={name}" |
||||||
|
android:textColor="@color/text_black" |
||||||
|
android:textSize="@dimen/text_size_normal" |
||||||
|
/> |
||||||
|
|
||||||
|
</RelativeLayout> |
||||||
|
|
||||||
|
<LinearLayout |
||||||
|
android:layout_width="match_parent" |
||||||
|
android:layout_height="wrap_content" |
||||||
|
android:orientation="horizontal" |
||||||
|
> |
||||||
|
<Button |
||||||
|
android:id="@+id/cancel" |
||||||
|
android:layout_width="wrap_content" |
||||||
|
android:layout_height="wrap_content" |
||||||
|
android:layout_weight="1" |
||||||
|
android:text="@string/cancel" |
||||||
|
style="?buttonBarButtonStyle" |
||||||
|
/> |
||||||
|
|
||||||
|
<Button |
||||||
|
android:id="@+id/enter" |
||||||
|
android:layout_width="wrap_content" |
||||||
|
android:layout_height="wrap_content" |
||||||
|
android:layout_weight="1" |
||||||
|
android:text="@string/enter" |
||||||
|
style="?buttonBarButtonStyle" |
||||||
|
/> |
||||||
|
|
||||||
|
</LinearLayout> |
||||||
|
|
||||||
|
|
||||||
|
</LinearLayout> |
||||||
|
</layout> |
@ -0,0 +1,31 @@ |
|||||||
|
<?xml version="1.0" encoding="utf-8"?> |
||||||
|
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android" |
||||||
|
xmlns:app="http://schemas.android.com/apk/res-auto" |
||||||
|
android:layout_width="match_parent" |
||||||
|
android:layout_height="@dimen/bar_height" |
||||||
|
android:background="@drawable/item_bg" |
||||||
|
android:orientation="vertical" |
||||||
|
> |
||||||
|
|
||||||
|
<android.support.v7.widget.AppCompatImageView |
||||||
|
android:id="@+id/icon" |
||||||
|
android:layout_width="@dimen/icon_size" |
||||||
|
android:layout_height="@dimen/icon_size" |
||||||
|
android:layout_centerVertical="true" |
||||||
|
app:srcCompat="@drawable/ic_dir" |
||||||
|
/> |
||||||
|
|
||||||
|
<TextView |
||||||
|
android:id="@+id/text" |
||||||
|
android:layout_width="wrap_content" |
||||||
|
android:layout_height="wrap_content" |
||||||
|
android:layout_centerVertical="true" |
||||||
|
android:layout_marginLeft="8dp" |
||||||
|
android:layout_toRightOf="@+id/icon" |
||||||
|
android:ellipsize="marquee" |
||||||
|
android:singleLine="true" |
||||||
|
android:textColor="@color/text_black" |
||||||
|
android:textSize="@dimen/text_size_normal" |
||||||
|
/> |
||||||
|
|
||||||
|
</RelativeLayout> |
@ -0,0 +1,44 @@ |
|||||||
|
<?xml version="1.0" encoding="utf-8"?> |
||||||
|
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" |
||||||
|
xmlns:app="http://schemas.android.com/apk/res-auto" |
||||||
|
android:layout_width="match_parent" |
||||||
|
android:layout_height="match_parent" |
||||||
|
android:orientation="vertical" |
||||||
|
> |
||||||
|
|
||||||
|
|
||||||
|
<RelativeLayout |
||||||
|
android:layout_width="match_parent" |
||||||
|
android:layout_height="wrap_content" |
||||||
|
android:layout_marginLeft="16dp" |
||||||
|
android:layout_marginRight="16dp" |
||||||
|
> |
||||||
|
<TextView |
||||||
|
android:id="@+id/hint" |
||||||
|
android:layout_width="wrap_content" |
||||||
|
android:layout_height="wrap_content" |
||||||
|
android:layout_centerVertical="true" |
||||||
|
android:text="@string/code_simple" |
||||||
|
android:textColor="@color/text_black" |
||||||
|
android:textSize="@dimen/text_size_normal" |
||||||
|
android:textStyle="bold" |
||||||
|
/> |
||||||
|
<android.support.v7.widget.AppCompatImageView |
||||||
|
android:id="@+id/full_screen" |
||||||
|
android:layout_width="wrap_content" |
||||||
|
android:layout_height="wrap_content" |
||||||
|
android:layout_alignParentRight="true" |
||||||
|
android:layout_centerVertical="true" |
||||||
|
app:srcCompat="@drawable/ic_full_screen" |
||||||
|
/> |
||||||
|
|
||||||
|
</RelativeLayout> |
||||||
|
|
||||||
|
<com.pddstudio.highlightjs.HighlightJsView |
||||||
|
android:id="@+id/js_view" |
||||||
|
android:layout_width="match_parent" |
||||||
|
android:layout_height="match_parent" |
||||||
|
/> |
||||||
|
|
||||||
|
|
||||||
|
</LinearLayout> |
@ -0,0 +1,12 @@ |
|||||||
|
<?xml version="1.0" encoding="utf-8"?> |
||||||
|
<resources> |
||||||
|
|
||||||
|
<string name="code_ftp_upload"> |
||||||
|
Aria.upload(this) |
||||||
|
.loadFtp("/mnt/sdcard/gggg.apk") //上传文件路径 |
||||||
|
.setUploadUrl(URL) //上传的ftp服务器地址 |
||||||
|
.login("lao", "123456") |
||||||
|
.start(); |
||||||
|
</string> |
||||||
|
|
||||||
|
</resources> |
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in new issue