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<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) // 返回数组
|
}
|
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<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) // 下载成功时返回文件
|
}
|
}
|
})
|
}
|
|
// 等待下载完成
|
|
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"
|
}
|
|
}
|