Cleanup Android project (Minor refactorings, etc.) (#1244)
* (Android) Get rid of double bangs by using Kotlin view binding Instead of holding a nullable reference to the WebView, we are now accessing the WebView using the view binding utility of Kotlin's Android Extensions. Further reading: https://kotlinlang.org/docs/tutorials/android-plugin.html * (Android) Enable WebView debugging in debug builds This enables debugging the app's WebView using Chrome's DevTools. https://developers.google.com/web/tools/chrome-devtools/remote-debugging/webviews * (Android) Make MainActivity.kt adhere to common Kotlin conventions * (Android) Update dependencies and improve formatting of Gradle files This updates the Kotlin plugin to 1.3.21 and the Gradle plugin to 3.3.2 * (Android) Remove unnecessary ConstraintLayout container Layout files should generally have as few nested layers as possible, because every layer affects the performance. * (Android) Use JSONObject class to construct a JSON string It is way safer to construct a JSON string using classes that are meant for doing that, instead of concatenating raw strings. * (Android) Suppress JavaScript lint warning * (Android) Use Kotlin string templates instead of concatenating strings * (Android) Add missing SuppressLint import
This commit is contained in:
parent
373da3f090
commit
48b5d85904
4 changed files with 110 additions and 123 deletions
|
@ -1,7 +1,5 @@
|
|||
apply plugin: 'com.android.application'
|
||||
|
||||
apply plugin: 'kotlin-android'
|
||||
|
||||
apply plugin: 'kotlin-android-extensions'
|
||||
|
||||
android {
|
||||
|
@ -31,7 +29,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}"
|
||||
implementation "org.mozilla.components:service-firefox-accounts:$android_components_version"
|
||||
}
|
||||
|
||||
task generateAndLinkBundle(type: Exec, description: 'Generate the android.js bundle and link it into the assets directory') {
|
||||
|
|
|
@ -1,39 +1,39 @@
|
|||
package org.mozilla.firefoxsend
|
||||
|
||||
|
||||
import android.support.v7.app.AppCompatActivity
|
||||
import android.os.Bundle
|
||||
import im.delight.android.webview.AdvancedWebView
|
||||
import android.graphics.Bitmap
|
||||
import android.content.Intent
|
||||
import android.annotation.SuppressLint
|
||||
import android.content.ComponentName
|
||||
import android.content.Intent
|
||||
import android.graphics.Bitmap
|
||||
import android.net.Uri
|
||||
import android.webkit.WebView
|
||||
import android.webkit.WebMessage
|
||||
import android.util.Log
|
||||
import android.os.Bundle
|
||||
import android.support.v7.app.AppCompatActivity
|
||||
import android.util.Base64
|
||||
import android.util.Log
|
||||
import android.view.View
|
||||
import android.webkit.ConsoleMessage
|
||||
import android.webkit.JavascriptInterface
|
||||
import android.webkit.WebChromeClient
|
||||
import android.webkit.*
|
||||
import im.delight.android.webview.AdvancedWebView
|
||||
import kotlinx.android.synthetic.main.activity_main.*
|
||||
import mozilla.components.service.fxa.Config
|
||||
import mozilla.components.service.fxa.FirefoxAccount
|
||||
import mozilla.components.service.fxa.Profile
|
||||
import mozilla.components.service.fxa.FxaResult
|
||||
import org.json.JSONObject
|
||||
|
||||
internal class LoggingWebChromeClient : WebChromeClient() {
|
||||
override fun onConsoleMessage(cm: ConsoleMessage): Boolean {
|
||||
Log.w("CONTENT", String.format("%s @ %d: %s",
|
||||
Log.d(TAG, String.format("%s @ %d: %s",
|
||||
cm.message(), cm.lineNumber(), cm.sourceId()))
|
||||
return true
|
||||
}
|
||||
|
||||
companion object {
|
||||
private const val TAG = "CONTENT"
|
||||
}
|
||||
}
|
||||
|
||||
class WebAppInterface(private val mContext: MainActivity) {
|
||||
@JavascriptInterface
|
||||
fun beginOAuthFlow() {
|
||||
mContext.beginOAuthFlow();
|
||||
mContext.beginOAuthFlow()
|
||||
}
|
||||
|
||||
@JavascriptInterface
|
||||
|
@ -43,176 +43,176 @@ class WebAppInterface(private val mContext: MainActivity) {
|
|||
}
|
||||
|
||||
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
|
||||
|
||||
@SuppressLint("SetJavaScriptEnabled")
|
||||
override fun onCreate(savedInstanceState: Bundle?) {
|
||||
super.onCreate(savedInstanceState)
|
||||
setContentView(R.layout.activity_main)
|
||||
|
||||
// https://developers.google.com/web/tools/chrome-devtools/remote-debugging/webviews
|
||||
// WebView.setWebContentsDebuggingEnabled(true); // TODO only dev builds
|
||||
WebView.setWebContentsDebuggingEnabled(BuildConfig.DEBUG)
|
||||
webView.apply {
|
||||
setListener(this@MainActivity, this@MainActivity)
|
||||
addJavascriptInterface(WebAppInterface(this@MainActivity), JS_INTERFACE_NAME)
|
||||
setLayerType(View.LAYER_TYPE_HARDWARE, null)
|
||||
webChromeClient = LoggingWebChromeClient()
|
||||
|
||||
mWebView = findViewById<WebView>(R.id.webview) as AdvancedWebView
|
||||
mWebView!!.setListener(this, this)
|
||||
mWebView!!.setWebChromeClient(LoggingWebChromeClient())
|
||||
mWebView!!.addJavascriptInterface(WebAppInterface(this), "Android")
|
||||
mWebView!!.setLayerType(View.LAYER_TYPE_HARDWARE, null);
|
||||
settings.apply {
|
||||
userAgentString = "Send Android"
|
||||
allowUniversalAccessFromFileURLs = true
|
||||
javaScriptEnabled = true
|
||||
}
|
||||
}
|
||||
|
||||
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 type = intent.type
|
||||
if (Intent.ACTION_SEND == intent.action && type != null) {
|
||||
if (type == "text/plain") {
|
||||
val sharedText = intent.getStringExtra(Intent.EXTRA_TEXT)
|
||||
Log.w("INTENT", "text/plain " + sharedText)
|
||||
Log.d(TAG_INTENT, "text/plain $sharedText")
|
||||
mToShare = "data:text/plain;base64," + Base64.encodeToString(sharedText.toByteArray(), 16).trim()
|
||||
} else if (type.startsWith("image/")) {
|
||||
val imageUri = intent.getParcelableExtra(Intent.EXTRA_STREAM) as Uri
|
||||
Log.w("INTENT", "image/ " + imageUri)
|
||||
Log.d(TAG_INTENT, "image/ $imageUri")
|
||||
mToShare = "data:text/plain;base64," + Base64.encodeToString(imageUri.path.toByteArray(), 16).trim()
|
||||
}
|
||||
}
|
||||
mWebView!!.loadUrl("file:///android_asset/android.html")
|
||||
|
||||
webView.loadUrl("file:///android_asset/android.html")
|
||||
}
|
||||
|
||||
fun beginOAuthFlow() {
|
||||
Config.release().then(fun (value: Config): FxaResult<Unit> {
|
||||
Config.release().then { value ->
|
||||
mAccount = FirefoxAccount(value, "20f7931c9054d833", "https://send.firefox.com/fxa/android-redirect.html")
|
||||
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)
|
||||
})
|
||||
mAccount?.beginOAuthFlow(arrayOf("profile", "https://identity.mozilla.com/apps/send"), true)
|
||||
?.then { url ->
|
||||
Log.d(TAG_CONFIG, "GOT A URL $url")
|
||||
this@MainActivity.runOnUiThread {
|
||||
webView.loadUrl(url)
|
||||
}
|
||||
FxaResult.fromValue(Unit)
|
||||
}
|
||||
Log.d(TAG_CONFIG, "CREATED FIREFOXACCOUNT")
|
||||
FxaResult.fromValue(Unit)
|
||||
}
|
||||
}
|
||||
|
||||
fun shareUrl(url: String) {
|
||||
val shareIntent = Intent()
|
||||
shareIntent.action = Intent.ACTION_SEND
|
||||
shareIntent.type = "text/plain"
|
||||
shareIntent.putExtra(Intent.EXTRA_TEXT, url)
|
||||
val shareIntent = Intent().apply {
|
||||
action = Intent.ACTION_SEND
|
||||
type = "text/plain"
|
||||
putExtra(Intent.EXTRA_TEXT, url)
|
||||
}
|
||||
|
||||
val components = arrayOf(ComponentName(applicationContext, MainActivity::class.java))
|
||||
val chooser = Intent.createChooser(shareIntent, "")
|
||||
chooser.putExtra(Intent.EXTRA_EXCLUDE_COMPONENTS, arrayOf(ComponentName(applicationContext, MainActivity::class.java)))
|
||||
.putExtra(Intent.EXTRA_EXCLUDE_COMPONENTS, components)
|
||||
|
||||
startActivity(chooser)
|
||||
}
|
||||
|
||||
@SuppressLint("NewApi")
|
||||
override fun onResume() {
|
||||
super.onResume()
|
||||
mWebView!!.onResume()
|
||||
// ...
|
||||
webView.onResume()
|
||||
}
|
||||
|
||||
@SuppressLint("NewApi")
|
||||
override fun onPause() {
|
||||
mWebView!!.onPause()
|
||||
// ...
|
||||
webView.onPause()
|
||||
super.onPause()
|
||||
}
|
||||
|
||||
override fun onDestroy() {
|
||||
mWebView!!.onDestroy()
|
||||
// ...
|
||||
webView.onDestroy()
|
||||
super.onDestroy()
|
||||
}
|
||||
|
||||
override fun onActivityResult(requestCode: Int, resultCode: Int, intent: Intent?) {
|
||||
super.onActivityResult(requestCode, resultCode, intent)
|
||||
mWebView!!.onActivityResult(requestCode, resultCode, intent)
|
||||
// ...
|
||||
webView.onActivityResult(requestCode, resultCode, intent)
|
||||
}
|
||||
|
||||
override fun onBackPressed() {
|
||||
if (!mWebView!!.onBackPressed()) {
|
||||
if (!webView.onBackPressed()) {
|
||||
return
|
||||
}
|
||||
// ...
|
||||
super.onBackPressed()
|
||||
}
|
||||
|
||||
override fun onPageStarted(url: String, favicon: Bitmap?) {
|
||||
if (url.startsWith("https://send.firefox.com/fxa/android-redirect.html")) {
|
||||
// We load this here so the user doesn't see the android-redirect.html page
|
||||
mWebView!!.loadUrl("file:///android_asset/android.html")
|
||||
webView.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 ->
|
||||
val uri = Uri.parse(url)
|
||||
uri.getQueryParameter("code")?.let { code ->
|
||||
uri.getQueryParameter("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({
|
||||
mAccount?.getProfile(false)?.then { profile ->
|
||||
val profileJsonPayload = JSONObject()
|
||||
.put("accessToken", info.accessToken)
|
||||
.put("keys", info.keys)
|
||||
.put("avatar", profile.avatar)
|
||||
.put("displayName", profile.displayName)
|
||||
.put("email", profile.email)
|
||||
.put("uid", profile.uid).toString()
|
||||
mToCall = "finishLogin($profileJsonPayload)"
|
||||
this@MainActivity.runOnUiThread {
|
||||
// Clear the history so that the user can't use the back button to see broken pages
|
||||
// that were inserted into the history by the login process.
|
||||
mWebView!!.clearHistory()
|
||||
webView.clearHistory()
|
||||
|
||||
// We also reload this here because we need to make sure onPageFinished runs after mToCall has been set.
|
||||
// We can't guarantee that onPageFinished wasn't already called at this point.
|
||||
mWebView!!.loadUrl("file:///android_asset/android.html")
|
||||
})
|
||||
|
||||
|
||||
return FxaResult.fromValue(Unit)
|
||||
})
|
||||
webView.loadUrl("file:///android_asset/android.html")
|
||||
}
|
||||
FxaResult.fromValue(Unit)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
Log.w("MAIN", "onPageStarted");
|
||||
Log.d(TAG_MAIN, "onPageStarted")
|
||||
}
|
||||
|
||||
override fun onPageFinished(url: String) {
|
||||
Log.w("MAIN", "onPageFinished")
|
||||
Log.d(TAG_MAIN, "onPageFinished")
|
||||
if (mToShare != null) {
|
||||
Log.w("INTENT", mToShare)
|
||||
Log.d(TAG_INTENT, mToShare)
|
||||
|
||||
mWebView?.postWebMessage(WebMessage(mToShare), Uri.EMPTY)
|
||||
webView.postWebMessage(WebMessage(mToShare), Uri.EMPTY)
|
||||
mToShare = null
|
||||
}
|
||||
if (mToCall != null) {
|
||||
this@MainActivity.runOnUiThread({
|
||||
mWebView?.evaluateJavascript(mToCall, fun (value: String) {
|
||||
this@MainActivity.runOnUiThread {
|
||||
webView.evaluateJavascript(mToCall) {
|
||||
mToCall = null
|
||||
})
|
||||
})
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
override fun onPageError(errorCode: Int, description: String, failingUrl: String) {
|
||||
Log.w("MAIN", "onPageError " + description)
|
||||
Log.d(TAG_MAIN, "onPageError($errorCode, $description, $failingUrl)")
|
||||
}
|
||||
|
||||
override fun onDownloadRequested(url: String, suggestedFilename: String, mimeType: String, contentLength: Long, contentDisposition: String, userAgent: String) {
|
||||
Log.w("MAIN", "onDownloadRequested")
|
||||
override fun onDownloadRequested(url: String,
|
||||
suggestedFilename: String,
|
||||
mimeType: String,
|
||||
contentLength: Long,
|
||||
contentDisposition: String,
|
||||
userAgent: String) {
|
||||
Log.d(TAG_MAIN, "onDownloadRequested")
|
||||
}
|
||||
|
||||
override fun onExternalPageRequest(url: String) {
|
||||
Log.w("MAIN", "onExternalPageRequest")
|
||||
Log.d(TAG_MAIN, "onExternalPageRequest($url)")
|
||||
}
|
||||
|
||||
companion object {
|
||||
private const val TAG_MAIN = "MAIN"
|
||||
private const val TAG_INTENT = "INTENT"
|
||||
private const val TAG_CONFIG = "CONFIG"
|
||||
private const val JS_INTERFACE_NAME = "Android"
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,13 +1,7 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<android.support.constraint.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
xmlns:app="http://schemas.android.com/apk/res-auto"
|
||||
<im.delight.android.webview.AdvancedWebView xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
xmlns:tools="http://schemas.android.com/tools"
|
||||
android:id="@+id/webView"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent"
|
||||
tools:context=".MainActivity">
|
||||
|
||||
<im.delight.android.webview.AdvancedWebView
|
||||
android:id="@+id/webview"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent" />
|
||||
</android.support.constraint.ConstraintLayout>
|
||||
tools:context=".MainActivity" />
|
|
@ -8,20 +8,15 @@ buildscript {
|
|||
jcenter()
|
||||
}
|
||||
dependencies {
|
||||
classpath 'com.android.tools.build:gradle:3.3.1'
|
||||
classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:1.3.20"
|
||||
|
||||
// NOTE: Do not place your application dependencies here; they belong
|
||||
// in the individual module build.gradle files
|
||||
classpath 'com.android.tools.build:gradle:3.3.2'
|
||||
classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:1.3.21"
|
||||
}
|
||||
}
|
||||
|
||||
allprojects {
|
||||
repositories {
|
||||
google()
|
||||
maven {
|
||||
url "https://maven.mozilla.org/maven2"
|
||||
}
|
||||
maven { url "https://maven.mozilla.org/maven2" }
|
||||
jcenter()
|
||||
maven { url "https://jitpack.io" }
|
||||
}
|
||||
|
|
Loading…
Reference in a new issue