diff --git a/.idea/codeStyles/Project.xml b/.idea/codeStyles/Project.xml new file mode 100644 index 0000000..7643783 --- /dev/null +++ b/.idea/codeStyles/Project.xml @@ -0,0 +1,123 @@ + + + + + + + + + + \ No newline at end of file diff --git a/.idea/codeStyles/codeStyleConfig.xml b/.idea/codeStyles/codeStyleConfig.xml new file mode 100644 index 0000000..79ee123 --- /dev/null +++ b/.idea/codeStyles/codeStyleConfig.xml @@ -0,0 +1,5 @@ + + + + \ No newline at end of file diff --git a/.idea/misc.xml b/.idea/misc.xml index 0ad17cb..8978d23 100644 --- a/.idea/misc.xml +++ b/.idea/misc.xml @@ -1,4 +1,3 @@ - diff --git a/app/build.gradle.kts b/app/build.gradle.kts index 5f0fff5..ad4994d 100644 --- a/app/build.gradle.kts +++ b/app/build.gradle.kts @@ -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") } \ No newline at end of file diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml index 343a860..99a9930 100644 --- a/app/src/main/AndroidManifest.xml +++ b/app/src/main/AndroidManifest.xml @@ -9,6 +9,10 @@ + + + + diff --git a/app/src/main/java/com/bing89/travebing/MainActivity.kt b/app/src/main/java/com/bing89/travebing/MainActivity.kt index c5e67fa..e5a9e31 100644 --- a/app/src/main/java/com/bing89/travebing/MainActivity.kt +++ b/app/src/main/java/com/bing89/travebing/MainActivity.kt @@ -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() -// } - takePhoto() + 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( - permission.ACCESS_COARSE_LOCATION, - permission.ACCESS_FINE_LOCATION - ), 22) + ActivityCompat.requestPermissions( + this, arrayOf( + permission.ACCESS_COARSE_LOCATION, + permission.ACCESS_FINE_LOCATION + ), 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 } } diff --git a/app/src/main/res/layout/activity_main.xml b/app/src/main/res/layout/activity_main.xml index 4e2a9db..6cc9d8b 100644 --- a/app/src/main/res/layout/activity_main.xml +++ b/app/src/main/res/layout/activity_main.xml @@ -47,7 +47,7 @@ tools:srcCompat="@tools:sample/backgrounds/scenic" /> + + + \ No newline at end of file