package com.shsening.autouploadvideos.utils import SessionProfile import android.app.Instrumentation import android.content.Context import android.content.Intent import android.os.SystemClock import android.view.MotionEvent import android.webkit.CookieManager import android.widget.Toast import androidx.core.content.FileProvider import com.shsening.autouploadvideos.AppConfig import com.shsening.autouploadvideos.PlatformItem import kotlinx.coroutines.CompletableDeferred import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.withContext import okhttp3.Call import okhttp3.Callback import okhttp3.OkHttpClient import okhttp3.Request import okhttp3.Response import org.json.JSONArray import org.json.JSONObject import java.io.File import java.io.IOException import java.io.Serializable import java.text.SimpleDateFormat import java.util.Calendar import java.util.Date import java.util.Locale object Utils { data class AppUpdateInfo( val needUpdate: Boolean, val newVersion: String, val newApkUrl: String ) data class VideoUploadTaskInfo( val taskId:String, val title: String, val description: String, val datetime: String, val videoUrl: String, val coverUrl: String, var filename: String = generateFilename(datetime), var sessionProfileId: String? =null, var sessionProfileName: String? =null, var platform: PlatformItem? =null ) : Serializable { companion object { private fun generateFilename(datetime: String): String { return datetime.replace("-", "") .replace(" ", "") .replace(":", "") } } } fun instrumentationSwipe( instrumentation: Instrumentation, startX: Float, startY: Float, endX: Float, endY: Float ) { // 模拟按下事件 val downTime = SystemClock.uptimeMillis() val eventTime = SystemClock.uptimeMillis() instrumentation.sendPointerSync( MotionEvent.obtain( downTime, eventTime, MotionEvent.ACTION_DOWN, startX, startY, 0 ) ) // 模拟移动事件 val stepCount = 20 for (i in 1..stepCount) { val x = startX + (endX - startX) * i / stepCount instrumentation.sendPointerSync( MotionEvent.obtain( downTime, eventTime + i * 10, MotionEvent.ACTION_MOVE, x, startY, 0 ) ) } // 模拟抬起事件 instrumentation.sendPointerSync( MotionEvent.obtain( downTime, eventTime + stepCount * 10, MotionEvent.ACTION_UP, endX, endY, 0 ) ) } fun instrumentationClick(instrumentation: Instrumentation, x: Float, y: Float) { val downTime = SystemClock.uptimeMillis() val eventTime = SystemClock.uptimeMillis() // 发送按下事件 instrumentation.sendPointerSync( MotionEvent.obtain( downTime, eventTime, MotionEvent.ACTION_DOWN, x, y, 0 ) ) // 发送抬起事件 instrumentation.sendPointerSync( MotionEvent.obtain( downTime, eventTime + 100, MotionEvent.ACTION_UP, x, y, 0 ) ) } fun validateAndReturnDateTime(dateTimeString: String): IntArray? { // 正则表达式匹配 yyyy-mm-dd hh:mm 格式 val regex = Regex("""^\d{4}\d{2}\d{2}\d{2}\d{2}$""") if (!regex.matches(dateTimeString)) { return null } // 解析日期和时间 val year = dateTimeString.substring(0, 4).toInt() val month = dateTimeString.substring(4, 6).toInt() - 1 // 月份从0开始 val day = dateTimeString.substring(6, 8).toInt() val hour = dateTimeString.substring(8, 10).toInt() val minute = dateTimeString.substring(10, 12).toInt() // 创建日期对象 val calendar = Calendar.getInstance().apply { set(year, month, day, hour, minute) } // 验证各个部分 return if (calendar.get(Calendar.YEAR) == year && calendar.get(Calendar.MONTH) == month && calendar.get(Calendar.DAY_OF_MONTH) == day && calendar.get(Calendar.HOUR_OF_DAY) == hour && calendar.get(Calendar.MINUTE) == minute ) { intArrayOf(year, month + 1, day, hour, minute) // 返回数组,月份加1 } else { null } } fun getDatePosition(year: Int,month: Int,day: Int,): Array? { // 获取该月的第一天 val firstDayOfMonth = Calendar.getInstance().apply { set(year, month-1, 1) } val firstDayOfWeek = firstDayOfMonth.get(Calendar.DAY_OF_WEEK) // 星期日为1,星期一为2,...,星期六为7 // 计算日期所在的行和列 val row = (day + firstDayOfWeek - 2) / 7 + 1 // 行数从1开始 val column = (day + firstDayOfWeek - 2) % 7 + 1 // 列数从1开始 return arrayOf(row, column) // 返回数组 } fun sendVideoUploadedMessage(taskId: String, callback: (Boolean, Exception?) -> Unit) { try { val client = OkHttpClient() val request = Request.Builder() .url(AppConfig.URL_CONFIRM_TASK_UPLOADED + taskId) .build() // 异步执行网络请求 client.newCall(request).enqueue(object : okhttp3.Callback { override fun onFailure(call: okhttp3.Call, e: IOException) { // 请求失败,返回错误 callback(false, e) } override fun onResponse(call: okhttp3.Call, response: okhttp3.Response) { try { val json = JSONObject(response.body?.string() ?: "") val result = json.getString("msg") // 成功回调 callback(true, Exception(result)) } catch (e: Exception) { // 处理解析错误 callback(false, e) } finally { response.close() } } }) } catch (e: Exception) { e.printStackTrace() // 处理其他异常 callback(false, e) } } suspend fun checkForUpdates(context: Context): AppUpdateInfo { try { val client = OkHttpClient() val request = Request.Builder() .url(AppConfig.URL_CHECK_APP_VERSION_UPDATE + AppConfig.APP_VERSION_NAME) .build() return withContext(Dispatchers.IO) { var needUpdate = false var newVersion = "" var newApkUrl = "" val response = client.newCall(request).execute() if (!response.isSuccessful) throw IOException("Unexpected code $response") val json = JSONObject(response.body?.string() ?: "") newVersion = json.getString("newVersion") if (newVersion != AppConfig.APP_VERSION_NAME) { needUpdate = true newApkUrl = json.getString("newApkUrl") } AppUpdateInfo(needUpdate, newVersion, newApkUrl) } }catch(e:Exception){ e.printStackTrace() return AppUpdateInfo(false, "", "") } } interface FetchTasksCallback { fun onTasksFetched(uploadTaskList: MutableList) } interface UploadCallback { fun onSuccess() fun onError() } fun fetchTasksBySessionProfile(profile: SessionProfile, callback: FetchTasksCallback) { val uploadTaskList: MutableList = mutableListOf() val client = OkHttpClient() val request = Request.Builder() .url(AppConfig.URL_CHECK_CLIENT_TASKS + AppConfig.CLIENT_ANDROID_ID + "&sessionprofile=" + profile.id + "&videotype=" + profile.type) // 替换为你的任务地址 .build() client.newCall(request).enqueue(object : Callback { override fun onFailure(call: Call, e: IOException) { e.printStackTrace() // 可以在这里通知调用者出错 } override fun onResponse(call: Call, response: Response) { response.use { if (!response.isSuccessful) throw IOException("Unexpected code $response") val jsonArray = JSONArray(response.body?.string() ?: "") if (jsonArray.length() > 0) { for (i in 0 until jsonArray.length()) { val jsonObject = jsonArray.getJSONObject(i) try { val task = Utils.VideoUploadTaskInfo( taskId = jsonObject.getString("taskId"), title = jsonObject.getString("title"), description = jsonObject.getString("description"), datetime = jsonObject.getString("datetime"), videoUrl = jsonObject.getString("videoUrl"), coverUrl = jsonObject.getString("coverUrl") ) uploadTaskList.add(task) } catch (e: Exception) { e.printStackTrace() } } } } // 在请求成功并解析完任务列表后,调用回调函数 callback.onTasksFetched(uploadTaskList) } }) } suspend fun downloadCommonFileToExternalFilesDir(context: Context, url: String, filename: String, install: Boolean) { val tmpFile = File(context.getExternalFilesDir(null), filename) try{ if (tmpFile.exists()) { return } val deferred = CompletableDeferred() withContext(Dispatchers.IO) { val client = OkHttpClient() val request = Request.Builder().url(url).build() client.newCall(request).enqueue(object : Callback { override fun onFailure(call: Call, e: IOException) { deferred.completeExceptionally(e) // 下载失败时标记为异常 } override fun onResponse(call: Call, response: Response) { response.use { if (!response.isSuccessful) { deferred.completeExceptionally(IOException("Unexpected code $response")) // 如果响应不成功,标记为异常 return } tmpFile.outputStream().use { outputStream -> outputStream.write(response.body?.bytes()) } if (install) { installApk(context,tmpFile) } deferred.complete(Unit) // 下载成功时返回文件 } } }) } // 等待下载完成 Toast.makeText(context, filename + " " + AppConfig.MSG_DOWNLOAD_FINISHED, Toast.LENGTH_SHORT).show() deferred.await() }catch(e:Exception){ e.printStackTrace() Toast.makeText(context, filename+" download fails, Please try again later!", Toast.LENGTH_SHORT) .show() } } fun installApk(context: Context,apkFile: File) { val intent = Intent(Intent.ACTION_VIEW) val apkUri = FileProvider.getUriForFile(context, AppConfig.FILE_PROVIDER, apkFile) intent.setDataAndType(apkUri, "application/vnd.android.package-archive") intent.addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION) // 允许读取 URI intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK) // 新任务标志 context.startActivity(intent) } fun getMonthAbbreviation(month: Int): String { val months = listOf( "Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec" ) return if (month in 1..12) months[month - 1] else "Invalid" } // 获取 key 的位置 fun getSessionProfileValuePosition(key: String): Int { val index = AppConfig.SESSION_PROFILE_TYPE.values.toList().indexOf(key) return if (index == -1) 0 else index } fun getCurrentTimeString(): String { val formatter = SimpleDateFormat("yyyy-MM-dd HH:mm:ss", Locale.getDefault()) // 自定义格式 val currentDate = Date() // 获取当前时间 return formatter.format(currentDate) // 格式化为字符串 } // 根据位置获取 key fun getSessionProfileValueByPosition(position: Int?): String? { if (position==null){ return "Unknown" } return AppConfig.SESSION_PROFILE_TYPE.values.toList().getOrElse(position) { "Unknown" } } }