Compare commits

...

1 Commits
v0.3.0 ... main

  1. 4
      PROJECT_STATE.md
  2. 4
      README.md
  3. 4
      app/build.gradle
  4. 2
      app/src/main/AndroidManifest.xml
  5. 72
      app/src/main/java/top/outsidethebox/otbcloud/MainActivity.kt
  6. 26
      app/src/main/res/layout/activity_main.xml

4
PROJECT_STATE.md

@ -1,4 +1,4 @@
## [v0.3.0] - Streaming upload rewrite ## [v0.3.3] - Streaming upload rewrite
### Upload behavior ### Upload behavior
- Uses HttpURLConnection chunked streaming mode - Uses HttpURLConnection chunked streaming mode
@ -39,4 +39,4 @@
- Long video upload runs can still hit OutOfMemoryError - Long video upload runs can still hit OutOfMemoryError
### Next target ### Next target
- v0.3.0 streaming upload rewrite - v0.3.3 streaming upload rewrite

4
README.md

@ -1,9 +1,9 @@
# OTB Cloud Android Client # OTB Cloud Android Client
## Version ## Version
v0.3.0 v0.3.3
## v0.3.0 notes ## v0.3.3 notes
- Reworked upload path to use chunked streaming mode - Reworked upload path to use chunked streaming mode
- Forces per-file HTTP connection teardown - Forces per-file HTTP connection teardown
- Keeps batched upload handling - Keeps batched upload handling

4
app/build.gradle

@ -11,8 +11,8 @@ android {
applicationId "top.outsidethebox.otbcloud" applicationId "top.outsidethebox.otbcloud"
minSdk 23 minSdk 23
targetSdk 34 targetSdk 34
versionCode 30 versionCode 33
versionName "0.3.0" versionName "0.3.3"
testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner" testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"
} }

2
app/src/main/AndroidManifest.xml

@ -17,7 +17,7 @@
<application <application
android:allowBackup="true" android:allowBackup="true"
android:icon="@drawable/favicon" android:icon="@drawable/favicon"
android:label="OTB Cloud v0.3.0" android:label="OTB Cloud v0.3.3"
android:supportsRtl="true" android:supportsRtl="true"
android:theme="@style/Theme.OtbCloud"> android:theme="@style/Theme.OtbCloud">

72
app/src/main/java/top/outsidethebox/otbcloud/MainActivity.kt

@ -31,6 +31,10 @@ class MainActivity : AppCompatActivity() {
private lateinit var binding: ActivityMainBinding private lateinit var binding: ActivityMainBinding
private val executor = Executors.newSingleThreadExecutor() private val executor = Executors.newSingleThreadExecutor()
private val mediaItems = mutableListOf<MediaItem>() private val mediaItems = mutableListOf<MediaItem>()
private var uploadMode = UploadMode.ALL
private var lastXCount = 0
private lateinit var mediaAdapter: ArrayAdapter<String> private lateinit var mediaAdapter: ArrayAdapter<String>
companion object { companion object {
@ -49,7 +53,7 @@ class MainActivity : AppCompatActivity() {
private const val REQ_READ_STORAGE = 2001 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 VIDEO_BATCH_SIZE = 2
private const val IMAGE_BATCH_SIZE = 25 private const val IMAGE_BATCH_SIZE = 25
private const val PAUSE_BETWEEN_FILES_MS = 350L private const val PAUSE_BETWEEN_FILES_MS = 350L
@ -65,6 +69,12 @@ class MainActivity : AppCompatActivity() {
BOTH BOTH
} }
enum class UploadMode {
ALL,
SELECTED,
LAST_X
}
data class MediaItem( data class MediaItem(
val path: String, val path: String,
val displayName: String, val displayName: String,
@ -118,12 +128,7 @@ class MainActivity : AppCompatActivity() {
} }
binding.uploadSelectedButton.setOnClickListener { binding.uploadSelectedButton.setOnClickListener {
val selected = getSelectedItems() showUploadModeDialog()
if (selected.isEmpty()) {
Toast.makeText(this, "No media selected", Toast.LENGTH_SHORT).show()
return@setOnClickListener
}
uploadSelectedMedia(selected)
} }
binding.resetActivationButton.setOnClickListener { binding.resetActivationButton.setOnClickListener {
@ -604,6 +609,59 @@ class MainActivity : AppCompatActivity() {
} }
} }
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<MediaItem> {
return when (uploadMode) {
UploadMode.ALL -> mediaItems
UploadMode.SELECTED -> getSelectedItems()
UploadMode.LAST_X -> mediaItems.take(lastXCount)
}
}
private fun getSelectedItems(): List<MediaItem> { private fun getSelectedItems(): List<MediaItem> {
val selected = mutableListOf<MediaItem>() val selected = mutableListOf<MediaItem>()
for (i in mediaItems.indices) { for (i in mediaItems.indices) {

26
app/src/main/res/layout/activity_main.xml

@ -15,7 +15,7 @@
android:id="@+id/titleText" android:id="@+id/titleText"
android:layout_width="wrap_content" android:layout_width="wrap_content"
android:layout_height="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:textColor="#FFFFFF"
android:textSize="28sp" android:textSize="28sp"
android:textStyle="bold" android:textStyle="bold"
@ -25,7 +25,7 @@
android:id="@+id/subtitleText" android:id="@+id/subtitleText"
android:layout_width="wrap_content" android:layout_width="wrap_content"
android:layout_height="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:textColor="#B8C7E0"
android:textSize="16sp" android:textSize="16sp"
android:layout_marginTop="8dp" android:layout_marginTop="8dp"
@ -78,6 +78,7 @@
android:text="Use New Token" android:text="Use New Token"
android:layout_marginBottom="12dp" /> android:layout_marginBottom="12dp" />
<Button <Button
android:id="@+id/scanButton" android:id="@+id/scanButton"
android:layout_width="match_parent" android:layout_width="match_parent"
@ -121,7 +122,7 @@
android:id="@+id/uploadSelectedButton" android:id="@+id/uploadSelectedButton"
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:text="Upload Selected" /> android:text="Upload" />
<TextView <TextView
android:id="@+id/selectionInfoText" android:id="@+id/selectionInfoText"
@ -133,6 +134,17 @@
android:layout_marginTop="12dp" android:layout_marginTop="12dp"
android:layout_marginBottom="12dp" /> android:layout_marginBottom="12dp" />
<TextView
android:id="@+id/statusText"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="Ready"
android:textColor="#7CC7F5"
android:textSize="14sp"
android:layout_marginTop="0dp"
android:layout_marginBottom="12dp" />
<ListView <ListView
android:id="@+id/mediaListView" android:id="@+id/mediaListView"
android:layout_width="match_parent" android:layout_width="match_parent"
@ -143,14 +155,6 @@
android:dividerHeight="1dp" /> android:dividerHeight="1dp" />
</LinearLayout> </LinearLayout>
<TextView
android:id="@+id/statusText"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="Ready"
android:textColor="#B8C7E0"
android:textSize="14sp"
android:layout_marginTop="16dp" />
</LinearLayout> </LinearLayout>
</ScrollView> </ScrollView>

Loading…
Cancel
Save