diff --git a/.idea/deploymentTargetDropDown.xml b/.idea/deploymentTargetDropDown.xml index 0c0c3383890637b4721df1f49d0b229e55c0f361..9cc81e63d1cf3099f2e26ffdd831d0cb30c4bcca 100644 --- a/.idea/deploymentTargetDropDown.xml +++ b/.idea/deploymentTargetDropDown.xml @@ -3,7 +3,20 @@ <component name="deploymentTargetDropDown"> <value> <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> </value> </component> diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml index 57c2e7338eabc669c126c43cd79abe5c59212ca4..951f3a0a53b8061febf51fdb4de278736d7c9599 100644 --- a/app/src/main/AndroidManifest.xml +++ b/app/src/main/AndroidManifest.xml @@ -7,6 +7,8 @@ <uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" /> <uses-permission android:name="android.permission.INTERNET" /> <uses-permission android:name="android.permission.CAMERA"/> + <uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" /> + <!-- Always include this permission --> <uses-permission android:name="android.permission.ACCESS_COARSE_LOCATION" /> @@ -27,9 +29,9 @@ tools:targetApi="31" tools:ignore="ForegroundServicePermission"> <service android:name="LocationService" android:foregroundServiceType="location" android:enabled="true" android:exported="false"/> - <activity android:name=".LoginActivity"/> + <activity android:name=".MainActivity"/> <activity - android:name=".MainActivity" + android:name=".LoginActivity" android:exported="true" android:theme="@style/Theme.AppCompat.Light.NoActionBar" > diff --git a/app/src/main/java/com/example/bondoman/LoginActivity.kt b/app/src/main/java/com/example/bondoman/LoginActivity.kt index 0b07382716725d6866e1f53f5b133343e9b2a0f6..3d1ac63ac0df379fc9521c057223fba276b77d3b 100644 --- a/app/src/main/java/com/example/bondoman/LoginActivity.kt +++ b/app/src/main/java/com/example/bondoman/LoginActivity.kt @@ -32,6 +32,7 @@ class LoginActivity : AppCompatActivity() { private fun replaceFragment(fragment: Fragment){ val fragmentManager = supportFragmentManager val fragmentTransaction = fragmentManager.beginTransaction() + fragmentTransaction.setCustomAnimations(android.R.anim.fade_in, android.R.anim.fade_out) fragmentTransaction.replace(R.id.login_layout,fragment) fragmentTransaction.commit() } diff --git a/app/src/main/java/com/example/bondoman/services/RetrofitInstance.kt b/app/src/main/java/com/example/bondoman/services/RetrofitInstance.kt index f7abcc7a544270da5c3eca0f5dfd5f0b373c12a9..48f1e06e0e686f387050f99f6f8ea17b16077496 100644 --- a/app/src/main/java/com/example/bondoman/services/RetrofitInstance.kt +++ b/app/src/main/java/com/example/bondoman/services/RetrofitInstance.kt @@ -24,7 +24,6 @@ object RetrofitClient { val originalRequest: Request = chain.request() val requestBuilder: Request.Builder = originalRequest.newBuilder() if (!authToken.isNullOrEmpty()) { - println(authToken) requestBuilder.header("Authorization", "Bearer $authToken") // Add authorization header here } val request: Request = requestBuilder.build() diff --git a/app/src/main/java/com/example/bondoman/ui/login/LoginFragment.kt b/app/src/main/java/com/example/bondoman/ui/login/LoginFragment.kt index b4bebcc4e98708ebf8d7398c03ca0d76203effec..bd759b9ee8d60eb2c6c7fea2d5e8b67fdf6b199e 100644 --- a/app/src/main/java/com/example/bondoman/ui/login/LoginFragment.kt +++ b/app/src/main/java/com/example/bondoman/ui/login/LoginFragment.kt @@ -17,6 +17,10 @@ import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.withContext import retrofit2.HttpException 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 @@ -60,41 +64,56 @@ class LoginFragment : Fragment() { val email = binding.emailInput.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 service = RetrofitClient.createRetrofitService(requireContext()) - - CoroutineScope(Dispatchers.IO).launch { - val response = service.login(loginRequest) - withContext(Dispatchers.Main) { - try { - if (response.isSuccessful) { - val token = response.body()?.token - if(!token.isNullOrEmpty()){ - val sharedPreferences = requireActivity().getSharedPreferences("BondoMan", Context.MODE_PRIVATE) - val editor = sharedPreferences.edit() - editor.putString("token", token) - editor.apply() - startActivity(Intent(activity, MainActivity::class.java)) - activity?.finish() + if (!isNetworkConnected(requireContext())){ + Toast.makeText(requireContext(), "Network is Disconnected Unable To Login", Toast.LENGTH_LONG).show() + } + else { + CoroutineScope(Dispatchers.IO).launch { + val response = service.login(loginRequest) + withContext(Dispatchers.Main) { + try { + if (response.isSuccessful) { + val token = response.body()?.token + if (!token.isNullOrEmpty()) { + val sharedPreferences = requireActivity().getSharedPreferences( + "BondoMan", + 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 { - println("Error: ${response.code()}") + } catch (e: HttpException) { + 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 { /** * Use this factory method to create a new instance of diff --git a/app/src/main/java/com/example/bondoman/ui/scan/ScanFragment.kt b/app/src/main/java/com/example/bondoman/ui/scan/ScanFragment.kt index 91be4a2ef472d0eab7bbc3ae712c9e281adbafdd..fd29788ef94324c0c0e10664486759862c27caa5 100644 --- a/app/src/main/java/com/example/bondoman/ui/scan/ScanFragment.kt +++ b/app/src/main/java/com/example/bondoman/ui/scan/ScanFragment.kt @@ -2,6 +2,7 @@ package com.example.bondoman.ui.scan import android.Manifest import android.annotation.SuppressLint +import android.app.Activity import android.app.AlertDialog import android.content.pm.PackageManager import android.os.Bundle @@ -16,7 +17,9 @@ 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.provider.MediaStore import android.widget.Toast import androidx.activity.result.contract.ActivityResultContracts import androidx.camera.lifecycle.ProcessCameraProvider @@ -48,6 +51,10 @@ class ScanFragment : Fragment() { private lateinit var cameraExecutor: ExecutorService + private lateinit var imageView: ImageView + + private val PICK_IMAGE_REQUEST = 1 + private val activityResultLauncher = registerForActivityResult( ActivityResultContracts.RequestMultiplePermissions()) @@ -91,6 +98,8 @@ class ScanFragment : Fragment() { viewBinding.imageCaptureButton.setOnClickListener { takePhoto() } + viewBinding.imagePickerButton.setOnClickListener { pickImageFromGallery() } + cameraExecutor = Executors.newSingleThreadExecutor() } @@ -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>?) { // Inflate the layout for the dialog @@ -226,7 +256,8 @@ class ScanFragment : Fragment() { private const val TAG = "BondoMan" private val REQUIRED_PERMISSIONS = mutableListOf( - Manifest.permission.CAMERA + Manifest.permission.CAMERA, + Manifest.permission.READ_EXTERNAL_STORAGE ).toTypedArray() } } \ No newline at end of file diff --git a/app/src/main/java/com/example/bondoman/ui/twibbon/TwibbonFragment.kt b/app/src/main/java/com/example/bondoman/ui/twibbon/TwibbonFragment.kt index aff5734550c0b443e1e03c10e0f7edd8f13e2cfb..c52485de8a0681e6d091385997746b8cedfa3563 100644 --- a/app/src/main/java/com/example/bondoman/ui/twibbon/TwibbonFragment.kt +++ b/app/src/main/java/com/example/bondoman/ui/twibbon/TwibbonFragment.kt @@ -1,33 +1,80 @@ 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 androidx.fragment.app.Fragment import android.view.LayoutInflater import android.view.View import android.view.ViewGroup +import androidx.camera.core.ImageCapture +import androidx.core.content.ContextCompat 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() { // TODO: Rename and change types of parameters - private var param1: String? = null - private var param2: String? = null + private lateinit var viewBinding: FragmentTwibbonBinding + + 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?) { super.onCreate(savedInstanceState) - arguments?.let { - param1 = it.getString(ARG_PARAM1) - param2 = it.getString(ARG_PARAM2) - } } override fun onCreateView( @@ -38,23 +85,104 @@ class TwibbonFragment : Fragment() { return inflater.inflate(R.layout.fragment_twibbon, container, false) } - companion object { - /** - * Use this factory method to create a new instance of - * this fragment using the provided parameters. - * - * @param param1 Parameter 1. - * @param param2 Parameter 2. - * @return A new instance of fragment TwibbonFragment. - */ - // TODO: Rename and change types and number of parameters - @JvmStatic - fun newInstance(param1: String, param2: String) = - TwibbonFragment().apply { - arguments = Bundle().apply { - putString(ARG_PARAM1, param1) - putString(ARG_PARAM2, param2) + override fun onViewCreated(view: View, savedInstanceState: Bundle?) { + super.onViewCreated(view, savedInstanceState) + + viewBinding = FragmentTwibbonBinding.bind(view) + + if (allPermissionsGranted()){ + startCamera() + }else{ + requestPermissions() + } + + viewBinding.twibbonCaptureButton.setOnClickListener { takePhoto() } + viewBinding.changeTwibbonButton.setOnClickListener { pickTwibbonFromGallery() } + + cameraExecutor = Executors.newSingleThreadExecutor() + } + + 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 diff --git a/app/src/main/res/drawable/rounded_div.xml b/app/src/main/res/drawable/rounded_div.xml index 492e3db8ecc9786c346010f0c46e6c3cd34a269a..e655fbbbbf01bae91f47e5619bab7ff976b9b939 100644 --- a/app/src/main/res/drawable/rounded_div.xml +++ b/app/src/main/res/drawable/rounded_div.xml @@ -2,5 +2,5 @@ <shape xmlns:android="http://schemas.android.com/apk/res/android" android:shape="rectangle"> <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> diff --git a/app/src/main/res/drawable/take_photo_button.xml b/app/src/main/res/drawable/take_photo_button.xml new file mode 100644 index 0000000000000000000000000000000000000000..f8c52371980679bd6018e065e2031de712dec409 --- /dev/null +++ b/app/src/main/res/drawable/take_photo_button.xml @@ -0,0 +1,4 @@ +<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 diff --git a/app/src/main/res/layout/fragment_scan.xml b/app/src/main/res/layout/fragment_scan.xml index 9ba2001fdf45705d6a60b7a008a5cff421454e11..e200e4d5e56b28cc177a6fb4b9a35de6a7c3e43b 100644 --- a/app/src/main/res/layout/fragment_scan.xml +++ b/app/src/main/res/layout/fragment_scan.xml @@ -10,20 +10,34 @@ <androidx.camera.view.PreviewView android:id="@+id/viewFinder" android:layout_width="match_parent" - android:layout_height="match_parent" > - </androidx.camera.view.PreviewView> + android:layout_height="match_parent" + tools:layout_editor_absoluteX="16dp" + tools:layout_editor_absoluteY="0dp"/> <Button android:id="@+id/imageCaptureButton" - android:layout_width="wrap_content" - android:layout_height="wrap_content" - android:text="Button" + android:layout_width="60dp" + android:layout_height="60dp" + 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_constraintEnd_toEndOf="parent" + app:layout_constraintHorizontal_bias="0.17" app:layout_constraintStart_toStartOf="parent" app:layout_constraintTop_toTopOf="parent" - app:layout_constraintVertical_bias="0.796" /> + app:layout_constraintVertical_bias="0.831" /> <androidx.constraintlayout.widget.Guideline android:id="@+id/vertical_centerline" diff --git a/app/src/main/res/layout/fragment_twibbon.xml b/app/src/main/res/layout/fragment_twibbon.xml index 2a891590177012eeb82766ecee1891a000a2d9ef..ea8bc086dd0ac0738863ea8022af79c80cf8c163 100644 --- a/app/src/main/res/layout/fragment_twibbon.xml +++ b/app/src/main/res/layout/fragment_twibbon.xml @@ -1,16 +1,78 @@ <?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" android:layout_width="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 - android:layout_width="match_parent" - android:layout_height="match_parent" - android:text="Twibbon Fragment" - android:textSize="26sp" - android:layout_gravity="center" /> + android:id="@+id/textView2" + android:layout_width="wrap_content" + android:layout_height="wrap_content" + android:layout_marginTop="48dp" + 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> \ No newline at end of file +</androidx.constraintlayout.widget.ConstraintLayout> \ No newline at end of file diff --git a/gradle/wrapper/gradle-wrapper.properties b/gradle/wrapper/gradle-wrapper.properties index 02904965b35e9ae3b165ce4a37be9137ffdf1893..a8c69a358c4d7579f04295edb8927a99450052d2 100644 --- a/gradle/wrapper/gradle-wrapper.properties +++ b/gradle/wrapper/gradle-wrapper.properties @@ -1,6 +1,6 @@ #Mon Mar 11 18:56:50 WIB 2024 distributionBase=GRADLE_USER_HOME 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 zipStorePath=wrapper/dists