增加定时功能

This commit is contained in:
kun1h 2024-02-02 18:26:55 +08:00
parent 5f7c021712
commit ed9eb13cd0
8 changed files with 297 additions and 116 deletions

View File

@ -0,0 +1,123 @@
<component name="ProjectCodeStyleConfiguration">
<code_scheme name="Project" version="173">
<JetCodeStyleSettings>
<option name="CODE_STYLE_DEFAULTS" value="KOTLIN_OFFICIAL" />
</JetCodeStyleSettings>
<codeStyleSettings language="XML">
<option name="FORCE_REARRANGE_MODE" value="1" />
<indentOptions>
<option name="CONTINUATION_INDENT_SIZE" value="4" />
</indentOptions>
<arrangement>
<rules>
<section>
<rule>
<match>
<AND>
<NAME>xmlns:android</NAME>
<XML_ATTRIBUTE />
<XML_NAMESPACE>^$</XML_NAMESPACE>
</AND>
</match>
</rule>
</section>
<section>
<rule>
<match>
<AND>
<NAME>xmlns:.*</NAME>
<XML_ATTRIBUTE />
<XML_NAMESPACE>^$</XML_NAMESPACE>
</AND>
</match>
<order>BY_NAME</order>
</rule>
</section>
<section>
<rule>
<match>
<AND>
<NAME>.*:id</NAME>
<XML_ATTRIBUTE />
<XML_NAMESPACE>http://schemas.android.com/apk/res/android</XML_NAMESPACE>
</AND>
</match>
</rule>
</section>
<section>
<rule>
<match>
<AND>
<NAME>.*:name</NAME>
<XML_ATTRIBUTE />
<XML_NAMESPACE>http://schemas.android.com/apk/res/android</XML_NAMESPACE>
</AND>
</match>
</rule>
</section>
<section>
<rule>
<match>
<AND>
<NAME>name</NAME>
<XML_ATTRIBUTE />
<XML_NAMESPACE>^$</XML_NAMESPACE>
</AND>
</match>
</rule>
</section>
<section>
<rule>
<match>
<AND>
<NAME>style</NAME>
<XML_ATTRIBUTE />
<XML_NAMESPACE>^$</XML_NAMESPACE>
</AND>
</match>
</rule>
</section>
<section>
<rule>
<match>
<AND>
<NAME>.*</NAME>
<XML_ATTRIBUTE />
<XML_NAMESPACE>^$</XML_NAMESPACE>
</AND>
</match>
<order>BY_NAME</order>
</rule>
</section>
<section>
<rule>
<match>
<AND>
<NAME>.*</NAME>
<XML_ATTRIBUTE />
<XML_NAMESPACE>http://schemas.android.com/apk/res/android</XML_NAMESPACE>
</AND>
</match>
<order>ANDROID_ATTRIBUTE_ORDER</order>
</rule>
</section>
<section>
<rule>
<match>
<AND>
<NAME>.*</NAME>
<XML_ATTRIBUTE />
<XML_NAMESPACE>.*</XML_NAMESPACE>
</AND>
</match>
<order>BY_NAME</order>
</rule>
</section>
</rules>
</arrangement>
</codeStyleSettings>
<codeStyleSettings language="kotlin">
<option name="CODE_STYLE_DEFAULTS" value="KOTLIN_OFFICIAL" />
</codeStyleSettings>
</code_scheme>
</component>

View File

@ -0,0 +1,5 @@
<component name="ProjectCodeStyleConfiguration">
<state>
<option name="USE_PER_PROJECT_SETTINGS" value="true" />
</state>
</component>

View File

@ -1,4 +1,3 @@
<?xml version="1.0" encoding="UTF-8"?>
<project version="4">
<component name="ExternalStorageConfigurationManager" enabled="true" />
<component name="ProjectRootManager" version="2" languageLevel="JDK_17" default="true" project-jdk-name="jbr-17" project-jdk-type="JavaSDK">

View File

@ -50,4 +50,5 @@ dependencies {
testImplementation("junit:junit:4.13.2")
androidTestImplementation("androidx.test.ext:junit:1.1.5")
androidTestImplementation("androidx.test.espresso:espresso-core:3.5.1")
implementation ("com.squareup.okhttp3:okhttp:4.12.0")
}

