Skip to content
This repository has been archived by the owner on Nov 1, 2022. It is now read-only.

Commit

Permalink
Merge branch 'master' into tc_icon
Browse files Browse the repository at this point in the history
  • Loading branch information
Amejia481 authored Aug 1, 2019
2 parents 2ee9d45 + 1801731 commit af68ee7
Show file tree
Hide file tree
Showing 32 changed files with 499 additions and 27 deletions.
4 changes: 4 additions & 0 deletions .buildconfig.yml
Original file line number Diff line number Diff line change
Expand Up @@ -264,6 +264,10 @@ projects:
path: components/support/rustlog
description: 'A bridge allowing log messages from Rust code to be sent to the log system in support-base'
publish: true
support-rusthttp:
path: components/support/rusthttp
description: 'A bridge allowing configuration of Rust HTTP requests without directly depending on the application services library'
publish: true
support-locale:
path: components/support/locale
description: 'A component to allow apps to change the system defined language by their custom one'
Expand Down
11 changes: 8 additions & 3 deletions buildSrc/src/main/java/Dependencies.kt
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,7 @@ object Versions {
const val disklrucache = "2.0.2"
const val leakcanary = "1.6.3"

const val mozilla_appservices = "0.34.0"
const val mozilla_appservices = "0.36.0"
const val servo = "0.0.1.20181017.aa95911"

const val material = "1.0.0"
Expand Down Expand Up @@ -113,11 +113,16 @@ object Dependencies {
const val tools_linttests = "com.android.tools.lint:lint-tests:${Versions.lint}"

const val mozilla_fxa = "org.mozilla.appservices:fxaclient:${Versions.mozilla_appservices}"
const val mozilla_support = "org.mozilla.appservices:support:${Versions.mozilla_appservices}"

const val mozilla_sync_logins = "org.mozilla.appservices:logins:${Versions.mozilla_appservices}"
const val mozilla_places = "org.mozilla.appservices:places:${Versions.mozilla_appservices}"
const val mozilla_places_forUnitTests = "org.mozilla.appservices:places-forUnitTests:${Versions.mozilla_appservices}"

const val mozilla_push = "org.mozilla.appservices:push:${Versions.mozilla_appservices}"

const val mozilla_httpconfig = "org.mozilla.appservices:httpconfig:${Versions.mozilla_appservices}"
const val mozilla_full_megazord = "org.mozilla.appservices:full-megazord:${Versions.mozilla_appservices}"
const val mozilla_full_megazord_forUnitTests = "org.mozilla.appservices:full-megazord-forUnitTests:${Versions.mozilla_appservices}"

const val mozilla_rustlog = "org.mozilla.appservices:rustlog:${Versions.mozilla_appservices}"
const val mozilla_servo_arm = "org.mozilla.servoview:servoview-armv7:${Versions.servo}"
const val mozilla_servo_x86 = "org.mozilla.servoview:servoview-x86:${Versions.servo}"
Expand Down
6 changes: 4 additions & 2 deletions components/browser/storage-sync/build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -64,11 +64,13 @@ dependencies {
testImplementation Dependencies.testing_mockito

jnaForTest Dependencies.thirdparty_jna
testImplementation Dependencies.mozilla_support
testImplementation files(configurations.jnaForTest.copyRecursive().files)
testImplementation Dependencies.mozilla_places_forUnitTests

testImplementation Dependencies.mozilla_places
testImplementation Dependencies.testing_mockwebserver
testImplementation Dependencies.androidx_work_testing

testImplementation Dependencies.mozilla_full_megazord_forUnitTests
}

apply from: '../../../publish.gradle'
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,7 @@ import androidx.browser.customtabs.CustomTabsIntent.KEY_PENDING_INTENT
import androidx.browser.customtabs.CustomTabsIntent.NO_TITLE
import androidx.browser.customtabs.CustomTabsIntent.SHOW_PAGE_TITLE
import androidx.browser.customtabs.CustomTabsIntent.TOOLBAR_ACTION_BUTTON_ID
import androidx.browser.customtabs.TrustedWebUtils.EXTRA_LAUNCH_AS_TRUSTED_WEB_ACTIVITY
import mozilla.components.browser.session.tab.CustomTabActionButtonConfig
import mozilla.components.browser.session.tab.CustomTabConfig
import mozilla.components.browser.session.tab.CustomTabConfig.Companion.EXTRA_NAVIGATION_BAR_COLOR
Expand All @@ -50,11 +51,28 @@ fun isCustomTabIntent(intent: Intent) = isCustomTabIntent(intent.toSafeIntent())
/**
* Checks if the provided intent is a custom tab intent.
*
* @param intent the intent to check, wrapped as a SafeIntent.
* @param safeIntent the intent to check, wrapped as a SafeIntent.
* @return true if the intent is a custom tab intent, otherwise false.
*/
fun isCustomTabIntent(safeIntent: SafeIntent) = safeIntent.hasExtra(EXTRA_SESSION)

/**
* Checks if the provided intent is a trusted web activity intent.
*
* @param intent the intent to check.
* @return true if the intent is a trusted web activity intent, otherwise false.
*/
fun isTrustedWebActivityIntent(intent: Intent) = isTrustedWebActivityIntent(intent.toSafeIntent())

/**
* Checks if the provided intent is a trusted web activity intent.
*
* @param safeIntent the intent to check, wrapped as a SafeIntent.
* @return true if the intent is a trusted web activity intent, otherwise false.
*/
fun isTrustedWebActivityIntent(safeIntent: SafeIntent) = isCustomTabIntent(safeIntent) &&
safeIntent.getBooleanExtra(EXTRA_LAUNCH_AS_TRUSTED_WEB_ACTIVITY, false)

/**
* Creates a [CustomTabConfig] instance based on the provided intent.
*
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ import android.graphics.Bitmap
import android.graphics.Color
import android.os.Bundle
import androidx.browser.customtabs.CustomTabsIntent
import androidx.browser.customtabs.TrustedWebUtils
import androidx.test.ext.junit.runners.AndroidJUnit4
import mozilla.components.browser.session.tab.CustomTabConfig.Companion.EXTRA_NAVIGATION_BAR_COLOR
import mozilla.components.support.test.mock
Expand All @@ -35,6 +36,19 @@ class CustomTabConfigHelperTest {
assertFalse(isCustomTabIntent(mock<Intent>()))
}

@Test
fun isTrustedWebActivityIntent() {
val customTabsIntent = CustomTabsIntent.Builder().build().intent
val trustedWebActivityIntent = Intent(customTabsIntent)
.putExtra(TrustedWebUtils.EXTRA_LAUNCH_AS_TRUSTED_WEB_ACTIVITY, true)
assertTrue(isTrustedWebActivityIntent(trustedWebActivityIntent))
assertFalse(isTrustedWebActivityIntent(customTabsIntent))
assertFalse(isTrustedWebActivityIntent(mock<Intent>()))
assertFalse(isTrustedWebActivityIntent(
Intent().putExtra(TrustedWebUtils.EXTRA_LAUNCH_AS_TRUSTED_WEB_ACTIVITY, true)
))
}

@Test
fun createFromIntentAssignsId() {
val customTabsIntent = CustomTabsIntent.Builder().build()
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,148 @@
/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */

package mozilla.components.feature.pwa.feature

import android.app.Notification
import android.app.NotificationChannel
import android.app.NotificationManager
import android.app.PendingIntent
import android.content.BroadcastReceiver
import android.content.ClipData
import android.content.ClipboardManager
import android.content.Context
import android.content.Intent
import android.content.IntentFilter
import android.os.Build
import android.widget.Toast
import androidx.core.app.NotificationCompat
import androidx.core.app.NotificationCompat.BADGE_ICON_NONE
import androidx.core.app.NotificationManagerCompat
import androidx.core.content.getSystemService
import androidx.lifecycle.Lifecycle
import androidx.lifecycle.LifecycleObserver
import androidx.lifecycle.OnLifecycleEvent
import mozilla.components.browser.session.SessionManager
import mozilla.components.concept.engine.manifest.WebAppManifest
import mozilla.components.feature.pwa.R
import mozilla.components.feature.session.SessionUseCases
import mozilla.components.support.base.ids.cancel
import mozilla.components.support.base.ids.notify

/**
* Displays site controls notification for fullscreen web apps.
*/
class WebAppSiteControlsFeature(
private val applicationContext: Context,
private val sessionManager: SessionManager,
private val reloadUrlUseCase: SessionUseCases.ReloadUrlUseCase,
private val sessionId: String,
private val manifest: WebAppManifest?
) : BroadcastReceiver(), LifecycleObserver {

@OnLifecycleEvent(Lifecycle.Event.ON_RESUME)
fun onResume() {
val filter = IntentFilter().apply {
addAction(ACTION_COPY)
addAction(ACTION_REFRESH)
}
applicationContext.registerReceiver(this, filter)

NotificationManagerCompat.from(applicationContext)
.notify(applicationContext, NOTIFICATION_TAG, buildNotification())
}

@OnLifecycleEvent(Lifecycle.Event.ON_PAUSE)
fun onPause() {
applicationContext.unregisterReceiver(this)

NotificationManagerCompat.from(applicationContext)
.cancel(applicationContext, NOTIFICATION_TAG)
}

/**
* Responds to [PendingIntent]s fired by the site controls notification.
*/
override fun onReceive(context: Context, intent: Intent) {
sessionManager.findSessionById(sessionId)?.also { session ->
when (intent.action) {
ACTION_COPY -> {
applicationContext.getSystemService<ClipboardManager>()?.let { clipboardManager ->
clipboardManager.setPrimaryClip(ClipData.newPlainText(session.url, session.url))
Toast.makeText(
applicationContext,
applicationContext.getString(R.string.mozac_feature_pwa_copy_success),
Toast.LENGTH_SHORT
).show()
}
}
ACTION_REFRESH -> reloadUrlUseCase(session)
}
}
}

/**
* Build the notification with site controls to be displayed while the web app is active.
*/
@Suppress("LongMethod")
private fun buildNotification(): Notification {
val channelId = ensureChannelExists()

with(applicationContext) {
val copyIntent = createPendingIntent(ACTION_COPY, 1)
val refreshAction = NotificationCompat.Action(
R.drawable.ic_refresh,
getString(R.string.mozac_feature_pwa_site_controls_refresh),
createPendingIntent(ACTION_REFRESH, 2)
)

return NotificationCompat.Builder(this, channelId)
.setSmallIcon(R.drawable.ic_pwa)
.setContentTitle(manifest?.name)
.setContentText(getString(R.string.mozac_feature_pwa_site_controls_notification_text))
.setBadgeIconType(BADGE_ICON_NONE)
.setColor(manifest?.themeColor ?: NotificationCompat.COLOR_DEFAULT)
.setPriority(NotificationCompat.PRIORITY_MIN)
.setShowWhen(false)
.setContentIntent(copyIntent)
.addAction(refreshAction)
.setOngoing(true)
.build()
}
}

/**
* Make sure a notification channel for site controls notifications exists.
*
* Returns the channel id to be used for notifications.
*/
private fun ensureChannelExists(): String {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
val notificationManager: NotificationManager = applicationContext.getSystemService()!!

val channel = NotificationChannel(
NOTIFICATION_CHANNEL_ID,
applicationContext.getString(R.string.mozac_feature_pwa_site_controls_notification_channel),
NotificationManager.IMPORTANCE_MIN
)

notificationManager.createNotificationChannel(channel)
}

return NOTIFICATION_CHANNEL_ID
}

private fun createPendingIntent(action: String, requestCode: Int): PendingIntent {
val intent = Intent(action)
intent.setPackage(applicationContext.packageName)
return PendingIntent.getBroadcast(applicationContext, requestCode, intent, 0)
}

companion object {
private const val NOTIFICATION_CHANNEL_ID = "Site Controls"
private const val NOTIFICATION_TAG = "SiteControls"
private const val ACTION_COPY = "mozilla.components.feature.pwa.COPY"
private const val ACTION_REFRESH = "mozilla.components.feature.pwa.REFRESH"
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,7 @@ class WebAppIntentProcessor(
) : IntentProcessor {

/**
* Returns true this intent should launch a progressive web app.
* Returns true if this intent should launch a progressive web app.
*/
override fun matches(intent: Intent) =
intent.toSafeIntent().action == ACTION_VIEW_PWA
Expand Down
13 changes: 13 additions & 0 deletions components/feature/pwa/src/main/res/drawable/ic_pwa.xml
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
<?xml version="1.0" encoding="utf-8"?>
<!-- This Source Code Form is subject to the terms of the Mozilla Public
- License, v. 2.0. If a copy of the MPL was not distributed with this
- file, You can obtain one at http://mozilla.org/MPL/2.0/. -->
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="24dp"
android:height="24dp"
android:viewportWidth="24.0"
android:viewportHeight="24.0">
<path
android:fillColor="@android:color/white"
android:pathData="M16.72 14.42l0.58-1.46h1.67l-0.8-2.22 1-2.5L22 15.76h-2.1l-0.48-1.35h-2.7zM14.94 15.77l3.03-7.54h-2.01l-2.08 4.87-1.48-4.86h-1.54L9.27 13.1l-1.12-2.22-1 3.12 1.02 1.77h1.98l1.43-4.37 1.37 4.37h1.99zM3.91 13.18h1.24c0.38 0 0.71-0.04 1-0.13l0.32-0.98 0.9-2.76A2.2 2.2 0 0 0 7.14 9c-0.46-0.51-1.14-0.77-2.02-0.77H2v7.54h1.91v-2.59zm1.64-3.21c0.18 0.18 0.27 0.42 0.27 0.72s-0.08 0.55-0.24 0.73c-0.17 0.2-0.49 0.3-0.95 0.3H3.9V9.7h0.72c0.44 0 0.74 0.09 0.92 0.27z"/>
</vector>
13 changes: 13 additions & 0 deletions components/feature/pwa/src/main/res/drawable/ic_refresh.xml
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
<?xml version="1.0" encoding="utf-8"?>
<!-- This Source Code Form is subject to the terms of the Mozilla Public
- License, v. 2.0. If a copy of the MPL was not distributed with this
- file, You can obtain one at http://mozilla.org/MPL/2.0/. -->
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="24dp"
android:height="24dp"
android:viewportWidth="24.0"
android:viewportHeight="24.0">
<path
android:fillColor="@android:color/white"
android:pathData="M21,4.04a0.96,0.96 0,0 0,-0.96 0.96V8a8.981,8.981 0,1 0,-1.676 10.361A1,1 0,0 0,16.95 16.95a7.031,7.031 0,1 1,1.72 -6.91H15a0.96,0.96 0,0 0,0 1.92h6a0.96,0.96 0,0 0,0.96 -0.96V5A0.96,0.96 0,0 0,21 4.04Z"/>
</vector>
9 changes: 9 additions & 0 deletions components/feature/pwa/src/main/res/values/strings.xml
Original file line number Diff line number Diff line change
Expand Up @@ -5,4 +5,13 @@
<resources>
<!-- Default shortcut label used if website has no title -->
<string name="mozac_feature_pwa_default_shortcut_label">Website</string>

<!-- Name of the "notification channel" used for displaying site controls notification. See https://developer.android.com/training/notify-user/channels -->
<string name="mozac_feature_pwa_site_controls_notification_channel">Full screen site controls</string>
<!-- Text shown on the second row of the site controls notification. -->
<string name="mozac_feature_pwa_site_controls_notification_text">Tap to copy the URL for this app</string>
<!-- Refresh button in site controls notification. -->
<string name="mozac_feature_pwa_site_controls_refresh">Refresh</string>
<!-- Toast displayed when the website URL is copied. -->
<string name="mozac_feature_pwa_copy_success">URL copied.</string>
</resources>
Original file line number Diff line number Diff line change
@@ -0,0 +1,61 @@
/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */

package mozilla.components.feature.pwa.feature

import android.content.Intent
import androidx.test.ext.junit.runners.AndroidJUnit4
import mozilla.components.browser.session.Session
import mozilla.components.browser.session.SessionManager
import mozilla.components.feature.session.SessionUseCases
import mozilla.components.support.test.any
import mozilla.components.support.test.eq
import mozilla.components.support.test.mock
import mozilla.components.support.test.robolectric.testContext
import org.junit.Test
import org.junit.runner.RunWith
import org.mockito.Mockito.doNothing
import org.mockito.Mockito.doReturn
import org.mockito.Mockito.spy
import org.mockito.Mockito.verify

@RunWith(AndroidJUnit4::class)
class WebAppSiteControlsFeatureTest {

@Test
fun `register receiver on resume`() {
val context = spy(testContext)

val feature = WebAppSiteControlsFeature(context, mock(), mock(), "session-id", mock())
feature.onResume()

verify(context).registerReceiver(eq(feature), any())
}

@Test
fun `unregister receiver on pause`() {
val context = spy(testContext)

doNothing().`when`(context).unregisterReceiver(any())

val feature = WebAppSiteControlsFeature(context, mock(), mock(), "session-id", mock())
feature.onPause()

verify(context).unregisterReceiver(feature)
}

@Test
fun `reload page when reload action is activated`() {
val sessionManager: SessionManager = mock()
val session: Session = mock()
val reloadUrlUseCase: SessionUseCases.ReloadUrlUseCase = mock()

doReturn(session).`when`(sessionManager).findSessionById("session-id")

val feature = WebAppSiteControlsFeature(testContext, sessionManager, reloadUrlUseCase, "session-id", mock())
feature.onReceive(testContext, Intent("mozilla.components.feature.pwa.REFRESH"))

verify(reloadUrlUseCase).invoke(session)
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -72,7 +72,7 @@ class FxaDeviceConstellation(
state?.currentDevice?.let {
return scope.async {
handleFxaExceptions(logger, "destroying current device") {
account.destroyDevice(it.id)
account.disconnect() // Not sure if this is right!
}
}
}
Expand Down
Loading

0 comments on commit af68ee7

Please sign in to comment.