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<Int>? {
|
|
// 获取该月的第一天
|
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<Utils.VideoUploadTaskInfo>)
|
}
|
interface UploadCallback {
|
fun onSuccess()
|
fun onError()
|
}
|
|
fun fetchTasksBySessionProfile(profile: SessionProfile, callback: FetchTasksCallback) {
|
val uploadTaskList: MutableList<Utils.VideoUploadTaskInfo> = mutableListOf()
|
|
val client = OkHttpClient()
|
val request = Request.Builder()
|
.url(AppConfig.URL_CHECK_CLIENT_TASKS + AppConfig.CLIENT_ANDROID_ID + "&sessionprofile=" + profile.id + "&videotype=" + profile.type + "&name="+profile.name) // 替换为你的任务地址
|
.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<Unit>()
|
|
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) // 下载成功时返回文件
|
}
|
}
|
})
|
}
|
|
// Switch to main thread for Toast
|
withContext(Dispatchers.Main) {
|
Toast.makeText(context, "$filename ${AppConfig.MSG_DOWNLOAD_FINISHED}", Toast.LENGTH_SHORT).show()
|
}
|
|
deferred.await()
|
} catch (e: Exception) {
|
e.printStackTrace()
|
|
// Switch to main thread for Toast
|
withContext(Dispatchers.Main) {
|
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" }
|
}
|
|
}
|