From 2b8635e45ad7a9e917899791c3bb5897dc03de35 Mon Sep 17 00:00:00 2001 From: level <1364007522@qq.com> Date: Mon, 30 Sep 2024 10:26:42 +0800 Subject: [PATCH] =?UTF-8?q?=E6=8F=8F=E8=BF=B0=EF=BC=9A=20=E6=96=B0?= =?UTF-8?q?=E5=A2=9E=E6=8A=A5=E9=94=99=E6=97=A5=E5=BF=97=E6=8A=93=E5=8F=96?= =?UTF-8?q?=20=E5=B9=B6=E4=B8=8A=E4=BC=A0=E8=87=B3=E9=98=BF=E9=87=8C?= =?UTF-8?q?=E4=BA=91oss?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- app/build.gradle.kts | 2 +- .../com/yingmao/clash/act/NewMainActivity.kt | 130 ++++++++++- .../com/yingmao/clash/app/MainApplication.kt | 6 + .../com/yingmao/clash/net/http/HttpApi.kt | 13 ++ .../com/yingmao/clash/net/http/HttpReqType.kt | 4 +- .../clash/net/http/entity/UploadParamData.kt | 24 ++ .../yingmao/clash/util/CrashCatchHandler.java | 206 ++++++++++++++++++ .../java/com/yingmao/clash/util/DataUtils.kt | 136 ++++++++++++ .../view/permissions/InvisibleFragment.kt | 28 +++ .../clash/view/permissions/PermissionX.kt | 20 ++ .../java/com/yingmao/clash/vm/MainAtyVM.kt | 12 + app/src/main/res/layout/new_main_activity.xml | 3 +- 12 files changed, 578 insertions(+), 6 deletions(-) create mode 100644 app/src/main/java/com/yingmao/clash/net/http/entity/UploadParamData.kt create mode 100644 app/src/main/java/com/yingmao/clash/util/CrashCatchHandler.java create mode 100644 app/src/main/java/com/yingmao/clash/util/DataUtils.kt create mode 100644 app/src/main/java/com/yingmao/clash/view/permissions/InvisibleFragment.kt create mode 100644 app/src/main/java/com/yingmao/clash/view/permissions/PermissionX.kt diff --git a/app/build.gradle.kts b/app/build.gradle.kts index 5a4bd12..92ff46f 100644 --- a/app/build.gradle.kts +++ b/app/build.gradle.kts @@ -103,7 +103,7 @@ dependencies { // https://mvnrepository.com/artifact/com.github.bumptech.glide/glide implementation("com.github.bumptech.glide:glide:4.13.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) { diff --git a/app/src/main/java/com/yingmao/clash/act/NewMainActivity.kt b/app/src/main/java/com/yingmao/clash/act/NewMainActivity.kt index 7e51bc1..7e3a82d 100644 --- a/app/src/main/java/com/yingmao/clash/act/NewMainActivity.kt +++ b/app/src/main/java/com/yingmao/clash/act/NewMainActivity.kt @@ -16,6 +16,7 @@ import android.widget.ImageView import android.widget.LinearLayout import android.widget.ProgressBar import android.widget.TextView +import android.widget.Toast import androidx.core.content.FileProvider import androidx.core.view.GravityCompat 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.getCustomView 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.JsonArray 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.GetActivityRsp 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.SubscriptionRsp +import com.yingmao.clash.net.http.entity.UploadParamData import com.yingmao.clash.net.http.entity.VipSignRsp import com.yingmao.clash.remote.Remote 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.util.AESUtil 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.view.RxToast 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.DialogNotice import com.yingmao.clash.view.dialog.RxDialogShapeLoading +import com.yingmao.clash.view.permissions.PermissionX import com.yingmao.clash.vm.MainAtyVM import com.yingmao.clash.webview.BZYWebViewKeFu import com.yingmao.clash.webview.BZYWebViewNormal @@ -87,6 +102,7 @@ import okhttp3.RequestBody import java.io.File import java.text.SimpleDateFormat import java.util.Date +import java.util.Locale import java.util.Timer import java.util.TimerTask import java.util.UUID @@ -249,7 +265,7 @@ class NewMainActivity : BaseActivity() { startLog() // showBindAccount() 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() stopUploadTimer() } + + private fun uploadForAliOss(data: ParamData, filePath: List, 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 { + 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() + } + } + + + } + } + + } \ No newline at end of file diff --git a/app/src/main/java/com/yingmao/clash/app/MainApplication.kt b/app/src/main/java/com/yingmao/clash/app/MainApplication.kt index 617be37..0df5b6b 100644 --- a/app/src/main/java/com/yingmao/clash/app/MainApplication.kt +++ b/app/src/main/java/com/yingmao/clash/app/MainApplication.kt @@ -19,6 +19,7 @@ import com.yingmao.clash.common.compat.currentProcessName import com.yingmao.clash.common.log.Log import com.yingmao.clash.entity.CheckLoginRsp import com.yingmao.clash.entity.UserData +import com.yingmao.clash.util.CrashCatchHandler import com.yingmao.clash.util.clashDir import java.io.File @@ -68,6 +69,11 @@ class MainApplication : Application() { val MMKVRootDir = MMKV.initialize(this) Logger.i("MMKV_ROOT_DIR:$MMKVRootDir") updateNotificationChannels() + /* + * 错误日志上传捕获 + * */ + val crashCatchHandler: CrashCatchHandler = CrashCatchHandler.getInstance() //获得单例 + crashCatchHandler.init(applicationContext) //初始化,传入context } private fun extractGeoFiles() { diff --git a/app/src/main/java/com/yingmao/clash/net/http/HttpApi.kt b/app/src/main/java/com/yingmao/clash/net/http/HttpApi.kt index eb0a048..244faf9 100644 --- a/app/src/main/java/com/yingmao/clash/net/http/HttpApi.kt +++ b/app/src/main/java/com/yingmao/clash/net/http/HttpApi.kt @@ -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.LoginRsp2 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 okhttp3.RequestBody import retrofit2.Call @@ -489,4 +490,16 @@ interface HttpApi { BZYConstants.MMKV_KEY_TOKEN ).toString() ): GetActivityRsp + + //获取阿里云上传数据 + @Headers( + "User-Agent: Octopus_Android" + ) + @POST("/netbarcloud/vpn/getOssAssumeRoleToken") + suspend fun getUploadParameters( + @QueryMap params: Map, + @Header("token") string: String = MMKV.defaultMMKV().decodeString( + BZYConstants.MMKV_KEY_TOKEN + ).toString() + ): UploadParamData } \ No newline at end of file diff --git a/app/src/main/java/com/yingmao/clash/net/http/HttpReqType.kt b/app/src/main/java/com/yingmao/clash/net/http/HttpReqType.kt index 0906606..75f1bfd 100644 --- a/app/src/main/java/com/yingmao/clash/net/http/HttpReqType.kt +++ b/app/src/main/java/com/yingmao/clash/net/http/HttpReqType.kt @@ -46,6 +46,6 @@ enum class HttpReqType(type: Int) { Get_Node_Other(42), GetActivity(43), GetRuningNotice(44), - unionPay(45) - + unionPay(45), + UploadParameters(46) } \ No newline at end of file diff --git a/app/src/main/java/com/yingmao/clash/net/http/entity/UploadParamData.kt b/app/src/main/java/com/yingmao/clash/net/http/entity/UploadParamData.kt new file mode 100644 index 0000000..08f3f19 --- /dev/null +++ b/app/src/main/java/com/yingmao/clash/net/http/entity/UploadParamData.kt @@ -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 +) \ No newline at end of file diff --git a/app/src/main/java/com/yingmao/clash/util/CrashCatchHandler.java b/app/src/main/java/com/yingmao/clash/util/CrashCatchHandler.java new file mode 100644 index 0000000..97ab935 --- /dev/null +++ b/app/src/main/java/com/yingmao/clash/util/CrashCatchHandler.java @@ -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 infoMap = new HashMap(); // 用来存储设备信息和异常信息 + + /** + * 私有构造方法,保证只有一个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 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(); + } + +} \ No newline at end of file diff --git a/app/src/main/java/com/yingmao/clash/util/DataUtils.kt b/app/src/main/java/com/yingmao/clash/util/DataUtils.kt new file mode 100644 index 0000000..fb36b5e --- /dev/null +++ b/app/src/main/java/com/yingmao/clash/util/DataUtils.kt @@ -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 { + val fileList =mutableListOf() + 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 + } +} \ No newline at end of file diff --git a/app/src/main/java/com/yingmao/clash/view/permissions/InvisibleFragment.kt b/app/src/main/java/com/yingmao/clash/view/permissions/InvisibleFragment.kt new file mode 100644 index 0000000..8c29757 --- /dev/null +++ b/app/src/main/java/com/yingmao/clash/view/permissions/InvisibleFragment.kt @@ -0,0 +1,28 @@ +package com.yingmao.clash.view.permissions + +import android.content.pm.PackageManager +import androidx.fragment.app.Fragment +typealias PermissionCallback = (Boolean, List) -> Unit +class InvisibleFragment : Fragment() { + private var callback: ((Boolean, List) -> Unit)? = null + fun requestNow(cb: (Boolean, List) -> Unit, vararg permissions: String) { + callback = cb + requestPermissions(permissions, 1) + } + + override fun onRequestPermissionsResult( + requestCode: Int, + permissions: Array, grantResults: IntArray + ) { + if (requestCode == 1) { + val deniedList = ArrayList() + for ((index, result) in grantResults.withIndex()) { + if (result != PackageManager.PERMISSION_GRANTED) { + deniedList.add(permissions[index]) + } + } + val allGranted = deniedList.isEmpty() + callback?.let { it(allGranted, deniedList) } + } + } +} \ No newline at end of file diff --git a/app/src/main/java/com/yingmao/clash/view/permissions/PermissionX.kt b/app/src/main/java/com/yingmao/clash/view/permissions/PermissionX.kt new file mode 100644 index 0000000..88571b1 --- /dev/null +++ b/app/src/main/java/com/yingmao/clash/view/permissions/PermissionX.kt @@ -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) + } +} diff --git a/app/src/main/java/com/yingmao/clash/vm/MainAtyVM.kt b/app/src/main/java/com/yingmao/clash/vm/MainAtyVM.kt index c33f996..c4eee40 100644 --- a/app/src/main/java/com/yingmao/clash/vm/MainAtyVM.kt +++ b/app/src/main/java/com/yingmao/clash/vm/MainAtyVM.kt @@ -179,4 +179,16 @@ class MainAtyVM : PublicViewMode() { ) 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) } + } } \ No newline at end of file diff --git a/app/src/main/res/layout/new_main_activity.xml b/app/src/main/res/layout/new_main_activity.xml index fd2b581..94888d9 100644 --- a/app/src/main/res/layout/new_main_activity.xml +++ b/app/src/main/res/layout/new_main_activity.xml @@ -19,7 +19,8 @@ + android:layout_height="match_parent" > +