Skip to content
This repository has been archived by the owner on May 22, 2021. It is now read-only.

Commit

Permalink
Cleanup Android project (Minor refactorings, etc.) (#1244)
Browse files Browse the repository at this point in the history
* (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
  • Loading branch information
christxph authored and fzzzy committed Apr 4, 2019
1 parent 373da3f commit 48b5d85
Show file tree
Hide file tree
Showing 4 changed files with 111 additions and 124 deletions.
4 changes: 1 addition & 3 deletions android/app/build.gradle
Original file line number Diff line number Diff line change
@@ -1,7 +1,5 @@
apply plugin: 'com.android.application'

apply plugin: 'kotlin-android'

apply plugin: 'kotlin-android-extensions'

android {
Expand Down Expand Up @@ -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') {
Expand Down
208 changes: 104 additions & 104 deletions android/app/src/main/java/org/mozilla/firefoxsend/MainActivity.kt
Original file line number Diff line number Diff line change
@@ -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
Expand All @@ -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

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);

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()
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()

settings.apply {
userAgentString = "Send Android"
allowUniversalAccessFromFileURLs = true
javaScriptEnabled = true
}
}

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"
}
}
12 changes: 3 additions & 9 deletions android/app/src/main/res/layout/activity_main.xml
Original file line number Diff line number Diff line change
@@ -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" />
11 changes: 3 additions & 8 deletions android/build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -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" }
}
Expand Down

0 comments on commit 48b5d85

Please sign in to comment.