package com.shsening.autouploadvideos import android.app.Instrumentation import android.content.Context import android.content.Intent import android.os.SystemClock import android.view.MotionEvent import android.widget.Toast import androidx.core.content.FileProvider 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.JSONObject import java.io.File import java.io.IOException import java.io.Serializable import java.util.Calendar 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) ) : 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) // 返回数组 } suspend fun sendVideoUploadedMessage(context: Context,taskId: String): Boolean { try { val client = OkHttpClient() val request = Request.Builder() .url(AppConfig.URL_CONFIRM_TASK_UPLOADED + taskId) .build() return withContext(Dispatchers.IO) { val response = client.newCall(request).execute() if (!response.isSuccessful) throw IOException("Unexpected code $response") val json = JSONObject(response.body?.string() ?: "") json.getBoolean("result") } }catch(e:Exception){ e.printStackTrace() return false } } 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, "", "") } } 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" } }