Skip to content

Commit

Permalink
WIP
Browse files Browse the repository at this point in the history
  • Loading branch information
alexbakker committed Oct 19, 2024
1 parent 9f7d185 commit dccd80b
Show file tree
Hide file tree
Showing 14 changed files with 317 additions and 17 deletions.
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
*.iml
.gradle
.kotlin/
/local.properties
/.idea
.DS_Store
Expand Down
2 changes: 1 addition & 1 deletion app/build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -140,10 +140,10 @@ dependencies {
implementation "com.mikepenz:aboutlibraries-core:$aboutLibrariesVersion"
implementation 'com.squareup.okhttp3:okhttp:4.12.0'
implementation 'com.squareup.okhttp3:logging-interceptor:4.12.0'
implementation 'io.github.rburgst:okhttp-digest:3.1.0'
implementation 'com.squareup.retrofit2:retrofit:2.11.0'
implementation 'com.squareup.retrofit2:converter-simplexml:2.11.0'
implementation 'com.github.thegrizzlylabs:sardine-android:0.9'
implementation 'io.github.rburgst:okhttp-digest:3.1.0'
implementation "org.jetbrains.kotlin:kotlin-stdlib:$kotlinVersion"
implementation 'org.jetbrains.kotlinx:kotlinx-coroutines-android:1.9.0'
kapt "androidx.room:room-compiler:$roomVersion"
Expand Down
176 changes: 176 additions & 0 deletions app/schemas/dev.rocli.android.webdav.data.AppDatabase/5.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,176 @@
{
"formatVersion": 1,
"database": {
"version": 5,
"identityHash": "c98cd93c63f8ee003926006b68582ecf",
"entities": [
{
"tableName": "account",
"createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `name` TEXT, `url` TEXT, `protocol` TEXT NOT NULL DEFAULT 'AUTO', `verify_certs` INTEGER NOT NULL, `auth_type` TEXT NOT NULL DEFAULT 'NONE', `username` TEXT, `password` TEXT, `client_cert` TEXT, `max_cache_file_size` INTEGER NOT NULL, `act_as_local_storage` INTEGER NOT NULL DEFAULT false)",
"fields": [
{
"fieldPath": "id",
"columnName": "id",
"affinity": "INTEGER",
"notNull": true
},
{
"fieldPath": "name",
"columnName": "name",
"affinity": "TEXT",
"notNull": false
},
{
"fieldPath": "url",
"columnName": "url",
"affinity": "TEXT",
"notNull": false
},
{
"fieldPath": "protocol",
"columnName": "protocol",
"affinity": "TEXT",
"notNull": true,
"defaultValue": "'AUTO'"
},
{
"fieldPath": "verifyCerts",
"columnName": "verify_certs",
"affinity": "INTEGER",
"notNull": true
},
{
"fieldPath": "authType",
"columnName": "auth_type",
"affinity": "TEXT",
"notNull": true,
"defaultValue": "'NONE'"
},
{
"fieldPath": "username",
"columnName": "username",
"affinity": "TEXT",
"notNull": false
},
{
"fieldPath": "password",
"columnName": "password",
"affinity": "TEXT",
"notNull": false
},
{
"fieldPath": "clientCert",
"columnName": "client_cert",
"affinity": "TEXT",
"notNull": false
},
{
"fieldPath": "maxCacheFileSize",
"columnName": "max_cache_file_size",
"affinity": "INTEGER",
"notNull": true
},
{
"fieldPath": "actAsLocalStorage",
"columnName": "act_as_local_storage",
"affinity": "INTEGER",
"notNull": true,
"defaultValue": "false"
}
],
"primaryKey": {
"autoGenerate": true,
"columnNames": [
"id"
]
},
"indices": [],
"foreignKeys": []
},
{
"tableName": "cache_entry",
"createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `account_id` INTEGER NOT NULL, `status` TEXT NOT NULL, `path` TEXT NOT NULL, `etag` TEXT, `content_length` INTEGER, `last_modified` INTEGER, FOREIGN KEY(`account_id`) REFERENCES `account`(`id`) ON UPDATE NO ACTION ON DELETE CASCADE )",
"fields": [
{
"fieldPath": "id",
"columnName": "id",
"affinity": "INTEGER",
"notNull": true
},
{
"fieldPath": "accountId",
"columnName": "account_id",
"affinity": "INTEGER",
"notNull": true
},
{
"fieldPath": "status",
"columnName": "status",
"affinity": "TEXT",
"notNull": true
},
{
"fieldPath": "path",
"columnName": "path",
"affinity": "TEXT",
"notNull": true
},
{
"fieldPath": "etag",
"columnName": "etag",
"affinity": "TEXT",
"notNull": false
},
{
"fieldPath": "contentLength",
"columnName": "content_length",
"affinity": "INTEGER",
"notNull": false
},
{
"fieldPath": "lastModified",
"columnName": "last_modified",
"affinity": "INTEGER",
"notNull": false
}
],
"primaryKey": {
"autoGenerate": true,
"columnNames": [
"id"
]
},
"indices": [
{
"name": "index_cache_entry_account_id_path",
"unique": true,
"columnNames": [
"account_id",
"path"
],
"orders": [],
"createSql": "CREATE UNIQUE INDEX IF NOT EXISTS `index_cache_entry_account_id_path` ON `${TABLE_NAME}` (`account_id`, `path`)"
}
],
"foreignKeys": [
{
"table": "account",
"onDelete": "CASCADE",
"onUpdate": "NO ACTION",
"columns": [
"account_id"
],
"referencedColumns": [
"id"
]
}
]
}
],
"views": [],
"setupQueries": [
"CREATE TABLE IF NOT EXISTS room_master_table (id INTEGER PRIMARY KEY,identity_hash TEXT)",
"INSERT OR REPLACE INTO room_master_table (id,identity_hash) VALUES(42, 'c98cd93c63f8ee003926006b68582ecf')"
]
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -54,13 +54,12 @@ class DocumentsProviderTest(private val testName: String, private val account: A
@Parameterized.Parameters(name = "{0}")
fun params(): Collection<Array<Any>> {
return listOf(
arrayOf("hacdias", Account(
/*arrayOf("hacdias", Account(
name = "Hacdias",
url = "http://${HOST}:8001",
username = SecretString("test"),
password = SecretString("test")
)
),
)),
arrayOf("nginx", Account(
name = "Nginx",
url = "http://${HOST}:8002"
Expand All @@ -78,6 +77,12 @@ class DocumentsProviderTest(private val testName: String, private val account: A
arrayOf("apache-subpath", Account(
name = "Apache (subpath) proxied through Nginx",
url = "http://${HOST}:8005/webdav"
)),*/
arrayOf("apache-digest", Account(
name = "Apache using digest auth",
url = "http://${HOST}:8006",
username = SecretString("test"),
password = SecretString("test")
)),
)
}
Expand Down
9 changes: 9 additions & 0 deletions app/src/main/java/dev/rocli/android/webdav/data/Account.kt
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,9 @@ data class Account(
@ColumnInfo(name = "verify_certs")
var verifyCerts: Boolean = true,

@ColumnInfo(name = "auth_type", defaultValue = "NONE")
var authType: AuthType = AuthType.NONE,

@ColumnInfo(name = "username")
var username: SecretString? = null,

Expand Down Expand Up @@ -73,6 +76,12 @@ data class Account(
enum class Protocol {
AUTO, HTTP1
}

enum class AuthType {
NONE,
BASIC,
DIGEST
}
}

fun List<Account>.byId(id: Long): Account {
Expand Down
13 changes: 11 additions & 2 deletions app/src/main/java/dev/rocli/android/webdav/data/AppDatabase.kt
Original file line number Diff line number Diff line change
Expand Up @@ -4,19 +4,28 @@ import androidx.room.AutoMigration
import androidx.room.Database
import androidx.room.RoomDatabase
import androidx.room.TypeConverters
import androidx.room.migration.AutoMigrationSpec
import androidx.sqlite.db.SupportSQLiteDatabase

@Database(
version = 4,
version = 5,
exportSchema = true,
entities = [Account::class, CacheEntry::class],
autoMigrations = [
AutoMigration(from = 1, to = 2),
AutoMigration(from = 2, to = 3),
AutoMigration(from = 3, to = 4)
AutoMigration(from = 3, to = 4),
AutoMigration(from = 4, to = 5, spec = AppDatabase.AuthTypeMigration::class)
]
)
@TypeConverters(SecretStringConverter::class)
abstract class AppDatabase : RoomDatabase() {
abstract fun accountDao(): AccountDao
abstract fun cacheDao(): CacheDao

class AuthTypeMigration : AutoMigrationSpec {
override fun onPostMigrate(db: SupportSQLiteDatabase) {
db.execSQL("UPDATE account SET auth_type = 'BASIC' WHERE username IS NOT NULL OR password IS NOT NULL")
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ import android.widget.Toast
import androidx.activity.OnBackPressedCallback
import androidx.appcompat.app.AppCompatActivity
import androidx.core.view.children
import androidx.core.widget.doAfterTextChanged
import androidx.databinding.BindingAdapter
import androidx.databinding.DataBindingUtil
import androidx.databinding.InverseBindingAdapter
Expand All @@ -29,8 +30,6 @@ import com.google.android.material.snackbar.Snackbar
import com.google.android.material.textfield.TextInputEditText
import com.google.android.material.textfield.TextInputLayout
import dagger.hilt.android.AndroidEntryPoint
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.launch
import dev.rocli.android.webdav.R
import dev.rocli.android.webdav.data.Account
import dev.rocli.android.webdav.data.AccountDao
Expand All @@ -41,6 +40,8 @@ import dev.rocli.android.webdav.provider.WebDavCache
import dev.rocli.android.webdav.provider.WebDavClientManager
import dev.rocli.android.webdav.provider.WebDavPath
import dev.rocli.android.webdav.provider.WebDavProvider
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.launch
import okhttp3.HttpUrl.Companion.toHttpUrl
import javax.inject.Inject

Expand Down Expand Up @@ -71,8 +72,19 @@ class AccountFragment : Fragment() {
binding.account = Account()
}

val adapter = ArrayAdapter.createFromResource(requireContext(), R.array.protocol_options, R.layout.dropdown_list_item)
binding.dropdownProtocol.setAdapter(adapter)
val protocolAdapter = ArrayAdapter.createFromResource(requireContext(), R.array.protocol_options, R.layout.dropdown_list_item)
binding.dropdownProtocol.setAdapter(protocolAdapter)

val authTypeAdapter = ArrayAdapter.createFromResource(requireContext(), R.array.auth_type_options, R.layout.dropdown_list_item)
binding.dropdownAuthType.setAdapter(authTypeAdapter)
binding.dropdownAuthType.doAfterTextChanged {
binding.account?.let {
it.username = null
it.password = null
}
updateUserPassVisibility()
}
updateUserPassVisibility()

if (binding.account!!.clientCert.isNullOrBlank()) {
binding.textLayoutCertificate.setEndIconDrawable(R.drawable.ic_outline_add_24)
Expand Down Expand Up @@ -202,6 +214,13 @@ class AccountFragment : Fragment() {
return true
}

private fun updateUserPassVisibility() {
val visibility = if (binding.account!!.authType == Account.AuthType.NONE)
View.GONE else View.VISIBLE
binding.textUsername.visibility = visibility
binding.textPassword.visibility = visibility
}

private fun validateForm(clientCert: Boolean = false): Boolean {
var res = true
if (binding.textName.text.toString().isBlank()) {
Expand Down Expand Up @@ -306,7 +325,7 @@ fun Slider.getSliderValueLong(): Long {
}

@BindingAdapter("android:text")
fun <T : Enum<T>> AutoCompleteTextView.setDropdownValueEnum(newValue: T) {
fun AutoCompleteTextView.setDropdownValueProtocol(newValue: Account.Protocol) {
val array = this.resources!!.getStringArray(R.array.protocol_options)
val text = array[newValue.ordinal]
if (this.text.toString() != text) {
Expand All @@ -320,6 +339,21 @@ fun AutoCompleteTextView.getDropdownValueProtocol(): Account.Protocol {
return Account.Protocol.entries[array.indexOf(this.text.toString())]
}

@BindingAdapter("android:text")
fun AutoCompleteTextView.setDropdownValueAuthType(newValue: Account.AuthType) {
val array = this.resources!!.getStringArray(R.array.auth_type_options)
val text = array[newValue.ordinal]
if (this.text.toString() != text) {
this.setText(text, false)
}
}

@InverseBindingAdapter(attribute = "android:text")
fun AutoCompleteTextView.getDropdownValueAuthType(): Account.AuthType {
val array = this.resources!!.getStringArray(R.array.auth_type_options)
return Account.AuthType.entries[array.indexOf(this.text.toString())]
}

@BindingAdapter("android:text")
fun TextInputEditText.setSecretStringValue(newValue: SecretString?) {
this.setText(newValue?.value)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -249,7 +249,6 @@ class WebDavClient(

builder.authenticator(CachingAuthenticatorDecorator(authenticator, authCache))
builder.addInterceptor(AuthenticationCacheInterceptor(authCache))

}

val serializer = buildSerializer()
Expand Down
Loading

0 comments on commit dccd80b

Please sign in to comment.