Implement the mechanics of fxa login on android, but don't show ui fo… (#1000)

* Implement the mechanics of fxa login on android, but don't show ui for it yet. Also, scopedKeys are not yet implemented.

* Hopefully fix the package-lock conflict?

* WIP on android scoped keys

* Finish implementing login.

* created android/user.js to handle android logins
This commit is contained in:
Donovan Preston 2018-11-08 16:35:19 -05:00 committed by GitHub
parent ffac4ae5b1
commit cab6f1bafb
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
17 changed files with 592 additions and 304 deletions

View file

@ -8,7 +8,6 @@
<application
android:allowBackup="true"
android:icon="@mipmap/ic_launcher"
android:label="@string/app_name"
android:roundIcon="@mipmap/ic_launcher_round"
android:supportsRtl="true"
android:theme="@style/AppTheme">

View file

@ -5,6 +5,7 @@ import android.support.v7.app.AppCompatActivity
import android.os.Bundle
import im.delight.android.webview.AdvancedWebView
import android.graphics.Bitmap
import android.content.Context
import android.content.Intent
import android.annotation.SuppressLint
import android.net.Uri
@ -13,7 +14,13 @@ import android.webkit.WebMessage
import android.util.Log
import android.util.Base64
import android.webkit.ConsoleMessage
import android.webkit.JavascriptInterface
import android.webkit.WebChromeClient
import mozilla.components.service.fxa.Config
import mozilla.components.service.fxa.FirefoxAccount
import mozilla.components.service.fxa.OAuthInfo
import mozilla.components.service.fxa.Profile
import mozilla.components.service.fxa.FxaResult
internal class LoggingWebChromeClient : WebChromeClient() {
override fun onConsoleMessage(cm: ConsoleMessage): Boolean {
@ -23,9 +30,18 @@ internal class LoggingWebChromeClient : WebChromeClient() {
}
}
class WebAppInterface(private val mContext: MainActivity) {
@JavascriptInterface
fun beginOAuthFlow() {
mContext.beginOAuthFlow();
}
}
class MainActivity : AppCompatActivity(), AdvancedWebView.Listener {
private var mWebView: AdvancedWebView? = null
private var mToShare: String? = null
private var mToCall: String? = null
private var mAccount: FirefoxAccount? = null
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
@ -34,14 +50,17 @@ class MainActivity : AppCompatActivity(), AdvancedWebView.Listener {
mWebView = findViewById<WebView>(R.id.webview) as AdvancedWebView
mWebView!!.setListener(this, this)
mWebView!!.setWebChromeClient(LoggingWebChromeClient())
mWebView!!.addJavascriptInterface(WebAppInterface(this), "Android")
val webSettings = mWebView!!.getSettings()
webSettings.setUserAgentString("Send Android")
webSettings.setAllowUniversalAccessFromFileURLs(true)
webSettings.setJavaScriptEnabled(true)
val intent = getIntent()
val action = intent.getAction()
val type = intent.getType()
if (Intent.ACTION_SEND.equals(action) && type != null) {
if (type.equals("text/plain")) {
val sharedText = intent.getStringExtra(Intent.EXTRA_TEXT)
@ -51,12 +70,25 @@ class MainActivity : AppCompatActivity(), AdvancedWebView.Listener {
val imageUri = intent.getParcelableExtra(Intent.EXTRA_STREAM) as Uri
Log.w("INTENT", "image/ " + imageUri)
mToShare = "data:text/plain;base64," + Base64.encodeToString(imageUri.path.toByteArray(), 16).trim()
// TODO Currently this causes a Permission Denied error
// val stream = contentResolver.openInputStream(imageUri)
}
}
mWebView!!.loadUrl("file:///android_asset/android.html")
}
fun beginOAuthFlow() {
Config.custom("https://send-fxa.dev.lcip.org").then(fun (value: Config): FxaResult<Unit> {
mAccount = FirefoxAccount(value, "12cc4070a481bc73", "fxaclient://android.redirect")
mAccount?.beginOAuthFlow(arrayOf("profile", "https://identity.mozilla.com/apps/send"), true)?.then(fun (url: String): FxaResult<Unit> {
Log.w("CONFIG", "GOT A URL " + url)
this@MainActivity.runOnUiThread({
mWebView!!.loadUrl(url)
})
return FxaResult.fromValue(Unit)
})
Log.w("CONFIG", "CREATED FIREFOXACCOUNT")
return FxaResult.fromValue(Unit)
})
}
@SuppressLint("NewApi")
@ -94,7 +126,48 @@ class MainActivity : AppCompatActivity(), AdvancedWebView.Listener {
}
override fun onPageStarted(url: String, favicon: Bitmap?) {
if (url.startsWith("fxaclient")) {
// We load this here so the user doesn't see an ugly screen that says "can't handle fxaclient urls"...
mWebView!!.loadUrl("file:///android_asset/android.html")
val parsed = Uri.parse(url)
val code = parsed.getQueryParameter("code")
val state = parsed.getQueryParameter("state")
code?.let { code ->
state?.let { state ->
mAccount?.completeOAuthFlow(code, state)?.whenComplete { info ->
//displayAndPersistProfile(code, state)
val profile = mAccount?.getProfile(false)?.then(fun (profile: Profile): FxaResult<Unit> {
val accessToken = info.accessToken
val keys = info.keys
val avatar = profile.avatar
val displayName = profile.displayName
val email = profile.email
val uid = profile.uid
val toPass = "{\"accessToken\": \"${accessToken}}\", \"keys\": '${keys}', \"avatar\": \"${avatar}\", \"displayName\": \"${displayName}\", \"email\": \"${email}\", \"uid\": \"${uid}\"}"
mToCall = "finishLogin(${toPass})"
this@MainActivity.runOnUiThread({
// But then we also reload this here because we need to make sure onPageFinished runs after mToCall has been set.
// We can't guarantee that onPageFinished has already been called at this point.
mWebView!!.loadUrl("file:///android_asset/android.html")
})
return FxaResult.fromValue(Unit)
})
// TODO get k from it.keys
// TODO get profile from mAccount.getProfile
// TODO get access_token
//mToShare = "data:text/plain;base64," + Base64.encodeToString(toSend.toByteArray(), 16).trim()
}
}
}
}
Log.w("MAIN", "onPageStarted");
// account.completeOAuthFlow()
}
override fun onPageFinished(url: String) {
@ -102,14 +175,22 @@ class MainActivity : AppCompatActivity(), AdvancedWebView.Listener {
if (mToShare != null) {
Log.w("INTENT", mToShare)
val webView = findViewById<WebView>(R.id.webview) as AdvancedWebView
webView.postWebMessage(WebMessage(mToShare), Uri.EMPTY)
mWebView?.postWebMessage(WebMessage(mToShare), Uri.EMPTY)
mToShare = null
}
if (mToCall != null) {
this@MainActivity.runOnUiThread({
mWebView?.evaluateJavascript(mToCall, fun (value: String) {
// noop
})
})
mToCall = null
}
}
override fun onPageError(errorCode: Int, description: String, failingUrl: String) {
Log.w("MAIN", "onPageError")
Log.w("MAIN", "onPageError " + description)
}
override fun onDownloadRequested(url: String, suggestedFilename: String, mimeType: String, contentLength: Long, contentDisposition: String, userAgent: String) {

View file

@ -1,7 +1,7 @@
<resources>
<!-- Base application theme. -->
<style name="AppTheme" parent="Theme.AppCompat.Light.DarkActionBar">
<style name="AppTheme" parent="Theme.AppCompat.Light.NoActionBar">
<!-- Customize your theme here. -->
<item name="colorPrimary">@color/colorPrimary</item>
<item name="colorPrimaryDark">@color/colorPrimaryDark</item>