From 53c21a814eb49262f12100caa94036a34a0fbf2c Mon Sep 17 00:00:00 2001 From: Davide Depau Date: Sat, 22 Dec 2018 01:16:44 +0100 Subject: [PATCH] Structure refactor --- app/src/main/AndroidManifest.xml | 16 +-- .../java/eu/depau/etchdroid/StateKeeper.kt | 4 +- .../etchdroid/activities/LicensesActivity.kt | 72 ---------- .../adapters/LicenseRecyclerViewAdapter.kt | 43 ------ .../eu/depau/etchdroid/enums/PartitionType.kt | 3 - .../services/UsbApiDmgWriteService.kt | 8 +- .../services/UsbApiImgWriteService.kt | 6 +- .../etchdroid/services/UsbApiWriteService.kt | 6 +- .../etchdroid/services/UsbWriteService.kt | 6 +- .../{ => ui}/activities/ActivityBase.kt | 8 +- .../activities/ConfirmationActivity.kt | 12 +- .../{ => ui}/activities/ErrorActivity.kt | 2 +- .../{ => ui}/activities/StartActivity.kt | 6 +- .../activities/UsbDrivePickerActivity.kt | 14 +- .../PartitionTableRecyclerViewAdapter.kt | 4 +- .../adapters/UsbDrivesRecyclerViewAdapter.kt | 4 +- .../{utils => ui/misc}/ClickListener.kt | 2 +- .../misc}/DoNotShowAgainDialogFragment.kt | 2 +- .../{utils => ui/misc}/EmptyRecyclerView.kt | 2 +- .../{utils => ui/misc}/NightModeHelper.kt | 2 +- .../misc}/RecyclerViewTouchListener.kt | 2 +- .../java/eu/depau/etchdroid/utils/License.kt | 10 -- .../eu/depau/etchdroid/utils/Partition.kt | 4 +- .../depau/etchdroid/utils/PartitionBuilder.kt | 4 +- .../{ => utils}/enums/FilesystemType.kt | 2 +- .../{ => utils}/enums/FlashMethod.kt | 2 +- .../{ => utils}/enums/ImageLocation.kt | 2 +- .../{ => utils}/enums/PartitionTableType.kt | 2 +- .../etchdroid/utils/enums/PartitionType.kt | 3 + .../etchdroid/{ => utils}/enums/WizardStep.kt | 2 +- .../exception/CannotGetFilePathException.kt | 3 + .../utils/exception/UsbWriteException.kt | 8 ++ .../etchdroid/utils/imagetypes/DMGImage.kt | 112 +++++++++++++++ .../depau/etchdroid/utils/imagetypes/Image.kt | 10 ++ .../utils/ktexts/ContextGetBinary.kt | 56 ++++++++ .../etchdroid/utils/ktexts/ContextToast.kt | 8 ++ .../utils/ktexts/InputStreamToString.kt | 18 +++ .../utils/ktexts/NumberToSizeString.kt | 18 +++ .../utils/ktexts/NumberToTimeString.kt | 31 ++++ .../utils/ktexts/UriGetDisplayName.kt | 29 ++++ .../etchdroid/utils/ktexts/UriGetFileExt.kt | 16 +++ .../etchdroid/utils/ktexts/UriGetFilePath.kt | 135 ++++++++++++++++++ .../etchdroid/utils/ktexts/UriGetFileSize.kt | 39 +++++ .../utils/ktexts/UsbDeviceVidPidName.kt | 17 +++ .../etchdroid/utils/ktexts/ViewSnackbar.kt | 8 ++ .../main/res/layout/activity_confirmation.xml | 2 +- app/src/main/res/layout/activity_error.xml | 2 +- app/src/main/res/layout/activity_licenses.xml | 2 +- app/src/main/res/layout/activity_start.xml | 2 +- .../res/layout/activity_usb_drive_picker.xml | 2 +- app/src/main/res/menu/menu_main.xml | 2 +- 51 files changed, 579 insertions(+), 196 deletions(-) delete mode 100644 app/src/main/java/eu/depau/etchdroid/activities/LicensesActivity.kt delete mode 100644 app/src/main/java/eu/depau/etchdroid/adapters/LicenseRecyclerViewAdapter.kt delete mode 100644 app/src/main/java/eu/depau/etchdroid/enums/PartitionType.kt rename app/src/main/java/eu/depau/etchdroid/{ => ui}/activities/ActivityBase.kt (95%) rename app/src/main/java/eu/depau/etchdroid/{ => ui}/activities/ConfirmationActivity.kt (95%) rename app/src/main/java/eu/depau/etchdroid/{ => ui}/activities/ErrorActivity.kt (93%) rename app/src/main/java/eu/depau/etchdroid/{ => ui}/activities/StartActivity.kt (97%) rename app/src/main/java/eu/depau/etchdroid/{ => ui}/activities/UsbDrivePickerActivity.kt (95%) rename app/src/main/java/eu/depau/etchdroid/{ => ui}/adapters/PartitionTableRecyclerViewAdapter.kt (96%) rename app/src/main/java/eu/depau/etchdroid/{ => ui}/adapters/UsbDrivesRecyclerViewAdapter.kt (95%) rename app/src/main/java/eu/depau/etchdroid/{utils => ui/misc}/ClickListener.kt (80%) rename app/src/main/java/eu/depau/etchdroid/{utils => ui/misc}/DoNotShowAgainDialogFragment.kt (98%) rename app/src/main/java/eu/depau/etchdroid/{utils => ui/misc}/EmptyRecyclerView.kt (97%) rename app/src/main/java/eu/depau/etchdroid/{utils => ui/misc}/NightModeHelper.kt (99%) rename app/src/main/java/eu/depau/etchdroid/{utils => ui/misc}/RecyclerViewTouchListener.kt (97%) delete mode 100644 app/src/main/java/eu/depau/etchdroid/utils/License.kt rename app/src/main/java/eu/depau/etchdroid/{ => utils}/enums/FilesystemType.kt (98%) rename app/src/main/java/eu/depau/etchdroid/{ => utils}/enums/FlashMethod.kt (71%) rename app/src/main/java/eu/depau/etchdroid/{ => utils}/enums/ImageLocation.kt (55%) rename app/src/main/java/eu/depau/etchdroid/{ => utils}/enums/PartitionTableType.kt (96%) create mode 100644 app/src/main/java/eu/depau/etchdroid/utils/enums/PartitionType.kt rename app/src/main/java/eu/depau/etchdroid/{ => utils}/enums/WizardStep.kt (72%) create mode 100644 app/src/main/java/eu/depau/etchdroid/utils/exception/CannotGetFilePathException.kt create mode 100644 app/src/main/java/eu/depau/etchdroid/utils/exception/UsbWriteException.kt create mode 100644 app/src/main/java/eu/depau/etchdroid/utils/imagetypes/DMGImage.kt create mode 100644 app/src/main/java/eu/depau/etchdroid/utils/imagetypes/Image.kt create mode 100644 app/src/main/java/eu/depau/etchdroid/utils/ktexts/ContextGetBinary.kt create mode 100644 app/src/main/java/eu/depau/etchdroid/utils/ktexts/ContextToast.kt create mode 100644 app/src/main/java/eu/depau/etchdroid/utils/ktexts/InputStreamToString.kt create mode 100644 app/src/main/java/eu/depau/etchdroid/utils/ktexts/NumberToSizeString.kt create mode 100644 app/src/main/java/eu/depau/etchdroid/utils/ktexts/NumberToTimeString.kt create mode 100644 app/src/main/java/eu/depau/etchdroid/utils/ktexts/UriGetDisplayName.kt create mode 100644 app/src/main/java/eu/depau/etchdroid/utils/ktexts/UriGetFileExt.kt create mode 100644 app/src/main/java/eu/depau/etchdroid/utils/ktexts/UriGetFilePath.kt create mode 100644 app/src/main/java/eu/depau/etchdroid/utils/ktexts/UriGetFileSize.kt create mode 100644 app/src/main/java/eu/depau/etchdroid/utils/ktexts/UsbDeviceVidPidName.kt create mode 100644 app/src/main/java/eu/depau/etchdroid/utils/ktexts/ViewSnackbar.kt diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml index d0b32cf..e397b26 100644 --- a/app/src/main/AndroidManifest.xml +++ b/app/src/main/AndroidManifest.xml @@ -21,7 +21,7 @@ android:theme="@style/AppTheme" tools:ignore="GoogleAppIndexingWarning"> @@ -31,14 +31,14 @@ + android:value="eu.depau.etchdroid.ui.activities.StartActivity"/> @@ -126,17 +126,17 @@ + android:value="eu.depau.etchdroid.ui.activities.UsbDrivePickerActivity"/> - - private fun updateLicenses() { - if (!::licenses.isInitialized) { - licenses = arrayOf( - License(getString(R.string.this_app), Uri.parse("https://github.com/Depau/EtchDroid"), getString(R.string.license_gpl3)), - License("Storage Chooser 2.0", Uri.parse("https://github.com/codekidX/storage-chooser"), getString(R.string.license_mpl_2_0), getString(R.string.storagechooser_license_description)), - License("libaums (fork)", Uri.parse("https://github.com/Depau/libaums"), getString(R.string.license_apache2_0), getString(R.string.libaums_license_desc)), - License("dmg2img (fork)", Uri.parse("https://github.com/Depau/dmg2img-cmake"), getString(R.string.license_gpl2), getString(R.string.dmg2img_license_desc)), - License("bzip2", Uri.parse("https://github.com/LuaDist/bzip2/"), getString(R.string.license_bzip2)), - License("LibreSSL", Uri.parse("https://github.com/libressl-portable/portable"), getString(R.string.license_custom)) - ) - } - } - - override fun onCreate(savedInstanceState: Bundle?) { - super.onCreate(savedInstanceState) - setContentView(R.layout.activity_licenses) - updateLicenses() - - // Enable back button in action bar - supportActionBar!!.setDisplayHomeAsUpEnabled(true) - - viewManager = LinearLayoutManager(this) - recyclerView = licenses_recycler_view - viewAdapter = LicenseRecyclerViewAdapter(licenses) - recyclerView.adapter = viewAdapter - recyclerView.layoutManager = viewManager - - recyclerView.addOnItemTouchListener(RecyclerViewTouchListener(this, recyclerView, object : ClickListener { - override fun onClick(view: View, position: Int) { - val intent = Intent(Intent.ACTION_VIEW, viewAdapter.get(position).url) - startActivity(intent) - } - - override fun onLongClick(view: View, position: Int) {} - })) - } - - override fun onOptionsItemSelected(item: MenuItem?): Boolean { - when (item?.itemId) { - android.R.id.home -> { - finish() - return true - } - } - return super.onOptionsItemSelected(item) - } -} diff --git a/app/src/main/java/eu/depau/etchdroid/adapters/LicenseRecyclerViewAdapter.kt b/app/src/main/java/eu/depau/etchdroid/adapters/LicenseRecyclerViewAdapter.kt deleted file mode 100644 index 01dbffb..0000000 --- a/app/src/main/java/eu/depau/etchdroid/adapters/LicenseRecyclerViewAdapter.kt +++ /dev/null @@ -1,43 +0,0 @@ -package eu.depau.etchdroid.adapters - -import android.annotation.SuppressLint -import android.view.LayoutInflater -import android.view.View -import android.view.ViewGroup -import android.widget.RelativeLayout -import androidx.recyclerview.widget.RecyclerView -import eu.depau.etchdroid.R -import eu.depau.etchdroid.utils.License -import kotlinx.android.synthetic.main.license_row.view.* - - -class LicenseRecyclerViewAdapter(private val dataset: Array) : RecyclerView.Adapter() { - - class ViewHolder(val relLayout: RelativeLayout) : RecyclerView.ViewHolder(relLayout) - - override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): - ViewHolder { - - val relLayout = LayoutInflater.from(parent.context) - .inflate(R.layout.license_row, parent, false) as RelativeLayout - return ViewHolder(relLayout) - } - - @SuppressLint("SetTextI18n") - override fun onBindViewHolder(holder: ViewHolder, position: Int) { - val license = dataset[position] - - holder.relLayout.software_name.text = license.name - holder.relLayout.software_desc.text = license.description - holder.relLayout.software_license.text = license.license - holder.relLayout.software_url.text = license.url.toString() - - holder.relLayout.software_desc.visibility = if (license.description == null) View.GONE else View.VISIBLE - } - - override fun getItemCount(): Int = dataset.size - - fun get(position: Int): License { - return dataset[position] - } -} diff --git a/app/src/main/java/eu/depau/etchdroid/enums/PartitionType.kt b/app/src/main/java/eu/depau/etchdroid/enums/PartitionType.kt deleted file mode 100644 index ebc8d6f..0000000 --- a/app/src/main/java/eu/depau/etchdroid/enums/PartitionType.kt +++ /dev/null @@ -1,3 +0,0 @@ -package eu.depau.etchdroid.enums - -enum class PartitionType \ No newline at end of file diff --git a/app/src/main/java/eu/depau/etchdroid/services/UsbApiDmgWriteService.kt b/app/src/main/java/eu/depau/etchdroid/services/UsbApiDmgWriteService.kt index f3f7925..4c73d9b 100644 --- a/app/src/main/java/eu/depau/etchdroid/services/UsbApiDmgWriteService.kt +++ b/app/src/main/java/eu/depau/etchdroid/services/UsbApiDmgWriteService.kt @@ -5,10 +5,10 @@ import android.net.Uri import com.google.common.util.concurrent.SimpleTimeLimiter import com.google.common.util.concurrent.TimeLimiter import com.google.common.util.concurrent.UncheckedTimeoutException -import eu.depau.etchdroid.kotlin_exts.getBinary -import eu.depau.etchdroid.kotlin_exts.getFileName -import eu.depau.etchdroid.kotlin_exts.getFileSize -import eu.depau.etchdroid.kotlin_exts.name +import eu.depau.etchdroid.utils.ktexts.getBinary +import eu.depau.etchdroid.utils.ktexts.getFileName +import eu.depau.etchdroid.utils.ktexts.getFileSize +import eu.depau.etchdroid.utils.ktexts.name import java.io.BufferedReader import java.io.InputStream import java.util.concurrent.Executors diff --git a/app/src/main/java/eu/depau/etchdroid/services/UsbApiImgWriteService.kt b/app/src/main/java/eu/depau/etchdroid/services/UsbApiImgWriteService.kt index 9e86d62..5917283 100644 --- a/app/src/main/java/eu/depau/etchdroid/services/UsbApiImgWriteService.kt +++ b/app/src/main/java/eu/depau/etchdroid/services/UsbApiImgWriteService.kt @@ -2,9 +2,9 @@ package eu.depau.etchdroid.services import android.hardware.usb.UsbDevice import android.net.Uri -import eu.depau.etchdroid.kotlin_exts.getFileName -import eu.depau.etchdroid.kotlin_exts.getFileSize -import eu.depau.etchdroid.kotlin_exts.name +import eu.depau.etchdroid.utils.ktexts.getFileName +import eu.depau.etchdroid.utils.ktexts.getFileSize +import eu.depau.etchdroid.utils.ktexts.name import java.io.InputStream class UsbApiImgWriteService : UsbApiWriteService("UsbApiImgWriteService") { diff --git a/app/src/main/java/eu/depau/etchdroid/services/UsbApiWriteService.kt b/app/src/main/java/eu/depau/etchdroid/services/UsbApiWriteService.kt index ca2438f..34aeed7 100644 --- a/app/src/main/java/eu/depau/etchdroid/services/UsbApiWriteService.kt +++ b/app/src/main/java/eu/depau/etchdroid/services/UsbApiWriteService.kt @@ -5,9 +5,9 @@ import android.hardware.usb.UsbDevice import android.net.Uri import android.util.Log import com.github.mjdev.libaums.UsbMassStorageDevice -import eu.depau.etchdroid.exceptions.UsbWriteException -import eu.depau.etchdroid.kotlin_exts.getFileName -import eu.depau.etchdroid.kotlin_exts.name +import eu.depau.etchdroid.utils.exception.UsbWriteException +import eu.depau.etchdroid.utils.ktexts.getFileName +import eu.depau.etchdroid.utils.ktexts.name import java.io.BufferedInputStream import java.io.InputStream import java.nio.ByteBuffer diff --git a/app/src/main/java/eu/depau/etchdroid/services/UsbWriteService.kt b/app/src/main/java/eu/depau/etchdroid/services/UsbWriteService.kt index 9f2e0e8..39689e4 100644 --- a/app/src/main/java/eu/depau/etchdroid/services/UsbWriteService.kt +++ b/app/src/main/java/eu/depau/etchdroid/services/UsbWriteService.kt @@ -7,9 +7,9 @@ import android.os.Build import android.os.PowerManager import androidx.core.app.NotificationCompat import eu.depau.etchdroid.R -import eu.depau.etchdroid.activities.ErrorActivity -import eu.depau.etchdroid.kotlin_exts.toHRSize -import eu.depau.etchdroid.kotlin_exts.toHRTime +import eu.depau.etchdroid.ui.activities.ErrorActivity +import eu.depau.etchdroid.utils.ktexts.toHRSize +import eu.depau.etchdroid.utils.ktexts.toHRTime import java.util.* import kotlin.math.max diff --git a/app/src/main/java/eu/depau/etchdroid/activities/ActivityBase.kt b/app/src/main/java/eu/depau/etchdroid/ui/activities/ActivityBase.kt similarity index 95% rename from app/src/main/java/eu/depau/etchdroid/activities/ActivityBase.kt rename to app/src/main/java/eu/depau/etchdroid/ui/activities/ActivityBase.kt index d3e9019..a02fcb1 100644 --- a/app/src/main/java/eu/depau/etchdroid/activities/ActivityBase.kt +++ b/app/src/main/java/eu/depau/etchdroid/ui/activities/ActivityBase.kt @@ -1,4 +1,4 @@ -package eu.depau.etchdroid.activities +package eu.depau.etchdroid.ui.activities import android.Manifest import android.content.pm.PackageManager @@ -10,9 +10,9 @@ import androidx.appcompat.app.AppCompatActivity import androidx.core.app.ActivityCompat import androidx.core.content.ContextCompat import eu.depau.etchdroid.R -import eu.depau.etchdroid.kotlin_exts.toast -import eu.depau.etchdroid.utils.DoNotShowAgainDialogFragment -import eu.depau.etchdroid.utils.NightModeHelper +import eu.depau.etchdroid.utils.ktexts.toast +import eu.depau.etchdroid.ui.misc.DoNotShowAgainDialogFragment +import eu.depau.etchdroid.ui.misc.NightModeHelper import me.jfenn.attribouter.Attribouter import android.content.Intent import android.net.Uri diff --git a/app/src/main/java/eu/depau/etchdroid/activities/ConfirmationActivity.kt b/app/src/main/java/eu/depau/etchdroid/ui/activities/ConfirmationActivity.kt similarity index 95% rename from app/src/main/java/eu/depau/etchdroid/activities/ConfirmationActivity.kt rename to app/src/main/java/eu/depau/etchdroid/ui/activities/ConfirmationActivity.kt index 0bbcf06..8af296e 100644 --- a/app/src/main/java/eu/depau/etchdroid/activities/ConfirmationActivity.kt +++ b/app/src/main/java/eu/depau/etchdroid/ui/activities/ConfirmationActivity.kt @@ -1,4 +1,4 @@ -package eu.depau.etchdroid.activities +package eu.depau.etchdroid.ui.activities import android.content.Intent import android.os.Build @@ -8,13 +8,13 @@ import android.widget.Toast import androidx.recyclerview.widget.LinearLayoutManager import eu.depau.etchdroid.R import eu.depau.etchdroid.StateKeeper -import eu.depau.etchdroid.adapters.PartitionTableRecyclerViewAdapter -import eu.depau.etchdroid.enums.FlashMethod -import eu.depau.etchdroid.img_types.DMGImage -import eu.depau.etchdroid.kotlin_exts.* +import eu.depau.etchdroid.ui.adapters.PartitionTableRecyclerViewAdapter +import eu.depau.etchdroid.utils.enums.FlashMethod +import eu.depau.etchdroid.utils.imagetypes.DMGImage +import eu.depau.etchdroid.utils.ktexts.* import eu.depau.etchdroid.services.UsbApiDmgWriteService import eu.depau.etchdroid.services.UsbApiImgWriteService -import eu.depau.etchdroid.utils.DoNotShowAgainDialogFragment +import eu.depau.etchdroid.ui.misc.DoNotShowAgainDialogFragment import kotlinx.android.synthetic.main.activity_confirmation.* import java.io.IOException diff --git a/app/src/main/java/eu/depau/etchdroid/activities/ErrorActivity.kt b/app/src/main/java/eu/depau/etchdroid/ui/activities/ErrorActivity.kt similarity index 93% rename from app/src/main/java/eu/depau/etchdroid/activities/ErrorActivity.kt rename to app/src/main/java/eu/depau/etchdroid/ui/activities/ErrorActivity.kt index 50a35ed..15a14f8 100644 --- a/app/src/main/java/eu/depau/etchdroid/activities/ErrorActivity.kt +++ b/app/src/main/java/eu/depau/etchdroid/ui/activities/ErrorActivity.kt @@ -1,4 +1,4 @@ -package eu.depau.etchdroid.activities +package eu.depau.etchdroid.ui.activities import android.os.Bundle import eu.depau.etchdroid.R diff --git a/app/src/main/java/eu/depau/etchdroid/activities/StartActivity.kt b/app/src/main/java/eu/depau/etchdroid/ui/activities/StartActivity.kt similarity index 97% rename from app/src/main/java/eu/depau/etchdroid/activities/StartActivity.kt rename to app/src/main/java/eu/depau/etchdroid/ui/activities/StartActivity.kt index 1f60bbd..2e8207a 100644 --- a/app/src/main/java/eu/depau/etchdroid/activities/StartActivity.kt +++ b/app/src/main/java/eu/depau/etchdroid/ui/activities/StartActivity.kt @@ -1,4 +1,4 @@ -package eu.depau.etchdroid.activities +package eu.depau.etchdroid.ui.activities import android.content.Intent import android.content.pm.PackageManager @@ -10,8 +10,8 @@ import androidx.appcompat.app.AppCompatActivity import com.codekidlabs.storagechooser.StorageChooser import eu.depau.etchdroid.R import eu.depau.etchdroid.StateKeeper -import eu.depau.etchdroid.enums.FlashMethod -import eu.depau.etchdroid.utils.DoNotShowAgainDialogFragment +import eu.depau.etchdroid.utils.enums.FlashMethod +import eu.depau.etchdroid.ui.misc.DoNotShowAgainDialogFragment import kotlinx.android.synthetic.main.activity_start.* import java.io.File diff --git a/app/src/main/java/eu/depau/etchdroid/activities/UsbDrivePickerActivity.kt b/app/src/main/java/eu/depau/etchdroid/ui/activities/UsbDrivePickerActivity.kt similarity index 95% rename from app/src/main/java/eu/depau/etchdroid/activities/UsbDrivePickerActivity.kt rename to app/src/main/java/eu/depau/etchdroid/ui/activities/UsbDrivePickerActivity.kt index 6463dd5..621d384 100644 --- a/app/src/main/java/eu/depau/etchdroid/activities/UsbDrivePickerActivity.kt +++ b/app/src/main/java/eu/depau/etchdroid/ui/activities/UsbDrivePickerActivity.kt @@ -1,4 +1,4 @@ -package eu.depau.etchdroid.activities +package eu.depau.etchdroid.ui.activities import android.app.PendingIntent import android.content.BroadcastReceiver @@ -19,12 +19,12 @@ import androidx.swiperefreshlayout.widget.SwipeRefreshLayout import com.github.mjdev.libaums.UsbMassStorageDevice import eu.depau.etchdroid.R import eu.depau.etchdroid.StateKeeper -import eu.depau.etchdroid.adapters.UsbDrivesRecyclerViewAdapter -import eu.depau.etchdroid.enums.FlashMethod -import eu.depau.etchdroid.kotlin_exts.* -import eu.depau.etchdroid.utils.ClickListener -import eu.depau.etchdroid.utils.EmptyRecyclerView -import eu.depau.etchdroid.utils.RecyclerViewTouchListener +import eu.depau.etchdroid.ui.adapters.UsbDrivesRecyclerViewAdapter +import eu.depau.etchdroid.utils.enums.FlashMethod +import eu.depau.etchdroid.utils.ktexts.* +import eu.depau.etchdroid.ui.misc.ClickListener +import eu.depau.etchdroid.ui.misc.EmptyRecyclerView +import eu.depau.etchdroid.ui.misc.RecyclerViewTouchListener import kotlinx.android.synthetic.main.activity_usb_drive_picker.* import java.io.File diff --git a/app/src/main/java/eu/depau/etchdroid/adapters/PartitionTableRecyclerViewAdapter.kt b/app/src/main/java/eu/depau/etchdroid/ui/adapters/PartitionTableRecyclerViewAdapter.kt similarity index 96% rename from app/src/main/java/eu/depau/etchdroid/adapters/PartitionTableRecyclerViewAdapter.kt rename to app/src/main/java/eu/depau/etchdroid/ui/adapters/PartitionTableRecyclerViewAdapter.kt index d2602b8..a145c11 100644 --- a/app/src/main/java/eu/depau/etchdroid/adapters/PartitionTableRecyclerViewAdapter.kt +++ b/app/src/main/java/eu/depau/etchdroid/ui/adapters/PartitionTableRecyclerViewAdapter.kt @@ -1,11 +1,11 @@ -package eu.depau.etchdroid.adapters +package eu.depau.etchdroid.ui.adapters import android.view.LayoutInflater import android.view.ViewGroup import android.widget.LinearLayout import androidx.recyclerview.widget.RecyclerView import eu.depau.etchdroid.R -import eu.depau.etchdroid.kotlin_exts.toHRSize +import eu.depau.etchdroid.utils.ktexts.toHRSize import eu.depau.etchdroid.utils.Partition import kotlinx.android.synthetic.main.part_data_keyvalue.view.* import kotlinx.android.synthetic.main.partition_row.view.* diff --git a/app/src/main/java/eu/depau/etchdroid/adapters/UsbDrivesRecyclerViewAdapter.kt b/app/src/main/java/eu/depau/etchdroid/ui/adapters/UsbDrivesRecyclerViewAdapter.kt similarity index 95% rename from app/src/main/java/eu/depau/etchdroid/adapters/UsbDrivesRecyclerViewAdapter.kt rename to app/src/main/java/eu/depau/etchdroid/ui/adapters/UsbDrivesRecyclerViewAdapter.kt index 3ee467d..e015a94 100644 --- a/app/src/main/java/eu/depau/etchdroid/adapters/UsbDrivesRecyclerViewAdapter.kt +++ b/app/src/main/java/eu/depau/etchdroid/ui/adapters/UsbDrivesRecyclerViewAdapter.kt @@ -1,4 +1,4 @@ -package eu.depau.etchdroid.adapters +package eu.depau.etchdroid.ui.adapters import android.annotation.SuppressLint import android.os.Build @@ -8,7 +8,7 @@ import android.widget.RelativeLayout import androidx.recyclerview.widget.RecyclerView import com.github.mjdev.libaums.UsbMassStorageDevice import eu.depau.etchdroid.R -import eu.depau.etchdroid.kotlin_exts.vidpid +import eu.depau.etchdroid.utils.ktexts.vidpid import kotlinx.android.synthetic.main.usb_device_row.view.* class UsbDrivesRecyclerViewAdapter(private val dataset: Array) : RecyclerView.Adapter() { diff --git a/app/src/main/java/eu/depau/etchdroid/utils/ClickListener.kt b/app/src/main/java/eu/depau/etchdroid/ui/misc/ClickListener.kt similarity index 80% rename from app/src/main/java/eu/depau/etchdroid/utils/ClickListener.kt rename to app/src/main/java/eu/depau/etchdroid/ui/misc/ClickListener.kt index 835fd9e..6f2a0ed 100644 --- a/app/src/main/java/eu/depau/etchdroid/utils/ClickListener.kt +++ b/app/src/main/java/eu/depau/etchdroid/ui/misc/ClickListener.kt @@ -1,4 +1,4 @@ -package eu.depau.etchdroid.utils +package eu.depau.etchdroid.ui.misc import android.view.View diff --git a/app/src/main/java/eu/depau/etchdroid/utils/DoNotShowAgainDialogFragment.kt b/app/src/main/java/eu/depau/etchdroid/ui/misc/DoNotShowAgainDialogFragment.kt similarity index 98% rename from app/src/main/java/eu/depau/etchdroid/utils/DoNotShowAgainDialogFragment.kt rename to app/src/main/java/eu/depau/etchdroid/ui/misc/DoNotShowAgainDialogFragment.kt index 8b05be1..dc51347 100644 --- a/app/src/main/java/eu/depau/etchdroid/utils/DoNotShowAgainDialogFragment.kt +++ b/app/src/main/java/eu/depau/etchdroid/ui/misc/DoNotShowAgainDialogFragment.kt @@ -1,4 +1,4 @@ -package eu.depau.etchdroid.utils +package eu.depau.etchdroid.ui.misc import android.annotation.SuppressLint import android.app.Dialog diff --git a/app/src/main/java/eu/depau/etchdroid/utils/EmptyRecyclerView.kt b/app/src/main/java/eu/depau/etchdroid/ui/misc/EmptyRecyclerView.kt similarity index 97% rename from app/src/main/java/eu/depau/etchdroid/utils/EmptyRecyclerView.kt rename to app/src/main/java/eu/depau/etchdroid/ui/misc/EmptyRecyclerView.kt index b0479ce..025036a 100644 --- a/app/src/main/java/eu/depau/etchdroid/utils/EmptyRecyclerView.kt +++ b/app/src/main/java/eu/depau/etchdroid/ui/misc/EmptyRecyclerView.kt @@ -1,4 +1,4 @@ -package eu.depau.etchdroid.utils +package eu.depau.etchdroid.ui.misc import android.content.Context import android.util.AttributeSet diff --git a/app/src/main/java/eu/depau/etchdroid/utils/NightModeHelper.kt b/app/src/main/java/eu/depau/etchdroid/ui/misc/NightModeHelper.kt similarity index 99% rename from app/src/main/java/eu/depau/etchdroid/utils/NightModeHelper.kt rename to app/src/main/java/eu/depau/etchdroid/ui/misc/NightModeHelper.kt index 510313b..cff1fe5 100644 --- a/app/src/main/java/eu/depau/etchdroid/utils/NightModeHelper.kt +++ b/app/src/main/java/eu/depau/etchdroid/ui/misc/NightModeHelper.kt @@ -1,4 +1,4 @@ -package eu.depau.etchdroid.utils +package eu.depau.etchdroid.ui.misc import android.content.SharedPreferences import android.content.res.Configuration diff --git a/app/src/main/java/eu/depau/etchdroid/utils/RecyclerViewTouchListener.kt b/app/src/main/java/eu/depau/etchdroid/ui/misc/RecyclerViewTouchListener.kt similarity index 97% rename from app/src/main/java/eu/depau/etchdroid/utils/RecyclerViewTouchListener.kt rename to app/src/main/java/eu/depau/etchdroid/ui/misc/RecyclerViewTouchListener.kt index aca5810..7f17f9d 100644 --- a/app/src/main/java/eu/depau/etchdroid/utils/RecyclerViewTouchListener.kt +++ b/app/src/main/java/eu/depau/etchdroid/ui/misc/RecyclerViewTouchListener.kt @@ -1,4 +1,4 @@ -package eu.depau.etchdroid.utils +package eu.depau.etchdroid.ui.misc import android.content.Context import android.view.GestureDetector diff --git a/app/src/main/java/eu/depau/etchdroid/utils/License.kt b/app/src/main/java/eu/depau/etchdroid/utils/License.kt deleted file mode 100644 index bfe3048..0000000 --- a/app/src/main/java/eu/depau/etchdroid/utils/License.kt +++ /dev/null @@ -1,10 +0,0 @@ -package eu.depau.etchdroid.utils - -import android.net.Uri - -data class License( - val name: String, - val url: Uri, - val license: String, - val description: String? = null -) \ No newline at end of file diff --git a/app/src/main/java/eu/depau/etchdroid/utils/Partition.kt b/app/src/main/java/eu/depau/etchdroid/utils/Partition.kt index 31d2305..7ab46ea 100644 --- a/app/src/main/java/eu/depau/etchdroid/utils/Partition.kt +++ b/app/src/main/java/eu/depau/etchdroid/utils/Partition.kt @@ -1,7 +1,7 @@ package eu.depau.etchdroid.utils -import eu.depau.etchdroid.enums.FilesystemType -import eu.depau.etchdroid.enums.PartitionType +import eu.depau.etchdroid.utils.enums.FilesystemType +import eu.depau.etchdroid.utils.enums.PartitionType data class Partition( val number: Int?, diff --git a/app/src/main/java/eu/depau/etchdroid/utils/PartitionBuilder.kt b/app/src/main/java/eu/depau/etchdroid/utils/PartitionBuilder.kt index 363dbcc..901e6d2 100644 --- a/app/src/main/java/eu/depau/etchdroid/utils/PartitionBuilder.kt +++ b/app/src/main/java/eu/depau/etchdroid/utils/PartitionBuilder.kt @@ -1,7 +1,7 @@ package eu.depau.etchdroid.utils -import eu.depau.etchdroid.enums.FilesystemType -import eu.depau.etchdroid.enums.PartitionType +import eu.depau.etchdroid.utils.enums.FilesystemType +import eu.depau.etchdroid.utils.enums.PartitionType class PartitionBuilder { var number: Int? = null diff --git a/app/src/main/java/eu/depau/etchdroid/enums/FilesystemType.kt b/app/src/main/java/eu/depau/etchdroid/utils/enums/FilesystemType.kt similarity index 98% rename from app/src/main/java/eu/depau/etchdroid/enums/FilesystemType.kt rename to app/src/main/java/eu/depau/etchdroid/utils/enums/FilesystemType.kt index 43349dd..c0c26c7 100644 --- a/app/src/main/java/eu/depau/etchdroid/enums/FilesystemType.kt +++ b/app/src/main/java/eu/depau/etchdroid/utils/enums/FilesystemType.kt @@ -1,4 +1,4 @@ -package eu.depau.etchdroid.enums +package eu.depau.etchdroid.utils.enums import android.content.Context import eu.depau.etchdroid.R diff --git a/app/src/main/java/eu/depau/etchdroid/enums/FlashMethod.kt b/app/src/main/java/eu/depau/etchdroid/utils/enums/FlashMethod.kt similarity index 71% rename from app/src/main/java/eu/depau/etchdroid/enums/FlashMethod.kt rename to app/src/main/java/eu/depau/etchdroid/utils/enums/FlashMethod.kt index 42ca9ef..464f0a1 100644 --- a/app/src/main/java/eu/depau/etchdroid/enums/FlashMethod.kt +++ b/app/src/main/java/eu/depau/etchdroid/utils/enums/FlashMethod.kt @@ -1,4 +1,4 @@ -package eu.depau.etchdroid.enums +package eu.depau.etchdroid.utils.enums enum class FlashMethod { FLASH_API, diff --git a/app/src/main/java/eu/depau/etchdroid/enums/ImageLocation.kt b/app/src/main/java/eu/depau/etchdroid/utils/enums/ImageLocation.kt similarity index 55% rename from app/src/main/java/eu/depau/etchdroid/enums/ImageLocation.kt rename to app/src/main/java/eu/depau/etchdroid/utils/enums/ImageLocation.kt index aa2c410..af8f6af 100644 --- a/app/src/main/java/eu/depau/etchdroid/enums/ImageLocation.kt +++ b/app/src/main/java/eu/depau/etchdroid/utils/enums/ImageLocation.kt @@ -1,4 +1,4 @@ -package eu.depau.etchdroid.enums +package eu.depau.etchdroid.utils.enums enum class ImageLocation { REMOTE, diff --git a/app/src/main/java/eu/depau/etchdroid/enums/PartitionTableType.kt b/app/src/main/java/eu/depau/etchdroid/utils/enums/PartitionTableType.kt similarity index 96% rename from app/src/main/java/eu/depau/etchdroid/enums/PartitionTableType.kt rename to app/src/main/java/eu/depau/etchdroid/utils/enums/PartitionTableType.kt index d687bfe..45fa1c6 100644 --- a/app/src/main/java/eu/depau/etchdroid/enums/PartitionTableType.kt +++ b/app/src/main/java/eu/depau/etchdroid/utils/enums/PartitionTableType.kt @@ -1,4 +1,4 @@ -package eu.depau.etchdroid.enums +package eu.depau.etchdroid.utils.enums import android.content.Context import eu.depau.etchdroid.R diff --git a/app/src/main/java/eu/depau/etchdroid/utils/enums/PartitionType.kt b/app/src/main/java/eu/depau/etchdroid/utils/enums/PartitionType.kt new file mode 100644 index 0000000..8c75168 --- /dev/null +++ b/app/src/main/java/eu/depau/etchdroid/utils/enums/PartitionType.kt @@ -0,0 +1,3 @@ +package eu.depau.etchdroid.utils.enums + +enum class PartitionType \ No newline at end of file diff --git a/app/src/main/java/eu/depau/etchdroid/enums/WizardStep.kt b/app/src/main/java/eu/depau/etchdroid/utils/enums/WizardStep.kt similarity index 72% rename from app/src/main/java/eu/depau/etchdroid/enums/WizardStep.kt rename to app/src/main/java/eu/depau/etchdroid/utils/enums/WizardStep.kt index 645ae36..5569f1d 100644 --- a/app/src/main/java/eu/depau/etchdroid/enums/WizardStep.kt +++ b/app/src/main/java/eu/depau/etchdroid/utils/enums/WizardStep.kt @@ -1,4 +1,4 @@ -package eu.depau.etchdroid.enums +package eu.depau.etchdroid.utils.enums enum class WizardStep { SELECT_FLASH_METHOD, diff --git a/app/src/main/java/eu/depau/etchdroid/utils/exception/CannotGetFilePathException.kt b/app/src/main/java/eu/depau/etchdroid/utils/exception/CannotGetFilePathException.kt new file mode 100644 index 0000000..180b596 --- /dev/null +++ b/app/src/main/java/eu/depau/etchdroid/utils/exception/CannotGetFilePathException.kt @@ -0,0 +1,3 @@ +package eu.depau.etchdroid.utils.exception + +class CannotGetFilePathException(cause: Exception) : Exception(cause) \ No newline at end of file diff --git a/app/src/main/java/eu/depau/etchdroid/utils/exception/UsbWriteException.kt b/app/src/main/java/eu/depau/etchdroid/utils/exception/UsbWriteException.kt new file mode 100644 index 0000000..3a54fad --- /dev/null +++ b/app/src/main/java/eu/depau/etchdroid/utils/exception/UsbWriteException.kt @@ -0,0 +1,8 @@ +package eu.depau.etchdroid.utils.exception + +import eu.depau.etchdroid.utils.ktexts.toHRSize +import java.io.IOException + +class UsbWriteException(offset: Long, writtenBytes: Long, exc: Exception) : IOException( + "Write failed at block $offset, ${writtenBytes.toHRSize()} written. Error: $exc", exc +) \ No newline at end of file diff --git a/app/src/main/java/eu/depau/etchdroid/utils/imagetypes/DMGImage.kt b/app/src/main/java/eu/depau/etchdroid/utils/imagetypes/DMGImage.kt new file mode 100644 index 0000000..8c73f0a --- /dev/null +++ b/app/src/main/java/eu/depau/etchdroid/utils/imagetypes/DMGImage.kt @@ -0,0 +1,112 @@ +package eu.depau.etchdroid.utils.imagetypes + +import android.content.Context +import android.net.Uri +import eu.depau.etchdroid.utils.enums.FilesystemType +import eu.depau.etchdroid.utils.enums.PartitionTableType +import eu.depau.etchdroid.utils.ktexts.getBinary +import eu.depau.etchdroid.utils.ktexts.readString +import eu.depau.etchdroid.utils.Partition +import eu.depau.etchdroid.utils.PartitionBuilder +import java.io.File + +val SECTOR_SIZE = 512 +private val partRegex = Regex("partition (\\d+): begin=(\\d+), size=(\\d+), decoded=(\\d+), firstsector=(\\d+), sectorcount=(\\d+), blocksruncount=(\\d+)\\s+(.*) \\((.+) : \\d+\\)", RegexOption.MULTILINE) + +private fun readPartitionTable(dmg2img: File, libDir: String, file: File): Triple?, Long?> { + val pt = ArrayList() + var ptType: PartitionTableType? = null + var imgSize = 0L + + val pb = ProcessBuilder(dmg2img.path, "-v", "-l", file.path) + pb.environment()["LD_LIBRARY_PATH"] = libDir + pb.redirectErrorStream(true) + + val p = pb.start() + val out = p.inputStream.readString() + System.err.println(out) + p.waitFor() + val matches = partRegex.findAll(out) + + matchloop@ for (m in matches) { + val ( + number, begin, size, + decoded, firstsector, + sectorcount, blocksruncount, + label, type + ) = m.destructured + + val pb = PartitionBuilder() + + pb.number = number.toInt() + pb.size = SECTOR_SIZE * sectorcount.toLong() + imgSize += pb.size!! + + if (label.isNotEmpty()) + pb.fsLabel = label + + pb.partLabel = type + + when (type) { + "Apple_partition_map" -> { + ptType = PartitionTableType.MAC + } + "MBR" -> { + ptType = PartitionTableType.MSDOS + continue@matchloop + } + } + + pb.fsType = when { + type == "Apple_Empty" || type == "Apple_Scratch" || type == "Apple_Free" -> FilesystemType.FREE + type == "Apple_HFS" -> FilesystemType.HFSPLUS + type == "Apple_HFSX" -> FilesystemType.HFSPLUS + type == "Windows_FAT_32" -> FilesystemType.FAT32 + type.startsWith("Apple_") || type == "DDM" -> FilesystemType.APT_DATA + else -> FilesystemType.UNKNOWN + } + + pt.add(pb.build()) + } + + return Triple(ptType, pt, imgSize) +} + +class DMGImage(private val uri: Uri, private val context: Context) : Image { + private val dmg2img: File = context.getBinary("dmg2img") + private val libDir: String = context.applicationInfo.nativeLibraryDir + private var loaded: Boolean = false + private var partTable: List? = null + private var partTableType: PartitionTableType? = null + private var imgSize: Long? = null + + private fun readInfo() { + if (loaded) + return + val triple = readPartitionTable(dmg2img, libDir, File(uri.path)) + loaded = true + partTableType = triple.first + partTable = triple.second + imgSize = triple.third + } + + override val partitionTable: List? + get() { + readInfo() + return partTable + } + + + override val tableType: PartitionTableType? + get() { + readInfo() + return partTableType + } + + override val size: Long? + get() { + readInfo() + return imgSize + } + +} \ No newline at end of file diff --git a/app/src/main/java/eu/depau/etchdroid/utils/imagetypes/Image.kt b/app/src/main/java/eu/depau/etchdroid/utils/imagetypes/Image.kt new file mode 100644 index 0000000..93722f9 --- /dev/null +++ b/app/src/main/java/eu/depau/etchdroid/utils/imagetypes/Image.kt @@ -0,0 +1,10 @@ +package eu.depau.etchdroid.utils.imagetypes + +import eu.depau.etchdroid.utils.enums.PartitionTableType +import eu.depau.etchdroid.utils.Partition + +interface Image { + val partitionTable: List? + val tableType: PartitionTableType? + val size: Long? +} \ No newline at end of file diff --git a/app/src/main/java/eu/depau/etchdroid/utils/ktexts/ContextGetBinary.kt b/app/src/main/java/eu/depau/etchdroid/utils/ktexts/ContextGetBinary.kt new file mode 100644 index 0000000..8644ef0 --- /dev/null +++ b/app/src/main/java/eu/depau/etchdroid/utils/ktexts/ContextGetBinary.kt @@ -0,0 +1,56 @@ +package eu.depau.etchdroid.utils.ktexts + +import android.content.Context +import android.os.Build +import java.io.File +import java.io.FileOutputStream + + +private val copiedBinaries: MutableMap = HashMap() + + +fun Context.getBinary(name: String): File { + if (name in copiedBinaries.keys) + return copiedBinaries[name]!! + + val abi = if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) + Build.SUPPORTED_ABIS[0] + else + Build.CPU_ABI + + val arch = when { + abi.contains("armeabi-v7a") -> "armeabi-v7a" + abi.contains("x86_64") -> "x86_64" + abi.contains("x86") -> "x86" + abi.contains("arm64-v8a") -> "arm64-v8a" + else -> null!! + } + + val assetManager = assets + val assetIn = assetManager.open("bin/$arch/$name") + + val bin = File("$filesDir/bin/$arch/$name") + bin.parentFile?.mkdirs() + if (!bin.exists()) + bin.createNewFile() + val binOut = FileOutputStream(bin) + + // Copy executable + var size: Long = 0 + val buff = ByteArray(1024) + var nRead = assetIn.read(buff) + + while (nRead != -1) { + binOut.write(buff, 0, nRead) + size += nRead.toLong() + nRead = assetIn.read(buff) + } + assetIn.close() + binOut.close() + + bin.setExecutable(true) + + copiedBinaries[name] = bin + + return bin +} \ No newline at end of file diff --git a/app/src/main/java/eu/depau/etchdroid/utils/ktexts/ContextToast.kt b/app/src/main/java/eu/depau/etchdroid/utils/ktexts/ContextToast.kt new file mode 100644 index 0000000..03e9dfe --- /dev/null +++ b/app/src/main/java/eu/depau/etchdroid/utils/ktexts/ContextToast.kt @@ -0,0 +1,8 @@ +package eu.depau.etchdroid.utils.ktexts + +import android.content.Context +import android.widget.Toast + +fun Context.toast(message: CharSequence, duration: Int = Toast.LENGTH_LONG) { + Toast.makeText(this, message, duration).show() +} \ No newline at end of file diff --git a/app/src/main/java/eu/depau/etchdroid/utils/ktexts/InputStreamToString.kt b/app/src/main/java/eu/depau/etchdroid/utils/ktexts/InputStreamToString.kt new file mode 100644 index 0000000..6b9a752 --- /dev/null +++ b/app/src/main/java/eu/depau/etchdroid/utils/ktexts/InputStreamToString.kt @@ -0,0 +1,18 @@ +package eu.depau.etchdroid.utils.ktexts + +import java.io.ByteArrayOutputStream +import java.io.IOException +import java.io.InputStream + +@Throws(IOException::class) +fun InputStream.readString(): String { + val baos = ByteArrayOutputStream() + val buffer = ByteArray(1024) + var length = this.read(buffer) + + while (length != -1) { + baos.write(buffer, 0, length) + length = this.read(buffer) + } + return baos.toString("UTF-8") +} \ No newline at end of file diff --git a/app/src/main/java/eu/depau/etchdroid/utils/ktexts/NumberToSizeString.kt b/app/src/main/java/eu/depau/etchdroid/utils/ktexts/NumberToSizeString.kt new file mode 100644 index 0000000..1e65fab --- /dev/null +++ b/app/src/main/java/eu/depau/etchdroid/utils/ktexts/NumberToSizeString.kt @@ -0,0 +1,18 @@ +package eu.depau.etchdroid.utils.ktexts + +// https://stackoverflow.com/a/3758880/1124621 + +private fun humanReadableByteCount(bytes: T, si: Boolean = true): String where T : Comparable, T : Number { + val unit: Long = if (si) 1000 else 1024 + if (bytes.toLong() < unit) return String.format("%.1f B", bytes.toDouble()) + val exp = (Math.log(bytes.toDouble()) / Math.log(unit.toDouble())).toInt() + val pre = (if (si) "kMGTPE" else "KMGTPE")[exp - 1] + if (si) "" else "i" + return String.format("%.1f %sB", bytes.toDouble() / Math.pow(unit.toDouble(), exp.toDouble()), pre) +} + +fun Long.toHRSize(si: Boolean = true) = humanReadableByteCount(this, si) +fun Float.toHRSize(si: Boolean = true) = humanReadableByteCount(this, si) +fun Double.toHRSize(si: Boolean = true) = humanReadableByteCount(this, si) +fun Int.toHRSize(si: Boolean = true) = humanReadableByteCount(this, si) +fun Byte.toHRSize(si: Boolean = true) = humanReadableByteCount(this, si) +fun Short.toHRSize(si: Boolean = true) = humanReadableByteCount(this, si) \ No newline at end of file diff --git a/app/src/main/java/eu/depau/etchdroid/utils/ktexts/NumberToTimeString.kt b/app/src/main/java/eu/depau/etchdroid/utils/ktexts/NumberToTimeString.kt new file mode 100644 index 0000000..983c43b --- /dev/null +++ b/app/src/main/java/eu/depau/etchdroid/utils/ktexts/NumberToTimeString.kt @@ -0,0 +1,31 @@ +package eu.depau.etchdroid.utils.ktexts + +private val timeStrings = arrayOf("s", "m", "h", "d") +private val timeDivs = arrayOf(60, 60, 24) + +fun humanReadableTimeDelta(time: T): String where T : Number { + var dbTime = time.toDouble() / 1000.0 + var outString = "" + + for (i in 0..(timeDivs.size - 1)) { + val div = timeDivs[i] + val str = timeStrings[i] + + outString = "${(dbTime % div).toInt()}$str$outString" + + if (dbTime < div) + return outString + + outString = " $outString" + dbTime /= div + } + + return "${dbTime.toInt()}${timeStrings[-1]} $outString" +} + +fun Long.toHRTime() = humanReadableTimeDelta(this) +fun Float.toHRTime() = humanReadableTimeDelta(this) +fun Double.toHRTime() = humanReadableTimeDelta(this) +fun Int.toHRTime() = humanReadableTimeDelta(this) +fun Byte.toHRTime() = humanReadableTimeDelta(this) +fun Short.toHRTime() = humanReadableTimeDelta(this) \ No newline at end of file diff --git a/app/src/main/java/eu/depau/etchdroid/utils/ktexts/UriGetDisplayName.kt b/app/src/main/java/eu/depau/etchdroid/utils/ktexts/UriGetDisplayName.kt new file mode 100644 index 0000000..34d8fbf --- /dev/null +++ b/app/src/main/java/eu/depau/etchdroid/utils/ktexts/UriGetDisplayName.kt @@ -0,0 +1,29 @@ +package eu.depau.etchdroid.utils.ktexts + +import android.content.Context +import android.net.Uri +import android.provider.OpenableColumns + + +fun Uri.getFileName(context: Context): String? { + val TAG = "UriGetFileNameExt" + var result: String? = null + + if (this.scheme == "content") { + val cursor = context.getContentResolver().query(this, null, null, null, null) + cursor.use { + if (it != null && it.moveToFirst()) { + result = it.getString(it.getColumnIndex(OpenableColumns.DISPLAY_NAME)) + } + } + } + if (result == null) { + result = this.getPath() + val cut = result!!.lastIndexOf('/') + if (cut != -1) { + result = result!!.substring(cut + 1) + } + } + + return result +} \ No newline at end of file diff --git a/app/src/main/java/eu/depau/etchdroid/utils/ktexts/UriGetFileExt.kt b/app/src/main/java/eu/depau/etchdroid/utils/ktexts/UriGetFileExt.kt new file mode 100644 index 0000000..2e058a7 --- /dev/null +++ b/app/src/main/java/eu/depau/etchdroid/utils/ktexts/UriGetFileExt.kt @@ -0,0 +1,16 @@ +package eu.depau.etchdroid.utils.ktexts + +import android.content.ContentResolver +import android.net.Uri +import android.webkit.MimeTypeMap +import java.io.File + +fun Uri.getExtension(contentResolver: ContentResolver): String { + return when (scheme) { + ContentResolver.SCHEME_CONTENT -> { + val mime = MimeTypeMap.getSingleton() + mime.getExtensionFromMimeType(contentResolver.getType(this))!! + } + else -> MimeTypeMap.getFileExtensionFromUrl(Uri.fromFile(File(path)).toString()) + } +} \ No newline at end of file diff --git a/app/src/main/java/eu/depau/etchdroid/utils/ktexts/UriGetFilePath.kt b/app/src/main/java/eu/depau/etchdroid/utils/ktexts/UriGetFilePath.kt new file mode 100644 index 0000000..6981ca3 --- /dev/null +++ b/app/src/main/java/eu/depau/etchdroid/utils/ktexts/UriGetFilePath.kt @@ -0,0 +1,135 @@ +package eu.depau.etchdroid.utils.ktexts + +import android.content.ContentUris +import android.content.Context +import android.net.Uri +import android.os.Build +import android.os.Environment +import android.provider.DocumentsContract +import android.provider.MediaStore +import eu.depau.etchdroid.utils.exception.CannotGetFilePathException + + +/** + * Get a file path from a Uri. This will get the the path for Storage Access + * Framework Documents, as well as the _data field for the MediaStore and + * other file-based ContentProviders. + * + * @param context The context. + * @author paulburke + * + * https://stackoverflow.com/a/27271131/1124621 + */ +fun Uri.getFilePath(context: Context): String? { + val isKitKat = Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT + + try { + if (isKitKat && DocumentsContract.isDocumentUri(context, this)) { + // DocumentProvider + + if (isExternalStorageDocument) { + // ExternalStorageProvider + + val docId = DocumentsContract.getDocumentId(this) + val split = docId.split(":".toRegex()).dropLastWhile { it.isEmpty() }.toTypedArray() + val type = split[0] + + if (type.equals("primary", ignoreCase = true)) { + return Environment.getExternalStorageDirectory().path + "/" + split[1] + } + + // TODO handle non-primary volumes + + } else if (isDownloadsDocument) { + // DownloadsProvider + + val id = DocumentsContract.getDocumentId(this) + + if (id.startsWith("raw:/")) + return Uri.parse(id).path + + val contentUri = ContentUris.withAppendedId( + Uri.parse("content://downloads/public_downloads"), java.lang.Long.valueOf(id)) + + + return contentUri.getDataColumn(context, null, null) + + } else if (isMediaDocument) { + // MediaProvider + + val docId = DocumentsContract.getDocumentId(this) + val split = docId.split(":".toRegex()).dropLastWhile { it.isEmpty() }.toTypedArray() + val type = split[0] + + val contentUri = when (type) { + "image" -> MediaStore.Images.Media.EXTERNAL_CONTENT_URI + "video" -> MediaStore.Video.Media.EXTERNAL_CONTENT_URI + "audio" -> MediaStore.Audio.Media.EXTERNAL_CONTENT_URI + else -> null + } + + val selection = "_id=?" + val selectionArgs = arrayOf(split[1]) + + return contentUri?.getDataColumn(context, selection, selectionArgs) + } + + } else if ("content".equals(scheme, ignoreCase = true)) { + // MediaStore (and general) + + return getDataColumn(context, null, null) + + } else if ("file".equals(scheme, ignoreCase = true)) { + // File + + return path + } + } catch (e: Exception) { + // Wrap into own exception to make debugging easier + throw CannotGetFilePathException(e) + } + + return null +} + +/** + * Get the value of the data column for this Uri. This is useful for + * MediaStore Uris, and other file-based ContentProviders. + * + * @param context The context. + * @param selection (Optional) Filter used in the query. + * @param selectionArgs (Optional) Selection arguments used in the query. + * @return The value of the _data column, which is typically a file path. + */ +fun Uri.getDataColumn(context: Context, selection: String?, selectionArgs: Array?): String? { + val column = "_data" + val projection = arrayOf(column) + + context.contentResolver.query(this, projection, selection, selectionArgs, null)?.use { + if (it.moveToFirst()) { + val columnIndex = it.getColumnIndexOrThrow(column) + return it.getString(columnIndex) + } + } + + return null +} + + +/** + * Whether the Uri authority is ExternalStorageProvider. + */ +val Uri.isExternalStorageDocument: Boolean + get() = "com.android.externalstorage.documents" == authority + +/** + * Whether the Uri authority is DownloadsProvider. + */ +val Uri.isDownloadsDocument: Boolean + get() = "com.android.providers.downloads.documents" == authority + +/** + * Whether the Uri authority is MediaProvider. + */ +val Uri.isMediaDocument: Boolean + get() = "com.android.providers.media.documents" == authority \ No newline at end of file diff --git a/app/src/main/java/eu/depau/etchdroid/utils/ktexts/UriGetFileSize.kt b/app/src/main/java/eu/depau/etchdroid/utils/ktexts/UriGetFileSize.kt new file mode 100644 index 0000000..91fa893 --- /dev/null +++ b/app/src/main/java/eu/depau/etchdroid/utils/ktexts/UriGetFileSize.kt @@ -0,0 +1,39 @@ +package eu.depau.etchdroid.utils.ktexts + +import android.content.ContentResolver +import android.content.Context +import android.database.Cursor +import android.database.SQLException +import android.net.Uri +import android.provider.OpenableColumns +import java.io.File + +// https://github.com/android-rcs/rcsjta/blob/master/RI/src/com/gsma/rcs/ri/utils/FileUtils.java#L214 + +fun Uri.getFileSize(context: Context): Long { + when (this.scheme) { + ContentResolver.SCHEME_FILE -> { + val f = File(this.path) + return f.length() + } + + ContentResolver.SCHEME_CONTENT -> { + val cursor: Cursor? = context.contentResolver.query(this, null, null, null, null) + + cursor.use { + if (it == null) { + throw SQLException("Failed to query file $this") + } + return if (it.moveToFirst()) { + java.lang.Long.valueOf(it.getString(it + .getColumnIndexOrThrow(OpenableColumns.SIZE))) + } else { + throw IllegalArgumentException( + "Error in retrieving this size form the URI") + } + } + } + + else -> throw IllegalArgumentException("Unsupported URI scheme") + } +} \ No newline at end of file diff --git a/app/src/main/java/eu/depau/etchdroid/utils/ktexts/UsbDeviceVidPidName.kt b/app/src/main/java/eu/depau/etchdroid/utils/ktexts/UsbDeviceVidPidName.kt new file mode 100644 index 0000000..b0cd036 --- /dev/null +++ b/app/src/main/java/eu/depau/etchdroid/utils/ktexts/UsbDeviceVidPidName.kt @@ -0,0 +1,17 @@ +package eu.depau.etchdroid.utils.ktexts + +import android.hardware.usb.UsbDevice +import android.os.Build + +fun formatID(id: Int): String = Integer.toHexString(id).padStart(4, '0') + +val UsbDevice.vidpid: String + get() = "${formatID(this.vendorId)}:${formatID(this.productId)}" + + +val UsbDevice.name: String + get() = if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) { + "${this.manufacturerName} ${this.productName}" + } else { + this.deviceName + } \ No newline at end of file diff --git a/app/src/main/java/eu/depau/etchdroid/utils/ktexts/ViewSnackbar.kt b/app/src/main/java/eu/depau/etchdroid/utils/ktexts/ViewSnackbar.kt new file mode 100644 index 0000000..9e67127 --- /dev/null +++ b/app/src/main/java/eu/depau/etchdroid/utils/ktexts/ViewSnackbar.kt @@ -0,0 +1,8 @@ +package eu.depau.etchdroid.utils.ktexts + +import android.view.View +import com.google.android.material.snackbar.Snackbar + +fun View.snackbar(message: CharSequence, duration: Int = Snackbar.LENGTH_LONG) { + Snackbar.make(this, message, duration).show() +} \ No newline at end of file diff --git a/app/src/main/res/layout/activity_confirmation.xml b/app/src/main/res/layout/activity_confirmation.xml index 283aabd..13f5458 100644 --- a/app/src/main/res/layout/activity_confirmation.xml +++ b/app/src/main/res/layout/activity_confirmation.xml @@ -7,7 +7,7 @@ android:layout_width="match_parent" android:layout_height="match_parent" android:theme="@style/DarkThemeOverlay" - tools:context=".activities.ConfirmationActivity"> + tools:context=".ui.activities.ConfirmationActivity"> + tools:context=".ui.activities.ErrorActivity"> + tools:context=".ui.activities.LicensesActivity"> + tools:context=".ui.activities.StartActivity"> + tools:context=".ui.activities.UsbDrivePickerActivity"> + tools:context=".ui.activities.StartActivity">