diff --git a/app/build.gradle.kts b/app/build.gradle.kts index 81b18cb43ac94d72ac7482c7658fe4e559bcf316..852a074606a7f04fc1967521dfbc630f783c13df 100644 --- a/app/build.gradle.kts +++ b/app/build.gradle.kts @@ -58,6 +58,7 @@ dependencies { implementation("androidx.camera:camera-lifecycle:1.3.2") implementation("androidx.camera:camera-view:1.3.2") implementation("androidx.exifinterface:exifinterface:1.3.7") + implementation("com.squareup.okhttp3:okhttp:4.9.3") testImplementation("junit:junit:4.13.2") androidTestImplementation("androidx.test.ext:junit:1.1.5") androidTestImplementation("androidx.test.espresso:espresso-core:3.5.1") diff --git a/app/src/main/java/com/example/nerbos/fragments/scan/ScanFragment.kt b/app/src/main/java/com/example/nerbos/fragments/scan/ScanFragment.kt index cdf2eef685c9eb40a9d9ee154a15caa5aabc25f8..44a497abe884370e93903bfdf157cce66022d6db 100644 --- a/app/src/main/java/com/example/nerbos/fragments/scan/ScanFragment.kt +++ b/app/src/main/java/com/example/nerbos/fragments/scan/ScanFragment.kt @@ -8,6 +8,8 @@ import android.graphics.Bitmap import android.graphics.BitmapFactory import android.graphics.Matrix import android.os.Bundle +import android.os.Handler +import android.os.Looper import android.provider.MediaStore import android.view.LayoutInflater import android.view.View @@ -28,9 +30,14 @@ import androidx.core.net.toUri import androidx.exifinterface.media.ExifInterface import com.google.android.material.dialog.MaterialAlertDialogBuilder import com.example.nerbos.R +import com.example.nerbos.service.Authentication import java.io.File +import java.io.IOException import java.util.concurrent.ExecutorService import java.util.concurrent.Executors +import okhttp3.* +import okhttp3.MediaType.Companion.toMediaTypeOrNull +import okhttp3.RequestBody.Companion.asRequestBody class ScanFragment : Fragment() { @@ -40,6 +47,10 @@ class ScanFragment : Fragment() { private val requestCameraPermissionCode = Manifest.permission.CAMERA + private val uploadURL: String by lazy { + requireContext().getString(R.string.backend_api_scan) + } + override fun onCreateView( inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle? @@ -160,7 +171,15 @@ class ScanFragment : Fragment() { MaterialAlertDialogBuilder(requireContext()) .setTitle("Use this picture?") .setPositiveButton("Yes") { dialog, _ -> - // Handle using the picture here + val imageFile = File.createTempFile("image", ".jpg", requireContext().cacheDir) + imageBitmap.compress(Bitmap.CompressFormat.JPEG, 100, imageFile.outputStream()) + uploadImageToServer(imageFile) { success, message -> + if (success) { + showToastOnUIThread("Image uploaded successfully") + } else { + showToastOnUIThread(message ?: "Failed to upload image. Please try again later.") + } + } dialog.dismiss() } .setNegativeButton("No") { dialog, _ -> @@ -176,6 +195,47 @@ class ScanFragment : Fragment() { } } + private fun uploadImageToServer(imageFile: File, callback: (Boolean, String?) -> Unit) { + val client = OkHttpClient() + + val requestBody = MultipartBody.Builder() + .setType(MultipartBody.FORM) + .addFormDataPart("file", imageFile.name, imageFile.asRequestBody("image/*".toMediaTypeOrNull())) + .build() + + val authentication = Authentication(requireContext()) + val token = authentication.getToken() + + val request = Request.Builder() + .url(uploadURL) + .header("Authorization", "Bearer $token") + .post(requestBody) + .build() + + client.newCall(request).enqueue(object : Callback { + override fun onResponse(call: Call, response: Response) { + if (response.isSuccessful) { + showToastOnUIThread("Image uploaded successfully") + callback(true, "Image uploaded successfully") + } else { + showToastOnUIThread("Failed to upload image. Please try again later.") + callback(false, "Failed to upload image. Please try again later.") + } + } + + override fun onFailure(call: Call, e: IOException) { + showToastOnUIThread("Failed to upload image: ${e.message}") + callback(false, e.message) + } + }) + } + + private fun showToastOnUIThread(message: String) { + Handler(Looper.getMainLooper()).post { + Toast.makeText(requireContext(), message, Toast.LENGTH_SHORT).show() + } + } + private val requestCameraPermission = registerForActivityResult(ActivityResultContracts.RequestPermission()) { isGranted -> if (isGranted) { diff --git a/app/src/main/java/com/example/nerbos/service/Authentication.kt b/app/src/main/java/com/example/nerbos/service/Authentication.kt index 62cde4837bfdccf20a592ff3b52445bf623e1450..ca362b71f0e9ecefbb3d227e2b12bc23aaee4016 100644 --- a/app/src/main/java/com/example/nerbos/service/Authentication.kt +++ b/app/src/main/java/com/example/nerbos/service/Authentication.kt @@ -84,7 +84,7 @@ class Authentication(private val context: Context) { return email.toInt() } - private fun getToken(): String { + internal fun getToken(): String { val sharedPreferences : SharedPreferences = context.getSharedPreferences(context.getString(R.string.preferences), Context.MODE_PRIVATE) return sharedPreferences.getString(context.getString(R.string.token), "") ?: "" diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml index f22dee3922939bc83d4f1650e68e0bc2233df33f..a0ce03409bf105f779d4f1c88d8cc52cb4639ff5 100644 --- a/app/src/main/res/values/strings.xml +++ b/app/src/main/res/values/strings.xml @@ -23,6 +23,7 @@ <string name="backend_api_login">https://pbd-backend-2024.vercel.app/api/auth/login</string> <string name="backend_api_token">https://pbd-backend-2024.vercel.app/api/auth/token</string> + <string name="backend_api_scan">https://pbd-backend-2024.vercel.app/api/bill/upload</string> <string name="ok">OK</string> <string name="welcome">Welcome!</string>