mirror of
https://github.com/home-assistant/android
synced 2024-11-05 18:28:42 +00:00
Authentication Dialog Enhancement (#633)
* Authentication Dialog Enhancement * Add "Remember" checkbox * Add button for password visibility
This commit is contained in:
parent
f419fca874
commit
50d80413ce
12 changed files with 218 additions and 25 deletions
|
@ -114,6 +114,10 @@ dependencies {
|
|||
implementation(Config.Dependency.AndroidX.preference)
|
||||
implementation(Config.Dependency.Google.material)
|
||||
|
||||
implementation(Config.Dependency.AndroidX.roomRuntime)
|
||||
implementation(Config.Dependency.AndroidX.roomKtx)
|
||||
kapt(Config.Dependency.AndroidX.roomCompiler)
|
||||
|
||||
implementation(Config.Dependency.Misc.threeTenAbp) {
|
||||
exclude(group = "org.threeten")
|
||||
}
|
||||
|
|
|
@ -0,0 +1,9 @@
|
|||
package io.homeassistant.companion.android.database
|
||||
|
||||
import androidx.room.Database
|
||||
import androidx.room.RoomDatabase
|
||||
|
||||
@Database(entities = [AuthenticationList::class], version = 1)
|
||||
abstract class AppDataBase : RoomDatabase() {
|
||||
abstract fun authenticationDatabaseDao(): AuthenticationDataBaseDao
|
||||
}
|
|
@ -0,0 +1,22 @@
|
|||
package io.homeassistant.companion.android.database
|
||||
|
||||
import androidx.room.Dao
|
||||
import androidx.room.Insert
|
||||
import androidx.room.Query
|
||||
import androidx.room.Update
|
||||
|
||||
@Dao
|
||||
interface AuthenticationDataBaseDao {
|
||||
|
||||
@Insert
|
||||
fun insert(authentication: AuthenticationList)
|
||||
|
||||
@Update
|
||||
fun update(authentication: AuthenticationList)
|
||||
|
||||
@Query("SELECT * from Authentication_List WHERE Host = :key")
|
||||
fun get(key: String): AuthenticationList?
|
||||
|
||||
@Query("DELETE FROM Authentication_List")
|
||||
fun clear()
|
||||
}
|
|
@ -0,0 +1,17 @@
|
|||
package io.homeassistant.companion.android.database
|
||||
|
||||
import androidx.room.ColumnInfo
|
||||
import androidx.room.Entity
|
||||
import androidx.room.PrimaryKey
|
||||
|
||||
@Entity(tableName = "Authentication_List")
|
||||
data class AuthenticationList(
|
||||
@PrimaryKey
|
||||
var host: String,
|
||||
|
||||
@ColumnInfo(name = "Username")
|
||||
val username: String,
|
||||
|
||||
@ColumnInfo(name = "Password")
|
||||
var password: String
|
||||
)
|
|
@ -1,7 +1,6 @@
|
|||
package io.homeassistant.companion.android.webview
|
||||
|
||||
import android.net.http.SslError
|
||||
import android.webkit.HttpAuthHandler
|
||||
|
||||
interface WebView {
|
||||
|
||||
|
@ -14,6 +13,4 @@ interface WebView {
|
|||
fun openOnBoarding()
|
||||
|
||||
fun showError(isAuthenticationError: Boolean = false, error: SslError? = null, description: String? = null)
|
||||
|
||||
fun authenticationDialog(handler: HttpAuthHandler)
|
||||
}
|
||||
|
|
|
@ -13,6 +13,8 @@ import android.net.http.SslError
|
|||
import android.os.Build
|
||||
import android.os.Bundle
|
||||
import android.os.Handler
|
||||
import android.text.method.HideReturnsTransformationMethod
|
||||
import android.text.method.PasswordTransformationMethod
|
||||
import android.util.Log
|
||||
import android.util.Rational
|
||||
import android.view.MenuInflater
|
||||
|
@ -28,11 +30,14 @@ import android.webkit.WebResourceRequest
|
|||
import android.webkit.WebResourceResponse
|
||||
import android.webkit.WebView
|
||||
import android.webkit.WebViewClient
|
||||
import android.widget.CheckBox
|
||||
import android.widget.EditText
|
||||
import android.widget.FrameLayout
|
||||
import android.widget.ImageView
|
||||
import android.widget.Toast
|
||||
import androidx.appcompat.app.AlertDialog
|
||||
import androidx.appcompat.app.AppCompatActivity
|
||||
import androidx.room.Room
|
||||
import com.lokalise.sdk.LokaliseContextWrapper
|
||||
import com.lokalise.sdk.menu_inflater.LokaliseMenuInflater
|
||||
import eightbitlab.com.blurview.RenderScriptBlur
|
||||
|
@ -43,6 +48,8 @@ import io.homeassistant.companion.android.R
|
|||
import io.homeassistant.companion.android.authenticator.Authenticator
|
||||
import io.homeassistant.companion.android.background.LocationBroadcastReceiver
|
||||
import io.homeassistant.companion.android.common.dagger.GraphComponentAccessor
|
||||
import io.homeassistant.companion.android.database.AppDataBase
|
||||
import io.homeassistant.companion.android.database.AuthenticationList
|
||||
import io.homeassistant.companion.android.onboarding.OnboardingActivity
|
||||
import io.homeassistant.companion.android.settings.SettingsActivity
|
||||
import io.homeassistant.companion.android.util.PermissionManager
|
||||
|
@ -80,6 +87,8 @@ class WebViewActivity : AppCompatActivity(), io.homeassistant.companion.android.
|
|||
private var alertDialog: AlertDialog? = null
|
||||
private var isVideoFullScreen = false
|
||||
private var videoHeight = 0
|
||||
private var firstAuthTime: Long = 0
|
||||
private var resourceURL: String = ""
|
||||
private var unlocked = false
|
||||
|
||||
@SuppressLint("SetJavaScriptEnabled")
|
||||
|
@ -127,7 +136,7 @@ class WebViewActivity : AppCompatActivity(), io.homeassistant.companion.android.
|
|||
) {
|
||||
Log.e(TAG, "onReceivedHttpError: errorCode: $errorCode url:$failingUrl")
|
||||
if (failingUrl == loadedUrl) {
|
||||
showError(description = description)
|
||||
showError()
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -148,7 +157,10 @@ class WebViewActivity : AppCompatActivity(), io.homeassistant.companion.android.
|
|||
host: String,
|
||||
realm: String
|
||||
) {
|
||||
authenticationDialog(handler)
|
||||
var authError = false
|
||||
if (System.currentTimeMillis() <= (firstAuthTime + 500))
|
||||
authError = true
|
||||
authenticationDialog(handler, host, realm, authError)
|
||||
}
|
||||
|
||||
override fun onReceivedSslError(
|
||||
|
@ -157,7 +169,14 @@ class WebViewActivity : AppCompatActivity(), io.homeassistant.companion.android.
|
|||
error: SslError?
|
||||
) {
|
||||
Log.e(TAG, "onReceivedHttpError: $error")
|
||||
showError(error = error)
|
||||
showError()
|
||||
}
|
||||
|
||||
override fun onLoadResource(
|
||||
view: WebView?,
|
||||
url: String?
|
||||
) {
|
||||
resourceURL = url!!
|
||||
}
|
||||
|
||||
override fun shouldOverrideUrlLoading(
|
||||
|
@ -517,22 +536,77 @@ class WebViewActivity : AppCompatActivity(), io.homeassistant.companion.android.
|
|||
}
|
||||
|
||||
@SuppressLint("InflateParams")
|
||||
override fun authenticationDialog(handler: HttpAuthHandler) {
|
||||
fun authenticationDialog(handler: HttpAuthHandler, host: String, realm: String, authError: Boolean) {
|
||||
val db = Room.databaseBuilder(
|
||||
applicationContext,
|
||||
AppDataBase::class.java, "HomeAssistantDB")
|
||||
.allowMainThreadQueries()
|
||||
.build()
|
||||
val authenticationDao = db.authenticationDatabaseDao()
|
||||
val httpAuth = authenticationDao.get((resourceURL + realm))
|
||||
|
||||
val inflater = layoutInflater
|
||||
val dialogLayout = inflater.inflate(R.layout.dialog_authentication, null)
|
||||
val username = dialogLayout.findViewById<EditText>(R.id.username)
|
||||
val password = dialogLayout.findViewById<EditText>(R.id.password)
|
||||
val remember = dialogLayout.findViewById<CheckBox>(R.id.checkBox)
|
||||
val viewPassword = dialogLayout.findViewById<ImageView>(R.id.viewPassword)
|
||||
var autoAuth = false
|
||||
|
||||
AlertDialog.Builder(this)
|
||||
.setTitle(R.string.auth_request)
|
||||
.setView(dialogLayout)
|
||||
.setPositiveButton(android.R.string.ok) { _, _ ->
|
||||
handler.proceed(username.text.toString(), password.text.toString())
|
||||
viewPassword.setOnClickListener() {
|
||||
if (password.transformationMethod == PasswordTransformationMethod.getInstance()) {
|
||||
password.transformationMethod = HideReturnsTransformationMethod.getInstance()
|
||||
viewPassword.setImageResource(R.drawable.ic_visibility_off)
|
||||
password.setSelection(password.text.length)
|
||||
} else {
|
||||
password.transformationMethod = PasswordTransformationMethod.getInstance()
|
||||
viewPassword.setImageResource(R.drawable.ic_visibility)
|
||||
password.setSelection(password.text.length)
|
||||
}
|
||||
.setNeutralButton(android.R.string.cancel) { _, _ ->
|
||||
Toast.makeText(applicationContext, R.string.auth_cancel, Toast.LENGTH_SHORT).show()
|
||||
}
|
||||
|
||||
if (!httpAuth?.host.isNullOrBlank()) {
|
||||
if (!authError) {
|
||||
handler.proceed(httpAuth?.username, httpAuth?.password)
|
||||
autoAuth = true
|
||||
firstAuthTime = System.currentTimeMillis()
|
||||
}
|
||||
.show()
|
||||
}
|
||||
|
||||
var message = host + " " + getString(R.string.required_fields)
|
||||
if (resourceURL.subSequence(0, 5).toString() == "http:")
|
||||
message = "http://" + message + " " + getString(R.string.not_private)
|
||||
else
|
||||
message = "https://" + message
|
||||
|
||||
if (!autoAuth || authError) {
|
||||
AlertDialog.Builder(this, R.style.Authentication_Dialog)
|
||||
.setTitle(R.string.auth_request)
|
||||
.setMessage(message)
|
||||
.setView(dialogLayout)
|
||||
.setPositiveButton(android.R.string.ok) { _, _ ->
|
||||
if (username.text.toString() != "" && password.text.toString() != "") {
|
||||
if (remember.isChecked) {
|
||||
if (authError)
|
||||
authenticationDao.update(AuthenticationList((resourceURL + realm), username.text.toString(), password.text.toString()))
|
||||
else
|
||||
authenticationDao.insert(AuthenticationList((resourceURL + realm), username.text.toString(), password.text.toString()))
|
||||
db.close()
|
||||
}
|
||||
handler.proceed(username.text.toString(), password.text.toString())
|
||||
} else AlertDialog.Builder(this)
|
||||
.setTitle(R.string.auth_cancel)
|
||||
.setMessage(R.string.auth_error_message)
|
||||
.setPositiveButton(android.R.string.ok) { _, _ ->
|
||||
authenticationDialog(handler, host, realm, authError)
|
||||
}
|
||||
.show()
|
||||
}
|
||||
.setNeutralButton(android.R.string.cancel) { _, _ ->
|
||||
Toast.makeText(applicationContext, R.string.auth_cancel, Toast.LENGTH_SHORT).show()
|
||||
}
|
||||
.show()
|
||||
}
|
||||
}
|
||||
|
||||
override fun onRequestPermissionsResult(
|
||||
|
|
9
app/src/main/res/drawable/ic_visibility.xml
Normal file
9
app/src/main/res/drawable/ic_visibility.xml
Normal file
|
@ -0,0 +1,9 @@
|
|||
<vector xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:width="24dp"
|
||||
android:height="24dp"
|
||||
android:viewportWidth="24"
|
||||
android:viewportHeight="24">
|
||||
<path
|
||||
android:fillColor="@color/colorAccent"
|
||||
android:pathData="M12,4.5C7,4.5 2.73,7.61 1,12c1.73,4.39 6,7.5 11,7.5s9.27,-3.11 11,-7.5c-1.73,-4.39 -6,-7.5 -11,-7.5zM12,17c-2.76,0 -5,-2.24 -5,-5s2.24,-5 5,-5 5,2.24 5,5 -2.24,5 -5,5zM12,9c-1.66,0 -3,1.34 -3,3s1.34,3 3,3 3,-1.34 3,-3 -1.34,-3 -3,-3z"/>
|
||||
</vector>
|
9
app/src/main/res/drawable/ic_visibility_off.xml
Normal file
9
app/src/main/res/drawable/ic_visibility_off.xml
Normal file
|
@ -0,0 +1,9 @@
|
|||
<vector xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:width="24dp"
|
||||
android:height="24dp"
|
||||
android:viewportWidth="24"
|
||||
android:viewportHeight="24">
|
||||
<path
|
||||
android:fillColor="@color/colorAccent"
|
||||
android:pathData="M12,7c2.76,0 5,2.24 5,5 0,0.65 -0.13,1.26 -0.36,1.83l2.92,2.92c1.51,-1.26 2.7,-2.89 3.43,-4.75 -1.73,-4.39 -6,-7.5 -11,-7.5 -1.4,0 -2.74,0.25 -3.98,0.7l2.16,2.16C10.74,7.13 11.35,7 12,7zM2,4.27l2.28,2.28 0.46,0.46C3.08,8.3 1.78,10.02 1,12c1.73,4.39 6,7.5 11,7.5 1.55,0 3.03,-0.3 4.38,-0.84l0.42,0.42L19.73,22 21,20.73 3.27,3 2,4.27zM7.53,9.8l1.55,1.55c-0.05,0.21 -0.08,0.43 -0.08,0.65 0,1.66 1.34,3 3,3 0.22,0 0.44,-0.03 0.65,-0.08l1.55,1.55c-0.67,0.33 -1.41,0.53 -2.2,0.53 -2.76,0 -5,-2.24 -5,-5 0,-0.79 0.2,-1.53 0.53,-2.2zM11.84,9.02l3.15,3.15 0.02,-0.16c0,-1.66 -1.34,-3 -3,-3l-0.17,0.01z"/>
|
||||
</vector>
|
|
@ -1,20 +1,57 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:orientation="vertical"
|
||||
xmlns:app="http://schemas.android.com/apk/res-auto"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent">
|
||||
android:layout_height="match_parent"
|
||||
android:orientation="vertical"
|
||||
android:paddingLeft="20dp"
|
||||
android:paddingRight="20dp">
|
||||
|
||||
<EditText
|
||||
android:id="@+id/username"
|
||||
<com.google.android.material.textfield.TextInputLayout
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:hint="@string/username" />
|
||||
app:boxBackgroundColor="@android:color/transparent">
|
||||
|
||||
<EditText
|
||||
android:id="@+id/password"
|
||||
<EditText
|
||||
android:id="@+id/username"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:hint="@string/username"
|
||||
android:singleLine="true" />
|
||||
</com.google.android.material.textfield.TextInputLayout>
|
||||
|
||||
<TableRow
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:hint="@string/password"
|
||||
android:inputType="textWebPassword" />
|
||||
android:orientation="horizontal">
|
||||
|
||||
<com.google.android.material.textfield.TextInputLayout
|
||||
android:layout_width="0dp"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_weight="1"
|
||||
app:boxBackgroundColor="@android:color/transparent">
|
||||
|
||||
<EditText
|
||||
android:id="@+id/password"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:hint="@string/password"
|
||||
android:inputType="textPassword"
|
||||
android:singleLine="true" />
|
||||
</com.google.android.material.textfield.TextInputLayout>
|
||||
|
||||
<ImageView
|
||||
android:id="@+id/viewPassword"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="match_parent"
|
||||
app:srcCompat="@drawable/ic_visibility" />
|
||||
|
||||
</TableRow>
|
||||
|
||||
<CheckBox
|
||||
android:id="@+id/checkBox"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:text="Remember" />
|
||||
|
||||
</LinearLayout>
|
|
@ -7,7 +7,9 @@
|
|||
<string name="application_version">Application Version</string>
|
||||
<string name="attempting_registration">Registering application…</string>
|
||||
<string name="auth_cancel">Authentication Cancelled</string>
|
||||
<string name="auth_request">Authentication Requested:</string>
|
||||
<string name="auth_error">Authentication Error</string>
|
||||
<string name="auth_error_message">The \'Username\' and \'Password\' fields must be completed</string>
|
||||
<string name="auth_request">Authentication Requested</string>
|
||||
<string name="biometric_message">Unlock using your biometric or screenlock credential</string>
|
||||
<string name="biometric_set_title">Confirm to continue</string>
|
||||
<string name="biometric_title">Home Assistant need to be unlock</string>
|
||||
|
@ -60,6 +62,7 @@ Home Assistant instance</string>
|
|||
<string name="manage_ssids_input_exists">This SSID already exists.</string>
|
||||
<string name="manual_setup">enter address manually</string>
|
||||
<string name="map">Map</string>
|
||||
<string name="not_private">Your connection to this site is not private.</string>
|
||||
<string name="other_settings">Other Settings</string>
|
||||
<string name="password">Password</string>
|
||||
<string name="permission_explanation">In order to use location tracking features or different connection urls based on WiFi SSID we need access to your location. If you want consistent background updates you will also need to allow background processing</string>
|
||||
|
@ -76,6 +79,7 @@ Home Assistant instance</string>
|
|||
<string name="rate_limit_notification.body">You have now sent more than %s notifications today. You will not receive new notifications until midnight UTC.</string>
|
||||
<string name="rate_limit_notification.title">Notifications Rate Limited</string>
|
||||
<string name="refresh">Refresh</string>
|
||||
<string name="required_fields">requires a username and password.</string>
|
||||
<string name="retry">Retry</string>
|
||||
<string name="save">Save</string>
|
||||
<string name="scan_again">scan again</string>
|
||||
|
|
|
@ -38,4 +38,9 @@
|
|||
|
||||
<style name="Theme.HomeAssistant.Dialog.Alert" parent="Theme.MaterialComponents.Light.Dialog.Alert" />
|
||||
|
||||
<style name="Authentication_Dialog" parent="Theme.MaterialComponents.Light.Dialog">
|
||||
<item name="windowMinWidthMajor">300dp</item>
|
||||
<item name="windowMinWidthMinor">0dp</item>
|
||||
</style>
|
||||
|
||||
</resources>
|
||||
|
|
|
@ -43,6 +43,7 @@ object Config {
|
|||
}
|
||||
|
||||
object AndroidX {
|
||||
|
||||
const val appcompat = "androidx.appcompat:appcompat:1.1.0"
|
||||
const val lifecycle = "androidx.lifecycle:lifecycle-runtime-ktx:2.2.0"
|
||||
const val recyclerview = "androidx.recyclerview:recyclerview:1.1.0"
|
||||
|
@ -51,6 +52,11 @@ object Config {
|
|||
|
||||
const val workManager = "androidx.work:work-runtime-ktx:2.3.4"
|
||||
const val biometric = "androidx.biometric:biometric:1.0.1"
|
||||
|
||||
private const val roomVersion = "2.2.5"
|
||||
const val roomRuntime = "androidx.room:room-runtime:${roomVersion}"
|
||||
const val roomKtx = "androidx.room:room-ktx:${roomVersion}"
|
||||
const val roomCompiler = "androidx.room:room-compiler:${roomVersion}"
|
||||
}
|
||||
|
||||
object Play {
|
||||
|
|
Loading…
Reference in a new issue