View File

@ -9,6 +9,10 @@
<uses-permission android:name="android.permission.RECORD_AUDIO" />
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE"
android:maxSdkVersion="28" />
<uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE"
android:maxSdkVersion="28" />
<uses-permission android:name="android.permission.INTERNET"/>
<uses-permission android:name="android.permission.ACCESS_NETWORK_STATE"/>
<application
android:allowBackup="true"
android:dataExtractionRules="@xml/data_extraction_rules"
@ -18,7 +22,9 @@
android:roundIcon="@mipmap/ic_launcher_round"
android:supportsRtl="true"
android:theme="@style/Theme.Travebing"
android:networkSecurityConfig="@xml/network_security_config"
tools:targetApi="31">
<activity
android:name=".MainActivity"
android:exported="true">

View File

@ -2,25 +2,21 @@
package com.bing89.travebing
import android.Manifest
import android.Manifest.*
import android.Manifest.permission
import android.annotation.SuppressLint
import android.content.ContentValues
import android.content.Context
import android.content.Intent
import android.content.pm.PackageManager
import android.location.Criteria
import android.location.Location
import android.location.LocationListener
import android.location.LocationManager
import android.os.Build
import android.net.Uri
import android.os.Bundle
import android.os.Handler
import android.os.Looper
import android.provider.MediaStore
import android.util.Base64
import android.util.Log
import android.widget.Button
import android.widget.EditText
import android.widget.ImageView
import android.widget.TextView
import android.widget.Toast
@ -32,14 +28,28 @@ import androidx.camera.core.ImageCaptureException
import androidx.camera.lifecycle.ProcessCameraProvider
import androidx.core.app.ActivityCompat
import androidx.core.content.ContextCompat
import androidx.core.content.FileProvider
import com.bing89.travebing.R.*
import kotlinx.coroutines.*
import java.io.ByteArrayOutputStream
import com.bing89.travebing.R.id
import com.bing89.travebing.R.layout
import kotlinx.coroutines.DelicateCoroutinesApi
import okhttp3.Call
import okhttp3.Callback
import okhttp3.MediaType.Companion.toMediaType
import okhttp3.MultipartBody
import okhttp3.OkHttpClient
import okhttp3.Request
import okhttp3.RequestBody.Companion.asRequestBody
import okhttp3.Response
import java.io.BufferedInputStream
import java.io.BufferedOutputStream
import java.io.File
import java.io.FileInputStream
import java.io.FileOutputStream
import java.io.IOException
import java.io.InputStream
import java.text.SimpleDateFormat
import java.util.Date
import java.util.Locale
import java.util.Timer
import java.util.TimerTask
class MainActivity : AppCompatActivity() {
@ -48,23 +58,33 @@ class MainActivity : AppCompatActivity() {
private lateinit var previewImageView: ImageView
private lateinit var gpsInfoTextView: TextView
private var imageCapture: ImageCapture? = null
private lateinit var planText: EditText
private var isMonitoring = false
private var photoFilePath: String? = null
private var locationManager: LocationManager? = null
private lateinit var lat: String
private lateinit var lon: String
private lateinit var currentImg:String
private lateinit var context:Context
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(layout.activity_main)
lat = ""
lon = ""
currentImg = ""
context = baseContext
setContentView(layout.activity_main)
startButton = findViewById(id.startButton)
previewImageView = findViewById(id.previewImageView)
gpsInfoTextView = findViewById(id.gpsInfoTextView)
planText = findViewById(id.planText)
startCamera()
startButton.setOnClickListener {
if (isMonitoring) {
stopMonitoring()
planText.isActivated = true
} else {
planText.isActivated = false
startMonitoring()
}
}
@ -73,47 +93,94 @@ class MainActivity : AppCompatActivity() {
locationManager = getSystemService(Context.LOCATION_SERVICE) as LocationManager
}
private fun updateWayCallback(file: File):Callback{
return object:Callback{
override fun onFailure(call: Call, e: IOException) {
file.delete()
println("Fail")
}
override fun onResponse(call: Call, response: Response) {
println(response.body.toString())
file.delete()
}
}
}
private fun updateWay(img:InputStream){
Log.d(TAG, "$img.jpg")
val client = OkHttpClient()
var builder: MultipartBody.Builder = MultipartBody.Builder()
.setType(MultipartBody.FORM)
.addFormDataPart("lat", lat)
.addFormDataPart("lon", lon)
val dst = File("${context.externalCacheDir}/tmp")
copyfile(img, dst )
val requestFile = dst.asRequestBody(MEDIA_TYPE_FILE)
builder = builder.addFormDataPart("file", "1.jpg", requestFile)
val multipartBody: MultipartBody = builder.build()
val request = Request.Builder()
.url("http://192.168.0.200:3000/api/v1/plans/" + planText.text + "/ways")
.post(multipartBody)
.build()
val call: Call = client.newCall(request)
call.enqueue(updateWayCallback(dst))
}
private fun startMonitoring() {
isMonitoring = true
startButton.text = "休息"
// if(imageCapture == null ){
// startCamera()
// }
val timer = Timer()
val task = object : TimerTask() {
override fun run() {
// 每隔 1 秒钟执行一次此方法
// 在这里写下你需要执行的代码
takePhoto()
}
}
// val intervalMillis = 60000L // 间隔时间这里设置为1分钟
// val handler = Handler(Looper.getMainLooper())
//
// handler.post(object : Runnable {
// override fun run() {
// if (isMonitoring) {
// getLocation()
//// capturePhotoAndUpload()
// takePhoto()
// handler.postDelayed(this, intervalMillis)
// }
// }
// })
timer.schedule(task, 0, 300000) // 每隔 300 秒钟执行一次
// updateWay(img)
}
private fun stopMonitoring() {
isMonitoring = false
startButton.text = "出发"
}
@RequiresPermission(
anyOf = [permission.ACCESS_COARSE_LOCATION, permission.ACCESS_FINE_LOCATION],
anyOf = [permission.ACCESS_COARSE_LOCATION,
permission.ACCESS_FINE_LOCATION,
permission.INTERNET,
permission.ACCESS_MEDIA_LOCATION,
permission.ACCESS_BACKGROUND_LOCATION,
permission.ACCESS_NETWORK_STATE,
permission.CAMERA,
permission.WRITE_EXTERNAL_STORAGE,
permission.READ_EXTERNAL_STORAGE,
permission.ACCESS_FINE_LOCATION],
)
override fun onActivityResult(requestCode: Int, resultCode: Int, data: Intent?) {
super.onActivityResult(requestCode, resultCode, data)
if (resultCode == PHOTO_REQUEST_CODE ) {
if (resultCode == PHOTO_REQUEST_CODE) {
// doSomeOperations()
}
}
private fun checkPermissions(): Boolean {
val requiredPermissions = arrayOf(
permission.ACCESS_COARSE_LOCATION,
permission.ACCESS_FINE_LOCATION,
permission.INTERNET,
permission.ACCESS_MEDIA_LOCATION,
permission.ACCESS_BACKGROUND_LOCATION,
permission.ACCESS_NETWORK_STATE,
permission.CAMERA,
permission.WRITE_EXTERNAL_STORAGE,
permission.READ_EXTERNAL_STORAGE,
permission.ACCESS_FINE_LOCATION
)
@ -123,55 +190,62 @@ class MainActivity : AppCompatActivity() {
permission
) != PackageManager.PERMISSION_GRANTED
) {
ActivityCompat.requestPermissions(this, requiredPermissions, PERMISSION_REQUEST_CODE)
ActivityCompat.requestPermissions(
this,
requiredPermissions,
PERMISSION_REQUEST_CODE
)
return false
}
}
return true
}
//define the listener
private val locationListener: LocationListener = object : LocationListener {
override fun onLocationChanged(location: Location) {
lat = location.latitude.toString()
lon = location.longitude.toString()
updateGpsInfo(location)
}
override fun onStatusChanged(provider: String, status: Int, extras: Bundle) {}
override fun onProviderEnabled(provider: String) {}
override fun onProviderDisabled(provider: String) {}
}
private fun createPhotoFile(): File? {
val timeStamp = SimpleDateFormat("yyyyMMdd_HHmmss", Locale.getDefault()).format(Date())
val storageDir = getExternalFilesDir(null)
return File.createTempFile("JPEG_${timeStamp}_", ".jpg", storageDir)
}
private fun getLocation(){
private fun getLocation() {
val locMan = getSystemService(Context.LOCATION_SERVICE) as LocationManager
val checkCameraPermission = ContextCompat.checkSelfPermission(this, permission.ACCESS_FINE_LOCATION)
val checkCameraPermission =
ContextCompat.checkSelfPermission(this, permission.ACCESS_FINE_LOCATION)
val checkCallPhonePermission =
ContextCompat.checkSelfPermission(this, permission.ACCESS_COARSE_LOCATION)
if (checkCallPhonePermission != PackageManager.PERMISSION_GRANTED || checkCameraPermission != PackageManager.PERMISSION_GRANTED) {
ActivityCompat.requestPermissions(this, arrayOf(
ActivityCompat.requestPermissions(
this, arrayOf(
permission.ACCESS_COARSE_LOCATION,
permission.ACCESS_FINE_LOCATION
), 22)
), 22
)
}
try {
// Request location updates
val providers = locMan.getProviders(false)
var provider = LocationManager.NETWORK_PROVIDER
if(providers.size != 0) {
if (providers.contains(LocationManager.GPS_PROVIDER)) {
if (providers.size != 0) {
if (providers.contains(LocationManager.NETWORK_PROVIDER)) {
provider = LocationManager.NETWORK_PROVIDER
}else if(providers.contains(LocationManager.GPS_PROVIDER)){
provider = LocationManager.GPS_PROVIDER
}else if (providers.contains(LocationManager.PASSIVE_PROVIDER)){
} else if (providers.contains(LocationManager.PASSIVE_PROVIDER)) {
provider = LocationManager.PASSIVE_PROVIDER
}
}
Toast.makeText(baseContext, "使用" + provider + "定位", Toast.LENGTH_SHORT).show()
Log.d(TAG, "==================>>>> get location from gps")
locMan.requestLocationUpdates(provider, 10000L, 100f, locationListener)
} catch(ex: SecurityException) {
} catch (ex: SecurityException) {
Log.d(TAG, "Security Exception, no location available")
}
}
@ -184,71 +258,23 @@ class MainActivity : AppCompatActivity() {
gpsInfoTextView.text = gpsInfo
}
private fun uploadPhotoAndGpsInfo(photoFilePath: String, gpsInfo: String) {
// 在此处实现将照片和GPS信息上传到api.abc.com/report接口的逻辑
// 可以使用 Retrofit 或其他网络库进行网络请求
// 将照片转换成 Base64 字符串,然后添加到请求中
// 请求完成后更新界面上的预览窗口等信息
GlobalScope.launch(Dispatchers.IO) {
try {
val photoFile = File(photoFilePath)
val photoBytes = ByteArrayOutputStream().use {
val inputStream = photoFile.inputStream()
inputStream.copyTo(it)
it.toByteArray()
}
val base64Photo = Base64.encodeToString(photoBytes, Base64.DEFAULT)
// 在此处添加上传逻辑
// 使用 Retrofit 或其他网络库发送照片和GPS信息到api.abc.com/report接口
// 请注意处理网络请求的结果和错误
} catch (e: Exception) {
e.printStackTrace()
}
}
}
companion object {
private const val PERMISSION_REQUEST_CODE = 1001
private const val PHOTO_REQUEST_CODE = 1002
private const val TAG = "Travebing"
private const val FILENAME_FORMAT = "yyyy-MM-dd-HH-mm-ss-SSS"
private const val REQUEST_CODE_PERMISSIONS = 10
val MEDIA_TYPE_JSON = "application/json; charset=utf-8".toMediaType()
val MEDIA_TYPE_MULTIPART = "multipart/form-data".toMediaType()
val MEDIA_TYPE_FILE = "application/octet-stream".toMediaType()
}
private fun startCamera() {
val cameraProviderFuture = ProcessCameraProvider.getInstance(this)
cameraProviderFuture.addListener({
// Used to bind the lifecycle of cameras to the lifecycle owner
val cameraProvider: ProcessCameraProvider = cameraProviderFuture.get()
// Preview
// val preview = Preview.Builder()
// .build()
// .also {
// it.setSurfaceProvider(viewBinding.viewFinder.surfaceProvider)
// }
// val recorder = Recorder.Builder()
// .setQualitySelector(QualitySelector.from(Quality.HIGHEST,
// FallbackStrategy.higherQualityOrLowerThan(Quality.SD)))
// .build()
// videoCapture = VideoCapture.withOutput(recorder)
imageCapture = ImageCapture.Builder().build()
/*
val imageAnalyzer = ImageAnalysis.Builder().build()
.also {
setAnalyzer(
cameraExecutor,
LuminosityAnalyzer { luma -> startCamera()
Log.d(TAG, "Average luminosity: $luma")
}
)
}
*/
// Select back camera as a default
val cameraSelector = CameraSelector.DEFAULT_BACK_CAMERA
@ -258,17 +284,28 @@ class MainActivity : AppCompatActivity() {
// Bind use cases to camera
cameraProvider.bindToLifecycle(
this, cameraSelector, imageCapture)
this, cameraSelector, imageCapture
)
} catch(exc: Exception) {
} catch (exc: Exception) {
Log.e(TAG, "Use case binding failed", exc)
}
}, ContextCompat.getMainExecutor(this))
}
private fun takePhoto() {
/**
* 单个文件复制
*/
fun copyfile(srcFile: InputStream, destFile: File) {
val fos = FileOutputStream(destFile)
srcFile.copyTo(fos)
fos.close()
srcFile.close()
}
private fun takePhoto(): String {
// Get a stable reference of the modifiable image capture use case
val imageCapture = imageCapture ?: return
val imageCapture = imageCapture ?: return ""
Log.d(TAG, "================>>>>> now do capture photo")
// Create time stamped name and MediaStore entry.
val name = SimpleDateFormat(FILENAME_FORMAT, Locale.US)
@ -276,14 +313,16 @@ class MainActivity : AppCompatActivity() {
val contentValues = ContentValues().apply {
put(MediaStore.MediaColumns.DISPLAY_NAME, name)
put(MediaStore.MediaColumns.MIME_TYPE, "image/jpeg")
put(MediaStore.Images.Media.RELATIVE_PATH, "Pictures/CameraX-Image")
put(MediaStore.Images.Media.RELATIVE_PATH, "Pictures/travebing")
}
// Create output options object which contains file + metadata
val outputOptions = ImageCapture.OutputFileOptions
.Builder(contentResolver,
.Builder(
contentResolver,
MediaStore.Images.Media.EXTERNAL_CONTENT_URI,
contentValues)
contentValues
)
.build()
// Set up image capture listener, which is triggered after photo has
@ -295,16 +334,20 @@ class MainActivity : AppCompatActivity() {
override fun onError(exc: ImageCaptureException) {
Log.e(TAG, "+++++++++++Photo capture failed: ${exc.message}", exc)
}
override fun
onImageSaved(output: ImageCapture.OutputFileResults){
@SuppressLint("Recycle")
override fun onImageSaved(output: ImageCapture.OutputFileResults) {
previewImageView.setImageURI(output.savedUri)
val msg = "--------- Photo capture succeeded: ${output.savedUri}"
Toast.makeText(baseContext, msg, Toast.LENGTH_SHORT).show()
Log.d(TAG, msg)
val resolve = context.contentResolver
val uri = Uri.withAppendedPath(output.savedUri, "")
val ins:InputStream = resolve.openInputStream(uri)!!
updateWay(ins)
}
}
)
return name
}
}

View File

@ -47,7 +47,7 @@
tools:srcCompat="@tools:sample/backgrounds/scenic" />
<EditText
android:id="@+id/editTextText"
android:id="@+id/planText"
android:layout_width="262dp"
android:layout_height="44dp"
android:layout_marginStart="18dp"
@ -56,7 +56,7 @@
android:layout_marginBottom="104dp"
android:ems="10"
android:inputType="text"
android:text="travel.bing89.com"
android:text="1"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"

View File

@ -0,0 +1,4 @@
<?xml version="1.0" encoding="utf-8"?>
<network-security-config>
<base-config cleartextTrafficPermitted="true"/>
</network-security-config>