parent
0fdf222a78
commit
2b8635e45a
|
|
@ -103,7 +103,7 @@ dependencies {
|
||||||
// https://mvnrepository.com/artifact/com.github.bumptech.glide/glide
|
// https://mvnrepository.com/artifact/com.github.bumptech.glide/glide
|
||||||
implementation("com.github.bumptech.glide:glide:4.13.2")
|
implementation("com.github.bumptech.glide:glide:4.13.2")
|
||||||
// implementation("com.luozm.captcha:captcha:1.1.2")
|
// implementation("com.luozm.captcha:captcha:1.1.2")
|
||||||
|
implementation("com.aliyun.dpa:oss-android-sdk:2.9.19")
|
||||||
}
|
}
|
||||||
|
|
||||||
tasks.getByName("clean", type = Delete::class) {
|
tasks.getByName("clean", type = Delete::class) {
|
||||||
|
|
|
||||||
|
|
@ -16,6 +16,7 @@ import android.widget.ImageView
|
||||||
import android.widget.LinearLayout
|
import android.widget.LinearLayout
|
||||||
import android.widget.ProgressBar
|
import android.widget.ProgressBar
|
||||||
import android.widget.TextView
|
import android.widget.TextView
|
||||||
|
import android.widget.Toast
|
||||||
import androidx.core.content.FileProvider
|
import androidx.core.content.FileProvider
|
||||||
import androidx.core.view.GravityCompat
|
import androidx.core.view.GravityCompat
|
||||||
import androidx.drawerlayout.widget.DrawerLayout
|
import androidx.drawerlayout.widget.DrawerLayout
|
||||||
|
|
@ -27,6 +28,15 @@ import com.afollestad.materialdialogs.MaterialDialog
|
||||||
import com.afollestad.materialdialogs.customview.customView
|
import com.afollestad.materialdialogs.customview.customView
|
||||||
import com.afollestad.materialdialogs.customview.getCustomView
|
import com.afollestad.materialdialogs.customview.getCustomView
|
||||||
import com.airbnb.lottie.LottieAnimationView
|
import com.airbnb.lottie.LottieAnimationView
|
||||||
|
import com.alibaba.sdk.android.oss.ClientException
|
||||||
|
import com.alibaba.sdk.android.oss.OSSClient
|
||||||
|
import com.alibaba.sdk.android.oss.ServiceException
|
||||||
|
import com.alibaba.sdk.android.oss.callback.OSSCompletedCallback
|
||||||
|
import com.alibaba.sdk.android.oss.callback.OSSProgressCallback
|
||||||
|
import com.alibaba.sdk.android.oss.common.auth.OSSStsTokenCredentialProvider
|
||||||
|
import com.alibaba.sdk.android.oss.internal.OSSAsyncTask
|
||||||
|
import com.alibaba.sdk.android.oss.model.PutObjectRequest
|
||||||
|
import com.alibaba.sdk.android.oss.model.PutObjectResult
|
||||||
import com.google.gson.Gson
|
import com.google.gson.Gson
|
||||||
import com.google.gson.JsonArray
|
import com.google.gson.JsonArray
|
||||||
import com.google.gson.JsonObject
|
import com.google.gson.JsonObject
|
||||||
|
|
@ -52,8 +62,10 @@ import com.yingmao.clash.net.http.NetService
|
||||||
import com.yingmao.clash.net.http.entity.AppVersionCheckRsp
|
import com.yingmao.clash.net.http.entity.AppVersionCheckRsp
|
||||||
import com.yingmao.clash.net.http.entity.GetActivityRsp
|
import com.yingmao.clash.net.http.entity.GetActivityRsp
|
||||||
import com.yingmao.clash.net.http.entity.HeartBeatRsp
|
import com.yingmao.clash.net.http.entity.HeartBeatRsp
|
||||||
|
import com.yingmao.clash.net.http.entity.ParamData
|
||||||
import com.yingmao.clash.net.http.entity.PhDownloadUrlRsp
|
import com.yingmao.clash.net.http.entity.PhDownloadUrlRsp
|
||||||
import com.yingmao.clash.net.http.entity.SubscriptionRsp
|
import com.yingmao.clash.net.http.entity.SubscriptionRsp
|
||||||
|
import com.yingmao.clash.net.http.entity.UploadParamData
|
||||||
import com.yingmao.clash.net.http.entity.VipSignRsp
|
import com.yingmao.clash.net.http.entity.VipSignRsp
|
||||||
import com.yingmao.clash.remote.Remote
|
import com.yingmao.clash.remote.Remote
|
||||||
import com.yingmao.clash.service.ClashManager
|
import com.yingmao.clash.service.ClashManager
|
||||||
|
|
@ -65,6 +77,8 @@ import com.yingmao.clash.service.remote.IProfileManager
|
||||||
import com.yingmao.clash.service.remote.wrap
|
import com.yingmao.clash.service.remote.wrap
|
||||||
import com.yingmao.clash.util.AESUtil
|
import com.yingmao.clash.util.AESUtil
|
||||||
import com.yingmao.clash.util.CommonUtils
|
import com.yingmao.clash.util.CommonUtils
|
||||||
|
import com.yingmao.clash.util.DataUtils
|
||||||
|
import com.yingmao.clash.util.DataUtils.getAllFilePath
|
||||||
import com.yingmao.clash.util.Utils
|
import com.yingmao.clash.util.Utils
|
||||||
import com.yingmao.clash.view.RxToast
|
import com.yingmao.clash.view.RxToast
|
||||||
import com.yingmao.clash.view.adapter.MainVPAdapter
|
import com.yingmao.clash.view.adapter.MainVPAdapter
|
||||||
|
|
@ -74,6 +88,7 @@ import com.yingmao.clash.view.dialog.DialogAppVersion
|
||||||
import com.yingmao.clash.view.dialog.DialogBandChoose
|
import com.yingmao.clash.view.dialog.DialogBandChoose
|
||||||
import com.yingmao.clash.view.dialog.DialogNotice
|
import com.yingmao.clash.view.dialog.DialogNotice
|
||||||
import com.yingmao.clash.view.dialog.RxDialogShapeLoading
|
import com.yingmao.clash.view.dialog.RxDialogShapeLoading
|
||||||
|
import com.yingmao.clash.view.permissions.PermissionX
|
||||||
import com.yingmao.clash.vm.MainAtyVM
|
import com.yingmao.clash.vm.MainAtyVM
|
||||||
import com.yingmao.clash.webview.BZYWebViewKeFu
|
import com.yingmao.clash.webview.BZYWebViewKeFu
|
||||||
import com.yingmao.clash.webview.BZYWebViewNormal
|
import com.yingmao.clash.webview.BZYWebViewNormal
|
||||||
|
|
@ -87,6 +102,7 @@ import okhttp3.RequestBody
|
||||||
import java.io.File
|
import java.io.File
|
||||||
import java.text.SimpleDateFormat
|
import java.text.SimpleDateFormat
|
||||||
import java.util.Date
|
import java.util.Date
|
||||||
|
import java.util.Locale
|
||||||
import java.util.Timer
|
import java.util.Timer
|
||||||
import java.util.TimerTask
|
import java.util.TimerTask
|
||||||
import java.util.UUID
|
import java.util.UUID
|
||||||
|
|
@ -249,7 +265,7 @@ class NewMainActivity : BaseActivity() {
|
||||||
startLog()
|
startLog()
|
||||||
// showBindAccount()
|
// showBindAccount()
|
||||||
firstEntry()
|
firstEntry()
|
||||||
|
checkPermission()
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
|
|
@ -543,7 +559,32 @@ class NewMainActivity : BaseActivity() {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
android.util.Log.i("levelData", "data:::${data}")
|
} else if (httpStatus.reqType == HttpReqType.UploadParameters) {
|
||||||
|
val upLoadData = httpStatus.rsp as UploadParamData
|
||||||
|
if (upLoadData.code == 1000) {
|
||||||
|
val directory: File? = this.getExternalFilesDir("CrashLog")
|
||||||
|
//多文件上传
|
||||||
|
if (directory != null) {
|
||||||
|
val allFilePath = getAllFilePath(directory.path)
|
||||||
|
if (upLoadData.data != null) {
|
||||||
|
if (allFilePath.isNotEmpty()) {
|
||||||
|
val username =
|
||||||
|
MMKV.defaultMMKV()
|
||||||
|
.decodeString(BZYConstants.MMKV_KEY_REMEMBER_USERNAME, "")!!
|
||||||
|
val nowTime = SimpleDateFormat(
|
||||||
|
"yyyyMMddkkmmss",
|
||||||
|
Locale.getDefault()
|
||||||
|
).format(Date())
|
||||||
|
uploadForAliOss(
|
||||||
|
upLoadData.data,
|
||||||
|
allFilePath,
|
||||||
|
"app/errlog/" + username + "_" + nowTime + ".txt"
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -1086,4 +1127,89 @@ class NewMainActivity : BaseActivity() {
|
||||||
super.onDestroy()
|
super.onDestroy()
|
||||||
stopUploadTimer()
|
stopUploadTimer()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private fun uploadForAliOss(data: ParamData, filePath: List<String>, dirStr: String) {
|
||||||
|
val endpoint = "oss-cn-hongkong.aliyuncs.com"
|
||||||
|
val accessKeyId = data.credentials?.accessKeyId
|
||||||
|
val accessKeySecret = data.credentials?.accessKeySecret
|
||||||
|
// 从STS服务获取的安全令牌(SecurityToken)。
|
||||||
|
val securityToken = data.credentials?.securityToken
|
||||||
|
val credentialProvider =
|
||||||
|
OSSStsTokenCredentialProvider(accessKeyId, accessKeySecret, securityToken)
|
||||||
|
val oss = OSSClient(applicationContext, endpoint, credentialProvider)
|
||||||
|
|
||||||
|
android.util.Log.i("uploadForAliOss", "filePath::::${filePath[0]}")
|
||||||
|
val put = PutObjectRequest("yingmao", dirStr, filePath[0])
|
||||||
|
// 异步上传时可以设置进度回调。
|
||||||
|
put.progressCallback =
|
||||||
|
OSSProgressCallback { request, currentSize, totalSize ->
|
||||||
|
android.util.Log.d(
|
||||||
|
"PutObject",
|
||||||
|
"currentSize: $currentSize totalSize: $totalSize"
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
val task: OSSAsyncTask<*> = oss.asyncPutObject(put,
|
||||||
|
object : OSSCompletedCallback<PutObjectRequest?, PutObjectResult> {
|
||||||
|
override fun onSuccess(request: PutObjectRequest?, result: PutObjectResult) {
|
||||||
|
if (result.statusCode == 200) {
|
||||||
|
//删除文件
|
||||||
|
DataUtils.deleteFile(filePath[0])
|
||||||
|
}
|
||||||
|
android.util.Log.d("PutObjectRequest", "$request")
|
||||||
|
android.util.Log.d("PutObjectResult", "$result")
|
||||||
|
android.util.Log.d("PutObject", "UploadSuccess")
|
||||||
|
android.util.Log.d("ETag", result.eTag)
|
||||||
|
android.util.Log.d("RequestId", result.requestId)
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun onFailure(
|
||||||
|
request: PutObjectRequest?,
|
||||||
|
clientExcepion: ClientException,
|
||||||
|
serviceException: ServiceException
|
||||||
|
) {
|
||||||
|
// 请求异常。
|
||||||
|
if (clientExcepion != null) {
|
||||||
|
// 本地异常,如网络异常等。
|
||||||
|
clientExcepion.printStackTrace()
|
||||||
|
}
|
||||||
|
if (serviceException != null) {
|
||||||
|
// 服务异常。
|
||||||
|
android.util.Log.e("ErrorCode", serviceException.errorCode)
|
||||||
|
android.util.Log.e("RequestId", serviceException.requestId)
|
||||||
|
android.util.Log.e("HostId", serviceException.hostId)
|
||||||
|
android.util.Log.e("RawMessage", serviceException.rawMessage)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun checkPermission() {
|
||||||
|
PermissionX.request(
|
||||||
|
this,
|
||||||
|
Manifest.permission.WRITE_EXTERNAL_STORAGE,
|
||||||
|
Manifest.permission.READ_EXTERNAL_STORAGE,
|
||||||
|
) { allGranted, deniedList ->
|
||||||
|
if (allGranted) {
|
||||||
|
android.util.Log.i("checkPermission", "checkPermission::所有申请的权限都已通过")
|
||||||
|
val directory: File? = this.getExternalFilesDir("CrashLog")
|
||||||
|
if (directory != null) {
|
||||||
|
val allFilePath = getAllFilePath(directory.path)
|
||||||
|
if (allFilePath.isNotEmpty()) {
|
||||||
|
mainAtyVM.getUploadParameters()
|
||||||
|
} else {
|
||||||
|
android.util.Log.i("checkPermission", "allFilePath is empty")
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
Toast.makeText(this, "You denied $deniedList", Toast.LENGTH_SHORT).show()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
@ -19,6 +19,7 @@ import com.yingmao.clash.common.compat.currentProcessName
|
||||||
import com.yingmao.clash.common.log.Log
|
import com.yingmao.clash.common.log.Log
|
||||||
import com.yingmao.clash.entity.CheckLoginRsp
|
import com.yingmao.clash.entity.CheckLoginRsp
|
||||||
import com.yingmao.clash.entity.UserData
|
import com.yingmao.clash.entity.UserData
|
||||||
|
import com.yingmao.clash.util.CrashCatchHandler
|
||||||
|
|
||||||
import com.yingmao.clash.util.clashDir
|
import com.yingmao.clash.util.clashDir
|
||||||
import java.io.File
|
import java.io.File
|
||||||
|
|
@ -68,6 +69,11 @@ class MainApplication : Application() {
|
||||||
val MMKVRootDir = MMKV.initialize(this)
|
val MMKVRootDir = MMKV.initialize(this)
|
||||||
Logger.i("MMKV_ROOT_DIR:$MMKVRootDir")
|
Logger.i("MMKV_ROOT_DIR:$MMKVRootDir")
|
||||||
updateNotificationChannels()
|
updateNotificationChannels()
|
||||||
|
/*
|
||||||
|
* 错误日志上传捕获
|
||||||
|
* */
|
||||||
|
val crashCatchHandler: CrashCatchHandler = CrashCatchHandler.getInstance() //获得单例
|
||||||
|
crashCatchHandler.init(applicationContext) //初始化,传入context
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun extractGeoFiles() {
|
private fun extractGeoFiles() {
|
||||||
|
|
|
||||||
|
|
@ -25,6 +25,7 @@ import com.yingmao.clash.net.http.entity.AppVersionCheckRsp
|
||||||
import com.yingmao.clash.net.http.entity.GetActivityRsp
|
import com.yingmao.clash.net.http.entity.GetActivityRsp
|
||||||
import com.yingmao.clash.net.http.entity.LoginRsp2
|
import com.yingmao.clash.net.http.entity.LoginRsp2
|
||||||
import com.yingmao.clash.net.http.entity.SendSmsRsp
|
import com.yingmao.clash.net.http.entity.SendSmsRsp
|
||||||
|
import com.yingmao.clash.net.http.entity.UploadParamData
|
||||||
import com.yingmao.clash.net.http.entity.VipSignRsp
|
import com.yingmao.clash.net.http.entity.VipSignRsp
|
||||||
import okhttp3.RequestBody
|
import okhttp3.RequestBody
|
||||||
import retrofit2.Call
|
import retrofit2.Call
|
||||||
|
|
@ -489,4 +490,16 @@ interface HttpApi {
|
||||||
BZYConstants.MMKV_KEY_TOKEN
|
BZYConstants.MMKV_KEY_TOKEN
|
||||||
).toString()
|
).toString()
|
||||||
): GetActivityRsp
|
): GetActivityRsp
|
||||||
|
|
||||||
|
//获取阿里云上传数据
|
||||||
|
@Headers(
|
||||||
|
"User-Agent: Octopus_Android"
|
||||||
|
)
|
||||||
|
@POST("/netbarcloud/vpn/getOssAssumeRoleToken")
|
||||||
|
suspend fun getUploadParameters(
|
||||||
|
@QueryMap params: Map<String, String>,
|
||||||
|
@Header("token") string: String = MMKV.defaultMMKV().decodeString(
|
||||||
|
BZYConstants.MMKV_KEY_TOKEN
|
||||||
|
).toString()
|
||||||
|
): UploadParamData
|
||||||
}
|
}
|
||||||
|
|
@ -46,6 +46,6 @@ enum class HttpReqType(type: Int) {
|
||||||
Get_Node_Other(42),
|
Get_Node_Other(42),
|
||||||
GetActivity(43),
|
GetActivity(43),
|
||||||
GetRuningNotice(44),
|
GetRuningNotice(44),
|
||||||
unionPay(45)
|
unionPay(45),
|
||||||
|
UploadParameters(46)
|
||||||
}
|
}
|
||||||
|
|
@ -0,0 +1,24 @@
|
||||||
|
package com.yingmao.clash.net.http.entity
|
||||||
|
|
||||||
|
import com.google.gson.annotations.SerializedName
|
||||||
|
|
||||||
|
data class UploadParamData (
|
||||||
|
@SerializedName("code") val code: Int,
|
||||||
|
@SerializedName("message") val message: String,
|
||||||
|
@SerializedName("data") val data: ParamData?
|
||||||
|
)
|
||||||
|
data class ParamData(
|
||||||
|
val requestId:String?,
|
||||||
|
val credentials:Credentials?,
|
||||||
|
val assumedRoleUser:AssumedRoleUser?
|
||||||
|
)
|
||||||
|
|
||||||
|
data class AssumedRoleUser(
|
||||||
|
val arn:String,
|
||||||
|
val assumedRoleId:String
|
||||||
|
)
|
||||||
|
data class Credentials(
|
||||||
|
val securityToken:String,
|
||||||
|
val accessKeySecret:String,
|
||||||
|
val accessKeyId:String
|
||||||
|
)
|
||||||
|
|
@ -0,0 +1,206 @@
|
||||||
|
package com.yingmao.clash.util;
|
||||||
|
|
||||||
|
import android.annotation.SuppressLint;
|
||||||
|
import android.content.Context;
|
||||||
|
import android.content.pm.PackageInfo;
|
||||||
|
import android.content.pm.PackageManager;
|
||||||
|
import android.content.pm.PackageManager.NameNotFoundException;
|
||||||
|
import android.os.Build;
|
||||||
|
import android.os.Looper;
|
||||||
|
import android.util.Log;
|
||||||
|
import android.widget.Toast;
|
||||||
|
|
||||||
|
import androidx.annotation.NonNull;
|
||||||
|
|
||||||
|
import java.io.File;
|
||||||
|
import java.io.FileNotFoundException;
|
||||||
|
import java.io.FileOutputStream;
|
||||||
|
import java.io.IOException;
|
||||||
|
import java.io.PrintWriter;
|
||||||
|
import java.io.StringWriter;
|
||||||
|
import java.io.Writer;
|
||||||
|
import java.lang.Thread.UncaughtExceptionHandler;
|
||||||
|
import java.lang.reflect.Field;
|
||||||
|
import java.text.SimpleDateFormat;
|
||||||
|
import java.util.Date;
|
||||||
|
import java.util.HashMap;
|
||||||
|
import java.util.Locale;
|
||||||
|
import java.util.Map;
|
||||||
|
import java.util.Objects;
|
||||||
|
|
||||||
|
public class CrashCatchHandler implements UncaughtExceptionHandler {
|
||||||
|
private final static String FILE_TYPE = "CrashLog";
|
||||||
|
public static final String TAG = "CrashHandler";
|
||||||
|
|
||||||
|
@SuppressLint("StaticFieldLeak")
|
||||||
|
private static final CrashCatchHandler INSTANCE = new CrashCatchHandler();// 单例模式
|
||||||
|
private Context context;
|
||||||
|
private UncaughtExceptionHandler defaultHandler;// 系统默认的UncaughtException处理类
|
||||||
|
private final Map<String, String> infoMap = new HashMap<String, String>(); // 用来存储设备信息和异常信息
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 私有构造方法,保证只有一个CrashHandler实例
|
||||||
|
*/
|
||||||
|
private CrashCatchHandler() {
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 获取CrashHandler,单例模式
|
||||||
|
*/
|
||||||
|
public static CrashCatchHandler getInstance() {
|
||||||
|
return INSTANCE;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 初始化
|
||||||
|
*/
|
||||||
|
public void init(Context context) {
|
||||||
|
this.context = context;
|
||||||
|
defaultHandler = Thread.getDefaultUncaughtExceptionHandler();// 获取系统默认的UncaughtException处理器
|
||||||
|
Thread.setDefaultUncaughtExceptionHandler(this);// 设置当前CrashHandler为程序的默认处理器
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 当UncaughtException发生时会转入该函数来处理
|
||||||
|
*/
|
||||||
|
@Override
|
||||||
|
public void uncaughtException(@NonNull Thread thread, @NonNull Throwable ex) {
|
||||||
|
try {
|
||||||
|
if (!handleException(ex) && defaultHandler != null) {
|
||||||
|
// 如果用户没有处理则让系统默认的异常处理器来处理
|
||||||
|
defaultHandler.uncaughtException(thread, ex);
|
||||||
|
} else {
|
||||||
|
try {
|
||||||
|
Thread.sleep(3000);
|
||||||
|
} catch (InterruptedException e) {
|
||||||
|
Log.e(TAG, "exception : ", e);
|
||||||
|
e.printStackTrace();
|
||||||
|
}
|
||||||
|
// 杀死进程
|
||||||
|
android.os.Process.killProcess(android.os.Process.myPid());
|
||||||
|
System.exit(1);
|
||||||
|
}
|
||||||
|
} catch (IOException e) {
|
||||||
|
throw new RuntimeException(e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 自定义错误处理,收集错误信息 发送错误报告等操作均在此完成.
|
||||||
|
*
|
||||||
|
* @return 如果处理了该异常信息, 返回true;否则返回false.
|
||||||
|
*/
|
||||||
|
private boolean handleException(Throwable ex) throws IOException {
|
||||||
|
if (ex == null) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
// 使用Toast显示异常信息
|
||||||
|
new Thread() {
|
||||||
|
public void run() {
|
||||||
|
Looper.prepare();
|
||||||
|
Toast.makeText(context, "程序异常停止,请稍后重启应用!", Toast.LENGTH_SHORT).show();
|
||||||
|
Looper.loop();
|
||||||
|
}
|
||||||
|
}.start();
|
||||||
|
|
||||||
|
collectDeviceInfo(context);// 收集设备参数信息
|
||||||
|
saveCrashInfoToFile(ex);// 保存日志文件
|
||||||
|
//上传错误文件
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 收集设备信息
|
||||||
|
*/
|
||||||
|
public void collectDeviceInfo(Context context) {
|
||||||
|
// 使用包管理器获取信息
|
||||||
|
PackageManager pm = context.getPackageManager();
|
||||||
|
SimpleDateFormat nowTime = new SimpleDateFormat("yyyy年MM月dd日HH时mm分ss秒E", Locale.getDefault());
|
||||||
|
String nowTimeStr = nowTime.format(System.currentTimeMillis());
|
||||||
|
|
||||||
|
|
||||||
|
try {
|
||||||
|
PackageInfo pi = pm.getPackageInfo(context.getPackageName(), PackageManager.GET_ACTIVITIES);
|
||||||
|
if (pi != null) {
|
||||||
|
// TODO 在这里得到包的信息
|
||||||
|
String versionName = pi.versionName == null ? "" : pi.versionName;// 版本名;若versionName==null,则="null";否则=versionName
|
||||||
|
String versionCode = pi.versionCode + "";// 版本号
|
||||||
|
infoMap.put("versionName", versionName);
|
||||||
|
infoMap.put("versionCode", versionCode);
|
||||||
|
infoMap.put("当前时间", String.valueOf(nowTimeStr));
|
||||||
|
}
|
||||||
|
} catch (NameNotFoundException e) {
|
||||||
|
Log.e(TAG, "an NameNotFoundException occured when collect package info");
|
||||||
|
}
|
||||||
|
|
||||||
|
// 使用反射获取获取系统的硬件信息
|
||||||
|
Field[] fields = Build.class.getDeclaredFields();// 获得某个类的所有申明的字段,即包括public、private和proteced,
|
||||||
|
for (Field field : fields) {
|
||||||
|
field.setAccessible(true);// 暴力反射 ,获取私有的信息;类中的成员变量为private,故必须进行此操作
|
||||||
|
try {
|
||||||
|
infoMap.put(field.getName(), Objects.requireNonNull(field.get(null)).toString());
|
||||||
|
Log.d(TAG, field.getName() + " : " + field.get(null));
|
||||||
|
} catch (IllegalArgumentException e) {
|
||||||
|
Log.e(TAG, "an IllegalArgumentException occured when collect reflect field info", e);
|
||||||
|
} catch (IllegalAccessException e) {
|
||||||
|
Log.e(TAG, "an IllegalAccessException occured when collect reflect field info", e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 保存错误信息到文件中
|
||||||
|
*/
|
||||||
|
private void saveCrashInfoToFile(Throwable ex) throws IOException {
|
||||||
|
// 字符串流
|
||||||
|
StringBuilder stringBuffer = new StringBuilder();
|
||||||
|
// 获得设备信息
|
||||||
|
for (Map.Entry<String, String> entry : infoMap.entrySet()) {// 遍历map中的值
|
||||||
|
String key = entry.getKey();
|
||||||
|
String value = entry.getValue();
|
||||||
|
stringBuffer.append(key).append("=").append(value).append("\n");
|
||||||
|
}
|
||||||
|
// 获得错误信息
|
||||||
|
Writer writer = new StringWriter();// 这个writer下面还会用到,所以需要它的实例
|
||||||
|
PrintWriter printWriter = new PrintWriter(writer);// 输出错误栈信息需要用到PrintWriter
|
||||||
|
ex.printStackTrace(printWriter);
|
||||||
|
Throwable cause = ex.getCause();
|
||||||
|
while (cause != null) {// 循环,把所有的cause都输出到printWriter中
|
||||||
|
cause.printStackTrace(printWriter);
|
||||||
|
cause = ex.getCause();
|
||||||
|
}
|
||||||
|
printWriter.close();
|
||||||
|
String result = writer.toString();
|
||||||
|
stringBuffer.append(result);
|
||||||
|
stringBuffer.append("\n");
|
||||||
|
stringBuffer.append("==============================================================================================================");
|
||||||
|
File directory = context.getExternalFilesDir(FILE_TYPE);
|
||||||
|
if (directory != null) {
|
||||||
|
if (!directory.isDirectory()) {
|
||||||
|
directory.delete();
|
||||||
|
}
|
||||||
|
if (!directory.exists()) {
|
||||||
|
directory.mkdirs();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
File file = new File(directory, new SimpleDateFormat("yyyyMMdd", Locale.getDefault()).format(new Date()) + ".txt");
|
||||||
|
if (!file.isFile()) {
|
||||||
|
file.delete();
|
||||||
|
}
|
||||||
|
if (!file.exists()) {
|
||||||
|
file.createNewFile();
|
||||||
|
}
|
||||||
|
try {
|
||||||
|
FileOutputStream fos = new FileOutputStream(file, true);
|
||||||
|
fos.write(stringBuffer.toString().getBytes());
|
||||||
|
fos.close();
|
||||||
|
} catch (FileNotFoundException e) {
|
||||||
|
Log.e(TAG, "an FileNotFoundException when write to sdcard", e);
|
||||||
|
} catch (IOException e) {
|
||||||
|
Log.e(TAG, "an IOException when write to sdcard", e);
|
||||||
|
}
|
||||||
|
file.getPath();
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,136 @@
|
||||||
|
package com.yingmao.clash.util
|
||||||
|
|
||||||
|
import android.util.Log
|
||||||
|
import android.widget.Toast
|
||||||
|
import com.alibaba.sdk.android.oss.ClientException
|
||||||
|
import com.alibaba.sdk.android.oss.OSSClient
|
||||||
|
import com.alibaba.sdk.android.oss.ServiceException
|
||||||
|
import com.alibaba.sdk.android.oss.callback.OSSCompletedCallback
|
||||||
|
import com.alibaba.sdk.android.oss.common.auth.OSSStsTokenCredentialProvider
|
||||||
|
import com.alibaba.sdk.android.oss.internal.OSSAsyncTask
|
||||||
|
import com.alibaba.sdk.android.oss.model.PutObjectRequest
|
||||||
|
import com.alibaba.sdk.android.oss.model.PutObjectResult
|
||||||
|
import com.yingmao.clash.net.http.entity.ParamData
|
||||||
|
import java.io.File
|
||||||
|
|
||||||
|
object DataUtils {
|
||||||
|
/** 删除文件,可以是文件或文件夹
|
||||||
|
* @param delFile 要删除的文件夹或文件名
|
||||||
|
* @return 删除成功返回true,否则返回false
|
||||||
|
*/
|
||||||
|
fun deleteFile(delFile: String): Boolean {
|
||||||
|
val file = File(delFile)
|
||||||
|
if (!file.exists()) {
|
||||||
|
Log.e(
|
||||||
|
"--Method--",
|
||||||
|
"删除文件失败:\" + delFile + \"不存在!"
|
||||||
|
)
|
||||||
|
return false
|
||||||
|
} else {
|
||||||
|
return if (file.isFile) deleteSingleFile(delFile)
|
||||||
|
else deleteDirectory(delFile)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/** 删除单个文件
|
||||||
|
* @param 要删除的文件的文件名
|
||||||
|
* @return 单个文件删除成功返回true,否则返回false
|
||||||
|
*/
|
||||||
|
private fun deleteSingleFile(filePath: String): Boolean {
|
||||||
|
val file = File(filePath)
|
||||||
|
// 如果文件路径所对应的文件存在,并且是一个文件,则直接删除
|
||||||
|
if (file.exists() && file.isFile) {
|
||||||
|
if (file.delete()) {
|
||||||
|
Log.e(
|
||||||
|
"--Method--",
|
||||||
|
"Copy_Delete.deleteSingleFile: 删除单个文件" + `filePath` + "成功!"
|
||||||
|
)
|
||||||
|
return true
|
||||||
|
} else {
|
||||||
|
Log.e(
|
||||||
|
"--Method--",
|
||||||
|
"删除单个文件" + `filePath` + "失败!",
|
||||||
|
)
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
Log.e(
|
||||||
|
"--Method--",
|
||||||
|
"删除单个文件失败:" + `filePath` + "不存在!",
|
||||||
|
)
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/** 删除目录及目录下的文件
|
||||||
|
* @param filePath 要删除的目录的文件路径
|
||||||
|
* @return 目录删除成功返回true,否则返回false
|
||||||
|
*/
|
||||||
|
private fun deleteDirectory(filePath: String): Boolean {
|
||||||
|
// 如果dir不以文件分隔符结尾,自动添加文件分隔符
|
||||||
|
var filePath = filePath
|
||||||
|
if (!filePath.endsWith(File.separator)) filePath = filePath + File.separator
|
||||||
|
val dirFile = File(filePath)
|
||||||
|
// 如果dir对应的文件不存在,或者不是一个目录,则退出
|
||||||
|
if ((!dirFile.exists()) || (!dirFile.isDirectory)) {
|
||||||
|
Log.e(
|
||||||
|
"--Method--",
|
||||||
|
"删除目录失败:" + filePath + "不存在!"
|
||||||
|
)
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
var flag = true
|
||||||
|
// 删除文件夹中的所有文件包括子目录
|
||||||
|
val files = dirFile.listFiles()
|
||||||
|
for (file in files) {
|
||||||
|
// 删除子文件
|
||||||
|
if (file.isFile) {
|
||||||
|
flag = deleteSingleFile(file.absolutePath)
|
||||||
|
if (!flag) break
|
||||||
|
} else if (file.isDirectory) {
|
||||||
|
flag = deleteDirectory(
|
||||||
|
file
|
||||||
|
.absolutePath
|
||||||
|
)
|
||||||
|
if (!flag) break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (!flag) {
|
||||||
|
Log.e(
|
||||||
|
"--Method--",
|
||||||
|
"删除目录失败"
|
||||||
|
)
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
// 删除当前目录
|
||||||
|
if (dirFile.delete()) {
|
||||||
|
Log.e("--Method--", "Copy_Delete.deleteDirectory: 删除目录" + filePath + "成功!")
|
||||||
|
return true
|
||||||
|
} else {
|
||||||
|
Log.e(
|
||||||
|
"--Method--",
|
||||||
|
"删除目录:" + filePath + "失败!"
|
||||||
|
)
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fun getAllFilePath(folderPath: String): MutableList<String> {
|
||||||
|
val fileList =mutableListOf<String>()
|
||||||
|
val file = File(folderPath)
|
||||||
|
val tempList = file.listFiles()
|
||||||
|
if (tempList != null) {
|
||||||
|
for (i in tempList.indices) {
|
||||||
|
if (tempList[i].isFile) {
|
||||||
|
Log.i("dirpath","文 件:" + tempList[i].name)
|
||||||
|
val fileName = folderPath+"/"+tempList[i].name
|
||||||
|
Log.i("getAllFilePath","fileName:" +fileName)
|
||||||
|
if (fileName.endsWith(".txt")) { // 根据自己的需要进行类型筛选
|
||||||
|
fileList.add(fileName)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return fileList
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,28 @@
|
||||||
|
package com.yingmao.clash.view.permissions
|
||||||
|
|
||||||
|
import android.content.pm.PackageManager
|
||||||
|
import androidx.fragment.app.Fragment
|
||||||
|
typealias PermissionCallback = (Boolean, List<String>) -> Unit
|
||||||
|
class InvisibleFragment : Fragment() {
|
||||||
|
private var callback: ((Boolean, List<String>) -> Unit)? = null
|
||||||
|
fun requestNow(cb: (Boolean, List<String>) -> Unit, vararg permissions: String) {
|
||||||
|
callback = cb
|
||||||
|
requestPermissions(permissions, 1)
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun onRequestPermissionsResult(
|
||||||
|
requestCode: Int,
|
||||||
|
permissions: Array<String>, grantResults: IntArray
|
||||||
|
) {
|
||||||
|
if (requestCode == 1) {
|
||||||
|
val deniedList = ArrayList<String>()
|
||||||
|
for ((index, result) in grantResults.withIndex()) {
|
||||||
|
if (result != PackageManager.PERMISSION_GRANTED) {
|
||||||
|
deniedList.add(permissions[index])
|
||||||
|
}
|
||||||
|
}
|
||||||
|
val allGranted = deniedList.isEmpty()
|
||||||
|
callback?.let { it(allGranted, deniedList) }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,20 @@
|
||||||
|
package com.yingmao.clash.view.permissions
|
||||||
|
|
||||||
|
import androidx.fragment.app.FragmentActivity
|
||||||
|
|
||||||
|
object PermissionX {
|
||||||
|
private const val TAG = "InvisibleFragment"
|
||||||
|
fun request(activity: FragmentActivity, vararg permissions: String, callback:
|
||||||
|
PermissionCallback) {
|
||||||
|
val fragmentManager = activity.supportFragmentManager
|
||||||
|
val existedFragment = fragmentManager.findFragmentByTag(TAG)
|
||||||
|
val fragment = if (existedFragment != null) {
|
||||||
|
existedFragment as InvisibleFragment
|
||||||
|
} else {
|
||||||
|
val invisibleFragment = InvisibleFragment()
|
||||||
|
fragmentManager.beginTransaction().add(invisibleFragment, TAG).commitNow()
|
||||||
|
invisibleFragment
|
||||||
|
}
|
||||||
|
fragment.requestNow(callback, *permissions)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -179,4 +179,16 @@ class MainAtyVM : PublicViewMode() {
|
||||||
)
|
)
|
||||||
doAsync(HttpReqType.GetActivity) { httpApi.getActivity(params) }
|
doAsync(HttpReqType.GetActivity) { httpApi.getActivity(params) }
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fun getUploadParameters() {
|
||||||
|
val httpApi = NetService.instances.createHttps(HttpApi::class.java)
|
||||||
|
val username =
|
||||||
|
MMKV.defaultMMKV().decodeString(BZYConstants.MMKV_KEY_REMEMBER_USERNAME, "")!!
|
||||||
|
val phToken = MMKV.defaultMMKV().decodeString(BZYConstants.MMKV_KEY_PH_TOKEN, "")!!
|
||||||
|
val params = hashMapOf(
|
||||||
|
"phoneNumber" to username,
|
||||||
|
"phToken" to phToken,
|
||||||
|
)
|
||||||
|
doAsync(HttpReqType.UploadParameters) { httpApi.getUploadParameters(params) }
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -19,7 +19,8 @@
|
||||||
<androidx.viewpager2.widget.ViewPager2
|
<androidx.viewpager2.widget.ViewPager2
|
||||||
android:id="@+id/container"
|
android:id="@+id/container"
|
||||||
android:layout_width="match_parent"
|
android:layout_width="match_parent"
|
||||||
android:layout_height="match_parent" />
|
android:layout_height="match_parent" >
|
||||||
|
</androidx.viewpager2.widget.ViewPager2>
|
||||||
|
|
||||||
<ImageView
|
<ImageView
|
||||||
android:id="@+id/iv_slider"
|
android:id="@+id/iv_slider"
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue