Skip to content

Commit

Permalink
Closes mozilla-mobile#2624: ReaderView: Wire up messaging between fea…
Browse files Browse the repository at this point in the history
…ture and web ext.
  • Loading branch information
csadilek committed Apr 30, 2019
1 parent 671f998 commit 6c570ed
Show file tree
Hide file tree
Showing 15 changed files with 231 additions and 89 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -73,6 +73,7 @@ class Session(
fun onCrashStateChanged(session: Session, crashed: Boolean) = Unit
fun onIconChanged(session: Session, icon: Bitmap?) = Unit
fun onReaderableStateUpdated(session: Session, readerable: Boolean) = Unit
fun onReaderModeChanged(session: Session, enabled: Boolean) = Unit
}

/**
Expand Down Expand Up @@ -379,6 +380,13 @@ class Session(
notifyObservers { onReaderableStateUpdated(this@Session, new) }
}

/**
* Reader mode state, whether or not reader view is enabled, otherwise false.
*/
var readerMode: Boolean by Delegates.observable(false) { _, old, new ->
notifyObservers(old, new) { onReaderModeChanged(this@Session, new) }
}

/**
* Returns whether or not this session is used for a Custom Tab.
*/
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -958,4 +958,21 @@ class SessionTest {

assertTrue(session.readerable)
}

@Test
fun `observer is notified when reader mode state changes`() {
val observer = mock(Session.Observer::class.java)

val session = Session("https://www.mozilla.org")
session.register(observer)
assertFalse(session.readerMode)

session.readerMode = true

verify(observer).onReaderModeChanged(
eq(session),
eq(true))

assertTrue(session.readerMode)
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -29,12 +29,12 @@
color: #eeeeee;
}

