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:
parent
ffac4ae5b1
commit
cab6f1bafb
|
@ -1,13 +1,5 @@
|
|||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<module external.linked.project.id="android" external.linked.project.path="$MODULE_DIR$" external.root.project.path="$MODULE_DIR$" external.system.id="GRADLE" type="JAVA_MODULE" version="4">
|
||||
<component name="FacetManager">
|
||||
<facet type="java-gradle" name="Java-Gradle">
|
||||
<configuration>
|
||||
<option name="BUILD_FOLDER_PATH" value="$MODULE_DIR$/build" />
|
||||
<option name="BUILDABLE" value="false" />
|
||||
</configuration>
|
||||
</facet>
|
||||
</component>
|
||||
<component name="NewModuleRootManager" LANGUAGE_LEVEL="JDK_1_7" inherit-compiler-output="true">
|
||||
<exclude-output />
|
||||
<content url="file://$MODULE_DIR$">
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
/* global window */
|
||||
/* global window, navigator */
|
||||
|
||||
window.LIMITS = {
|
||||
ANON: {
|
||||
|
@ -27,13 +27,17 @@ const locale = require('../common/locales');
|
|||
const home = require('../app/ui/home');
|
||||
const app = choo();
|
||||
|
||||
if (navigator.userAgent === 'Send Android') {
|
||||
assets.setPrefix('/android_asset');
|
||||
}
|
||||
|
||||
function body(main) {
|
||||
return function(state, emit) {
|
||||
return html`<body class="flex flex-col items-center font-sans bg-blue-lightest md:h-screen md:bg-grey-lightest">
|
||||
${header(state, emit)}
|
||||
<a id="hamburger" class="absolute pin-t pin-r z-50" href="#" onclick=${clickPreferences}>
|
||||
<img src=${assets.get('preferences.png')} />
|
||||
</a>
|
||||
${header(state, emit)}
|
||||
${main(state, emit)}
|
||||
</body>`;
|
||||
|
||||
|
@ -44,15 +48,22 @@ function body(main) {
|
|||
};
|
||||
}
|
||||
|
||||
app.use(require('./stores/state').default);
|
||||
app.use((state, emitter) => {
|
||||
state.translate = locale.getTranslator();
|
||||
state.capabilities = {}; //TODO
|
||||
state.capabilities = {
|
||||
account: true
|
||||
}; //TODO
|
||||
|
||||
window.finishLogin = async function(accountInfo) {
|
||||
await state.user.finishLogin(accountInfo);
|
||||
emitter.emit('render');
|
||||
};
|
||||
|
||||
// for debugging
|
||||
window.appState = state;
|
||||
window.appEmit = emitter.emit.bind(emitter);
|
||||
});
|
||||
app.use(require('./stores/state').default);
|
||||
app.use(require('../app/fileManager').default);
|
||||
app.use(require('./stores/intents').default);
|
||||
app.route('/', body(home));
|
||||
|
|
|
@ -51,19 +51,19 @@
|
|||
</facet>
|
||||
</component>
|
||||
<component name="NewModuleRootManager" LANGUAGE_LEVEL="JDK_1_7">
|
||||
<output url="file://$MODULE_DIR$/build/intermediates/classes/debug" />
|
||||
<output-test url="file://$MODULE_DIR$/build/intermediates/classes/test/debug" />
|
||||
<output url="file://$MODULE_DIR$/build/intermediates/javac/debug/compileDebugJavaWithJavac/classes" />
|
||||
<output-test url="file://$MODULE_DIR$/build/intermediates/javac/debugUnitTest/compileDebugUnitTestJavaWithJavac/classes" />
|
||||
<exclude-output />
|
||||
<content url="file://$MODULE_DIR$">
|
||||
<sourceFolder url="file://$MODULE_DIR$/build/generated/source/apt/debug" isTestSource="false" generated="true" />
|
||||
<sourceFolder url="file://$MODULE_DIR$/build/generated/source/r/debug" isTestSource="false" generated="true" />
|
||||
<sourceFolder url="file://$MODULE_DIR$/build/generated/not_namespaced_r_class_sources/debug/processDebugResources/r" isTestSource="false" generated="true" />
|
||||
<sourceFolder url="file://$MODULE_DIR$/build/generated/source/aidl/debug" isTestSource="false" generated="true" />
|
||||
<sourceFolder url="file://$MODULE_DIR$/build/generated/source/buildConfig/debug" isTestSource="false" generated="true" />
|
||||
<sourceFolder url="file://$MODULE_DIR$/build/generated/source/rs/debug" isTestSource="false" generated="true" />
|
||||
<sourceFolder url="file://$MODULE_DIR$/build/generated/res/rs/debug" type="java-resource" />
|
||||
<sourceFolder url="file://$MODULE_DIR$/build/generated/res/resValues/debug" type="java-resource" />
|
||||
<sourceFolder url="file://$MODULE_DIR$/build/generated/source/apt/androidTest/debug" isTestSource="true" generated="true" />
|
||||
<sourceFolder url="file://$MODULE_DIR$/build/generated/source/r/androidTest/debug" isTestSource="true" generated="true" />
|
||||
<sourceFolder url="file://$MODULE_DIR$/build/generated/not_namespaced_r_class_sources/debugAndroidTest/processDebugAndroidTestResources/r" isTestSource="true" generated="true" />
|
||||
<sourceFolder url="file://$MODULE_DIR$/build/generated/source/aidl/androidTest/debug" isTestSource="true" generated="true" />
|
||||
<sourceFolder url="file://$MODULE_DIR$/build/generated/source/buildConfig/androidTest/debug" isTestSource="true" generated="true" />
|
||||
<sourceFolder url="file://$MODULE_DIR$/build/generated/source/rs/androidTest/debug" isTestSource="true" generated="true" />
|
||||
|
@ -112,31 +112,40 @@
|
|||
<sourceFolder url="file://$MODULE_DIR$/src/androidTest/java" isTestSource="true" />
|
||||
<sourceFolder url="file://$MODULE_DIR$/src/androidTest/rs" isTestSource="true" />
|
||||
<sourceFolder url="file://$MODULE_DIR$/src/androidTest/shaders" isTestSource="true" />
|
||||
<excludeFolder url="file://$MODULE_DIR$/build/.DS_Store" />
|
||||
<excludeFolder url="file://$MODULE_DIR$/build/intermediates/assets" />
|
||||
<excludeFolder url="file://$MODULE_DIR$/build/intermediates/annotation_processor_list" />
|
||||
<excludeFolder url="file://$MODULE_DIR$/build/intermediates/apk_list" />
|
||||
<excludeFolder url="file://$MODULE_DIR$/build/intermediates/blame" />
|
||||
<excludeFolder url="file://$MODULE_DIR$/build/intermediates/build-info" />
|
||||
<excludeFolder url="file://$MODULE_DIR$/build/intermediates/builds" />
|
||||
<excludeFolder url="file://$MODULE_DIR$/build/intermediates/check-libraries" />
|
||||
<excludeFolder url="file://$MODULE_DIR$/build/intermediates/check-manifest" />
|
||||
<excludeFolder url="file://$MODULE_DIR$/build/intermediates/classes" />
|
||||
<excludeFolder url="file://$MODULE_DIR$/build/intermediates/checkDebugClasspath" />
|
||||
<excludeFolder url="file://$MODULE_DIR$/build/intermediates/compatible_screen_manifest" />
|
||||
<excludeFolder url="file://$MODULE_DIR$/build/intermediates/incremental" />
|
||||
<excludeFolder url="file://$MODULE_DIR$/build/intermediates/incremental-classes" />
|
||||
<excludeFolder url="file://$MODULE_DIR$/build/intermediates/incremental-runtime-classes" />
|
||||
<excludeFolder url="file://$MODULE_DIR$/build/intermediates/incremental-verifier" />
|
||||
<excludeFolder url="file://$MODULE_DIR$/build/intermediates/instant-run-apk" />
|
||||
<excludeFolder url="file://$MODULE_DIR$/build/intermediates/instant-run-main-apk-res" />
|
||||
<excludeFolder url="file://$MODULE_DIR$/build/intermediates/javaPrecompile" />
|
||||
<excludeFolder url="file://$MODULE_DIR$/build/intermediates/instant_run_main_apk_resources" />
|
||||
<excludeFolder url="file://$MODULE_DIR$/build/intermediates/instant_run_merged_manifests" />
|
||||
<excludeFolder url="file://$MODULE_DIR$/build/intermediates/instant_run_split_apk_resources" />
|
||||
<excludeFolder url="file://$MODULE_DIR$/build/intermediates/javac" />
|
||||
<excludeFolder url="file://$MODULE_DIR$/build/intermediates/jniLibs" />
|
||||
<excludeFolder url="file://$MODULE_DIR$/build/intermediates/lint" />
|
||||
<excludeFolder url="file://$MODULE_DIR$/build/intermediates/lint_jar" />
|
||||
<excludeFolder url="file://$MODULE_DIR$/build/intermediates/manifest-checker" />
|
||||
<excludeFolder url="file://$MODULE_DIR$/build/intermediates/manifests" />
|
||||
<excludeFolder url="file://$MODULE_DIR$/build/intermediates/merged_assets" />
|
||||
<excludeFolder url="file://$MODULE_DIR$/build/intermediates/merged_manifests" />
|
||||
<excludeFolder url="file://$MODULE_DIR$/build/intermediates/prebuild" />
|
||||
<excludeFolder url="file://$MODULE_DIR$/build/intermediates/processed_res" />
|
||||
<excludeFolder url="file://$MODULE_DIR$/build/intermediates/reload-dex" />
|
||||
<excludeFolder url="file://$MODULE_DIR$/build/intermediates/res" />
|
||||
<excludeFolder url="file://$MODULE_DIR$/build/intermediates/resources" />
|
||||
<excludeFolder url="file://$MODULE_DIR$/build/intermediates/rs" />
|
||||
<excludeFolder url="file://$MODULE_DIR$/build/intermediates/shader_assets" />
|
||||
<excludeFolder url="file://$MODULE_DIR$/build/intermediates/shaders" />
|
||||
<excludeFolder url="file://$MODULE_DIR$/build/intermediates/split-apk" />
|
||||
<excludeFolder url="file://$MODULE_DIR$/build/intermediates/split_list" />
|
||||
<excludeFolder url="file://$MODULE_DIR$/build/intermediates/splits-support" />
|
||||
<excludeFolder url="file://$MODULE_DIR$/build/intermediates/symbols" />
|
||||
<excludeFolder url="file://$MODULE_DIR$/build/intermediates/transforms" />
|
||||
|
@ -146,9 +155,11 @@
|
|||
</content>
|
||||
<orderEntry type="jdk" jdkName="Android API 27 Platform" jdkType="Android SDK" />
|
||||
<orderEntry type="sourceFolder" forTests="false" />
|
||||
<orderEntry type="library" name="Gradle: net.java.dev.jna:jna-4.5.2" level="project" />
|
||||
<orderEntry type="library" scope="TEST" name="Gradle: com.android.support.test:runner-1.0.2" level="project" />
|
||||
<orderEntry type="library" name="Gradle: android.arch.lifecycle:common:1.1.0@jar" level="project" />
|
||||
<orderEntry type="library" name="Gradle: com.android.support:support-annotations:27.1.1@jar" level="project" />
|
||||
<orderEntry type="library" name="Gradle: org.jetbrains.kotlin:kotlin-stdlib:1.2.61@jar" level="project" />
|
||||
<orderEntry type="library" name="Gradle: com.android.support:animated-vector-drawable-27.1.1" level="project" />
|
||||
<orderEntry type="library" name="Gradle: com.android.support:support-compat-27.1.1" level="project" />
|
||||
<orderEntry type="library" name="Gradle: android.arch.lifecycle:viewmodel-1.1.0" level="project" />
|
||||
|
@ -162,21 +173,26 @@
|
|||
<orderEntry type="library" name="Gradle: com.github.delight-im:Android-AdvancedWebView-v3.0.0" level="project" />
|
||||
<orderEntry type="library" scope="TEST" name="Gradle: com.android.support.test.espresso:espresso-core-3.0.2" level="project" />
|
||||
<orderEntry type="library" scope="TEST" name="Gradle: javax.inject:javax.inject:1@jar" level="project" />
|
||||
<orderEntry type="library" name="Gradle: org.jetbrains.kotlinx:kotlinx-coroutines-android:0.23.4@jar" level="project" />
|
||||
<orderEntry type="library" name="Gradle: com.android.support:support-fragment-27.1.1" level="project" />
|
||||
<orderEntry type="library" scope="TEST" name="Gradle: junit:junit:4.12@jar" level="project" />
|
||||
<orderEntry type="library" name="Gradle: android.arch.core:runtime-1.1.0" level="project" />
|
||||
<orderEntry type="library" name="Gradle: org.jetbrains.kotlin:kotlin-stdlib-jdk7:1.2.61@jar" level="project" />
|
||||
<orderEntry type="library" scope="TEST" name="Gradle: org.hamcrest:hamcrest-core:1.3@jar" level="project" />
|
||||
<orderEntry type="library" scope="TEST" name="Gradle: com.android.support.test:monitor-1.0.2" level="project" />
|
||||
<orderEntry type="library" name="Gradle: org.jetbrains.kotlinx:kotlinx-coroutines-core:0.23.4@jar" level="project" />
|
||||
<orderEntry type="library" name="Gradle: com.android.support:appcompat-v7-27.1.1" level="project" />
|
||||
<orderEntry type="library" name="Gradle: org.mozilla.components:service-firefox-accounts-0.26.0" level="project" />
|
||||
<orderEntry type="library" scope="TEST" name="Gradle: com.android.support.test.espresso:espresso-idling-resource-3.0.2" level="project" />
|
||||
<orderEntry type="library" name="Gradle: com.android.support.constraint:constraint-layout-solver:1.1.2@jar" level="project" />
|
||||
<orderEntry type="library" name="Gradle: org.jetbrains.kotlin:kotlin-stdlib:1.2.60@jar" level="project" />
|
||||
<orderEntry type="library" name="Gradle: org.mozilla.fxa_client:fxa_client-0.5.1" level="project" />
|
||||
<orderEntry type="library" name="Gradle: android.arch.lifecycle:livedata-core-1.1.0" level="project" />
|
||||
<orderEntry type="library" name="Gradle: org.jetbrains.kotlinx:kotlinx-coroutines-core-common:0.23.4@jar" level="project" />
|
||||
<orderEntry type="library" scope="TEST" name="Gradle: org.hamcrest:hamcrest-library:1.3@jar" level="project" />
|
||||
<orderEntry type="library" scope="TEST" name="Gradle: org.hamcrest:hamcrest-integration:1.3@jar" level="project" />
|
||||
<orderEntry type="library" name="Gradle: org.jetbrains.kotlin:kotlin-stdlib-common:1.2.60@jar" level="project" />
|
||||
<orderEntry type="library" name="Gradle: org.jetbrains.kotlinx:atomicfu-common:0.10.3@jar" level="project" />
|
||||
<orderEntry type="library" name="Gradle: android.arch.core:common:1.1.0@jar" level="project" />
|
||||
<orderEntry type="library" name="Gradle: org.jetbrains.kotlin:kotlin-stdlib-jdk7:1.2.60@jar" level="project" />
|
||||
<orderEntry type="library" name="Gradle: org.jetbrains.kotlin:kotlin-stdlib-common:1.2.61@jar" level="project" />
|
||||
<orderEntry type="library" scope="TEST" name="Gradle: net.sf.kxml:kxml2:2.3.0@jar" level="project" />
|
||||
<orderEntry type="library" name="Gradle: android.arch.lifecycle:runtime-1.1.0" level="project" />
|
||||
</component>
|
||||
|
|
|
@ -31,6 +31,7 @@ dependencies {
|
|||
androidTestImplementation 'com.android.support.test:runner:1.0.2'
|
||||
androidTestImplementation 'com.android.support.test.espresso:espresso-core:3.0.2'
|
||||
implementation 'com.github.delight-im:Android-AdvancedWebView:v3.0.0'
|
||||
implementation "org.mozilla.components:service-firefox-accounts:${rootProject.ext.android_components_version}"
|
||||
}
|
||||
|
||||
task generateAndLinkBundle(type: Exec, description: 'Generate the android.js bundle and link it into the assets directory') {
|
||||
|
|
|
@ -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">
|
||||
|
|
|
@ -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) {
|
||||
|
|
|
@ -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>
|
||||
|
|
|
@ -2,12 +2,13 @@
|
|||
|
||||
buildscript {
|
||||
ext.kotlin_version = '1.2.60'
|
||||
ext.android_components_version = '0.26.0'
|
||||
repositories {
|
||||
google()
|
||||
jcenter()
|
||||
}
|
||||
dependencies {
|
||||
classpath 'com.android.tools.build:gradle:3.1.4'
|
||||
classpath 'com.android.tools.build:gradle:3.2.1'
|
||||
classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:1.2.60"
|
||||
|
||||
// NOTE: Do not place your application dependencies here; they belong
|
||||
|
@ -18,6 +19,9 @@ buildscript {
|
|||
allprojects {
|
||||
repositories {
|
||||
google()
|
||||
maven {
|
||||
url "https://maven.mozilla.org/maven2"
|
||||
}
|
||||
jcenter()
|
||||
maven { url "https://jitpack.io" }
|
||||
}
|
||||
|
|
|
@ -3,7 +3,7 @@ const path = require('path');
|
|||
|
||||
child_process.execSync('npm run build');
|
||||
child_process.execSync(
|
||||
`cp -R ${path.resolve(__dirname, '../dist')} ${path.resolve(
|
||||
`cp -R ${path.resolve(__dirname, '../dist/*')} ${path.resolve(
|
||||
__dirname,
|
||||
'app/src/main/assets'
|
||||
)}`
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
#Wed May 23 21:14:24 EDT 2018
|
||||
#Thu Oct 11 12:20:52 EDT 2018
|
||||
distributionBase=GRADLE_USER_HOME
|
||||
distributionPath=wrapper/dists
|
||||
zipStoreBase=GRADLE_USER_HOME
|
||||
zipStorePath=wrapper/dists
|
||||
distributionUrl=https\://services.gradle.org/distributions/gradle-4.4-all.zip
|
||||
distributionUrl=https\://services.gradle.org/distributions/gradle-4.6-all.zip
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
/* eslint-disable no-console */
|
||||
|
||||
import User from '../../app/user';
|
||||
import User from '../user';
|
||||
import storage from '../../app/storage';
|
||||
|
||||
export default function initialState(state, emitter) {
|
||||
|
|
|
@ -0,0 +1,26 @@
|
|||
/* global Android */
|
||||
import User from '../app/user';
|
||||
import { deriveFileListKey } from '../app/fxa';
|
||||
|
||||
export default class AndroidUser extends User {
|
||||
constructor(storage) {
|
||||
super(storage);
|
||||
}
|
||||
|
||||
async login() {
|
||||
Android.beginOAuthFlow();
|
||||
}
|
||||
|
||||
async finishLogin(accountInfo) {
|
||||
const jwks = JSON.parse(accountInfo.keys);
|
||||
const ikm = jwks['https://identity.mozilla.com/apps/send'].k;
|
||||
const profile = {
|
||||
displayName: accountInfo.displayName,
|
||||
email: accountInfo.email,
|
||||
avatar: accountInfo.avatar,
|
||||
access_token: accountInfo.accessToken
|
||||
};
|
||||
profile.fileListKey = await deriveFileListKey(ikm);
|
||||
this.info = profile;
|
||||
}
|
||||
}
|
12
app/fxa.js
12
app/fxa.js
|
@ -146,12 +146,10 @@ export async function preparePkce(storage) {
|
|||
return arrayToB64(new Uint8Array(challenge));
|
||||
}
|
||||
|
||||
export async function getFileListKey(storage, bundle) {
|
||||
const jwks = await decryptBundle(storage, bundle);
|
||||
const jwk = jwks['https://identity.mozilla.com/apps/send'];
|
||||
export async function deriveFileListKey(ikm) {
|
||||
const baseKey = await crypto.subtle.importKey(
|
||||
'raw',
|
||||
b64ToArray(jwk.k),
|
||||
b64ToArray(ikm),
|
||||
{ name: 'HKDF' },
|
||||
false,
|
||||
['deriveKey']
|
||||
|
@ -174,3 +172,9 @@ export async function getFileListKey(storage, bundle) {
|
|||
const rawFileListKey = await crypto.subtle.exportKey('raw', fileListKey);
|
||||
return arrayToB64(new Uint8Array(rawFileListKey));
|
||||
}
|
||||
|
||||
export async function getFileListKey(storage, bundle) {
|
||||
const jwks = await decryptBundle(storage, bundle);
|
||||
const jwk = jwks['https://identity.mozilla.com/apps/send'];
|
||||
return deriveFileListKey(jwk.k);
|
||||
}
|
||||
|
|
33
app/user.js
33
app/user.js
|
@ -16,28 +16,37 @@ export default class User {
|
|||
this.data = storage.user || {};
|
||||
}
|
||||
|
||||
get info() {
|
||||
return this.data || this.storage.user || {};
|
||||
}
|
||||
|
||||
set info(data) {
|
||||
this.data = data;
|
||||
this.storage.user = data;
|
||||
}
|
||||
|
||||
get avatar() {
|
||||
const defaultAvatar = assets.get('user.svg');
|
||||
if (this.data.avatarDefault) {
|
||||
if (this.info.avatarDefault) {
|
||||
return assets.get('firefox_logo-only.svg');
|
||||
}
|
||||
return this.data.avatar || defaultAvatar;
|
||||
return this.info.avatar || defaultAvatar;
|
||||
}
|
||||
|
||||
get name() {
|
||||
return this.data.displayName;
|
||||
return this.info.displayName;
|
||||
}
|
||||
|
||||
get email() {
|
||||
return this.data.email;
|
||||
return this.info.email;
|
||||
}
|
||||
|
||||
get loggedIn() {
|
||||
return !!this.data.access_token;
|
||||
return !!this.info.access_token;
|
||||
}
|
||||
|
||||
get bearerToken() {
|
||||
return this.data.access_token;
|
||||
return this.info.access_token;
|
||||
}
|
||||
|
||||
get maxSize() {
|
||||
|
@ -104,15 +113,13 @@ export default class User {
|
|||
const userInfo = await infoResponse.json();
|
||||
userInfo.access_token = auth.access_token;
|
||||
userInfo.fileListKey = await getFileListKey(this.storage, auth.keys_jwe);
|
||||
this.storage.user = userInfo;
|
||||
this.data = userInfo;
|
||||
this.info = userInfo;
|
||||
this.storage.remove('pkceVerifier');
|
||||
}
|
||||
|
||||
logout() {
|
||||
this.storage.user = null;
|
||||
this.storage.clearLocalFiles();
|
||||
this.data = {};
|
||||
this.info = {};
|
||||
}
|
||||
|
||||
async syncFileList() {
|
||||
|
@ -124,7 +131,7 @@ export default class User {
|
|||
try {
|
||||
const encrypted = await getFileList(this.bearerToken);
|
||||
const decrypted = await streamToArrayBuffer(
|
||||
decryptStream(encrypted, b64ToArray(this.data.fileListKey))
|
||||
decryptStream(encrypted, b64ToArray(this.info.fileListKey))
|
||||
);
|
||||
list = JSON.parse(textDecoder.decode(decrypted));
|
||||
} catch (e) {
|
||||
|
@ -142,7 +149,7 @@ export default class User {
|
|||
textEncoder.encode(JSON.stringify(this.storage.files))
|
||||
]);
|
||||
const encrypted = await streamToArrayBuffer(
|
||||
encryptStream(blobStream(blob), b64ToArray(this.data.fileListKey))
|
||||
encryptStream(blobStream(blob), b64ToArray(this.info.fileListKey))
|
||||
);
|
||||
await setFileList(this.bearerToken, encrypted);
|
||||
} catch (e) {
|
||||
|
@ -152,6 +159,6 @@ export default class User {
|
|||
}
|
||||
|
||||
toJSON() {
|
||||
return this.data;
|
||||
return this.info;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
const genmap = require('../build/generate_asset_map');
|
||||
const isServer = typeof genmap === 'function';
|
||||
const prefix = '';
|
||||
let prefix = '';
|
||||
let manifest = {};
|
||||
try {
|
||||
//eslint-disable-next-line node/no-missing-require
|
||||
|
@ -15,6 +15,10 @@ function getAsset(name) {
|
|||
return prefix + assets[name];
|
||||
}
|
||||
|
||||
function setPrefix(name) {
|
||||
prefix = name;
|
||||
}
|
||||
|
||||
function getMatches(match) {
|
||||
return Object.keys(assets)
|
||||
.filter(k => match.test(k))
|
||||
|
@ -22,6 +26,7 @@ function getMatches(match) {
|
|||
}
|
||||
|
||||
const instance = {
|
||||
setPrefix: setPrefix,
|
||||
get: getAsset,
|
||||
match: getMatches,
|
||||
setMiddleware: function(middleware) {
|
||||
|
|
File diff suppressed because it is too large
Load Diff
Loading…
Reference in New Issue