diff --git a/PROJECT_STATE.md b/PROJECT_STATE.md index b1d3f51..f12df8f 100644 --- a/PROJECT_STATE.md +++ b/PROJECT_STATE.md @@ -1,4 +1,4 @@ -## [v0.3.0] - Streaming upload rewrite +## [v0.3.3] - Streaming upload rewrite ### Upload behavior - Uses HttpURLConnection chunked streaming mode @@ -39,4 +39,4 @@ - Long video upload runs can still hit OutOfMemoryError ### Next target -- v0.3.0 streaming upload rewrite +- v0.3.3 streaming upload rewrite diff --git a/README.md b/README.md index 6654abb..bdbdd6f 100644 --- a/README.md +++ b/README.md @@ -1,9 +1,9 @@ # OTB Cloud Android Client ## Version -v0.3.0 +v0.3.3 -## v0.3.0 notes +## v0.3.3 notes - Reworked upload path to use chunked streaming mode - Forces per-file HTTP connection teardown - Keeps batched upload handling diff --git a/app/build.gradle b/app/build.gradle index 2c3779b..8945b1b 100644 --- a/app/build.gradle +++ b/app/build.gradle @@ -11,8 +11,8 @@ android { applicationId "top.outsidethebox.otbcloud" minSdk 23 targetSdk 34 - versionCode 30 - versionName "0.3.0" + versionCode 33 + versionName "0.3.3" testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner" } diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml index 0b14d8a..65b3ee1 100644 --- a/app/src/main/AndroidManifest.xml +++ b/app/src/main/AndroidManifest.xml @@ -17,7 +17,7 @@ diff --git a/app/src/main/java/top/outsidethebox/otbcloud/MainActivity.kt b/app/src/main/java/top/outsidethebox/otbcloud/MainActivity.kt index 43e1b68..2f7d284 100644 --- a/app/src/main/java/top/outsidethebox/otbcloud/MainActivity.kt +++ b/app/src/main/java/top/outsidethebox/otbcloud/MainActivity.kt @@ -31,6 +31,10 @@ class MainActivity : AppCompatActivity() { private lateinit var binding: ActivityMainBinding private val executor = Executors.newSingleThreadExecutor() private val mediaItems = mutableListOf() + private var uploadMode = UploadMode.ALL + private var lastXCount = 0 + + private lateinit var mediaAdapter: ArrayAdapter companion object { @@ -49,7 +53,7 @@ class MainActivity : AppCompatActivity() { private const val REQ_READ_STORAGE = 2001 - // v0.3.0 upload rewrite defaults + // v0.3.3 upload rewrite defaults private const val VIDEO_BATCH_SIZE = 2 private const val IMAGE_BATCH_SIZE = 25 private const val PAUSE_BETWEEN_FILES_MS = 350L @@ -65,6 +69,12 @@ class MainActivity : AppCompatActivity() { BOTH } + enum class UploadMode { + ALL, + SELECTED, + LAST_X + } + data class MediaItem( val path: String, val displayName: String, @@ -118,12 +128,7 @@ class MainActivity : AppCompatActivity() { } binding.uploadSelectedButton.setOnClickListener { - val selected = getSelectedItems() - if (selected.isEmpty()) { - Toast.makeText(this, "No media selected", Toast.LENGTH_SHORT).show() - return@setOnClickListener - } - uploadSelectedMedia(selected) + showUploadModeDialog() } binding.resetActivationButton.setOnClickListener { @@ -604,7 +609,60 @@ class MainActivity : AppCompatActivity() { } } - private fun getSelectedItems(): List { + + private fun showUploadModeDialog() { + val options = arrayOf("All Media", "Selected Media", "Last X Media") + + android.app.AlertDialog.Builder(this) + .setTitle("Upload Mode") + .setItems(options) { _, which -> + when (which) { + 0 -> { + uploadMode = UploadMode.ALL + uploadSelectedMedia(getItemsForUpload()) + } + 1 -> { + uploadMode = UploadMode.SELECTED + uploadSelectedMedia(getItemsForUpload()) + } + 2 -> { + promptForLastX() + } + } + } + .show() + } + + private fun promptForLastX() { + val input = android.widget.EditText(this) + input.inputType = android.text.InputType.TYPE_CLASS_NUMBER + + android.app.AlertDialog.Builder(this) + .setTitle("Last X Media") + .setMessage("Enter number of most recent items to upload") + .setView(input) + .setPositiveButton("OK") { _, _ -> + lastXCount = input.text.toString().toIntOrNull() ?: 0 + if (lastXCount > 0) { + uploadMode = UploadMode.LAST_X + uploadSelectedMedia(getItemsForUpload()) + } else { + Toast.makeText(this, "Invalid number", Toast.LENGTH_SHORT).show() + } + } + .setNegativeButton("Cancel", null) + .show() + } + + private fun getItemsForUpload(): List { + return when (uploadMode) { + UploadMode.ALL -> mediaItems + UploadMode.SELECTED -> getSelectedItems() + UploadMode.LAST_X -> mediaItems.take(lastXCount) + } + } + +private fun getSelectedItems(): List { val selected = mutableListOf() for (i in mediaItems.indices) { if (binding.mediaListView.isItemChecked(i)) { diff --git a/app/src/main/res/layout/activity_main.xml b/app/src/main/res/layout/activity_main.xml index ae70eab..9174039 100644 --- a/app/src/main/res/layout/activity_main.xml +++ b/app/src/main/res/layout/activity_main.xml @@ -15,7 +15,7 @@ android:id="@+id/titleText" android:layout_width="wrap_content" android:layout_height="wrap_content" - android:text="OTB Cloud v0.3.0" + android:text="OTB Cloud v0.3.3" android:textColor="#FFFFFF" android:textSize="28sp" android:textStyle="bold" @@ -25,7 +25,7 @@ android:id="@+id/subtitleText" android:layout_width="wrap_content" android:layout_height="wrap_content" - android:text="Android Backup Client v0.3.0" + android:text="Android Backup Client v0.3.3" android:textColor="#B8C7E0" android:textSize="16sp" android:layout_marginTop="8dp" @@ -78,6 +78,7 @@ android:text="Use New Token" android:layout_marginBottom="12dp" /> +