.mozac-readerview-body.sans-serif {
font-family: sans-serif;
.mozac-readerview-body.sans-serif * {
font-family: sans-serif !important;
}

.mozac-readerview-body.serif {
font-family: serif;
.mozac-readerview-body.serif * {
font-family: serif !important;
}

/* Override some controls and content styles based on color scheme */
Expand Down Expand Up @@ -106,7 +106,6 @@

.mozac-readerview-body > .container > .header > .credits {
font-size: 0.9em;
font-family: sans-serif;
}

.mozac-readerview-body > .container > .header > .domain {
Expand Down Expand Up @@ -139,6 +138,11 @@
margin-bottom: 32px;
}

.mozac-readerview-body > .container > .header > .meta-data {
font-size: 0.65em;
margin: 0 0 15px 0;
}

.mozac-readerview-body > .container > .content {
padding-left: 0px;
padding-right: 0px;
Expand All @@ -161,8 +165,22 @@
color: #00acff !important;
}

.mozac-readerview-content h1, h2, h3 {
margin-top: 16px;
margin-bottom: 16px;
font-weight: 700;
}

.mozac-readerview-content h1 {
font-size: 1.6em;
}

.mozac-readerview-content h2 {
margin-bottom: 20px !important;
font-size: 1.2em;
}

.mozac-readerview-content h3 {
font-size: 1em;
}

.mozac-readerview-content * {
Expand All @@ -171,6 +189,7 @@
}

.mozac-readerview-content p {
font-size: 1em !important;
line-height: 1.4em !important;
margin: 0px !important;
margin-bottom: 20px !important;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,7 @@ class ReaderView {
return false;
}

return isProbablyReaderable(document);
return isProbablyReaderable(document, ReaderView._isNodeVisible);
}

static get MIN_FONT_SIZE() {
Expand All @@ -37,6 +37,10 @@ class ReaderView {
return 9;
}

static _isNodeVisible(node) {
return node.clientHeight > 0 && node.clientWidth > 0;
}

constructor(document) {
this.document = document;
this.originalBody = document.body.outerHTML;
Expand All @@ -63,6 +67,7 @@ class ReaderView {

hide() {
document.body.outerHTML = this.originalBody;
location.reload(false)
}

/**
Expand Down Expand Up @@ -95,10 +100,6 @@ class ReaderView {
* @param fontType the font type to use.
*/
setFontType(fontType) {
if (this.fontType === fontType) {
return;
}

let bodyClasses = document.body.classList;

if (this.fontType) {
Expand All @@ -121,10 +122,6 @@ class ReaderView {
return;
}

if (this.colorScheme === colorScheme) {
return;
}

let bodyClasses = document.body.classList;

if (this.colorScheme) {
Expand Down Expand Up @@ -279,30 +276,30 @@ class ReaderView {
}
}

let port = browser.runtime.connectNative("mozacReaderview");
let readerView = new ReaderView(document);

let port = browser.runtime.connectNative("mozacReaderview");
port.onMessage.addListener((message) => {
switch (message.action) {
case 'show':
readerView.show({fontSize: 3, fontType: "serif", colorScheme: "light"});
readerView.show(message.value);
break;
case 'hide':
readerView.hide();
break;
case 'setColorScheme':
readerView.setColorScheme(message.value.toLowerCase());
break;
case 'changeFontSize':
readerView.changeFontSize(message.value);
break;
case 'setFontType':
readerView.setFontType(message.value.toLowerCase());
break;
case 'checkReaderable':
port.postMessage({readerable: ReaderView.isReaderable()});
break;
default:
console.error(`Received invalid action ${message.action}`);
}
});

// TODO remove hostname check (for testing purposes only)
// e.g. https://blog.mozilla.org/firefox/reader-view
if (ReaderView.isReaderable() && location.hostname.endsWith("blog.mozilla.org")) {
// TODO send message to app to inform that readerview is available
// For now we show reader view for every page on blog.mozilla.org
let readerView = new ReaderView(document);
// TODO Parameters need to be passed down in message to display readerview
readerView.show({fontSize: 3, fontType: "serif", colorScheme: "light"});
}
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,6 @@ import mozilla.components.support.base.feature.BackHandler
import mozilla.components.support.base.feature.LifecycleAwareFeature
import mozilla.components.support.base.log.logger.Logger
import org.json.JSONObject
import java.lang.IllegalStateException
import java.util.WeakHashMap
import kotlin.properties.Delegates

Expand Down Expand Up @@ -51,32 +50,38 @@ class ReaderViewFeature(
private val onReaderViewAvailableChange: OnReaderViewAvailableChange = { }
) : SelectionAwareSessionObserver(sessionManager), LifecycleAwareFeature, BackHandler {

private val config = Config(context.getSharedPreferences("mozac_feature_reader_view", Context.MODE_PRIVATE))
private val config = Config(context.getSharedPreferences("mozac_feature_reader_view", Context.MODE_PRIVATE), this)
private val controlsPresenter = ReaderViewControlsPresenter(controlsView, config)
private val controlsInteractor = ReaderViewControlsInteractor(controlsView, config)

class Config(prefs: SharedPreferences) {
enum class FontType { SANS_SERIF, SERIF }
class Config(val prefs: SharedPreferences, val readerViewFeature: ReaderViewFeature) {
enum class FontType(val value: String) { SANSSERIF("sans-serif"), SERIF("serif") }
enum class ColorScheme { LIGHT, SEPIA, DARK }

var colorScheme by Delegates.observable(ColorScheme.valueOf(prefs.getString(COLOR_SCHEME_KEY, "LIGHT")!!)) {
_, old, new -> saveAndSendMessage(old, new, COLOR_SCHEME_KEY)
}

@Suppress("MagicNumber")
var fontSize by Delegates.observable(prefs.getInt(FONT_SIZE_KEY, 3)) {
_, old, new -> saveAndSendMessage(old, new, FONT_SIZE_KEY)
_, old, new ->
if (old != new) {
val message = JSONObject().put(ACTION_MESSAGE_KEY, ACTION_SET_COLOR_SCHEME).put(ACTION_VALUE, new)
readerViewFeature.sendContentMessage(message)
prefs.edit().putString(COLOR_SCHEME_KEY, new.name).commit()
}
}

var fontType by Delegates.observable(FontType.valueOf(prefs.getString(FONT_TYPE_KEY, "SANS_SERIF")!!)) {
_, old, new -> saveAndSendMessage(old, new, FONT_TYPE_KEY)
var fontType by Delegates.observable(FontType.valueOf(prefs.getString(FONT_TYPE_KEY, "SERIF")!!)) {
_, old, new ->
if (old != new) {
val message = JSONObject().put(ACTION_MESSAGE_KEY, ACTION_SET_FONT_TYPE).put(ACTION_VALUE, new.name)
readerViewFeature.sendContentMessage(message)
prefs.edit().putString(FONT_TYPE_KEY, new.name).commit()
}
}

@Suppress("UNUSED_PARAMETER")
private fun saveAndSendMessage(old: Any, new: Any, key: String) {
@Suppress("MagicNumber")
var fontSize by Delegates.observable(prefs.getInt(FONT_SIZE_KEY, 3)) { _, old, new ->
if (old != new) {
// TODO save shared preference
// TODO send message to reader view web extension
val message = JSONObject().put(ACTION_MESSAGE_KEY, ACTION_CHANGE_FONT_SIZE).put(ACTION_VALUE, new - old)
readerViewFeature.sendContentMessage(message)
prefs.edit().putInt(FONT_SIZE_KEY, new).commit()
}
}

Expand Down Expand Up @@ -132,12 +137,16 @@ class ReaderViewFeature(
}

override fun onBackPressed(): Boolean {
// TODO send message to exit reader view (-> see ReaderView.hide())
return true
activeSession?.let {
if (it.readerMode) {
hideReaderView()
return true
}
}
return false
}

override fun onSessionSelected(session: Session) {
// TODO restore selected state of whether the controls are open or not
registerContentMessageHandler(activeSession)
checkReaderable()
super.onSessionSelected(session)
Expand All @@ -148,22 +157,44 @@ class ReaderViewFeature(
}

override fun onUrlChanged(session: Session, url: String) {
session.readerMode = false
checkReaderable()
}

override fun onLoadingStateChanged(session: Session, loading: Boolean) {
// If the page was refreshed and reader mode was turned on before,
// make sure it is still turned on.
if (!loading && activeSession?.readerMode == true) {
showReaderView()
}
}

override fun onReaderableStateUpdated(session: Session, readerable: Boolean) {
onReaderViewAvailableChange(readerable)
}

fun showReaderView() {
activeSession?.let {
sendMessage(JSONObject().put(ACTION_MESSAGE_KEY, ACTION_SHOW), it)
val config = JSONObject()
.put(ACTION_VALUE_FONT_SIZE, config.fontSize)
.put(ACTION_VALUE_FONT_TYPE, config.fontType.name.toLowerCase())
.put(ACTION_VALUE_COLOR_SCHEME, config.colorScheme.name.toLowerCase())

val message = JSONObject()
.put(ACTION_MESSAGE_KEY, ACTION_SHOW)
.put(ACTION_VALUE, config)

sendContentMessage(message, it)
it.readerMode = true
}
}

fun hideReaderView() {
activeSession?.let {
sendMessage(JSONObject().put(ACTION_MESSAGE_KEY, ACTION_HIDE), it)
it.readerMode = false
// We will re-determine if the original page is readerable when it's loaded.
it.readerable = false
sendContentMessage(JSONObject().put(ACTION_MESSAGE_KEY, ACTION_HIDE), it)
}
}

Expand All @@ -184,40 +215,40 @@ class ReaderViewFeature(
internal fun checkReaderable() {
activeSession?.let {
if (ports.containsKey(sessionManager.getEngineSession(it))) {
sendMessage(JSONObject().put(ACTION_MESSAGE_KEY, ACTION_CHECK_READERABLE), it)
sendContentMessage(JSONObject().put(ACTION_MESSAGE_KEY, ACTION_CHECK_READERABLE), it)
}
}
}

private fun sendMessage(msg: Any, session: Session) {
val port = ports[sessionManager.getEngineSession(session)]
port?.postMessage(msg) ?: throw IllegalStateException("No port connected for the provided session")
private fun sendContentMessage(msg: Any, session: Session? = activeSession) {
session?.let {
val port = ports[sessionManager.getEngineSession(session)]
port?.postMessage(msg) ?: Logger.error("No port connected for the provided session")
}
}

@VisibleForTesting
companion object {
@VisibleForTesting
internal const val READER_VIEW_EXTENSION_ID = "mozacReaderview"

@VisibleForTesting
internal const val READER_VIEW_EXTENSION_URL = "resource://android/assets/extensions/readerview/"

@VisibleForTesting
internal const val ACTION_MESSAGE_KEY = "action"

@VisibleForTesting
internal const val ACTION_SHOW = "show"

@VisibleForTesting
internal const val ACTION_HIDE = "hide"

@VisibleForTesting
internal const val ACTION_CHECK_READERABLE = "checkReaderable"
internal const val ACTION_SET_COLOR_SCHEME = "setColorScheme"
internal const val ACTION_CHANGE_FONT_SIZE = "changeFontSize"
internal const val ACTION_SET_FONT_TYPE = "setFontType"

internal const val ACTION_VALUE = "value"
internal const val ACTION_VALUE_FONT_SIZE = "fontSize"
internal const val ACTION_VALUE_FONT_TYPE = "fontType"
internal const val ACTION_VALUE_COLOR_SCHEME = "colorScheme"

@VisibleForTesting
internal const val READERABLE_RESPONSE_MESSAGE_KEY = "readerable"

@Volatile
@VisibleForTesting
internal var installedWebExt: WebExtension? = null

@Volatile
Expand Down
Loading

0 comments on commit 6c570ed

Please sign in to comment.