Skip to content
Snippets Groups Projects
Commit e730b79f authored by farhanfahreezy's avatar farhanfahreezy
Browse files

Merge branch 'main' of...

Merge branch 'main' of https://gitlab.informatika.org/if3210-2024-pmm/if3210-2024-android-pmm into transaction
parents 0ef2270b 57e5b6d3
Branches
Tags
1 merge request!3Transaction
Showing
with 376 additions and 90 deletions
...@@ -3,7 +3,20 @@ ...@@ -3,7 +3,20 @@
<component name="deploymentTargetDropDown"> <component name="deploymentTargetDropDown">
<value> <value>
<entry key="app"> <entry key="app">
<State /> <State>
<targetSelectedWithDropDown>
<Target>
<type value="QUICK_BOOT_TARGET" />
<deviceKey>
<Key>
<type value="VIRTUAL_DEVICE_PATH" />
<value value="C:\Users\dzafa\.android\avd\Pixel_7_Pro_API_34.avd" />
</Key>
</deviceKey>
</Target>
</targetSelectedWithDropDown>
<timeTargetWasSelectedWithDropDown value="2024-04-01T08:55:20.631393400Z" />
</State>
</entry> </entry>
</value> </value>
</component> </component>
......
...@@ -7,6 +7,8 @@ ...@@ -7,6 +7,8 @@
<uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" /> <uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />
<uses-permission android:name="android.permission.INTERNET" /> <uses-permission android:name="android.permission.INTERNET" />
<uses-permission android:name="android.permission.CAMERA"/> <uses-permission android:name="android.permission.CAMERA"/>
<uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" />
<!-- Always include this permission --> <!-- Always include this permission -->
<uses-permission android:name="android.permission.ACCESS_COARSE_LOCATION" /> <uses-permission android:name="android.permission.ACCESS_COARSE_LOCATION" />
...@@ -27,9 +29,9 @@ ...@@ -27,9 +29,9 @@
tools:targetApi="31" tools:targetApi="31"
tools:ignore="ForegroundServicePermission"> tools:ignore="ForegroundServicePermission">
<service android:name="LocationService" android:foregroundServiceType="location" android:enabled="true" android:exported="false"/> <service android:name="LocationService" android:foregroundServiceType="location" android:enabled="true" android:exported="false"/>
<activity android:name=".LoginActivity"/> <activity android:name=".MainActivity"/>
<activity <activity
android:name=".MainActivity" android:name=".LoginActivity"
android:exported="true" android:exported="true"
android:theme="@style/Theme.AppCompat.Light.NoActionBar" android:theme="@style/Theme.AppCompat.Light.NoActionBar"
> >
......
...@@ -32,6 +32,7 @@ class LoginActivity : AppCompatActivity() { ...@@ -32,6 +32,7 @@ class LoginActivity : AppCompatActivity() {
private fun replaceFragment(fragment: Fragment){ private fun replaceFragment(fragment: Fragment){
val fragmentManager = supportFragmentManager val fragmentManager = supportFragmentManager
val fragmentTransaction = fragmentManager.beginTransaction() val fragmentTransaction = fragmentManager.beginTransaction()
fragmentTransaction.setCustomAnimations(android.R.anim.fade_in, android.R.anim.fade_out)
fragmentTransaction.replace(R.id.login_layout,fragment) fragmentTransaction.replace(R.id.login_layout,fragment)
fragmentTransaction.commit() fragmentTransaction.commit()
} }
......
...@@ -4,6 +4,7 @@ import android.Manifest.permission.ACCESS_COARSE_LOCATION ...@@ -4,6 +4,7 @@ import android.Manifest.permission.ACCESS_COARSE_LOCATION
import android.Manifest.permission.ACCESS_FINE_LOCATION import android.Manifest.permission.ACCESS_FINE_LOCATION
import android.app.AlertDialog import android.app.AlertDialog
import android.content.Context import android.content.Context
import android.content.Intent
import androidx.appcompat.app.AppCompatActivity import androidx.appcompat.app.AppCompatActivity
import android.os.Bundle import android.os.Bundle
import android.os.Handler import android.os.Handler
...@@ -25,7 +26,12 @@ import androidx.activity.result.contract.ActivityResultContracts ...@@ -25,7 +26,12 @@ import androidx.activity.result.contract.ActivityResultContracts
import androidx.activity.viewModels import androidx.activity.viewModels
import com.example.bondoman.models.TransactionViewModel import com.example.bondoman.models.TransactionViewModel
import com.example.bondoman.room.BondomanDatabase import com.example.bondoman.room.BondomanDatabase
import android.os.Build
import android.util.Log.w
import android.widget.Toast
import androidx.appcompat.widget.Toolbar
import com.example.bondoman.ui.nointernet.NoInternetFragment import com.example.bondoman.ui.nointernet.NoInternetFragment
import kotlinx.coroutines.withContext
import kotlinx.coroutines.flow.collect import kotlinx.coroutines.flow.collect
import retrofit2.HttpException import retrofit2.HttpException
...@@ -55,19 +61,19 @@ class MainActivity : AppCompatActivity() { ...@@ -55,19 +61,19 @@ class MainActivity : AppCompatActivity() {
binding.bottomNavigationView.setOnItemSelectedListener { binding.bottomNavigationView.setOnItemSelectedListener {
when(it.itemId){ when(it.itemId){
R.id.bottom_nav_chart -> { R.id.bottom_nav_chart -> {
replaceFragment(ChartFragment(), NoInternetFragment(), "Chart") replaceFragment(ChartFragment(), ChartFragment(), "Chart")
} }
R.id.bottom_nav_scan -> { R.id.bottom_nav_scan -> {
replaceFragment(ScanFragment(), NoInternetFragment(), "Scan") replaceFragment(ScanFragment(), NoInternetFragment(), "Scan")
} }
R.id.bottom_nav_cart -> { R.id.bottom_nav_cart -> {
replaceFragment(CartFragment(), NoInternetFragment(),"Cart") replaceFragment(CartFragment(), CartFragment(),"Cart")
} }
R.id.bottom_nav_twibbon -> { R.id.bottom_nav_twibbon -> {
replaceFragment(TwibbonFragment(), NoInternetFragment(),"Twibbon") replaceFragment(TwibbonFragment(), TwibbonFragment(),"Twibbon")
} }
R.id.bottom_nav_settings -> { R.id.bottom_nav_settings -> {
replaceFragment(SettingsFragment(), NoInternetFragment(),"Settings") replaceFragment(SettingsFragment(),SettingsFragment(),"Settings")
} }
else -> { else -> {
...@@ -130,13 +136,20 @@ class MainActivity : AppCompatActivity() { ...@@ -130,13 +136,20 @@ class MainActivity : AppCompatActivity() {
private fun backgroundWorker(){ private fun backgroundWorker(){
println("excuting background worker") println("excuting background worker")
val service = RetrofitClient.createRetrofitService(this) val service = RetrofitClient.createRetrofitService(this)
val context = this@MainActivity
CoroutineScope(Dispatchers.IO).launch { CoroutineScope(Dispatchers.IO).launch {
try { try {
val response = service.checkToken() val response = service.checkToken()
if (response.isSuccessful) { if (response.isSuccessful) {
println(response) println("token still valid")
} else { } else {
println("Error: ${response.code()}") clearToken()
withContext(Dispatchers.Main) {
Toast.makeText(context, "Your Session Has Expired", Toast.LENGTH_LONG).show()
}
startActivity(Intent(context, LoginActivity::class.java))
finish()
} }
} catch (e: HttpException) { } catch (e: HttpException) {
println("Exception ${e.message}") println("Exception ${e.message}")
...@@ -146,6 +159,13 @@ class MainActivity : AppCompatActivity() { ...@@ -146,6 +159,13 @@ class MainActivity : AppCompatActivity() {
} }
} }
private fun clearToken() {
val sharedPreferences = getSharedPreferences("BondoMan", Context.MODE_PRIVATE)
val editor = sharedPreferences.edit()
editor.clear().apply()
}
private fun isNetworkConnected(context: Context): Boolean { private fun isNetworkConnected(context: Context): Boolean {
val connectivityManager = context.getSystemService(Context.CONNECTIVITY_SERVICE) as ConnectivityManager val connectivityManager = context.getSystemService(Context.CONNECTIVITY_SERVICE) as ConnectivityManager
val network = connectivityManager.activeNetwork ?: return false val network = connectivityManager.activeNetwork ?: return false
...@@ -153,15 +173,8 @@ class MainActivity : AppCompatActivity() { ...@@ -153,15 +173,8 @@ class MainActivity : AppCompatActivity() {
return networkCapabilities.hasCapability(NetworkCapabilities.NET_CAPABILITY_INTERNET) return networkCapabilities.hasCapability(NetworkCapabilities.NET_CAPABILITY_INTERNET)
} }
private fun showNoInternetDialog(context: Context) {
val builder = AlertDialog.Builder(context)
builder.setTitle("No Internet Connection")
.setMessage("Please check your internet connection and try again.")
.setPositiveButton("OK") { dialog, _ -> dialog.dismiss() }
val dialog = builder.create()
dialog.show()
}
private fun replaceFragment(fragment: Fragment, noInternetFragment: Fragment, headerTitle : String){ private fun replaceFragment(fragment: Fragment, noInternetFragment: Fragment, headerTitle : String){
val fragmentManager = supportFragmentManager val fragmentManager = supportFragmentManager
...@@ -169,7 +182,7 @@ class MainActivity : AppCompatActivity() { ...@@ -169,7 +182,7 @@ class MainActivity : AppCompatActivity() {
if (this.isNetworkConnected(this)){ if (this.isNetworkConnected(this)){
fragmentTransaction.replace(R.id.main_frame_layout,fragment) fragmentTransaction.replace(R.id.main_frame_layout,fragment)
}else{ }else{
showNoInternetDialog(this) Toast.makeText(this, "Network Has Been Disconnected", Toast.LENGTH_LONG).show()
fragmentTransaction.replace(R.id.main_frame_layout,noInternetFragment) fragmentTransaction.replace(R.id.main_frame_layout,noInternetFragment)
} }
setTitle(headerTitle) setTitle(headerTitle)
......
...@@ -24,7 +24,6 @@ object RetrofitClient { ...@@ -24,7 +24,6 @@ object RetrofitClient {
val originalRequest: Request = chain.request() val originalRequest: Request = chain.request()
val requestBuilder: Request.Builder = originalRequest.newBuilder() val requestBuilder: Request.Builder = originalRequest.newBuilder()
if (!authToken.isNullOrEmpty()) { if (!authToken.isNullOrEmpty()) {
println(authToken)
requestBuilder.header("Authorization", "Bearer $authToken") // Add authorization header here requestBuilder.header("Authorization", "Bearer $authToken") // Add authorization header here
} }
val request: Request = requestBuilder.build() val request: Request = requestBuilder.build()
......
...@@ -17,6 +17,10 @@ import kotlinx.coroutines.CoroutineScope ...@@ -17,6 +17,10 @@ import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.withContext import kotlinx.coroutines.withContext
import retrofit2.HttpException import retrofit2.HttpException
import android.content.Context import android.content.Context
import android.content.SharedPreferences
import android.net.ConnectivityManager
import android.net.NetworkCapabilities
import android.widget.Toast
// TODO: Rename parameter arguments, choose names that match // TODO: Rename parameter arguments, choose names that match
...@@ -60,41 +64,56 @@ class LoginFragment : Fragment() { ...@@ -60,41 +64,56 @@ class LoginFragment : Fragment() {
val email = binding.emailInput.text.toString() val email = binding.emailInput.text.toString()
val password = binding.passwordInput.text.toString() val password = binding.passwordInput.text.toString()
apiCall(email, password) login(email, password)
} }
} }
private fun apiCall(email: String, password: String) { private fun login(email: String, password: String) {
val loginRequest = LoginRequest(email, password) val loginRequest = LoginRequest(email, password)
val service = RetrofitClient.createRetrofitService(requireContext()) val service = RetrofitClient.createRetrofitService(requireContext())
if (!isNetworkConnected(requireContext())){
CoroutineScope(Dispatchers.IO).launch { Toast.makeText(requireContext(), "Network is Disconnected Unable To Login", Toast.LENGTH_LONG).show()
val response = service.login(loginRequest) }
withContext(Dispatchers.Main) { else {
try { CoroutineScope(Dispatchers.IO).launch {
if (response.isSuccessful) { val response = service.login(loginRequest)
val token = response.body()?.token withContext(Dispatchers.Main) {
if(!token.isNullOrEmpty()){ try {
val sharedPreferences = requireActivity().getSharedPreferences("BondoMan", Context.MODE_PRIVATE) if (response.isSuccessful) {
val editor = sharedPreferences.edit() val token = response.body()?.token
editor.putString("token", token) if (!token.isNullOrEmpty()) {
editor.apply() val sharedPreferences = requireActivity().getSharedPreferences(
startActivity(Intent(activity, MainActivity::class.java)) "BondoMan",
activity?.finish() Context.MODE_PRIVATE
)
val editor = sharedPreferences.edit()
editor.putString("token", token)
editor.putString("email", email)
editor.apply()
startActivity(Intent(activity, MainActivity::class.java))
activity?.finish()
}
} else {
println("Error: ${response.code()}")
} }
} else { } catch (e: HttpException) {
println("Error: ${response.code()}") println("Exception ${e.message}")
} catch (e: Throwable) {
println(e)
} }
} catch (e: HttpException) {
println("Exception ${e.message}")
} catch (e: Throwable) {
println(e)
} }
} }
} }
} }
private fun isNetworkConnected(context: Context): Boolean {
val connectivityManager = context.getSystemService(Context.CONNECTIVITY_SERVICE) as ConnectivityManager
val network = connectivityManager.activeNetwork ?: return false
val networkCapabilities = connectivityManager.getNetworkCapabilities(network) ?: return false
return networkCapabilities.hasCapability(NetworkCapabilities.NET_CAPABILITY_INTERNET)
}
companion object { companion object {
/** /**
* Use this factory method to create a new instance of * Use this factory method to create a new instance of
......
...@@ -2,6 +2,7 @@ package com.example.bondoman.ui.scan ...@@ -2,6 +2,7 @@ package com.example.bondoman.ui.scan
import android.Manifest import android.Manifest
import android.annotation.SuppressLint import android.annotation.SuppressLint
import android.app.Activity
import android.app.AlertDialog import android.app.AlertDialog
import android.content.pm.PackageManager import android.content.pm.PackageManager
import android.os.Bundle import android.os.Bundle
...@@ -16,7 +17,9 @@ import com.example.bondoman.databinding.FragmentScanBinding ...@@ -16,7 +17,9 @@ import com.example.bondoman.databinding.FragmentScanBinding
import java.util.concurrent.ExecutorService import java.util.concurrent.ExecutorService
import java.util.concurrent.Executors import java.util.concurrent.Executors
import android.content.Context import android.content.Context
import android.content.Intent
import android.graphics.BitmapFactory import android.graphics.BitmapFactory
import android.provider.MediaStore
import android.widget.Toast import android.widget.Toast
import androidx.activity.result.contract.ActivityResultContracts import androidx.activity.result.contract.ActivityResultContracts
import androidx.camera.lifecycle.ProcessCameraProvider import androidx.camera.lifecycle.ProcessCameraProvider
...@@ -48,6 +51,10 @@ class ScanFragment : Fragment() { ...@@ -48,6 +51,10 @@ class ScanFragment : Fragment() {
private lateinit var cameraExecutor: ExecutorService private lateinit var cameraExecutor: ExecutorService
private lateinit var imageView: ImageView
private val PICK_IMAGE_REQUEST = 1
private val activityResultLauncher = private val activityResultLauncher =
registerForActivityResult( registerForActivityResult(
ActivityResultContracts.RequestMultiplePermissions()) ActivityResultContracts.RequestMultiplePermissions())
...@@ -91,6 +98,8 @@ class ScanFragment : Fragment() { ...@@ -91,6 +98,8 @@ class ScanFragment : Fragment() {
viewBinding.imageCaptureButton.setOnClickListener { takePhoto() } viewBinding.imageCaptureButton.setOnClickListener { takePhoto() }
viewBinding.imagePickerButton.setOnClickListener { pickImageFromGallery() }
cameraExecutor = Executors.newSingleThreadExecutor() cameraExecutor = Executors.newSingleThreadExecutor()
} }
...@@ -188,6 +197,27 @@ class ScanFragment : Fragment() { ...@@ -188,6 +197,27 @@ class ScanFragment : Fragment() {
} }
} }
private fun pickImageFromGallery() {
val intent = Intent(Intent.ACTION_PICK)
intent.type = "image/*"
startActivityForResult(intent, PICK_IMAGE_REQUEST)
}
@Deprecated("Deprecated in Java")
override fun onActivityResult(requestCode: Int, resultCode: Int, data: Intent?) {
super.onActivityResult(requestCode, resultCode, data)
if (requestCode == PICK_IMAGE_REQUEST && resultCode == Activity.RESULT_OK && data != null) {
val imageUri = data.data
imageUri?.let { uri ->
val imageStream = requireActivity().contentResolver.openInputStream(uri)
val imageData = imageStream?.readBytes()
imageData?.let { bytes ->
uploadImage(bytes, requireActivity())
}
}
}
}
private fun showResultDialog(context: Context, imageData: ByteArray, items: List<Item>?) { private fun showResultDialog(context: Context, imageData: ByteArray, items: List<Item>?) {
// Inflate the layout for the dialog // Inflate the layout for the dialog
...@@ -226,7 +256,8 @@ class ScanFragment : Fragment() { ...@@ -226,7 +256,8 @@ class ScanFragment : Fragment() {
private const val TAG = "BondoMan" private const val TAG = "BondoMan"
private val REQUIRED_PERMISSIONS = private val REQUIRED_PERMISSIONS =
mutableListOf( mutableListOf(
Manifest.permission.CAMERA Manifest.permission.CAMERA,
Manifest.permission.READ_EXTERNAL_STORAGE
).toTypedArray() ).toTypedArray()
} }
} }
\ No newline at end of file
package com.example.bondoman.ui.twibbon package com.example.bondoman.ui.twibbon
import android.Manifest
import android.app.Activity
import android.app.AlertDialog
import android.content.pm.PackageManager
import android.os.Bundle import android.os.Bundle
import androidx.fragment.app.Fragment import androidx.fragment.app.Fragment
import android.view.LayoutInflater import android.view.LayoutInflater
import android.view.View import android.view.View
import android.view.ViewGroup import android.view.ViewGroup
import androidx.camera.core.ImageCapture
import androidx.core.content.ContextCompat
import com.example.bondoman.R import com.example.bondoman.R
import com.example.bondoman.databinding.FragmentScanBinding
import java.util.concurrent.ExecutorService
import java.util.concurrent.Executors
import android.content.Context
import android.content.Intent
import android.graphics.BitmapFactory
import android.net.Uri
import android.widget.Toast
import androidx.activity.result.contract.ActivityResultContracts
import androidx.camera.lifecycle.ProcessCameraProvider
import androidx.camera.core.Preview
import androidx.camera.core.CameraSelector
import android.util.Log
import android.widget.ImageView
import androidx.camera.core.ImageCaptureException
import androidx.camera.core.ImageProxy
import androidx.recyclerview.widget.LinearLayoutManager
import androidx.recyclerview.widget.RecyclerView
import com.example.bondoman.databinding.FragmentTwibbonBinding
import com.example.bondoman.models.Item
import com.example.bondoman.services.RetrofitClient
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.launch
import kotlinx.coroutines.withContext
import okhttp3.MediaType
import okhttp3.MultipartBody
import okhttp3.RequestBody
import retrofit2.HttpException
// TODO: Rename parameter arguments, choose names that match
// the fragment initialization parameters, e.g. ARG_ITEM_NUMBER
private const val ARG_PARAM1 = "param1"
private const val ARG_PARAM2 = "param2"
/**
* A simple [Fragment] subclass.
* Use the [TwibbonFragment.newInstance] factory method to
* create an instance of this fragment.
*/
class TwibbonFragment : Fragment() { class TwibbonFragment : Fragment() {
// TODO: Rename and change types of parameters // TODO: Rename and change types of parameters
private var param1: String? = null private lateinit var viewBinding: FragmentTwibbonBinding
private var param2: String? = null
private var imageCapture : ImageCapture? = null
private lateinit var cameraExecutor: ExecutorService
private var previewFrozen: Boolean = false
private val PICK_IMAGE_REQUEST = 1
private val activityResultLauncher =
registerForActivityResult(
ActivityResultContracts.RequestMultiplePermissions())
{ permissions ->
var permissionGranted = true
permissions.entries.forEach {
if (it.key in REQUIRED_PERMISSIONS && !it.value)
permissionGranted = false
}
if (!permissionGranted) {
Toast.makeText(requireContext(),
"Permission request denied",
Toast.LENGTH_SHORT).show()
} else {
startCamera()
}
}
override fun onCreate(savedInstanceState: Bundle?) { override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState) super.onCreate(savedInstanceState)
arguments?.let {
param1 = it.getString(ARG_PARAM1)
param2 = it.getString(ARG_PARAM2)
}
} }
override fun onCreateView( override fun onCreateView(
...@@ -38,23 +85,104 @@ class TwibbonFragment : Fragment() { ...@@ -38,23 +85,104 @@ class TwibbonFragment : Fragment() {
return inflater.inflate(R.layout.fragment_twibbon, container, false) return inflater.inflate(R.layout.fragment_twibbon, container, false)
} }
companion object { override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
/** super.onViewCreated(view, savedInstanceState)
* Use this factory method to create a new instance of
* this fragment using the provided parameters. viewBinding = FragmentTwibbonBinding.bind(view)
*
* @param param1 Parameter 1. if (allPermissionsGranted()){
* @param param2 Parameter 2. startCamera()
* @return A new instance of fragment TwibbonFragment. }else{
*/ requestPermissions()
// TODO: Rename and change types and number of parameters }
@JvmStatic
fun newInstance(param1: String, param2: String) = viewBinding.twibbonCaptureButton.setOnClickListener { takePhoto() }
TwibbonFragment().apply { viewBinding.changeTwibbonButton.setOnClickListener { pickTwibbonFromGallery() }
arguments = Bundle().apply {
putString(ARG_PARAM1, param1) cameraExecutor = Executors.newSingleThreadExecutor()
putString(ARG_PARAM2, param2) }
private fun startCamera() {
val cameraProviderFuture = ProcessCameraProvider.getInstance(requireActivity())
cameraProviderFuture.addListener({
val cameraProvider: ProcessCameraProvider = cameraProviderFuture.get()
val preview = Preview.Builder().build().also {
mPreview ->
if (!previewFrozen) {
mPreview.setSurfaceProvider(viewBinding.viewFinder.surfaceProvider)
} else {
mPreview.setSurfaceProvider(null)
} }
} }
imageCapture = ImageCapture.Builder()
.build()
val cameraSelector = CameraSelector.DEFAULT_FRONT_CAMERA
try {
cameraProvider.unbindAll()
cameraProvider.bindToLifecycle(
this, cameraSelector, preview, imageCapture)
} catch(exc: Exception) {
Log.e(TAG, "Use case binding failed", exc)
}
}, ContextCompat.getMainExecutor(requireActivity()))
}
private fun requestPermissions(){
activityResultLauncher.launch(REQUIRED_PERMISSIONS)
}
private fun allPermissionsGranted() = REQUIRED_PERMISSIONS.all {
ContextCompat.checkSelfPermission(
requireContext(), it
) == PackageManager.PERMISSION_GRANTED
}
private fun takePhoto() {
previewFrozen = !previewFrozen
if (!previewFrozen) {
viewBinding.twibbonCaptureButton.text = "Capture"
} else {
viewBinding.twibbonCaptureButton.text = "Retake"
}
startCamera()
}
private fun pickTwibbonFromGallery() {
val intent = Intent(Intent.ACTION_PICK)
intent.type = "image/png"
startActivityForResult(intent, PICK_IMAGE_REQUEST)
}
@Deprecated("Deprecated in Java")
override fun onActivityResult(requestCode: Int, resultCode: Int, data: Intent?) {
super.onActivityResult(requestCode, resultCode, data)
if (requestCode == PICK_IMAGE_REQUEST && resultCode == Activity.RESULT_OK && data != null) {
val imageUri: Uri? = data.data
viewBinding.twibbonOverlay.setImageURI(imageUri)
}
}
override fun onDestroy() {
super.onDestroy()
cameraExecutor.shutdown()
}
companion object {
private const val TAG = "BondoMan"
private val REQUIRED_PERMISSIONS =
mutableListOf(
Manifest.permission.CAMERA,
Manifest.permission.READ_EXTERNAL_STORAGE
).toTypedArray()
} }
} }
\ No newline at end of file
...@@ -2,5 +2,5 @@ ...@@ -2,5 +2,5 @@
<shape xmlns:android="http://schemas.android.com/apk/res/android" <shape xmlns:android="http://schemas.android.com/apk/res/android"
android:shape="rectangle"> android:shape="rectangle">
<corners android:radius="8dp" /> <!-- Adjust the radius to your preference --> <corners android:radius="8dp" /> <!-- Adjust the radius to your preference -->
<solid android:color="@android:color/white" /> <!-- Set the background color --> <solid android:color="@color/white" /> <!-- Set the background color -->
</shape> </shape>
<shape xmlns:android="http://schemas.android.com/apk/res/android"
android:shape="oval">
<solid android:color="@color/red.500" />
</shape>
\ No newline at end of file
...@@ -10,20 +10,34 @@ ...@@ -10,20 +10,34 @@
<androidx.camera.view.PreviewView <androidx.camera.view.PreviewView
android:id="@+id/viewFinder" android:id="@+id/viewFinder"
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="match_parent" > android:layout_height="match_parent"
</androidx.camera.view.PreviewView> tools:layout_editor_absoluteX="16dp"
tools:layout_editor_absoluteY="0dp"/>
<Button <Button
android:id="@+id/imageCaptureButton" android:id="@+id/imageCaptureButton"
android:layout_width="wrap_content" android:layout_width="60dp"
android:layout_height="wrap_content" android:layout_height="60dp"
android:text="Button" android:background="@drawable/take_photo_button"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintHorizontal_bias="0.498"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent"
app:layout_constraintVertical_bias="0.831" />
<ImageView
android:id="@+id/imagePickerButton"
android:layout_width="60dp"
android:layout_height="59dp"
android:src="@android:drawable/ic_menu_gallery"
app:layout_constraintBottom_toBottomOf="parent" app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent" app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintHorizontal_bias="0.17"
app:layout_constraintStart_toStartOf="parent" app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent" app:layout_constraintTop_toTopOf="parent"
app:layout_constraintVertical_bias="0.796" /> app:layout_constraintVertical_bias="0.831" />
<androidx.constraintlayout.widget.Guideline <androidx.constraintlayout.widget.Guideline
android:id="@+id/vertical_centerline" android:id="@+id/vertical_centerline"
......
<?xml version="1.0" encoding="utf-8"?> <?xml version="1.0" encoding="utf-8"?>
<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android" <androidx.constraintlayout.widget.ConstraintLayout
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools" xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="match_parent" android:layout_height="match_parent"
tools:context=".ui.twibbon.TwibbonFragment"> tools:context=".MainActivity">
<FrameLayout
android:id="@+id/frameLayout"
android:layout_width="411dp"
android:layout_height="wrap_content"
android:visibility="visible"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintHorizontal_bias="0.0"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent"
app:layout_constraintVertical_bias="0.0">
<androidx.camera.view.PreviewView
android:id="@+id/viewFinder"
android:layout_width="match_parent"
android:layout_height="372dp"
android:layout_marginTop="43dp" />
<ImageView
android:id="@+id/twibbonOverlay"
android:layout_width="wrap_content"
android:layout_height="369dp"
android:src="@drawable/bondoman_logo"
android:translationY="42dp" />
</FrameLayout>
<Button
android:id="@+id/twibbonCaptureButton"
android:layout_width="wrap_content"
android:layout_height="60dp"
android:fontFamily="sans-serif"
android:text="Capture"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintHorizontal_bias="0.498"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent"
app:layout_constraintVertical_bias="0.743" />
<ImageView
android:id="@+id/changeTwibbonButton"
android:layout_width="60dp"
android:layout_height="60dp"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintHorizontal_bias="0.15"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent"
app:layout_constraintVertical_bias="0.743"
app:srcCompat="@android:drawable/ic_menu_gallery" />
<!-- TODO: Update blank fragment layout -->
<TextView <TextView
android:layout_width="match_parent" android:id="@+id/textView2"
android:layout_height="match_parent" android:layout_width="wrap_content"
android:text="Twibbon Fragment" android:layout_height="wrap_content"
android:textSize="26sp" android:layout_marginTop="48dp"
android:layout_gravity="center" /> android:layout_marginBottom="32dp"
android:text="Press the button to capture or retake!"
android:textAlignment="center"
android:textColor="#000000"
android:textSize="20sp"
app:layout_constraintBottom_toTopOf="@+id/button"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@+id/frameLayout"
app:layout_constraintVertical_bias="0.49" />
</FrameLayout> </androidx.constraintlayout.widget.ConstraintLayout>
\ No newline at end of file \ No newline at end of file
#Mon Mar 11 18:56:50 WIB 2024 #Mon Mar 11 18:56:50 WIB 2024
distributionBase=GRADLE_USER_HOME distributionBase=GRADLE_USER_HOME
distributionPath=wrapper/dists distributionPath=wrapper/dists
distributionUrl=https\://services.gradle.org/distributions/gradle-8.2-bin.zip distributionUrl=https\://services.gradle.org/distributions/gradle-8.4-bin.zip
zipStoreBase=GRADLE_USER_HOME zipStoreBase=GRADLE_USER_HOME
zipStorePath=wrapper/dists zipStorePath=wrapper/dists
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment