diff options
author | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-05-15 03:35:49 +0000 |
---|---|---|
committer | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-05-15 03:35:49 +0000 |
commit | d8bbc7858622b6d9c278469aab701ca0b609cddf (patch) | |
tree | eff41dc61d9f714852212739e6b3738b82a2af87 /mobile/android/android-components/components/feature/readerview | |
parent | Releasing progress-linux version 125.0.3-1~progress7.99u1. (diff) | |
download | firefox-d8bbc7858622b6d9c278469aab701ca0b609cddf.tar.xz firefox-d8bbc7858622b6d9c278469aab701ca0b609cddf.zip |
Merging upstream version 126.0.
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to 'mobile/android/android-components/components/feature/readerview')
151 files changed, 10328 insertions, 0 deletions
diff --git a/mobile/android/android-components/components/feature/readerview/.gitignore b/mobile/android/android-components/components/feature/readerview/.gitignore new file mode 100644 index 0000000000..2ddf5f27b1 --- /dev/null +++ b/mobile/android/android-components/components/feature/readerview/.gitignore @@ -0,0 +1 @@ +manifest.json diff --git a/mobile/android/android-components/components/feature/readerview/README.md b/mobile/android/android-components/components/feature/readerview/README.md new file mode 100644 index 0000000000..c3a4439fdd --- /dev/null +++ b/mobile/android/android-components/components/feature/readerview/README.md @@ -0,0 +1,49 @@ +# [Android Components](../../../README.md) > Feature > Reader View + + A component wrapping/providing a Reader View WebExtension. + +## Usage + +### Setting up the dependency + +Use Gradle to download the library from [maven.mozilla.org](https://maven.mozilla.org/) ([Setup repository](../../../README.md#maven-repository)): + +```Groovy +implementation "org.mozilla.components:feature-readerview:{latest-version}" +``` + +### Integration + +Initializing the feature: + +```kotlin +val readerViewFeature = ReaderViewFeature( + context, + engine, + sessionManager, + onReaderViewAvailableChange = { + // e.g. readerViewToolbarActionVisible = it + } +) + +``` + +Showing and hiding Reader View: + +```kotlin +readerViewFeature.showReaderView() +readerViewFeature.hideReaderView() +``` + +Showing and hiding the Reader View appearance UI (to adjust font size, font type and color scheme). Note that changes to the appearance settings are automatically persisted as user preferences. + +```kotlin +readerViewFeature.showAppearanceControls() +readerViewFeature.hideAppearanceControls() +``` + +## License + + 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/ diff --git a/mobile/android/android-components/components/feature/readerview/build.gradle b/mobile/android/android-components/components/feature/readerview/build.gradle new file mode 100644 index 0000000000..896753f6d9 --- /dev/null +++ b/mobile/android/android-components/components/feature/readerview/build.gradle @@ -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/. */ + +import org.jetbrains.kotlin.gradle.tasks.KotlinCompile + +apply plugin: 'com.android.library' +apply plugin: 'kotlin-android' + +android { + defaultConfig { + minSdkVersion config.minSdkVersion + compileSdk config.compileSdkVersion + targetSdkVersion config.targetSdkVersion + } + + buildTypes { + release { + minifyEnabled false + proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro' + } + } + + namespace 'mozilla.components.feature.readerview' +} + +tasks.register("updateBuiltInExtensionVersion", Copy) { task -> + updateExtensionVersion(task, 'src/main/assets/extensions/readerview') +} + +tasks.withType(KotlinCompile).configureEach { + kotlinOptions.freeCompilerArgs += "-opt-in=kotlinx.coroutines.ExperimentalCoroutinesApi" +} + +dependencies { + implementation project(':browser-menu') + implementation project(':browser-state') + implementation project(':concept-engine') + implementation project(':support-base') + implementation project(':support-ktx') + implementation project(':support-utils') + implementation project(':support-webextensions') + implementation project(':ui-icons') + + implementation ComponentsDependencies.androidx_core_ktx + implementation ComponentsDependencies.kotlin_coroutines + + implementation ComponentsDependencies.androidx_constraintlayout + + testImplementation project(':support-test') + testImplementation ComponentsDependencies.androidx_test_core + testImplementation ComponentsDependencies.androidx_test_junit + testImplementation ComponentsDependencies.testing_robolectric + testImplementation ComponentsDependencies.testing_coroutines +} + +apply from: '../../../android-lint.gradle' +apply from: '../../../publish.gradle' +ext.configurePublish(config.componentsGroupId, archivesBaseName, project.ext.description) + +preBuild.dependsOn updateBuiltInExtensionVersion diff --git a/mobile/android/android-components/components/feature/readerview/proguard-rules.pro b/mobile/android/android-components/components/feature/readerview/proguard-rules.pro new file mode 100644 index 0000000000..f1b424510d --- /dev/null +++ b/mobile/android/android-components/components/feature/readerview/proguard-rules.pro @@ -0,0 +1,21 @@ +# Add project specific ProGuard rules here. +# You can control the set of applied configuration files using the +# proguardFiles setting in build.gradle. +# +# For more details, see +# http://developer.android.com/guide/developing/tools/proguard.html + +# If your project uses WebView with JS, uncomment the following +# and specify the fully qualified class name to the JavaScript interface +# class: +#-keepclassmembers class fqcn.of.javascript.interface.for.webview { +# public *; +#} + +# Uncomment this to preserve the line number information for +# debugging stack traces. +#-keepattributes SourceFile,LineNumberTable + +# If you keep the line number information, uncomment this to +# hide the original source file name. +#-renamesourcefileattribute SourceFile diff --git a/mobile/android/android-components/components/feature/readerview/src/main/AndroidManifest.xml b/mobile/android/android-components/components/feature/readerview/src/main/AndroidManifest.xml new file mode 100644 index 0000000000..1eccdee26a --- /dev/null +++ b/mobile/android/android-components/components/feature/readerview/src/main/AndroidManifest.xml @@ -0,0 +1,7 @@ +<!-- 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/. --> +<manifest xmlns:android="http://schemas.android.com/apk/res/android"> + + <application android:supportsRtl="true" /> +</manifest> diff --git a/mobile/android/android-components/components/feature/readerview/src/main/assets/extensions/readerview/manifest.template.json b/mobile/android/android-components/components/feature/readerview/src/main/assets/extensions/readerview/manifest.template.json new file mode 100644 index 0000000000..616b2036eb --- /dev/null +++ b/mobile/android/android-components/components/feature/readerview/src/main/assets/extensions/readerview/manifest.template.json @@ -0,0 +1,30 @@ +{ + "manifest_version": 2, + "browser_specific_settings": { + "gecko": { + "id": "readerview@mozac.org" + } + }, + "name": "Mozilla Android Components - ReaderView", + "version": "${version}", + "content_scripts": [ + { + "matches": ["<all_urls>"], + "js": ["readability/readability-readerable-0.4.2.js", "readerview-content.js"], + "run_at": "document_idle" + } + ], + "background": { + "scripts": ["readerview-background.js"], + "persistent": false + }, + "permissions": [ + "mozillaAddons", + "geckoViewAddons", + "nativeMessaging", + "nativeMessagingFromContent", + "storage", + "tabs", + "<all_urls>" + ] +} diff --git a/mobile/android/android-components/components/feature/readerview/src/main/assets/extensions/readerview/readability/JSDOMParser-0.4.2.js b/mobile/android/android-components/components/feature/readerview/src/main/assets/extensions/readerview/readability/JSDOMParser-0.4.2.js new file mode 100644 index 0000000000..a6a7f24666 --- /dev/null +++ b/mobile/android/android-components/components/feature/readerview/src/main/assets/extensions/readerview/readability/JSDOMParser-0.4.2.js @@ -0,0 +1,1196 @@ +/*eslint-env es6:false*/ +/* 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/. */ + +/** + * This is a relatively lightweight DOMParser that is safe to use in a web + * worker. This is far from a complete DOM implementation; however, it should + * contain the minimal set of functionality necessary for Readability.js. + * + * Aside from not implementing the full DOM API, there are other quirks to be + * aware of when using the JSDOMParser: + * + * 1) Properly formed HTML/XML must be used. This means you should be extra + * careful when using this parser on anything received directly from an + * XMLHttpRequest. Providing a serialized string from an XMLSerializer, + * however, should be safe (since the browser's XMLSerializer should + * generate valid HTML/XML). Therefore, if parsing a document from an XHR, + * the recommended approach is to do the XHR in the main thread, use + * XMLSerializer.serializeToString() on the responseXML, and pass the + * resulting string to the worker. + * + * 2) Live NodeLists are not supported. DOM methods and properties such as + * getElementsByTagName() and childNodes return standard arrays. If you + * want these lists to be updated when nodes are removed or added to the + * document, you must take care to manually update them yourself. + */ +(function (global) { + + // XML only defines these and the numeric ones: + + var entityTable = { + "lt": "<", + "gt": ">", + "amp": "&", + "quot": '"', + "apos": "'", + }; + + var reverseEntityTable = { + "<": "<", + ">": ">", + "&": "&", + '"': """, + "'": "'", + }; + + function encodeTextContentHTML(s) { + return s.replace(/[&<>]/g, function(x) { + return reverseEntityTable[x]; + }); + } + + function encodeHTML(s) { + return s.replace(/[&<>'"]/g, function(x) { + return reverseEntityTable[x]; + }); + } + + function decodeHTML(str) { + return str.replace(/&(quot|amp|apos|lt|gt);/g, function(match, tag) { + return entityTable[tag]; + }).replace(/&#(?:x([0-9a-z]{1,4})|([0-9]{1,4}));/gi, function(match, hex, numStr) { + var num = parseInt(hex || numStr, hex ? 16 : 10); // read num + return String.fromCharCode(num); + }); + } + + // When a style is set in JS, map it to the corresponding CSS attribute + var styleMap = { + "alignmentBaseline": "alignment-baseline", + "background": "background", + "backgroundAttachment": "background-attachment", + "backgroundClip": "background-clip", + "backgroundColor": "background-color", + "backgroundImage": "background-image", + "backgroundOrigin": "background-origin", + "backgroundPosition": "background-position", + "backgroundPositionX": "background-position-x", + "backgroundPositionY": "background-position-y", + "backgroundRepeat": "background-repeat", + "backgroundRepeatX": "background-repeat-x", + "backgroundRepeatY": "background-repeat-y", + "backgroundSize": "background-size", + "baselineShift": "baseline-shift", + "border": "border", + "borderBottom": "border-bottom", + "borderBottomColor": "border-bottom-color", + "borderBottomLeftRadius": "border-bottom-left-radius", + "borderBottomRightRadius": "border-bottom-right-radius", + "borderBottomStyle": "border-bottom-style", + "borderBottomWidth": "border-bottom-width", + "borderCollapse": "border-collapse", + "borderColor": "border-color", + "borderImage": "border-image", + "borderImageOutset": "border-image-outset", + "borderImageRepeat": "border-image-repeat", + "borderImageSlice": "border-image-slice", + "borderImageSource": "border-image-source", + "borderImageWidth": "border-image-width", + "borderLeft": "border-left", + "borderLeftColor": "border-left-color", + "borderLeftStyle": "border-left-style", + "borderLeftWidth": "border-left-width", + "borderRadius": "border-radius", + "borderRight": "border-right", + "borderRightColor": "border-right-color", + "borderRightStyle": "border-right-style", + "borderRightWidth": "border-right-width", + "borderSpacing": "border-spacing", + "borderStyle": "border-style", + "borderTop": "border-top", + "borderTopColor": "border-top-color", + "borderTopLeftRadius": "border-top-left-radius", + "borderTopRightRadius": "border-top-right-radius", + "borderTopStyle": "border-top-style", + "borderTopWidth": "border-top-width", + "borderWidth": "border-width", + "bottom": "bottom", + "boxShadow": "box-shadow", + "boxSizing": "box-sizing", + "captionSide": "caption-side", + "clear": "clear", + "clip": "clip", + "clipPath": "clip-path", + "clipRule": "clip-rule", + "color": "color", + "colorInterpolation": "color-interpolation", + "colorInterpolationFilters": "color-interpolation-filters", + "colorProfile": "color-profile", + "colorRendering": "color-rendering", + "content": "content", + "counterIncrement": "counter-increment", + "counterReset": "counter-reset", + "cursor": "cursor", + "direction": "direction", + "display": "display", + "dominantBaseline": "dominant-baseline", + "emptyCells": "empty-cells", + "enableBackground": "enable-background", + "fill": "fill", + "fillOpacity": "fill-opacity", + "fillRule": "fill-rule", + "filter": "filter", + "cssFloat": "float", + "floodColor": "flood-color", + "floodOpacity": "flood-opacity", + "font": "font", + "fontFamily": "font-family", + "fontSize": "font-size", + "fontStretch": "font-stretch", + "fontStyle": "font-style", + "fontVariant": "font-variant", + "fontWeight": "font-weight", + "glyphOrientationHorizontal": "glyph-orientation-horizontal", + "glyphOrientationVertical": "glyph-orientation-vertical", + "height": "height", + "imageRendering": "image-rendering", + "kerning": "kerning", + "left": "left", + "letterSpacing": "letter-spacing", + "lightingColor": "lighting-color", + "lineHeight": "line-height", + "listStyle": "list-style", + "listStyleImage": "list-style-image", + "listStylePosition": "list-style-position", + "listStyleType": "list-style-type", + "margin": "margin", + "marginBottom": "margin-bottom", + "marginLeft": "margin-left", + "marginRight": "margin-right", + "marginTop": "margin-top", + "marker": "marker", + "markerEnd": "marker-end", + "markerMid": "marker-mid", + "markerStart": "marker-start", + "mask": "mask", + "maxHeight": "max-height", + "maxWidth": "max-width", + "minHeight": "min-height", + "minWidth": "min-width", + "opacity": "opacity", + "orphans": "orphans", + "outline": "outline", + "outlineColor": "outline-color", + "outlineOffset": "outline-offset", + "outlineStyle": "outline-style", + "outlineWidth": "outline-width", + "overflow": "overflow", + "overflowX": "overflow-x", + "overflowY": "overflow-y", + "padding": "padding", + "paddingBottom": "padding-bottom", + "paddingLeft": "padding-left", + "paddingRight": "padding-right", + "paddingTop": "padding-top", + "page": "page", + "pageBreakAfter": "page-break-after", + "pageBreakBefore": "page-break-before", + "pageBreakInside": "page-break-inside", + "pointerEvents": "pointer-events", + "position": "position", + "quotes": "quotes", + "resize": "resize", + "right": "right", + "shapeRendering": "shape-rendering", + "size": "size", + "speak": "speak", + "src": "src", + "stopColor": "stop-color", + "stopOpacity": "stop-opacity", + "stroke": "stroke", + "strokeDasharray": "stroke-dasharray", + "strokeDashoffset": "stroke-dashoffset", + "strokeLinecap": "stroke-linecap", + "strokeLinejoin": "stroke-linejoin", + "strokeMiterlimit": "stroke-miterlimit", + "strokeOpacity": "stroke-opacity", + "strokeWidth": "stroke-width", + "tableLayout": "table-layout", + "textAlign": "text-align", + "textAnchor": "text-anchor", + "textDecoration": "text-decoration", + "textIndent": "text-indent", + "textLineThrough": "text-line-through", + "textLineThroughColor": "text-line-through-color", + "textLineThroughMode": "text-line-through-mode", + "textLineThroughStyle": "text-line-through-style", + "textLineThroughWidth": "text-line-through-width", + "textOverflow": "text-overflow", + "textOverline": "text-overline", + "textOverlineColor": "text-overline-color", + "textOverlineMode": "text-overline-mode", + "textOverlineStyle": "text-overline-style", + "textOverlineWidth": "text-overline-width", + "textRendering": "text-rendering", + "textShadow": "text-shadow", + "textTransform": "text-transform", + "textUnderline": "text-underline", + "textUnderlineColor": "text-underline-color", + "textUnderlineMode": "text-underline-mode", + "textUnderlineStyle": "text-underline-style", + "textUnderlineWidth": "text-underline-width", + "top": "top", + "unicodeBidi": "unicode-bidi", + "unicodeRange": "unicode-range", + "vectorEffect": "vector-effect", + "verticalAlign": "vertical-align", + "visibility": "visibility", + "whiteSpace": "white-space", + "widows": "widows", + "width": "width", + "wordBreak": "word-break", + "wordSpacing": "word-spacing", + "wordWrap": "word-wrap", + "writingMode": "writing-mode", + "zIndex": "z-index", + "zoom": "zoom", + }; + + // Elements that can be self-closing + var voidElems = { + "area": true, + "base": true, + "br": true, + "col": true, + "command": true, + "embed": true, + "hr": true, + "img": true, + "input": true, + "link": true, + "meta": true, + "param": true, + "source": true, + "wbr": true + }; + + var whitespace = [" ", "\t", "\n", "\r"]; + + // See https://developer.mozilla.org/en-US/docs/Web/API/Node/nodeType + var nodeTypes = { + ELEMENT_NODE: 1, + ATTRIBUTE_NODE: 2, + TEXT_NODE: 3, + CDATA_SECTION_NODE: 4, + ENTITY_REFERENCE_NODE: 5, + ENTITY_NODE: 6, + PROCESSING_INSTRUCTION_NODE: 7, + COMMENT_NODE: 8, + DOCUMENT_NODE: 9, + DOCUMENT_TYPE_NODE: 10, + DOCUMENT_FRAGMENT_NODE: 11, + NOTATION_NODE: 12 + }; + + function getElementsByTagName(tag) { + tag = tag.toUpperCase(); + var elems = []; + var allTags = (tag === "*"); + function getElems(node) { + var length = node.children.length; + for (var i = 0; i < length; i++) { + var child = node.children[i]; + if (allTags || (child.tagName === tag)) + elems.push(child); + getElems(child); + } + } + getElems(this); + elems._isLiveNodeList = true; + return elems; + } + + var Node = function () {}; + + Node.prototype = { + attributes: null, + childNodes: null, + localName: null, + nodeName: null, + parentNode: null, + textContent: null, + nextSibling: null, + previousSibling: null, + + get firstChild() { + return this.childNodes[0] || null; + }, + + get firstElementChild() { + return this.children[0] || null; + }, + + get lastChild() { + return this.childNodes[this.childNodes.length - 1] || null; + }, + + get lastElementChild() { + return this.children[this.children.length - 1] || null; + }, + + appendChild: function (child) { + if (child.parentNode) { + child.parentNode.removeChild(child); + } + + var last = this.lastChild; + if (last) + last.nextSibling = child; + child.previousSibling = last; + + if (child.nodeType === Node.ELEMENT_NODE) { + child.previousElementSibling = this.children[this.children.length - 1] || null; + this.children.push(child); + child.previousElementSibling && (child.previousElementSibling.nextElementSibling = child); + } + this.childNodes.push(child); + child.parentNode = this; + }, + + removeChild: function (child) { + var childNodes = this.childNodes; + var childIndex = childNodes.indexOf(child); + if (childIndex === -1) { + throw "removeChild: node not found"; + } else { + child.parentNode = null; + var prev = child.previousSibling; + var next = child.nextSibling; + if (prev) + prev.nextSibling = next; + if (next) + next.previousSibling = prev; + + if (child.nodeType === Node.ELEMENT_NODE) { + prev = child.previousElementSibling; + next = child.nextElementSibling; + if (prev) + prev.nextElementSibling = next; + if (next) + next.previousElementSibling = prev; + this.children.splice(this.children.indexOf(child), 1); + } + + child.previousSibling = child.nextSibling = null; + child.previousElementSibling = child.nextElementSibling = null; + + return childNodes.splice(childIndex, 1)[0]; + } + }, + + replaceChild: function (newNode, oldNode) { + var childNodes = this.childNodes; + var childIndex = childNodes.indexOf(oldNode); + if (childIndex === -1) { + throw "replaceChild: node not found"; + } else { + // This will take care of updating the new node if it was somewhere else before: + if (newNode.parentNode) + newNode.parentNode.removeChild(newNode); + + childNodes[childIndex] = newNode; + + // update the new node's sibling properties, and its new siblings' sibling properties + newNode.nextSibling = oldNode.nextSibling; + newNode.previousSibling = oldNode.previousSibling; + if (newNode.nextSibling) + newNode.nextSibling.previousSibling = newNode; + if (newNode.previousSibling) + newNode.previousSibling.nextSibling = newNode; + + newNode.parentNode = this; + + // Now deal with elements before we clear out those values for the old node, + // because it can help us take shortcuts here: + if (newNode.nodeType === Node.ELEMENT_NODE) { + if (oldNode.nodeType === Node.ELEMENT_NODE) { + // Both were elements, which makes this easier, we just swap things out: + newNode.previousElementSibling = oldNode.previousElementSibling; + newNode.nextElementSibling = oldNode.nextElementSibling; + if (newNode.previousElementSibling) + newNode.previousElementSibling.nextElementSibling = newNode; + if (newNode.nextElementSibling) + newNode.nextElementSibling.previousElementSibling = newNode; + this.children[this.children.indexOf(oldNode)] = newNode; + } else { + // Hard way: + newNode.previousElementSibling = (function() { + for (var i = childIndex - 1; i >= 0; i--) { + if (childNodes[i].nodeType === Node.ELEMENT_NODE) + return childNodes[i]; + } + return null; + })(); + if (newNode.previousElementSibling) { + newNode.nextElementSibling = newNode.previousElementSibling.nextElementSibling; + } else { + newNode.nextElementSibling = (function() { + for (var i = childIndex + 1; i < childNodes.length; i++) { + if (childNodes[i].nodeType === Node.ELEMENT_NODE) + return childNodes[i]; + } + return null; + })(); + } + if (newNode.previousElementSibling) + newNode.previousElementSibling.nextElementSibling = newNode; + if (newNode.nextElementSibling) + newNode.nextElementSibling.previousElementSibling = newNode; + + if (newNode.nextElementSibling) + this.children.splice(this.children.indexOf(newNode.nextElementSibling), 0, newNode); + else + this.children.push(newNode); + } + } else if (oldNode.nodeType === Node.ELEMENT_NODE) { + // new node is not an element node. + // if the old one was, update its element siblings: + if (oldNode.previousElementSibling) + oldNode.previousElementSibling.nextElementSibling = oldNode.nextElementSibling; + if (oldNode.nextElementSibling) + oldNode.nextElementSibling.previousElementSibling = oldNode.previousElementSibling; + this.children.splice(this.children.indexOf(oldNode), 1); + + // If the old node wasn't an element, neither the new nor the old node was an element, + // and the children array and its members shouldn't need any updating. + } + + + oldNode.parentNode = null; + oldNode.previousSibling = null; + oldNode.nextSibling = null; + if (oldNode.nodeType === Node.ELEMENT_NODE) { + oldNode.previousElementSibling = null; + oldNode.nextElementSibling = null; + } + return oldNode; + } + }, + + __JSDOMParser__: true, + }; + + for (var nodeType in nodeTypes) { + Node[nodeType] = Node.prototype[nodeType] = nodeTypes[nodeType]; + } + + var Attribute = function (name, value) { + this.name = name; + this._value = value; + }; + + Attribute.prototype = { + get value() { + return this._value; + }, + setValue: function(newValue) { + this._value = newValue; + }, + getEncodedValue: function() { + return encodeHTML(this._value); + }, + }; + + var Comment = function () { + this.childNodes = []; + }; + + Comment.prototype = { + __proto__: Node.prototype, + + nodeName: "#comment", + nodeType: Node.COMMENT_NODE + }; + + var Text = function () { + this.childNodes = []; + }; + + Text.prototype = { + __proto__: Node.prototype, + + nodeName: "#text", + nodeType: Node.TEXT_NODE, + get textContent() { + if (typeof this._textContent === "undefined") { + this._textContent = decodeHTML(this._innerHTML || ""); + } + return this._textContent; + }, + get innerHTML() { + if (typeof this._innerHTML === "undefined") { + this._innerHTML = encodeTextContentHTML(this._textContent || ""); + } + return this._innerHTML; + }, + + set innerHTML(newHTML) { + this._innerHTML = newHTML; + delete this._textContent; + }, + set textContent(newText) { + this._textContent = newText; + delete this._innerHTML; + }, + }; + + var Document = function (url) { + this.documentURI = url; + this.styleSheets = []; + this.childNodes = []; + this.children = []; + }; + + Document.prototype = { + __proto__: Node.prototype, + + nodeName: "#document", + nodeType: Node.DOCUMENT_NODE, + title: "", + + getElementsByTagName: getElementsByTagName, + + getElementById: function (id) { + function getElem(node) { + var length = node.children.length; + if (node.id === id) + return node; + for (var i = 0; i < length; i++) { + var el = getElem(node.children[i]); + if (el) + return el; + } + return null; + } + return getElem(this); + }, + + createElement: function (tag) { + var node = new Element(tag); + return node; + }, + + createTextNode: function (text) { + var node = new Text(); + node.textContent = text; + return node; + }, + + get baseURI() { + if (!this.hasOwnProperty("_baseURI")) { + this._baseURI = this.documentURI; + var baseElements = this.getElementsByTagName("base"); + var href = baseElements[0] && baseElements[0].getAttribute("href"); + if (href) { + try { + this._baseURI = (new URL(href, this._baseURI)).href; + } catch (ex) {/* Just fall back to documentURI */} + } + } + return this._baseURI; + }, + }; + + var Element = function (tag) { + // We use this to find the closing tag. + this._matchingTag = tag; + // We're explicitly a non-namespace aware parser, we just pretend it's all HTML. + var lastColonIndex = tag.lastIndexOf(":"); + if (lastColonIndex != -1) { + tag = tag.substring(lastColonIndex + 1); + } + this.attributes = []; + this.childNodes = []; + this.children = []; + this.nextElementSibling = this.previousElementSibling = null; + this.localName = tag.toLowerCase(); + this.tagName = tag.toUpperCase(); + this.style = new Style(this); + }; + + Element.prototype = { + __proto__: Node.prototype, + + nodeType: Node.ELEMENT_NODE, + + getElementsByTagName: getElementsByTagName, + + get className() { + return this.getAttribute("class") || ""; + }, + + set className(str) { + this.setAttribute("class", str); + }, + + get id() { + return this.getAttribute("id") || ""; + }, + + set id(str) { + this.setAttribute("id", str); + }, + + get href() { + return this.getAttribute("href") || ""; + }, + + set href(str) { + this.setAttribute("href", str); + }, + + get src() { + return this.getAttribute("src") || ""; + }, + + set src(str) { + this.setAttribute("src", str); + }, + + get srcset() { + return this.getAttribute("srcset") || ""; + }, + + set srcset(str) { + this.setAttribute("srcset", str); + }, + + get nodeName() { + return this.tagName; + }, + + get innerHTML() { + function getHTML(node) { + var i = 0; + for (i = 0; i < node.childNodes.length; i++) { + var child = node.childNodes[i]; + if (child.localName) { + arr.push("<" + child.localName); + + // serialize attribute list + for (var j = 0; j < child.attributes.length; j++) { + var attr = child.attributes[j]; + // the attribute value will be HTML escaped. + var val = attr.getEncodedValue(); + var quote = (val.indexOf('"') === -1 ? '"' : "'"); + arr.push(" " + attr.name + "=" + quote + val + quote); + } + + if (child.localName in voidElems && !child.childNodes.length) { + // if this is a self-closing element, end it here + arr.push("/>"); + } else { + // otherwise, add its children + arr.push(">"); + getHTML(child); + arr.push("</" + child.localName + ">"); + } + } else { + // This is a text node, so asking for innerHTML won't recurse. + arr.push(child.innerHTML); + } + } + } + + // Using Array.join() avoids the overhead from lazy string concatenation. + var arr = []; + getHTML(this); + return arr.join(""); + }, + + set innerHTML(html) { + var parser = new JSDOMParser(); + var node = parser.parse(html); + var i; + for (i = this.childNodes.length; --i >= 0;) { + this.childNodes[i].parentNode = null; + } + this.childNodes = node.childNodes; + this.children = node.children; + for (i = this.childNodes.length; --i >= 0;) { + this.childNodes[i].parentNode = this; + } + }, + + set textContent(text) { + // clear parentNodes for existing children + for (var i = this.childNodes.length; --i >= 0;) { + this.childNodes[i].parentNode = null; + } + + var node = new Text(); + this.childNodes = [ node ]; + this.children = []; + node.textContent = text; + node.parentNode = this; + }, + + get textContent() { + function getText(node) { + var nodes = node.childNodes; + for (var i = 0; i < nodes.length; i++) { + var child = nodes[i]; + if (child.nodeType === 3) { + text.push(child.textContent); + } else { + getText(child); + } + } + } + + // Using Array.join() avoids the overhead from lazy string concatenation. + // See http://blog.cdleary.com/2012/01/string-representation-in-spidermonkey/#ropes + var text = []; + getText(this); + return text.join(""); + }, + + getAttribute: function (name) { + for (var i = this.attributes.length; --i >= 0;) { + var attr = this.attributes[i]; + if (attr.name === name) { + return attr.value; + } + } + return undefined; + }, + + setAttribute: function (name, value) { + for (var i = this.attributes.length; --i >= 0;) { + var attr = this.attributes[i]; + if (attr.name === name) { + attr.setValue(value); + return; + } + } + this.attributes.push(new Attribute(name, value)); + }, + + removeAttribute: function (name) { + for (var i = this.attributes.length; --i >= 0;) { + var attr = this.attributes[i]; + if (attr.name === name) { + this.attributes.splice(i, 1); + break; + } + } + }, + + hasAttribute: function (name) { + return this.attributes.some(function (attr) { + return attr.name == name; + }); + }, + }; + + var Style = function (node) { + this.node = node; + }; + + // getStyle() and setStyle() use the style attribute string directly. This + // won't be very efficient if there are a lot of style manipulations, but + // it's the easiest way to make sure the style attribute string and the JS + // style property stay in sync. Readability.js doesn't do many style + // manipulations, so this should be okay. + Style.prototype = { + getStyle: function (styleName) { + var attr = this.node.getAttribute("style"); + if (!attr) + return undefined; + + var styles = attr.split(";"); + for (var i = 0; i < styles.length; i++) { + var style = styles[i].split(":"); + var name = style[0].trim(); + if (name === styleName) + return style[1].trim(); + } + + return undefined; + }, + + setStyle: function (styleName, styleValue) { + var value = this.node.getAttribute("style") || ""; + var index = 0; + do { + var next = value.indexOf(";", index) + 1; + var length = next - index - 1; + var style = (length > 0 ? value.substr(index, length) : value.substr(index)); + if (style.substr(0, style.indexOf(":")).trim() === styleName) { + value = value.substr(0, index).trim() + (next ? " " + value.substr(next).trim() : ""); + break; + } + index = next; + } while (index); + + value += " " + styleName + ": " + styleValue + ";"; + this.node.setAttribute("style", value.trim()); + } + }; + + // For each item in styleMap, define a getter and setter on the style + // property. + for (var jsName in styleMap) { + (function (cssName) { + Style.prototype.__defineGetter__(jsName, function () { + return this.getStyle(cssName); + }); + Style.prototype.__defineSetter__(jsName, function (value) { + this.setStyle(cssName, value); + }); + })(styleMap[jsName]); + } + + var JSDOMParser = function () { + this.currentChar = 0; + + // In makeElementNode() we build up many strings one char at a time. Using + // += for this results in lots of short-lived intermediate strings. It's + // better to build an array of single-char strings and then join() them + // together at the end. And reusing a single array (i.e. |this.strBuf|) + // over and over for this purpose uses less memory than using a new array + // for each string. + this.strBuf = []; + + // Similarly, we reuse this array to return the two arguments from + // makeElementNode(), which saves us from having to allocate a new array + // every time. + this.retPair = []; + + this.errorState = ""; + }; + + JSDOMParser.prototype = { + error: function(m) { + if (typeof dump !== "undefined") { + dump("JSDOMParser error: " + m + "\n"); + } else if (typeof console !== "undefined") { + console.log("JSDOMParser error: " + m + "\n"); + } + this.errorState += m + "\n"; + }, + + /** + * Look at the next character without advancing the index. + */ + peekNext: function () { + return this.html[this.currentChar]; + }, + + /** + * Get the next character and advance the index. + */ + nextChar: function () { + return this.html[this.currentChar++]; + }, + + /** + * Called after a quote character is read. This finds the next quote + * character and returns the text string in between. + */ + readString: function (quote) { + var str; + var n = this.html.indexOf(quote, this.currentChar); + if (n === -1) { + this.currentChar = this.html.length; + str = null; + } else { + str = this.html.substring(this.currentChar, n); + this.currentChar = n + 1; + } + + return str; + }, + + /** + * Called when parsing a node. This finds the next name/value attribute + * pair and adds the result to the attributes list. + */ + readAttribute: function (node) { + var name = ""; + + var n = this.html.indexOf("=", this.currentChar); + if (n === -1) { + this.currentChar = this.html.length; + } else { + // Read until a '=' character is hit; this will be the attribute key + name = this.html.substring(this.currentChar, n); + this.currentChar = n + 1; + } + + if (!name) + return; + + // After a '=', we should see a '"' for the attribute value + var c = this.nextChar(); + if (c !== '"' && c !== "'") { + this.error("Error reading attribute " + name + ", expecting '\"'"); + return; + } + + // Read the attribute value (and consume the matching quote) + var value = this.readString(c); + + node.attributes.push(new Attribute(name, decodeHTML(value))); + + return; + }, + + /** + * Parses and returns an Element node. This is called after a '<' has been + * read. + * + * @returns an array; the first index of the array is the parsed node; + * the second index is a boolean indicating whether this is a void + * Element + */ + makeElementNode: function (retPair) { + var c = this.nextChar(); + + // Read the Element tag name + var strBuf = this.strBuf; + strBuf.length = 0; + while (whitespace.indexOf(c) == -1 && c !== ">" && c !== "/") { + if (c === undefined) + return false; + strBuf.push(c); + c = this.nextChar(); + } + var tag = strBuf.join(""); + + if (!tag) + return false; + + var node = new Element(tag); + + // Read Element attributes + while (c !== "/" && c !== ">") { + if (c === undefined) + return false; + while (whitespace.indexOf(this.html[this.currentChar++]) != -1) { + // Advance cursor to first non-whitespace char. + } + this.currentChar--; + c = this.nextChar(); + if (c !== "/" && c !== ">") { + --this.currentChar; + this.readAttribute(node); + } + } + + // If this is a self-closing tag, read '/>' + var closed = false; + if (c === "/") { + closed = true; + c = this.nextChar(); + if (c !== ">") { + this.error("expected '>' to close " + tag); + return false; + } + } + + retPair[0] = node; + retPair[1] = closed; + return true; + }, + + /** + * If the current input matches this string, advance the input index; + * otherwise, do nothing. + * + * @returns whether input matched string + */ + match: function (str) { + var strlen = str.length; + if (this.html.substr(this.currentChar, strlen).toLowerCase() === str.toLowerCase()) { + this.currentChar += strlen; + return true; + } + return false; + }, + + /** + * Searches the input until a string is found and discards all input up to + * and including the matched string. + */ + discardTo: function (str) { + var index = this.html.indexOf(str, this.currentChar) + str.length; + if (index === -1) + this.currentChar = this.html.length; + this.currentChar = index; + }, + + /** + * Reads child nodes for the given node. + */ + readChildren: function (node) { + var child; + while ((child = this.readNode())) { + // Don't keep Comment nodes + if (child.nodeType !== 8) { + node.appendChild(child); + } + } + }, + + discardNextComment: function() { + if (this.match("--")) { + this.discardTo("-->"); + } else { + var c = this.nextChar(); + while (c !== ">") { + if (c === undefined) + return null; + if (c === '"' || c === "'") + this.readString(c); + c = this.nextChar(); + } + } + return new Comment(); + }, + + + /** + * Reads the next child node from the input. If we're reading a closing + * tag, or if we've reached the end of input, return null. + * + * @returns the node + */ + readNode: function () { + var c = this.nextChar(); + + if (c === undefined) + return null; + + // Read any text as Text node + var textNode; + if (c !== "<") { + --this.currentChar; + textNode = new Text(); + var n = this.html.indexOf("<", this.currentChar); + if (n === -1) { + textNode.innerHTML = this.html.substring(this.currentChar, this.html.length); + this.currentChar = this.html.length; + } else { + textNode.innerHTML = this.html.substring(this.currentChar, n); + this.currentChar = n; + } + return textNode; + } + + if (this.match("![CDATA[")) { + var endChar = this.html.indexOf("]]>", this.currentChar); + if (endChar === -1) { + this.error("unclosed CDATA section"); + return null; + } + textNode = new Text(); + textNode.textContent = this.html.substring(this.currentChar, endChar); + this.currentChar = endChar + ("]]>").length; + return textNode; + } + + c = this.peekNext(); + + // Read Comment node. Normally, Comment nodes know their inner + // textContent, but we don't really care about Comment nodes (we throw + // them away in readChildren()). So just returning an empty Comment node + // here is sufficient. + if (c === "!" || c === "?") { + // We're still before the ! or ? that is starting this comment: + this.currentChar++; + return this.discardNextComment(); + } + + // If we're reading a closing tag, return null. This means we've reached + // the end of this set of child nodes. + if (c === "/") { + --this.currentChar; + return null; + } + + // Otherwise, we're looking at an Element node + var result = this.makeElementNode(this.retPair); + if (!result) + return null; + + var node = this.retPair[0]; + var closed = this.retPair[1]; + var localName = node.localName; + + // If this isn't a void Element, read its child nodes + if (!closed) { + this.readChildren(node); + var closingTag = "</" + node._matchingTag + ">"; + if (!this.match(closingTag)) { + this.error("expected '" + closingTag + "' and got " + this.html.substr(this.currentChar, closingTag.length)); + return null; + } + } + + // Only use the first title, because SVG might have other + // title elements which we don't care about (medium.com + // does this, at least). + if (localName === "title" && !this.doc.title) { + this.doc.title = node.textContent.trim(); + } else if (localName === "head") { + this.doc.head = node; + } else if (localName === "body") { + this.doc.body = node; + } else if (localName === "html") { + this.doc.documentElement = node; + } + + return node; + }, + + /** + * Parses an HTML string and returns a JS implementation of the Document. + */ + parse: function (html, url) { + this.html = html; + var doc = this.doc = new Document(url); + this.readChildren(doc); + + // If this is an HTML document, remove root-level children except for the + // <html> node + if (doc.documentElement) { + for (var i = doc.childNodes.length; --i >= 0;) { + var child = doc.childNodes[i]; + if (child !== doc.documentElement) { + doc.removeChild(child); + } + } + } + + return doc; + } + }; + + // Attach the standard DOM types to the global scope + global.Node = Node; + global.Comment = Comment; + global.Document = Document; + global.Element = Element; + global.Text = Text; + + // Attach JSDOMParser to the global scope + global.JSDOMParser = JSDOMParser; + +})(this); + +if (typeof module === "object") { + module.exports = this.JSDOMParser; +}
\ No newline at end of file diff --git a/mobile/android/android-components/components/feature/readerview/src/main/assets/extensions/readerview/readability/readability-0.4.2.js b/mobile/android/android-components/components/feature/readerview/src/main/assets/extensions/readerview/readability/readability-0.4.2.js new file mode 100644 index 0000000000..ce06df459d --- /dev/null +++ b/mobile/android/android-components/components/feature/readerview/src/main/assets/extensions/readerview/readability/readability-0.4.2.js @@ -0,0 +1,2283 @@ +/*eslint-env es6:false*/ +/* + * Copyright (c) 2010 Arc90 Inc + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +/* + * This code is heavily based on Arc90's readability.js (1.7.1) script + * available at: http://code.google.com/p/arc90labs-readability + */ + +/** + * Public constructor. + * @param {HTMLDocument} doc The document to parse. + * @param {Object} options The options object. + */ +function Readability(doc, options) { + // In some older versions, people passed a URI as the first argument. Cope: + if (options && options.documentElement) { + doc = options; + options = arguments[2]; + } else if (!doc || !doc.documentElement) { + throw new Error("First argument to Readability constructor should be a document object."); + } + options = options || {}; + + this._doc = doc; + this._docJSDOMParser = this._doc.firstChild.__JSDOMParser__; + this._articleTitle = null; + this._articleByline = null; + this._articleDir = null; + this._articleSiteName = null; + this._attempts = []; + + // Configurable options + this._debug = !!options.debug; + this._maxElemsToParse = options.maxElemsToParse || this.DEFAULT_MAX_ELEMS_TO_PARSE; + this._nbTopCandidates = options.nbTopCandidates || this.DEFAULT_N_TOP_CANDIDATES; + this._charThreshold = options.charThreshold || this.DEFAULT_CHAR_THRESHOLD; + this._classesToPreserve = this.CLASSES_TO_PRESERVE.concat(options.classesToPreserve || []); + this._keepClasses = !!options.keepClasses; + this._serializer = options.serializer || function(el) { + return el.innerHTML; + }; + this._disableJSONLD = !!options.disableJSONLD; + + // Start with all flags set + this._flags = this.FLAG_STRIP_UNLIKELYS | + this.FLAG_WEIGHT_CLASSES | + this.FLAG_CLEAN_CONDITIONALLY; + + + // Control whether log messages are sent to the console + if (this._debug) { + let logNode = function(node) { + if (node.nodeType == node.TEXT_NODE) { + return `${node.nodeName} ("${node.textContent}")`; + } + let attrPairs = Array.from(node.attributes || [], function(attr) { + return `${attr.name}="${attr.value}"`; + }).join(" "); + return `<${node.localName} ${attrPairs}>`; + }; + this.log = function () { + if (typeof dump !== "undefined") { + var msg = Array.prototype.map.call(arguments, function(x) { + return (x && x.nodeName) ? logNode(x) : x; + }).join(" "); + dump("Reader: (Readability) " + msg + "\n"); + } else if (typeof console !== "undefined") { + let args = Array.from(arguments, arg => { + if (arg && arg.nodeType == this.ELEMENT_NODE) { + return logNode(arg); + } + return arg; + }); + args.unshift("Reader: (Readability)"); + console.log.apply(console, args); + } + }; + } else { + this.log = function () {}; + } +} + +Readability.prototype = { + FLAG_STRIP_UNLIKELYS: 0x1, + FLAG_WEIGHT_CLASSES: 0x2, + FLAG_CLEAN_CONDITIONALLY: 0x4, + + // https://developer.mozilla.org/en-US/docs/Web/API/Node/nodeType + ELEMENT_NODE: 1, + TEXT_NODE: 3, + + // Max number of nodes supported by this parser. Default: 0 (no limit) + DEFAULT_MAX_ELEMS_TO_PARSE: 0, + + // The number of top candidates to consider when analysing how + // tight the competition is among candidates. + DEFAULT_N_TOP_CANDIDATES: 5, + + // Element tags to score by default. + DEFAULT_TAGS_TO_SCORE: "section,h2,h3,h4,h5,h6,p,td,pre".toUpperCase().split(","), + + // The default number of chars an article must have in order to return a result + DEFAULT_CHAR_THRESHOLD: 500, + + // All of the regular expressions in use within readability. + // Defined up here so we don't instantiate them repeatedly in loops. + REGEXPS: { + // NOTE: These two regular expressions are duplicated in + // Readability-readerable.js. Please keep both copies in sync. + unlikelyCandidates: /-ad-|ai2html|banner|breadcrumbs|combx|comment|community|cover-wrap|disqus|extra|footer|gdpr|header|legends|menu|related|remark|replies|rss|shoutbox|sidebar|skyscraper|social|sponsor|supplemental|ad-break|agegate|pagination|pager|popup|yom-remote/i, + okMaybeItsACandidate: /and|article|body|column|content|main|shadow/i, + + positive: /article|body|content|entry|hentry|h-entry|main|page|pagination|post|text|blog|story/i, + negative: /-ad-|hidden|^hid$| hid$| hid |^hid |banner|combx|comment|com-|contact|foot|footer|footnote|gdpr|masthead|media|meta|outbrain|promo|related|scroll|share|shoutbox|sidebar|skyscraper|sponsor|shopping|tags|tool|widget/i, + extraneous: /print|archive|comment|discuss|e[\-]?mail|share|reply|all|login|sign|single|utility/i, + byline: /byline|author|dateline|writtenby|p-author/i, + replaceFonts: /<(\/?)font[^>]*>/gi, + normalize: /\s{2,}/g, + videos: /\/\/(www\.)?((dailymotion|youtube|youtube-nocookie|player\.vimeo|v\.qq)\.com|(archive|upload\.wikimedia)\.org|player\.twitch\.tv)/i, + shareElements: /(\b|_)(share|sharedaddy)(\b|_)/i, + nextLink: /(next|weiter|continue|>([^\|]|$)|»([^\|]|$))/i, + prevLink: /(prev|earl|old|new|<|«)/i, + tokenize: /\W+/g, + whitespace: /^\s*$/, + hasContent: /\S$/, + hashUrl: /^#.+/, + srcsetUrl: /(\S+)(\s+[\d.]+[xw])?(\s*(?:,|$))/g, + b64DataUrl: /^data:\s*([^\s;,]+)\s*;\s*base64\s*,/i, + // See: https://schema.org/Article + jsonLdArticleTypes: /^Article|AdvertiserContentArticle|NewsArticle|AnalysisNewsArticle|AskPublicNewsArticle|BackgroundNewsArticle|OpinionNewsArticle|ReportageNewsArticle|ReviewNewsArticle|Report|SatiricalArticle|ScholarlyArticle|MedicalScholarlyArticle|SocialMediaPosting|BlogPosting|LiveBlogPosting|DiscussionForumPosting|TechArticle|APIReference$/ + }, + + UNLIKELY_ROLES: [ "menu", "menubar", "complementary", "navigation", "alert", "alertdialog", "dialog" ], + + DIV_TO_P_ELEMS: new Set([ "BLOCKQUOTE", "DL", "DIV", "IMG", "OL", "P", "PRE", "TABLE", "UL" ]), + + ALTER_TO_DIV_EXCEPTIONS: ["DIV", "ARTICLE", "SECTION", "P"], + + PRESENTATIONAL_ATTRIBUTES: [ "align", "background", "bgcolor", "border", "cellpadding", "cellspacing", "frame", "hspace", "rules", "style", "valign", "vspace" ], + + DEPRECATED_SIZE_ATTRIBUTE_ELEMS: [ "TABLE", "TH", "TD", "HR", "PRE" ], + + // The commented out elements qualify as phrasing content but tend to be + // removed by readability when put into paragraphs, so we ignore them here. + PHRASING_ELEMS: [ + // "CANVAS", "IFRAME", "SVG", "VIDEO", + "ABBR", "AUDIO", "B", "BDO", "BR", "BUTTON", "CITE", "CODE", "DATA", + "DATALIST", "DFN", "EM", "EMBED", "I", "IMG", "INPUT", "KBD", "LABEL", + "MARK", "MATH", "METER", "NOSCRIPT", "OBJECT", "OUTPUT", "PROGRESS", "Q", + "RUBY", "SAMP", "SCRIPT", "SELECT", "SMALL", "SPAN", "STRONG", "SUB", + "SUP", "TEXTAREA", "TIME", "VAR", "WBR" + ], + + // These are the classes that readability sets itself. + CLASSES_TO_PRESERVE: [ "page" ], + + // These are the list of HTML entities that need to be escaped. + HTML_ESCAPE_MAP: { + "lt": "<", + "gt": ">", + "amp": "&", + "quot": '"', + "apos": "'", + }, + + /** + * Run any post-process modifications to article content as necessary. + * + * @param Element + * @return void + **/ + _postProcessContent: function(articleContent) { + // Readability cannot open relative uris so we convert them to absolute uris. + this._fixRelativeUris(articleContent); + + this._simplifyNestedElements(articleContent); + + if (!this._keepClasses) { + // Remove classes. + this._cleanClasses(articleContent); + } + }, + + /** + * Iterates over a NodeList, calls `filterFn` for each node and removes node + * if function returned `true`. + * + * If function is not passed, removes all the nodes in node list. + * + * @param NodeList nodeList The nodes to operate on + * @param Function filterFn the function to use as a filter + * @return void + */ + _removeNodes: function(nodeList, filterFn) { + // Avoid ever operating on live node lists. + if (this._docJSDOMParser && nodeList._isLiveNodeList) { + throw new Error("Do not pass live node lists to _removeNodes"); + } + for (var i = nodeList.length - 1; i >= 0; i--) { + var node = nodeList[i]; + var parentNode = node.parentNode; + if (parentNode) { + if (!filterFn || filterFn.call(this, node, i, nodeList)) { + parentNode.removeChild(node); + } + } + } + }, + + /** + * Iterates over a NodeList, and calls _setNodeTag for each node. + * + * @param NodeList nodeList The nodes to operate on + * @param String newTagName the new tag name to use + * @return void + */ + _replaceNodeTags: function(nodeList, newTagName) { + // Avoid ever operating on live node lists. + if (this._docJSDOMParser && nodeList._isLiveNodeList) { + throw new Error("Do not pass live node lists to _replaceNodeTags"); + } + for (const node of nodeList) { + this._setNodeTag(node, newTagName); + } + }, + + /** + * Iterate over a NodeList, which doesn't natively fully implement the Array + * interface. + * + * For convenience, the current object context is applied to the provided + * iterate function. + * + * @param NodeList nodeList The NodeList. + * @param Function fn The iterate function. + * @return void + */ + _forEachNode: function(nodeList, fn) { + Array.prototype.forEach.call(nodeList, fn, this); + }, + + /** + * Iterate over a NodeList, and return the first node that passes + * the supplied test function + * + * For convenience, the current object context is applied to the provided + * test function. + * + * @param NodeList nodeList The NodeList. + * @param Function fn The test function. + * @return void + */ + _findNode: function(nodeList, fn) { + return Array.prototype.find.call(nodeList, fn, this); + }, + + /** + * Iterate over a NodeList, return true if any of the provided iterate + * function calls returns true, false otherwise. + * + * For convenience, the current object context is applied to the + * provided iterate function. + * + * @param NodeList nodeList The NodeList. + * @param Function fn The iterate function. + * @return Boolean + */ + _someNode: function(nodeList, fn) { + return Array.prototype.some.call(nodeList, fn, this); + }, + + /** + * Iterate over a NodeList, return true if all of the provided iterate + * function calls return true, false otherwise. + * + * For convenience, the current object context is applied to the + * provided iterate function. + * + * @param NodeList nodeList The NodeList. + * @param Function fn The iterate function. + * @return Boolean + */ + _everyNode: function(nodeList, fn) { + return Array.prototype.every.call(nodeList, fn, this); + }, + + /** + * Concat all nodelists passed as arguments. + * + * @return ...NodeList + * @return Array + */ + _concatNodeLists: function() { + var slice = Array.prototype.slice; + var args = slice.call(arguments); + var nodeLists = args.map(function(list) { + return slice.call(list); + }); + return Array.prototype.concat.apply([], nodeLists); + }, + + _getAllNodesWithTag: function(node, tagNames) { + if (node.querySelectorAll) { + return node.querySelectorAll(tagNames.join(",")); + } + return [].concat.apply([], tagNames.map(function(tag) { + var collection = node.getElementsByTagName(tag); + return Array.isArray(collection) ? collection : Array.from(collection); + })); + }, + + /** + * Removes the class="" attribute from every element in the given + * subtree, except those that match CLASSES_TO_PRESERVE and + * the classesToPreserve array from the options object. + * + * @param Element + * @return void + */ + _cleanClasses: function(node) { + var classesToPreserve = this._classesToPreserve; + var className = (node.getAttribute("class") || "") + .split(/\s+/) + .filter(function(cls) { + return classesToPreserve.indexOf(cls) != -1; + }) + .join(" "); + + if (className) { + node.setAttribute("class", className); + } else { + node.removeAttribute("class"); + } + + for (node = node.firstElementChild; node; node = node.nextElementSibling) { + this._cleanClasses(node); + } + }, + + /** + * Converts each <a> and <img> uri in the given element to an absolute URI, + * ignoring #ref URIs. + * + * @param Element + * @return void + */ + _fixRelativeUris: function(articleContent) { + var baseURI = this._doc.baseURI; + var documentURI = this._doc.documentURI; + function toAbsoluteURI(uri) { + // Leave hash links alone if the base URI matches the document URI: + if (baseURI == documentURI && uri.charAt(0) == "#") { + return uri; + } + + // Otherwise, resolve against base URI: + try { + return new URL(uri, baseURI).href; + } catch (ex) { + // Something went wrong, just return the original: + } + return uri; + } + + var links = this._getAllNodesWithTag(articleContent, ["a"]); + this._forEachNode(links, function(link) { + var href = link.getAttribute("href"); + if (href) { + // Remove links with javascript: URIs, since + // they won't work after scripts have been removed from the page. + if (href.indexOf("javascript:") === 0) { + // if the link only contains simple text content, it can be converted to a text node + if (link.childNodes.length === 1 && link.childNodes[0].nodeType === this.TEXT_NODE) { + var text = this._doc.createTextNode(link.textContent); + link.parentNode.replaceChild(text, link); + } else { + // if the link has multiple children, they should all be preserved + var container = this._doc.createElement("span"); + while (link.firstChild) { + container.appendChild(link.firstChild); + } + link.parentNode.replaceChild(container, link); + } + } else { + link.setAttribute("href", toAbsoluteURI(href)); + } + } + }); + + var medias = this._getAllNodesWithTag(articleContent, [ + "img", "picture", "figure", "video", "audio", "source" + ]); + + this._forEachNode(medias, function(media) { + var src = media.getAttribute("src"); + var poster = media.getAttribute("poster"); + var srcset = media.getAttribute("srcset"); + + if (src) { + media.setAttribute("src", toAbsoluteURI(src)); + } + + if (poster) { + media.setAttribute("poster", toAbsoluteURI(poster)); + } + + if (srcset) { + var newSrcset = srcset.replace(this.REGEXPS.srcsetUrl, function(_, p1, p2, p3) { + return toAbsoluteURI(p1) + (p2 || "") + p3; + }); + + media.setAttribute("srcset", newSrcset); + } + }); + }, + + _simplifyNestedElements: function(articleContent) { + var node = articleContent; + + while (node) { + if (node.parentNode && ["DIV", "SECTION"].includes(node.tagName) && !(node.id && node.id.startsWith("readability"))) { + if (this._isElementWithoutContent(node)) { + node = this._removeAndGetNext(node); + continue; + } else if (this._hasSingleTagInsideElement(node, "DIV") || this._hasSingleTagInsideElement(node, "SECTION")) { + var child = node.children[0]; + for (var i = 0; i < node.attributes.length; i++) { + child.setAttribute(node.attributes[i].name, node.attributes[i].value); + } + node.parentNode.replaceChild(child, node); + node = child; + continue; + } + } + + node = this._getNextNode(node); + } + }, + + /** + * Get the article title as an H1. + * + * @return string + **/ + _getArticleTitle: function() { + var doc = this._doc; + var curTitle = ""; + var origTitle = ""; + + try { + curTitle = origTitle = doc.title.trim(); + + // If they had an element with id "title" in their HTML + if (typeof curTitle !== "string") + curTitle = origTitle = this._getInnerText(doc.getElementsByTagName("title")[0]); + } catch (e) {/* ignore exceptions setting the title. */} + + var titleHadHierarchicalSeparators = false; + function wordCount(str) { + return str.split(/\s+/).length; + } + + // If there's a separator in the title, first remove the final part + if ((/ [\|\-\\\/>»] /).test(curTitle)) { + titleHadHierarchicalSeparators = / [\\\/>»] /.test(curTitle); + curTitle = origTitle.replace(/(.*)[\|\-\\\/>»] .*/gi, "$1"); + + // If the resulting title is too short (3 words or fewer), remove + // the first part instead: + if (wordCount(curTitle) < 3) + curTitle = origTitle.replace(/[^\|\-\\\/>»]*[\|\-\\\/>»](.*)/gi, "$1"); + } else if (curTitle.indexOf(": ") !== -1) { + // Check if we have an heading containing this exact string, so we + // could assume it's the full title. + var headings = this._concatNodeLists( + doc.getElementsByTagName("h1"), + doc.getElementsByTagName("h2") + ); + var trimmedTitle = curTitle.trim(); + var match = this._someNode(headings, function(heading) { + return heading.textContent.trim() === trimmedTitle; + }); + + // If we don't, let's extract the title out of the original title string. + if (!match) { + curTitle = origTitle.substring(origTitle.lastIndexOf(":") + 1); + + // If the title is now too short, try the first colon instead: + if (wordCount(curTitle) < 3) { + curTitle = origTitle.substring(origTitle.indexOf(":") + 1); + // But if we have too many words before the colon there's something weird + // with the titles and the H tags so let's just use the original title instead + } else if (wordCount(origTitle.substr(0, origTitle.indexOf(":"))) > 5) { + curTitle = origTitle; + } + } + } else if (curTitle.length > 150 || curTitle.length < 15) { + var hOnes = doc.getElementsByTagName("h1"); + + if (hOnes.length === 1) + curTitle = this._getInnerText(hOnes[0]); + } + + curTitle = curTitle.trim().replace(this.REGEXPS.normalize, " "); + // If we now have 4 words or fewer as our title, and either no + // 'hierarchical' separators (\, /, > or ») were found in the original + // title or we decreased the number of words by more than 1 word, use + // the original title. + var curTitleWordCount = wordCount(curTitle); + if (curTitleWordCount <= 4 && + (!titleHadHierarchicalSeparators || + curTitleWordCount != wordCount(origTitle.replace(/[\|\-\\\/>»]+/g, "")) - 1)) { + curTitle = origTitle; + } + + return curTitle; + }, + + /** + * Prepare the HTML document for readability to scrape it. + * This includes things like stripping javascript, CSS, and handling terrible markup. + * + * @return void + **/ + _prepDocument: function() { + var doc = this._doc; + + // Remove all style tags in head + this._removeNodes(this._getAllNodesWithTag(doc, ["style"])); + + if (doc.body) { + this._replaceBrs(doc.body); + } + + this._replaceNodeTags(this._getAllNodesWithTag(doc, ["font"]), "SPAN"); + }, + + /** + * Finds the next node, starting from the given node, and ignoring + * whitespace in between. If the given node is an element, the same node is + * returned. + */ + _nextNode: function (node) { + var next = node; + while (next + && (next.nodeType != this.ELEMENT_NODE) + && this.REGEXPS.whitespace.test(next.textContent)) { + next = next.nextSibling; + } + return next; + }, + + /** + * Replaces 2 or more successive <br> elements with a single <p>. + * Whitespace between <br> elements are ignored. For example: + * <div>foo<br>bar<br> <br><br>abc</div> + * will become: + * <div>foo<br>bar<p>abc</p></div> + */ + _replaceBrs: function (elem) { + this._forEachNode(this._getAllNodesWithTag(elem, ["br"]), function(br) { + var next = br.nextSibling; + + // Whether 2 or more <br> elements have been found and replaced with a + // <p> block. + var replaced = false; + + // If we find a <br> chain, remove the <br>s until we hit another node + // or non-whitespace. This leaves behind the first <br> in the chain + // (which will be replaced with a <p> later). + while ((next = this._nextNode(next)) && (next.tagName == "BR")) { + replaced = true; + var brSibling = next.nextSibling; + next.parentNode.removeChild(next); + next = brSibling; + } + + // If we removed a <br> chain, replace the remaining <br> with a <p>. Add + // all sibling nodes as children of the <p> until we hit another <br> + // chain. + if (replaced) { + var p = this._doc.createElement("p"); + br.parentNode.replaceChild(p, br); + + next = p.nextSibling; + while (next) { + // If we've hit another <br><br>, we're done adding children to this <p>. + if (next.tagName == "BR") { + var nextElem = this._nextNode(next.nextSibling); + if (nextElem && nextElem.tagName == "BR") + break; + } + + if (!this._isPhrasingContent(next)) + break; + + // Otherwise, make this node a child of the new <p>. + var sibling = next.nextSibling; + p.appendChild(next); + next = sibling; + } + + while (p.lastChild && this._isWhitespace(p.lastChild)) { + p.removeChild(p.lastChild); + } + + if (p.parentNode.tagName === "P") + this._setNodeTag(p.parentNode, "DIV"); + } + }); + }, + + _setNodeTag: function (node, tag) { + this.log("_setNodeTag", node, tag); + if (this._docJSDOMParser) { + node.localName = tag.toLowerCase(); + node.tagName = tag.toUpperCase(); + return node; + } + + var replacement = node.ownerDocument.createElement(tag); + while (node.firstChild) { + replacement.appendChild(node.firstChild); + } + node.parentNode.replaceChild(replacement, node); + if (node.readability) + replacement.readability = node.readability; + + for (var i = 0; i < node.attributes.length; i++) { + try { + replacement.setAttribute(node.attributes[i].name, node.attributes[i].value); + } catch (ex) { + /* it's possible for setAttribute() to throw if the attribute name + * isn't a valid XML Name. Such attributes can however be parsed from + * source in HTML docs, see https://github.com/whatwg/html/issues/4275, + * so we can hit them here and then throw. We don't care about such + * attributes so we ignore them. + */ + } + } + return replacement; + }, + + /** + * Prepare the article node for display. Clean out any inline styles, + * iframes, forms, strip extraneous <p> tags, etc. + * + * @param Element + * @return void + **/ + _prepArticle: function(articleContent) { + this._cleanStyles(articleContent); + + // Check for data tables before we continue, to avoid removing items in + // those tables, which will often be isolated even though they're + // visually linked to other content-ful elements (text, images, etc.). + this._markDataTables(articleContent); + + this._fixLazyImages(articleContent); + + // Clean out junk from the article content + this._cleanConditionally(articleContent, "form"); + this._cleanConditionally(articleContent, "fieldset"); + this._clean(articleContent, "object"); + this._clean(articleContent, "embed"); + this._clean(articleContent, "footer"); + this._clean(articleContent, "link"); + this._clean(articleContent, "aside"); + + // Clean out elements with little content that have "share" in their id/class combinations from final top candidates, + // which means we don't remove the top candidates even they have "share". + + var shareElementThreshold = this.DEFAULT_CHAR_THRESHOLD; + + this._forEachNode(articleContent.children, function (topCandidate) { + this._cleanMatchedNodes(topCandidate, function (node, matchString) { + return this.REGEXPS.shareElements.test(matchString) && node.textContent.length < shareElementThreshold; + }); + }); + + this._clean(articleContent, "iframe"); + this._clean(articleContent, "input"); + this._clean(articleContent, "textarea"); + this._clean(articleContent, "select"); + this._clean(articleContent, "button"); + this._cleanHeaders(articleContent); + + // Do these last as the previous stuff may have removed junk + // that will affect these + this._cleanConditionally(articleContent, "table"); + this._cleanConditionally(articleContent, "ul"); + this._cleanConditionally(articleContent, "div"); + + // replace H1 with H2 as H1 should be only title that is displayed separately + this._replaceNodeTags(this._getAllNodesWithTag(articleContent, ["h1"]), "h2"); + + // Remove extra paragraphs + this._removeNodes(this._getAllNodesWithTag(articleContent, ["p"]), function (paragraph) { + var imgCount = paragraph.getElementsByTagName("img").length; + var embedCount = paragraph.getElementsByTagName("embed").length; + var objectCount = paragraph.getElementsByTagName("object").length; + // At this point, nasty iframes have been removed, only remain embedded video ones. + var iframeCount = paragraph.getElementsByTagName("iframe").length; + var totalCount = imgCount + embedCount + objectCount + iframeCount; + + return totalCount === 0 && !this._getInnerText(paragraph, false); + }); + + this._forEachNode(this._getAllNodesWithTag(articleContent, ["br"]), function(br) { + var next = this._nextNode(br.nextSibling); + if (next && next.tagName == "P") + br.parentNode.removeChild(br); + }); + + // Remove single-cell tables + this._forEachNode(this._getAllNodesWithTag(articleContent, ["table"]), function(table) { + var tbody = this._hasSingleTagInsideElement(table, "TBODY") ? table.firstElementChild : table; + if (this._hasSingleTagInsideElement(tbody, "TR")) { + var row = tbody.firstElementChild; + if (this._hasSingleTagInsideElement(row, "TD")) { + var cell = row.firstElementChild; + cell = this._setNodeTag(cell, this._everyNode(cell.childNodes, this._isPhrasingContent) ? "P" : "DIV"); + table.parentNode.replaceChild(cell, table); + } + } + }); + }, + + /** + * Initialize a node with the readability object. Also checks the + * className/id for special names to add to its score. + * + * @param Element + * @return void + **/ + _initializeNode: function(node) { + node.readability = {"contentScore": 0}; + + switch (node.tagName) { + case "DIV": + node.readability.contentScore += 5; + break; + + case "PRE": + case "TD": + case "BLOCKQUOTE": + node.readability.contentScore += 3; + break; + + case "ADDRESS": + case "OL": + case "UL": + case "DL": + case "DD": + case "DT": + case "LI": + case "FORM": + node.readability.contentScore -= 3; + break; + + case "H1": + case "H2": + case "H3": + case "H4": + case "H5": + case "H6": + case "TH": + node.readability.contentScore -= 5; + break; + } + + node.readability.contentScore += this._getClassWeight(node); + }, + + _removeAndGetNext: function(node) { + var nextNode = this._getNextNode(node, true); + node.parentNode.removeChild(node); + return nextNode; + }, + + /** + * Traverse the DOM from node to node, starting at the node passed in. + * Pass true for the second parameter to indicate this node itself + * (and its kids) are going away, and we want the next node over. + * + * Calling this in a loop will traverse the DOM depth-first. + */ + _getNextNode: function(node, ignoreSelfAndKids) { + // First check for kids if those aren't being ignored + if (!ignoreSelfAndKids && node.firstElementChild) { + return node.firstElementChild; + } + // Then for siblings... + if (node.nextElementSibling) { + return node.nextElementSibling; + } + // And finally, move up the parent chain *and* find a sibling + // (because this is depth-first traversal, we will have already + // seen the parent nodes themselves). + do { + node = node.parentNode; + } while (node && !node.nextElementSibling); + return node && node.nextElementSibling; + }, + + // compares second text to first one + // 1 = same text, 0 = completely different text + // works the way that it splits both texts into words and then finds words that are unique in second text + // the result is given by the lower length of unique parts + _textSimilarity: function(textA, textB) { + var tokensA = textA.toLowerCase().split(this.REGEXPS.tokenize).filter(Boolean); + var tokensB = textB.toLowerCase().split(this.REGEXPS.tokenize).filter(Boolean); + if (!tokensA.length || !tokensB.length) { + return 0; + } + var uniqTokensB = tokensB.filter(token => !tokensA.includes(token)); + var distanceB = uniqTokensB.join(" ").length / tokensB.join(" ").length; + return 1 - distanceB; + }, + + _checkByline: function(node, matchString) { + if (this._articleByline) { + return false; + } + + if (node.getAttribute !== undefined) { + var rel = node.getAttribute("rel"); + var itemprop = node.getAttribute("itemprop"); + } + + if ((rel === "author" || (itemprop && itemprop.indexOf("author") !== -1) || this.REGEXPS.byline.test(matchString)) && this._isValidByline(node.textContent)) { + this._articleByline = node.textContent.trim(); + return true; + } + + return false; + }, + + _getNodeAncestors: function(node, maxDepth) { + maxDepth = maxDepth || 0; + var i = 0, ancestors = []; + while (node.parentNode) { + ancestors.push(node.parentNode); + if (maxDepth && ++i === maxDepth) + break; + node = node.parentNode; + } + return ancestors; + }, + + /*** + * grabArticle - Using a variety of metrics (content score, classname, element types), find the content that is + * most likely to be the stuff a user wants to read. Then return it wrapped up in a div. + * + * @param page a document to run upon. Needs to be a full document, complete with body. + * @return Element + **/ + _grabArticle: function (page) { + this.log("**** grabArticle ****"); + var doc = this._doc; + var isPaging = page !== null; + page = page ? page : this._doc.body; + + // We can't grab an article if we don't have a page! + if (!page) { + this.log("No body found in document. Abort."); + return null; + } + + var pageCacheHtml = page.innerHTML; + + while (true) { + this.log("Starting grabArticle loop"); + var stripUnlikelyCandidates = this._flagIsActive(this.FLAG_STRIP_UNLIKELYS); + + // First, node prepping. Trash nodes that look cruddy (like ones with the + // class name "comment", etc), and turn divs into P tags where they have been + // used inappropriately (as in, where they contain no other block level elements.) + var elementsToScore = []; + var node = this._doc.documentElement; + + let shouldRemoveTitleHeader = true; + + while (node) { + + if (node.tagName === "HTML") { + this._articleLang = node.getAttribute("lang"); + } + + var matchString = node.className + " " + node.id; + + if (!this._isProbablyVisible(node)) { + this.log("Removing hidden node - " + matchString); + node = this._removeAndGetNext(node); + continue; + } + + // Check to see if this node is a byline, and remove it if it is. + if (this._checkByline(node, matchString)) { + node = this._removeAndGetNext(node); + continue; + } + + if (shouldRemoveTitleHeader && this._headerDuplicatesTitle(node)) { + this.log("Removing header: ", node.textContent.trim(), this._articleTitle.trim()); + shouldRemoveTitleHeader = false; + node = this._removeAndGetNext(node); + continue; + } + + // Remove unlikely candidates + if (stripUnlikelyCandidates) { + if (this.REGEXPS.unlikelyCandidates.test(matchString) && + !this.REGEXPS.okMaybeItsACandidate.test(matchString) && + !this._hasAncestorTag(node, "table") && + !this._hasAncestorTag(node, "code") && + node.tagName !== "BODY" && + node.tagName !== "A") { + this.log("Removing unlikely candidate - " + matchString); + node = this._removeAndGetNext(node); + continue; + } + + if (this.UNLIKELY_ROLES.includes(node.getAttribute("role"))) { + this.log("Removing content with role " + node.getAttribute("role") + " - " + matchString); + node = this._removeAndGetNext(node); + continue; + } + } + + // Remove DIV, SECTION, and HEADER nodes without any content(e.g. text, image, video, or iframe). + if ((node.tagName === "DIV" || node.tagName === "SECTION" || node.tagName === "HEADER" || + node.tagName === "H1" || node.tagName === "H2" || node.tagName === "H3" || + node.tagName === "H4" || node.tagName === "H5" || node.tagName === "H6") && + this._isElementWithoutContent(node)) { + node = this._removeAndGetNext(node); + continue; + } + + if (this.DEFAULT_TAGS_TO_SCORE.indexOf(node.tagName) !== -1) { + elementsToScore.push(node); + } + + // Turn all divs that don't have children block level elements into p's + if (node.tagName === "DIV") { + // Put phrasing content into paragraphs. + var p = null; + var childNode = node.firstChild; + while (childNode) { + var nextSibling = childNode.nextSibling; + if (this._isPhrasingContent(childNode)) { + if (p !== null) { + p.appendChild(childNode); + } else if (!this._isWhitespace(childNode)) { + p = doc.createElement("p"); + node.replaceChild(p, childNode); + p.appendChild(childNode); + } + } else if (p !== null) { + while (p.lastChild && this._isWhitespace(p.lastChild)) { + p.removeChild(p.lastChild); + } + p = null; + } + childNode = nextSibling; + } + + // Sites like http://mobile.slate.com encloses each paragraph with a DIV + // element. DIVs with only a P element inside and no text content can be + // safely converted into plain P elements to avoid confusing the scoring + // algorithm with DIVs with are, in practice, paragraphs. + if (this._hasSingleTagInsideElement(node, "P") && this._getLinkDensity(node) < 0.25) { + var newNode = node.children[0]; + node.parentNode.replaceChild(newNode, node); + node = newNode; + elementsToScore.push(node); + } else if (!this._hasChildBlockElement(node)) { + node = this._setNodeTag(node, "P"); + elementsToScore.push(node); + } + } + node = this._getNextNode(node); + } + + /** + * Loop through all paragraphs, and assign a score to them based on how content-y they look. + * Then add their score to their parent node. + * + * A score is determined by things like number of commas, class names, etc. Maybe eventually link density. + **/ + var candidates = []; + this._forEachNode(elementsToScore, function(elementToScore) { + if (!elementToScore.parentNode || typeof(elementToScore.parentNode.tagName) === "undefined") + return; + + // If this paragraph is less than 25 characters, don't even count it. + var innerText = this._getInnerText(elementToScore); + if (innerText.length < 25) + return; + + // Exclude nodes with no ancestor. + var ancestors = this._getNodeAncestors(elementToScore, 5); + if (ancestors.length === 0) + return; + + var contentScore = 0; + + // Add a point for the paragraph itself as a base. + contentScore += 1; + + // Add points for any commas within this paragraph. + contentScore += innerText.split(",").length; + + // For every 100 characters in this paragraph, add another point. Up to 3 points. + contentScore += Math.min(Math.floor(innerText.length / 100), 3); + + // Initialize and score ancestors. + this._forEachNode(ancestors, function(ancestor, level) { + if (!ancestor.tagName || !ancestor.parentNode || typeof(ancestor.parentNode.tagName) === "undefined") + return; + + if (typeof(ancestor.readability) === "undefined") { + this._initializeNode(ancestor); + candidates.push(ancestor); + } + + // Node score divider: + // - parent: 1 (no division) + // - grandparent: 2 + // - great grandparent+: ancestor level * 3 + if (level === 0) + var scoreDivider = 1; + else if (level === 1) + scoreDivider = 2; + else + scoreDivider = level * 3; + ancestor.readability.contentScore += contentScore / scoreDivider; + }); + }); + + // After we've calculated scores, loop through all of the possible + // candidate nodes we found and find the one with the highest score. + var topCandidates = []; + for (var c = 0, cl = candidates.length; c < cl; c += 1) { + var candidate = candidates[c]; + + // Scale the final candidates score based on link density. Good content + // should have a relatively small link density (5% or less) and be mostly + // unaffected by this operation. + var candidateScore = candidate.readability.contentScore * (1 - this._getLinkDensity(candidate)); + candidate.readability.contentScore = candidateScore; + + this.log("Candidate:", candidate, "with score " + candidateScore); + + for (var t = 0; t < this._nbTopCandidates; t++) { + var aTopCandidate = topCandidates[t]; + + if (!aTopCandidate || candidateScore > aTopCandidate.readability.contentScore) { + topCandidates.splice(t, 0, candidate); + if (topCandidates.length > this._nbTopCandidates) + topCandidates.pop(); + break; + } + } + } + + var topCandidate = topCandidates[0] || null; + var neededToCreateTopCandidate = false; + var parentOfTopCandidate; + + // If we still have no top candidate, just use the body as a last resort. + // We also have to copy the body node so it is something we can modify. + if (topCandidate === null || topCandidate.tagName === "BODY") { + // Move all of the page's children into topCandidate + topCandidate = doc.createElement("DIV"); + neededToCreateTopCandidate = true; + // Move everything (not just elements, also text nodes etc.) into the container + // so we even include text directly in the body: + while (page.firstChild) { + this.log("Moving child out:", page.firstChild); + topCandidate.appendChild(page.firstChild); + } + + page.appendChild(topCandidate); + + this._initializeNode(topCandidate); + } else if (topCandidate) { + // Find a better top candidate node if it contains (at least three) nodes which belong to `topCandidates` array + // and whose scores are quite closed with current `topCandidate` node. + var alternativeCandidateAncestors = []; + for (var i = 1; i < topCandidates.length; i++) { + if (topCandidates[i].readability.contentScore / topCandidate.readability.contentScore >= 0.75) { + alternativeCandidateAncestors.push(this._getNodeAncestors(topCandidates[i])); + } + } + var MINIMUM_TOPCANDIDATES = 3; + if (alternativeCandidateAncestors.length >= MINIMUM_TOPCANDIDATES) { + parentOfTopCandidate = topCandidate.parentNode; + while (parentOfTopCandidate.tagName !== "BODY") { + var listsContainingThisAncestor = 0; + for (var ancestorIndex = 0; ancestorIndex < alternativeCandidateAncestors.length && listsContainingThisAncestor < MINIMUM_TOPCANDIDATES; ancestorIndex++) { + listsContainingThisAncestor += Number(alternativeCandidateAncestors[ancestorIndex].includes(parentOfTopCandidate)); + } + if (listsContainingThisAncestor >= MINIMUM_TOPCANDIDATES) { + topCandidate = parentOfTopCandidate; + break; + } + parentOfTopCandidate = parentOfTopCandidate.parentNode; + } + } + if (!topCandidate.readability) { + this._initializeNode(topCandidate); + } + + // Because of our bonus system, parents of candidates might have scores + // themselves. They get half of the node. There won't be nodes with higher + // scores than our topCandidate, but if we see the score going *up* in the first + // few steps up the tree, that's a decent sign that there might be more content + // lurking in other places that we want to unify in. The sibling stuff + // below does some of that - but only if we've looked high enough up the DOM + // tree. + parentOfTopCandidate = topCandidate.parentNode; + var lastScore = topCandidate.readability.contentScore; + // The scores shouldn't get too low. + var scoreThreshold = lastScore / 3; + while (parentOfTopCandidate.tagName !== "BODY") { + if (!parentOfTopCandidate.readability) { + parentOfTopCandidate = parentOfTopCandidate.parentNode; + continue; + } + var parentScore = parentOfTopCandidate.readability.contentScore; + if (parentScore < scoreThreshold) + break; + if (parentScore > lastScore) { + // Alright! We found a better parent to use. + topCandidate = parentOfTopCandidate; + break; + } + lastScore = parentOfTopCandidate.readability.contentScore; + parentOfTopCandidate = parentOfTopCandidate.parentNode; + } + + // If the top candidate is the only child, use parent instead. This will help sibling + // joining logic when adjacent content is actually located in parent's sibling node. + parentOfTopCandidate = topCandidate.parentNode; + while (parentOfTopCandidate.tagName != "BODY" && parentOfTopCandidate.children.length == 1) { + topCandidate = parentOfTopCandidate; + parentOfTopCandidate = topCandidate.parentNode; + } + if (!topCandidate.readability) { + this._initializeNode(topCandidate); + } + } + + // Now that we have the top candidate, look through its siblings for content + // that might also be related. Things like preambles, content split by ads + // that we removed, etc. + var articleContent = doc.createElement("DIV"); + if (isPaging) + articleContent.id = "readability-content"; + + var siblingScoreThreshold = Math.max(10, topCandidate.readability.contentScore * 0.2); + // Keep potential top candidate's parent node to try to get text direction of it later. + parentOfTopCandidate = topCandidate.parentNode; + var siblings = parentOfTopCandidate.children; + + for (var s = 0, sl = siblings.length; s < sl; s++) { + var sibling = siblings[s]; + var append = false; + + this.log("Looking at sibling node:", sibling, sibling.readability ? ("with score " + sibling.readability.contentScore) : ""); + this.log("Sibling has score", sibling.readability ? sibling.readability.contentScore : "Unknown"); + + if (sibling === topCandidate) { + append = true; + } else { + var contentBonus = 0; + + // Give a bonus if sibling nodes and top candidates have the example same classname + if (sibling.className === topCandidate.className && topCandidate.className !== "") + contentBonus += topCandidate.readability.contentScore * 0.2; + + if (sibling.readability && + ((sibling.readability.contentScore + contentBonus) >= siblingScoreThreshold)) { + append = true; + } else if (sibling.nodeName === "P") { + var linkDensity = this._getLinkDensity(sibling); + var nodeContent = this._getInnerText(sibling); + var nodeLength = nodeContent.length; + + if (nodeLength > 80 && linkDensity < 0.25) { + append = true; + } else if (nodeLength < 80 && nodeLength > 0 && linkDensity === 0 && + nodeContent.search(/\.( |$)/) !== -1) { + append = true; + } + } + } + + if (append) { + this.log("Appending node:", sibling); + + if (this.ALTER_TO_DIV_EXCEPTIONS.indexOf(sibling.nodeName) === -1) { + // We have a node that isn't a common block level element, like a form or td tag. + // Turn it into a div so it doesn't get filtered out later by accident. + this.log("Altering sibling:", sibling, "to div."); + + sibling = this._setNodeTag(sibling, "DIV"); + } + + articleContent.appendChild(sibling); + // Fetch children again to make it compatible + // with DOM parsers without live collection support. + siblings = parentOfTopCandidate.children; + // siblings is a reference to the children array, and + // sibling is removed from the array when we call appendChild(). + // As a result, we must revisit this index since the nodes + // have been shifted. + s -= 1; + sl -= 1; + } + } + + if (this._debug) + this.log("Article content pre-prep: " + articleContent.innerHTML); + // So we have all of the content that we need. Now we clean it up for presentation. + this._prepArticle(articleContent); + if (this._debug) + this.log("Article content post-prep: " + articleContent.innerHTML); + + if (neededToCreateTopCandidate) { + // We already created a fake div thing, and there wouldn't have been any siblings left + // for the previous loop, so there's no point trying to create a new div, and then + // move all the children over. Just assign IDs and class names here. No need to append + // because that already happened anyway. + topCandidate.id = "readability-page-1"; + topCandidate.className = "page"; + } else { + var div = doc.createElement("DIV"); + div.id = "readability-page-1"; + div.className = "page"; + while (articleContent.firstChild) { + div.appendChild(articleContent.firstChild); + } + articleContent.appendChild(div); + } + + if (this._debug) + this.log("Article content after paging: " + articleContent.innerHTML); + + var parseSuccessful = true; + + // Now that we've gone through the full algorithm, check to see if + // we got any meaningful content. If we didn't, we may need to re-run + // grabArticle with different flags set. This gives us a higher likelihood of + // finding the content, and the sieve approach gives us a higher likelihood of + // finding the -right- content. + var textLength = this._getInnerText(articleContent, true).length; + if (textLength < this._charThreshold) { + parseSuccessful = false; + page.innerHTML = pageCacheHtml; + + if (this._flagIsActive(this.FLAG_STRIP_UNLIKELYS)) { + this._removeFlag(this.FLAG_STRIP_UNLIKELYS); + this._attempts.push({articleContent: articleContent, textLength: textLength}); + } else if (this._flagIsActive(this.FLAG_WEIGHT_CLASSES)) { + this._removeFlag(this.FLAG_WEIGHT_CLASSES); + this._attempts.push({articleContent: articleContent, textLength: textLength}); + } else if (this._flagIsActive(this.FLAG_CLEAN_CONDITIONALLY)) { + this._removeFlag(this.FLAG_CLEAN_CONDITIONALLY); + this._attempts.push({articleContent: articleContent, textLength: textLength}); + } else { + this._attempts.push({articleContent: articleContent, textLength: textLength}); + // No luck after removing flags, just return the longest text we found during the different loops + this._attempts.sort(function (a, b) { + return b.textLength - a.textLength; + }); + + // But first check if we actually have something + if (!this._attempts[0].textLength) { + return null; + } + + articleContent = this._attempts[0].articleContent; + parseSuccessful = true; + } + } + + if (parseSuccessful) { + // Find out text direction from ancestors of final top candidate. + var ancestors = [parentOfTopCandidate, topCandidate].concat(this._getNodeAncestors(parentOfTopCandidate)); + this._someNode(ancestors, function(ancestor) { + if (!ancestor.tagName) + return false; + var articleDir = ancestor.getAttribute("dir"); + if (articleDir) { + this._articleDir = articleDir; + return true; + } + return false; + }); + return articleContent; + } + } + }, + + /** + * Check whether the input string could be a byline. + * This verifies that the input is a string, and that the length + * is less than 100 chars. + * + * @param possibleByline {string} - a string to check whether its a byline. + * @return Boolean - whether the input string is a byline. + */ + _isValidByline: function(byline) { + if (typeof byline == "string" || byline instanceof String) { + byline = byline.trim(); + return (byline.length > 0) && (byline.length < 100); + } + return false; + }, + + /** + * Converts some of the common HTML entities in string to their corresponding characters. + * + * @param str {string} - a string to unescape. + * @return string without HTML entity. + */ + _unescapeHtmlEntities: function(str) { + if (!str) { + return str; + } + + var htmlEscapeMap = this.HTML_ESCAPE_MAP; + return str.replace(/&(quot|amp|apos|lt|gt);/g, function(_, tag) { + return htmlEscapeMap[tag]; + }).replace(/&#(?:x([0-9a-z]{1,4})|([0-9]{1,4}));/gi, function(_, hex, numStr) { + var num = parseInt(hex || numStr, hex ? 16 : 10); + return String.fromCharCode(num); + }); + }, + + /** + * Try to extract metadata from JSON-LD object. + * For now, only Schema.org objects of type Article or its subtypes are supported. + * @return Object with any metadata that could be extracted (possibly none) + */ + _getJSONLD: function (doc) { + var scripts = this._getAllNodesWithTag(doc, ["script"]); + + var metadata; + + this._forEachNode(scripts, function(jsonLdElement) { + if (!metadata && jsonLdElement.getAttribute("type") === "application/ld+json") { + try { + // Strip CDATA markers if present + var content = jsonLdElement.textContent.replace(/^\s*<!\[CDATA\[|\]\]>\s*$/g, ""); + var parsed = JSON.parse(content); + if ( + !parsed["@context"] || + !parsed["@context"].match(/^https?\:\/\/schema\.org$/) + ) { + return; + } + + if (!parsed["@type"] && Array.isArray(parsed["@graph"])) { + parsed = parsed["@graph"].find(function(it) { + return (it["@type"] || "").match( + this.REGEXPS.jsonLdArticleTypes + ); + }); + } + + if ( + !parsed || + !parsed["@type"] || + !parsed["@type"].match(this.REGEXPS.jsonLdArticleTypes) + ) { + return; + } + + metadata = {}; + + if (typeof parsed.name === "string" && typeof parsed.headline === "string" && parsed.name !== parsed.headline) { + // we have both name and headline element in the JSON-LD. They should both be the same but some websites like aktualne.cz + // put their own name into "name" and the article title to "headline" which confuses Readability. So we try to check if either + // "name" or "headline" closely matches the html title, and if so, use that one. If not, then we use "name" by default. + + var title = this._getArticleTitle(); + var nameMatches = this._textSimilarity(parsed.name, title) > 0.75; + var headlineMatches = this._textSimilarity(parsed.headline, title) > 0.75; + + if (headlineMatches && !nameMatches) { + metadata.title = parsed.headline; + } else { + metadata.title = parsed.name; + } + } else if (typeof parsed.name === "string") { + metadata.title = parsed.name.trim(); + } else if (typeof parsed.headline === "string") { + metadata.title = parsed.headline.trim(); + } + if (parsed.author) { + if (typeof parsed.author.name === "string") { + metadata.byline = parsed.author.name.trim(); + } else if (Array.isArray(parsed.author) && parsed.author[0] && typeof parsed.author[0].name === "string") { + metadata.byline = parsed.author + .filter(function(author) { + return author && typeof author.name === "string"; + }) + .map(function(author) { + return author.name.trim(); + }) + .join(", "); + } + } + if (typeof parsed.description === "string") { + metadata.excerpt = parsed.description.trim(); + } + if ( + parsed.publisher && + typeof parsed.publisher.name === "string" + ) { + metadata.siteName = parsed.publisher.name.trim(); + } + return; + } catch (err) { + this.log(err.message); + } + } + }); + return metadata ? metadata : {}; + }, + + /** + * Attempts to get excerpt and byline metadata for the article. + * + * @param {Object} jsonld — object containing any metadata that + * could be extracted from JSON-LD object. + * + * @return Object with optional "excerpt" and "byline" properties + */ + _getArticleMetadata: function(jsonld) { + var metadata = {}; + var values = {}; + var metaElements = this._doc.getElementsByTagName("meta"); + + // property is a space-separated list of values + var propertyPattern = /\s*(dc|dcterm|og|twitter)\s*:\s*(author|creator|description|title|site_name)\s*/gi; + + // name is a single value + var namePattern = /^\s*(?:(dc|dcterm|og|twitter|weibo:(article|webpage))\s*[\.:]\s*)?(author|creator|description|title|site_name)\s*$/i; + + // Find description tags. + this._forEachNode(metaElements, function(element) { + var elementName = element.getAttribute("name"); + var elementProperty = element.getAttribute("property"); + var content = element.getAttribute("content"); + if (!content) { + return; + } + var matches = null; + var name = null; + + if (elementProperty) { + matches = elementProperty.match(propertyPattern); + if (matches) { + // Convert to lowercase, and remove any whitespace + // so we can match below. + name = matches[0].toLowerCase().replace(/\s/g, ""); + // multiple authors + values[name] = content.trim(); + } + } + if (!matches && elementName && namePattern.test(elementName)) { + name = elementName; + if (content) { + // Convert to lowercase, remove any whitespace, and convert dots + // to colons so we can match below. + name = name.toLowerCase().replace(/\s/g, "").replace(/\./g, ":"); + values[name] = content.trim(); + } + } + }); + + // get title + metadata.title = jsonld.title || + values["dc:title"] || + values["dcterm:title"] || + values["og:title"] || + values["weibo:article:title"] || + values["weibo:webpage:title"] || + values["title"] || + values["twitter:title"]; + + if (!metadata.title) { + metadata.title = this._getArticleTitle(); + } + + // get author + metadata.byline = jsonld.byline || + values["dc:creator"] || + values["dcterm:creator"] || + values["author"]; + + // get description + metadata.excerpt = jsonld.excerpt || + values["dc:description"] || + values["dcterm:description"] || + values["og:description"] || + values["weibo:article:description"] || + values["weibo:webpage:description"] || + values["description"] || + values["twitter:description"]; + + // get site name + metadata.siteName = jsonld.siteName || + values["og:site_name"]; + + // in many sites the meta value is escaped with HTML entities, + // so here we need to unescape it + metadata.title = this._unescapeHtmlEntities(metadata.title); + metadata.byline = this._unescapeHtmlEntities(metadata.byline); + metadata.excerpt = this._unescapeHtmlEntities(metadata.excerpt); + metadata.siteName = this._unescapeHtmlEntities(metadata.siteName); + + return metadata; + }, + + /** + * Check if node is image, or if node contains exactly only one image + * whether as a direct child or as its descendants. + * + * @param Element + **/ + _isSingleImage: function(node) { + if (node.tagName === "IMG") { + return true; + } + + if (node.children.length !== 1 || node.textContent.trim() !== "") { + return false; + } + + return this._isSingleImage(node.children[0]); + }, + + /** + * Find all <noscript> that are located after <img> nodes, and which contain only one + * <img> element. Replace the first image with the image from inside the <noscript> tag, + * and remove the <noscript> tag. This improves the quality of the images we use on + * some sites (e.g. Medium). + * + * @param Element + **/ + _unwrapNoscriptImages: function(doc) { + // Find img without source or attributes that might contains image, and remove it. + // This is done to prevent a placeholder img is replaced by img from noscript in next step. + var imgs = Array.from(doc.getElementsByTagName("img")); + this._forEachNode(imgs, function(img) { + for (var i = 0; i < img.attributes.length; i++) { + var attr = img.attributes[i]; + switch (attr.name) { + case "src": + case "srcset": + case "data-src": + case "data-srcset": + return; + } + + if (/\.(jpg|jpeg|png|webp)/i.test(attr.value)) { + return; + } + } + + img.parentNode.removeChild(img); + }); + + // Next find noscript and try to extract its image + var noscripts = Array.from(doc.getElementsByTagName("noscript")); + this._forEachNode(noscripts, function(noscript) { + // Parse content of noscript and make sure it only contains image + var tmp = doc.createElement("div"); + tmp.innerHTML = noscript.innerHTML; + if (!this._isSingleImage(tmp)) { + return; + } + + // If noscript has previous sibling and it only contains image, + // replace it with noscript content. However we also keep old + // attributes that might contains image. + var prevElement = noscript.previousElementSibling; + if (prevElement && this._isSingleImage(prevElement)) { + var prevImg = prevElement; + if (prevImg.tagName !== "IMG") { + prevImg = prevElement.getElementsByTagName("img")[0]; + } + + var newImg = tmp.getElementsByTagName("img")[0]; + for (var i = 0; i < prevImg.attributes.length; i++) { + var attr = prevImg.attributes[i]; + if (attr.value === "") { + continue; + } + + if (attr.name === "src" || attr.name === "srcset" || /\.(jpg|jpeg|png|webp)/i.test(attr.value)) { + if (newImg.getAttribute(attr.name) === attr.value) { + continue; + } + + var attrName = attr.name; + if (newImg.hasAttribute(attrName)) { + attrName = "data-old-" + attrName; + } + + newImg.setAttribute(attrName, attr.value); + } + } + + noscript.parentNode.replaceChild(tmp.firstElementChild, prevElement); + } + }); + }, + + /** + * Removes script tags from the document. + * + * @param Element + **/ + _removeScripts: function(doc) { + this._removeNodes(this._getAllNodesWithTag(doc, ["script"]), function(scriptNode) { + scriptNode.nodeValue = ""; + scriptNode.removeAttribute("src"); + return true; + }); + this._removeNodes(this._getAllNodesWithTag(doc, ["noscript"])); + }, + + /** + * Check if this node has only whitespace and a single element with given tag + * Returns false if the DIV node contains non-empty text nodes + * or if it contains no element with given tag or more than 1 element. + * + * @param Element + * @param string tag of child element + **/ + _hasSingleTagInsideElement: function(element, tag) { + // There should be exactly 1 element child with given tag + if (element.children.length != 1 || element.children[0].tagName !== tag) { + return false; + } + + // And there should be no text nodes with real content + return !this._someNode(element.childNodes, function(node) { + return node.nodeType === this.TEXT_NODE && + this.REGEXPS.hasContent.test(node.textContent); + }); + }, + + _isElementWithoutContent: function(node) { + return node.nodeType === this.ELEMENT_NODE && + node.textContent.trim().length == 0 && + (node.children.length == 0 || + node.children.length == node.getElementsByTagName("br").length + node.getElementsByTagName("hr").length); + }, + + /** + * Determine whether element has any children block level elements. + * + * @param Element + */ + _hasChildBlockElement: function (element) { + return this._someNode(element.childNodes, function(node) { + return this.DIV_TO_P_ELEMS.has(node.tagName) || + this._hasChildBlockElement(node); + }); + }, + + /*** + * Determine if a node qualifies as phrasing content. + * https://developer.mozilla.org/en-US/docs/Web/Guide/HTML/Content_categories#Phrasing_content + **/ + _isPhrasingContent: function(node) { + return node.nodeType === this.TEXT_NODE || this.PHRASING_ELEMS.indexOf(node.tagName) !== -1 || + ((node.tagName === "A" || node.tagName === "DEL" || node.tagName === "INS") && + this._everyNode(node.childNodes, this._isPhrasingContent)); + }, + + _isWhitespace: function(node) { + return (node.nodeType === this.TEXT_NODE && node.textContent.trim().length === 0) || + (node.nodeType === this.ELEMENT_NODE && node.tagName === "BR"); + }, + + /** + * Get the inner text of a node - cross browser compatibly. + * This also strips out any excess whitespace to be found. + * + * @param Element + * @param Boolean normalizeSpaces (default: true) + * @return string + **/ + _getInnerText: function(e, normalizeSpaces) { + normalizeSpaces = (typeof normalizeSpaces === "undefined") ? true : normalizeSpaces; + var textContent = e.textContent.trim(); + + if (normalizeSpaces) { + return textContent.replace(this.REGEXPS.normalize, " "); + } + return textContent; + }, + + /** + * Get the number of times a string s appears in the node e. + * + * @param Element + * @param string - what to split on. Default is "," + * @return number (integer) + **/ + _getCharCount: function(e, s) { + s = s || ","; + return this._getInnerText(e).split(s).length - 1; + }, + + /** + * Remove the style attribute on every e and under. + * TODO: Test if getElementsByTagName(*) is faster. + * + * @param Element + * @return void + **/ + _cleanStyles: function(e) { + if (!e || e.tagName.toLowerCase() === "svg") + return; + + // Remove `style` and deprecated presentational attributes + for (var i = 0; i < this.PRESENTATIONAL_ATTRIBUTES.length; i++) { + e.removeAttribute(this.PRESENTATIONAL_ATTRIBUTES[i]); + } + + if (this.DEPRECATED_SIZE_ATTRIBUTE_ELEMS.indexOf(e.tagName) !== -1) { + e.removeAttribute("width"); + e.removeAttribute("height"); + } + + var cur = e.firstElementChild; + while (cur !== null) { + this._cleanStyles(cur); + cur = cur.nextElementSibling; + } + }, + + /** + * Get the density of links as a percentage of the content + * This is the amount of text that is inside a link divided by the total text in the node. + * + * @param Element + * @return number (float) + **/ + _getLinkDensity: function(element) { + var textLength = this._getInnerText(element).length; + if (textLength === 0) + return 0; + + var linkLength = 0; + + // XXX implement _reduceNodeList? + this._forEachNode(element.getElementsByTagName("a"), function(linkNode) { + var href = linkNode.getAttribute("href"); + var coefficient = href && this.REGEXPS.hashUrl.test(href) ? 0.3 : 1; + linkLength += this._getInnerText(linkNode).length * coefficient; + }); + + return linkLength / textLength; + }, + + /** + * Get an elements class/id weight. Uses regular expressions to tell if this + * element looks good or bad. + * + * @param Element + * @return number (Integer) + **/ + _getClassWeight: function(e) { + if (!this._flagIsActive(this.FLAG_WEIGHT_CLASSES)) + return 0; + + var weight = 0; + + // Look for a special classname + if (typeof(e.className) === "string" && e.className !== "") { + if (this.REGEXPS.negative.test(e.className)) + weight -= 25; + + if (this.REGEXPS.positive.test(e.className)) + weight += 25; + } + + // Look for a special ID + if (typeof(e.id) === "string" && e.id !== "") { + if (this.REGEXPS.negative.test(e.id)) + weight -= 25; + + if (this.REGEXPS.positive.test(e.id)) + weight += 25; + } + + return weight; + }, + + /** + * Clean a node of all elements of type "tag". + * (Unless it's a youtube/vimeo video. People love movies.) + * + * @param Element + * @param string tag to clean + * @return void + **/ + _clean: function(e, tag) { + var isEmbed = ["object", "embed", "iframe"].indexOf(tag) !== -1; + + this._removeNodes(this._getAllNodesWithTag(e, [tag]), function(element) { + // Allow youtube and vimeo videos through as people usually want to see those. + if (isEmbed) { + // First, check the elements attributes to see if any of them contain youtube or vimeo + for (var i = 0; i < element.attributes.length; i++) { + if (this.REGEXPS.videos.test(element.attributes[i].value)) { + return false; + } + } + + // For embed with <object> tag, check inner HTML as well. + if (element.tagName === "object" && this.REGEXPS.videos.test(element.innerHTML)) { + return false; + } + } + + return true; + }); + }, + + /** + * Check if a given node has one of its ancestor tag name matching the + * provided one. + * @param HTMLElement node + * @param String tagName + * @param Number maxDepth + * @param Function filterFn a filter to invoke to determine whether this node 'counts' + * @return Boolean + */ + _hasAncestorTag: function(node, tagName, maxDepth, filterFn) { + maxDepth = maxDepth || 3; + tagName = tagName.toUpperCase(); + var depth = 0; + while (node.parentNode) { + if (maxDepth > 0 && depth > maxDepth) + return false; + if (node.parentNode.tagName === tagName && (!filterFn || filterFn(node.parentNode))) + return true; + node = node.parentNode; + depth++; + } + return false; + }, + + /** + * Return an object indicating how many rows and columns this table has. + */ + _getRowAndColumnCount: function(table) { + var rows = 0; + var columns = 0; + var trs = table.getElementsByTagName("tr"); + for (var i = 0; i < trs.length; i++) { + var rowspan = trs[i].getAttribute("rowspan") || 0; + if (rowspan) { + rowspan = parseInt(rowspan, 10); + } + rows += (rowspan || 1); + + // Now look for column-related info + var columnsInThisRow = 0; + var cells = trs[i].getElementsByTagName("td"); + for (var j = 0; j < cells.length; j++) { + var colspan = cells[j].getAttribute("colspan") || 0; + if (colspan) { + colspan = parseInt(colspan, 10); + } + columnsInThisRow += (colspan || 1); + } + columns = Math.max(columns, columnsInThisRow); + } + return {rows: rows, columns: columns}; + }, + + /** + * Look for 'data' (as opposed to 'layout') tables, for which we use + * similar checks as + * https://searchfox.org/mozilla-central/rev/f82d5c549f046cb64ce5602bfd894b7ae807c8f8/accessible/generic/TableAccessible.cpp#19 + */ + _markDataTables: function(root) { + var tables = root.getElementsByTagName("table"); + for (var i = 0; i < tables.length; i++) { + var table = tables[i]; + var role = table.getAttribute("role"); + if (role == "presentation") { + table._readabilityDataTable = false; + continue; + } + var datatable = table.getAttribute("datatable"); + if (datatable == "0") { + table._readabilityDataTable = false; + continue; + } + var summary = table.getAttribute("summary"); + if (summary) { + table._readabilityDataTable = true; + continue; + } + + var caption = table.getElementsByTagName("caption")[0]; + if (caption && caption.childNodes.length > 0) { + table._readabilityDataTable = true; + continue; + } + + // If the table has a descendant with any of these tags, consider a data table: + var dataTableDescendants = ["col", "colgroup", "tfoot", "thead", "th"]; + var descendantExists = function(tag) { + return !!table.getElementsByTagName(tag)[0]; + }; + if (dataTableDescendants.some(descendantExists)) { + this.log("Data table because found data-y descendant"); + table._readabilityDataTable = true; + continue; + } + + // Nested tables indicate a layout table: + if (table.getElementsByTagName("table")[0]) { + table._readabilityDataTable = false; + continue; + } + + var sizeInfo = this._getRowAndColumnCount(table); + if (sizeInfo.rows >= 10 || sizeInfo.columns > 4) { + table._readabilityDataTable = true; + continue; + } + // Now just go by size entirely: + table._readabilityDataTable = sizeInfo.rows * sizeInfo.columns > 10; + } + }, + + /* convert images and figures that have properties like data-src into images that can be loaded without JS */ + _fixLazyImages: function (root) { + this._forEachNode(this._getAllNodesWithTag(root, ["img", "picture", "figure"]), function (elem) { + // In some sites (e.g. Kotaku), they put 1px square image as base64 data uri in the src attribute. + // So, here we check if the data uri is too short, just might as well remove it. + if (elem.src && this.REGEXPS.b64DataUrl.test(elem.src)) { + // Make sure it's not SVG, because SVG can have a meaningful image in under 133 bytes. + var parts = this.REGEXPS.b64DataUrl.exec(elem.src); + if (parts[1] === "image/svg+xml") { + return; + } + + // Make sure this element has other attributes which contains image. + // If it doesn't, then this src is important and shouldn't be removed. + var srcCouldBeRemoved = false; + for (var i = 0; i < elem.attributes.length; i++) { + var attr = elem.attributes[i]; + if (attr.name === "src") { + continue; + } + + if (/\.(jpg|jpeg|png|webp)/i.test(attr.value)) { + srcCouldBeRemoved = true; + break; + } + } + + // Here we assume if image is less than 100 bytes (or 133B after encoded to base64) + // it will be too small, therefore it might be placeholder image. + if (srcCouldBeRemoved) { + var b64starts = elem.src.search(/base64\s*/i) + 7; + var b64length = elem.src.length - b64starts; + if (b64length < 133) { + elem.removeAttribute("src"); + } + } + } + + // also check for "null" to work around https://github.com/jsdom/jsdom/issues/2580 + if ((elem.src || (elem.srcset && elem.srcset != "null")) && elem.className.toLowerCase().indexOf("lazy") === -1) { + return; + } + + for (var j = 0; j < elem.attributes.length; j++) { + attr = elem.attributes[j]; + if (attr.name === "src" || attr.name === "srcset" || attr.name === "alt") { + continue; + } + var copyTo = null; + if (/\.(jpg|jpeg|png|webp)\s+\d/.test(attr.value)) { + copyTo = "srcset"; + } else if (/^\s*\S+\.(jpg|jpeg|png|webp)\S*\s*$/.test(attr.value)) { + copyTo = "src"; + } + if (copyTo) { + //if this is an img or picture, set the attribute directly + if (elem.tagName === "IMG" || elem.tagName === "PICTURE") { + elem.setAttribute(copyTo, attr.value); + } else if (elem.tagName === "FIGURE" && !this._getAllNodesWithTag(elem, ["img", "picture"]).length) { + //if the item is a <figure> that does not contain an image or picture, create one and place it inside the figure + //see the nytimes-3 testcase for an example + var img = this._doc.createElement("img"); + img.setAttribute(copyTo, attr.value); + elem.appendChild(img); + } + } + } + }); + }, + + _getTextDensity: function(e, tags) { + var textLength = this._getInnerText(e, true).length; + if (textLength === 0) { + return 0; + } + var childrenLength = 0; + var children = this._getAllNodesWithTag(e, tags); + this._forEachNode(children, (child) => childrenLength += this._getInnerText(child, true).length); + return childrenLength / textLength; + }, + + /** + * Clean an element of all tags of type "tag" if they look fishy. + * "Fishy" is an algorithm based on content length, classnames, link density, number of images & embeds, etc. + * + * @return void + **/ + _cleanConditionally: function(e, tag) { + if (!this._flagIsActive(this.FLAG_CLEAN_CONDITIONALLY)) + return; + + // Gather counts for other typical elements embedded within. + // Traverse backwards so we can remove nodes at the same time + // without effecting the traversal. + // + // TODO: Consider taking into account original contentScore here. + this._removeNodes(this._getAllNodesWithTag(e, [tag]), function(node) { + // First check if this node IS data table, in which case don't remove it. + var isDataTable = function(t) { + return t._readabilityDataTable; + }; + + var isList = tag === "ul" || tag === "ol"; + if (!isList) { + var listLength = 0; + var listNodes = this._getAllNodesWithTag(node, ["ul", "ol"]); + this._forEachNode(listNodes, (list) => listLength += this._getInnerText(list).length); + isList = listLength / this._getInnerText(node).length > 0.9; + } + + if (tag === "table" && isDataTable(node)) { + return false; + } + + // Next check if we're inside a data table, in which case don't remove it as well. + if (this._hasAncestorTag(node, "table", -1, isDataTable)) { + return false; + } + + if (this._hasAncestorTag(node, "code")) { + return false; + } + + var weight = this._getClassWeight(node); + + this.log("Cleaning Conditionally", node); + + var contentScore = 0; + + if (weight + contentScore < 0) { + return true; + } + + if (this._getCharCount(node, ",") < 10) { + // If there are not very many commas, and the number of + // non-paragraph elements is more than paragraphs or other + // ominous signs, remove the element. + var p = node.getElementsByTagName("p").length; + var img = node.getElementsByTagName("img").length; + var li = node.getElementsByTagName("li").length - 100; + var input = node.getElementsByTagName("input").length; + var headingDensity = this._getTextDensity(node, ["h1", "h2", "h3", "h4", "h5", "h6"]); + + var embedCount = 0; + var embeds = this._getAllNodesWithTag(node, ["object", "embed", "iframe"]); + + for (var i = 0; i < embeds.length; i++) { + // If this embed has attribute that matches video regex, don't delete it. + for (var j = 0; j < embeds[i].attributes.length; j++) { + if (this.REGEXPS.videos.test(embeds[i].attributes[j].value)) { + return false; + } + } + + // For embed with <object> tag, check inner HTML as well. + if (embeds[i].tagName === "object" && this.REGEXPS.videos.test(embeds[i].innerHTML)) { + return false; + } + + embedCount++; + } + + var linkDensity = this._getLinkDensity(node); + var contentLength = this._getInnerText(node).length; + + var haveToRemove = + (img > 1 && p / img < 0.5 && !this._hasAncestorTag(node, "figure")) || + (!isList && li > p) || + (input > Math.floor(p/3)) || + (!isList && headingDensity < 0.9 && contentLength < 25 && (img === 0 || img > 2) && !this._hasAncestorTag(node, "figure")) || + (!isList && weight < 25 && linkDensity > 0.2) || + (weight >= 25 && linkDensity > 0.5) || + ((embedCount === 1 && contentLength < 75) || embedCount > 1); + return haveToRemove; + } + return false; + }); + }, + + /** + * Clean out elements that match the specified conditions + * + * @param Element + * @param Function determines whether a node should be removed + * @return void + **/ + _cleanMatchedNodes: function(e, filter) { + var endOfSearchMarkerNode = this._getNextNode(e, true); + var next = this._getNextNode(e); + while (next && next != endOfSearchMarkerNode) { + if (filter.call(this, next, next.className + " " + next.id)) { + next = this._removeAndGetNext(next); + } else { + next = this._getNextNode(next); + } + } + }, + + /** + * Clean out spurious headers from an Element. + * + * @param Element + * @return void + **/ + _cleanHeaders: function(e) { + let headingNodes = this._getAllNodesWithTag(e, ["h1", "h2"]); + this._removeNodes(headingNodes, function(node) { + let shouldRemove = this._getClassWeight(node) < 0; + if (shouldRemove) { + this.log("Removing header with low class weight:", node); + } + return shouldRemove; + }); + }, + + /** + * Check if this node is an H1 or H2 element whose content is mostly + * the same as the article title. + * + * @param Element the node to check. + * @return boolean indicating whether this is a title-like header. + */ + _headerDuplicatesTitle: function(node) { + if (node.tagName != "H1" && node.tagName != "H2") { + return false; + } + var heading = this._getInnerText(node, false); + this.log("Evaluating similarity of header:", heading, this._articleTitle); + return this._textSimilarity(this._articleTitle, heading) > 0.75; + }, + + _flagIsActive: function(flag) { + return (this._flags & flag) > 0; + }, + + _removeFlag: function(flag) { + this._flags = this._flags & ~flag; + }, + + _isProbablyVisible: function(node) { + // Have to null-check node.style and node.className.indexOf to deal with SVG and MathML nodes. + return (!node.style || node.style.display != "none") + && !node.hasAttribute("hidden") + //check for "fallback-image" so that wikimedia math images are displayed + && (!node.hasAttribute("aria-hidden") || node.getAttribute("aria-hidden") != "true" || (node.className && node.className.indexOf && node.className.indexOf("fallback-image") !== -1)); + }, + + /** + * Runs readability. + * + * Workflow: + * 1. Prep the document by removing script tags, css, etc. + * 2. Build readability's DOM tree. + * 3. Grab the article content from the current dom tree. + * 4. Replace the current DOM tree with the new one. + * 5. Read peacefully. + * + * @return void + **/ + parse: function () { + // Avoid parsing too large documents, as per configuration option + if (this._maxElemsToParse > 0) { + var numTags = this._doc.getElementsByTagName("*").length; + if (numTags > this._maxElemsToParse) { + throw new Error("Aborting parsing document; " + numTags + " elements found"); + } + } + + // Unwrap image from noscript + this._unwrapNoscriptImages(this._doc); + + // Extract JSON-LD metadata before removing scripts + var jsonLd = this._disableJSONLD ? {} : this._getJSONLD(this._doc); + + // Remove script tags from the document. + this._removeScripts(this._doc); + + this._prepDocument(); + + var metadata = this._getArticleMetadata(jsonLd); + this._articleTitle = metadata.title; + + var articleContent = this._grabArticle(); + if (!articleContent) + return null; + + this.log("Grabbed: " + articleContent.innerHTML); + + this._postProcessContent(articleContent); + + // If we haven't found an excerpt in the article's metadata, use the article's + // first paragraph as the excerpt. This is used for displaying a preview of + // the article's content. + if (!metadata.excerpt) { + var paragraphs = articleContent.getElementsByTagName("p"); + if (paragraphs.length > 0) { + metadata.excerpt = paragraphs[0].textContent.trim(); + } + } + + var textContent = articleContent.textContent; + return { + title: this._articleTitle, + byline: metadata.byline || this._articleByline, + dir: this._articleDir, + lang: this._articleLang, + content: this._serializer(articleContent), + textContent: textContent, + length: textContent.length, + excerpt: metadata.excerpt, + siteName: metadata.siteName || this._articleSiteName + }; + } +}; + +if (typeof module === "object") { + module.exports = Readability; +} diff --git a/mobile/android/android-components/components/feature/readerview/src/main/assets/extensions/readerview/readability/readability-readerable-0.4.2.js b/mobile/android/android-components/components/feature/readerview/src/main/assets/extensions/readerview/readability/readability-readerable-0.4.2.js new file mode 100644 index 0000000000..64be5e15e8 --- /dev/null +++ b/mobile/android/android-components/components/feature/readerview/src/main/assets/extensions/readerview/readability/readability-readerable-0.4.2.js @@ -0,0 +1,108 @@ +/* eslint-env es6:false */ +/* + * Copyright (c) 2010 Arc90 Inc + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +/* + * This code is heavily based on Arc90's readability.js (1.7.1) script + * available at: http://code.google.com/p/arc90labs-readability + */ + +var REGEXPS = { + // NOTE: These two regular expressions are duplicated in + // Readability.js. Please keep both copies in sync. + unlikelyCandidates: /-ad-|ai2html|banner|breadcrumbs|combx|comment|community|cover-wrap|disqus|extra|footer|gdpr|header|legends|menu|related|remark|replies|rss|shoutbox|sidebar|skyscraper|social|sponsor|supplemental|ad-break|agegate|pagination|pager|popup|yom-remote/i, + okMaybeItsACandidate: /and|article|body|column|content|main|shadow/i, +}; + +function isNodeVisible(node) { + // Have to null-check node.style and node.className.indexOf to deal with SVG and MathML nodes. + return (!node.style || node.style.display != "none") + && !node.hasAttribute("hidden") + //check for "fallback-image" so that wikimedia math images are displayed + && (!node.hasAttribute("aria-hidden") || node.getAttribute("aria-hidden") != "true" || (node.className && node.className.indexOf && node.className.indexOf("fallback-image") !== -1)); +} + +/** + * Decides whether or not the document is reader-able without parsing the whole thing. + * @param {Object} options Configuration object. + * @param {number} [options.minContentLength=140] The minimum node content length used to decide if the document is readerable. + * @param {number} [options.minScore=20] The minumum cumulated 'score' used to determine if the document is readerable. + * @param {Function} [options.visibilityChecker=isNodeVisible] The function used to determine if a node is visible. + * @return {boolean} Whether or not we suspect Readability.parse() will suceeed at returning an article object. + */ +function isProbablyReaderable(doc, options = {}) { + // For backward compatibility reasons 'options' can either be a configuration object or the function used + // to determine if a node is visible. + if (typeof options == "function") { + options = { visibilityChecker: options }; + } + + var defaultOptions = { minScore: 20, minContentLength: 140, visibilityChecker: isNodeVisible }; + options = Object.assign(defaultOptions, options); + + var nodes = doc.querySelectorAll("p, pre, article"); + + // Get <div> nodes which have <br> node(s) and append them into the `nodes` variable. + // Some articles' DOM structures might look like + // <div> + // Sentences<br> + // <br> + // Sentences<br> + // </div> + var brNodes = doc.querySelectorAll("div > br"); + if (brNodes.length) { + var set = new Set(nodes); + [].forEach.call(brNodes, function (node) { + set.add(node.parentNode); + }); + nodes = Array.from(set); + } + + var score = 0; + // This is a little cheeky, we use the accumulator 'score' to decide what to return from + // this callback: + return [].some.call(nodes, function (node) { + if (!options.visibilityChecker(node)) { + return false; + } + + var matchString = node.className + " " + node.id; + if (REGEXPS.unlikelyCandidates.test(matchString) && + !REGEXPS.okMaybeItsACandidate.test(matchString)) { + return false; + } + + if (node.matches("li p")) { + return false; + } + + var textContentLength = node.textContent.trim().length; + if (textContentLength < options.minContentLength) { + return false; + } + + score += Math.sqrt(textContentLength - options.minContentLength); + + if (score > options.minScore) { + return true; + } + return false; + }); +} + +if (typeof module === "object") { + module.exports = isProbablyReaderable; +} diff --git a/mobile/android/android-components/components/feature/readerview/src/main/assets/extensions/readerview/readerview-background.js b/mobile/android/android-components/components/feature/readerview/src/main/assets/extensions/readerview/readerview-background.js new file mode 100644 index 0000000000..136e5e40e3 --- /dev/null +++ b/mobile/android/android-components/components/feature/readerview/src/main/assets/extensions/readerview/readerview-background.js @@ -0,0 +1,22 @@ +/* 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/. */ + +// This background script is needed to update the current tab +// and activate reader view. + +browser.runtime.onMessage.addListener(message => { + switch (message.action) { + case 'addSerializedDoc': + browser.storage.session.set({ [message.id]: message.doc }); + return Promise.resolve(); + case 'getSerializedDoc': + return (async () => { + let doc = await browser.storage.session.get(message.id); + browser.storage.session.remove(message.id); + return doc[message.id]; + })(); + default: + console.error(`Received unsupported action ${message.action}`); + } +}); diff --git a/mobile/android/android-components/components/feature/readerview/src/main/assets/extensions/readerview/readerview-content.js b/mobile/android/android-components/components/feature/readerview/src/main/assets/extensions/readerview/readerview-content.js new file mode 100644 index 0000000000..1d5859e793 --- /dev/null +++ b/mobile/android/android-components/components/feature/readerview/src/main/assets/extensions/readerview/readerview-content.js @@ -0,0 +1,75 @@ +/* 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/. */ + +// This script is injected into content to determine whether or not a +// page is readerable, and to open a reader view extension page via +// the background script. + +const supportedProtocols = ["http:", "https:"]; + +// Prevent false positives for these sites. This list is taken from Fennec: +// https://dxr.mozilla.org/mozilla-central/rev/7d47e7fa2489550ffa83aae67715c5497048923f/toolkit/components/reader/Readerable.js#45 +const blockedHosts = [ + "amazon.com", + "github.com", + "mail.google.com", + "pinterest.com", + "reddit.com", + "twitter.com", + "youtube.com" +]; + +function isReaderable() { + if (!supportedProtocols.includes(location.protocol)) { + return false; + } + + if (blockedHosts.some(blockedHost => location.hostname.endsWith(blockedHost))) { + return false; + } + + if (location.pathname == "/") { + return false; + } + + return isProbablyReaderable(document, _isNodeVisible); +} + +function _isNodeVisible(node) { + return node.clientHeight > 0 && node.clientWidth > 0; +} + +function connectNativePort() { + let port = browser.runtime.connectNative("mozacReaderview"); + port.onMessage.addListener((message) => { + switch (message.action) { + case 'cachePage': + let serializedDoc = new XMLSerializer().serializeToString(document); + browser.runtime.sendMessage({action: "addSerializedDoc", doc: serializedDoc, id: message.id}); + break; + case 'checkReaderState': + port.postMessage({type: 'checkReaderState', baseUrl: browser.runtime.getURL("/"), readerable: isReaderable()}); + break; + default: + console.error(`Received unsupported action ${message.action}`); + } + }); + + return port; +} + +let port = connectNativePort(); + +// When navigating to a cached page, this content script won't run again, but we +// do want to connect a new native port to trigger a new readerable check and +// apply the same logic (as on page load) in our feature class (e.g. updating the +// toolbar etc.) +window.addEventListener("pageshow", (event) => { + port = (port != null)? port : connectNativePort(); +}); + +window.addEventListener("pagehide", (event) => { + port.disconnect(); + port = null; +}); diff --git a/mobile/android/android-components/components/feature/readerview/src/main/assets/extensions/readerview/readerview.css b/mobile/android/android-components/components/feature/readerview/src/main/assets/extensions/readerview/readerview.css new file mode 100644 index 0000000000..d058e12061 --- /dev/null +++ b/mobile/android/android-components/components/feature/readerview/src/main/assets/extensions/readerview/readerview.css @@ -0,0 +1,319 @@ +/* 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/. */ + +.mozac-readerview-body { + padding: 20px; + transition-property: background-color, color; + transition-duration: 0.4s; + max-width: 35em; + margin-left: auto; + margin-right: auto; +} + +.mozac-readerview-body.light { + background-color: #ffffff; + color: #222222; +} + +.mozac-readerview-body.sepia { + color: #5b4636; + background-color: #f4ecd8; +} + +.mozac-readerview-body.dark { + background-color: #1c1b22; + color: #eeeeee; +} + +.mozac-readerview-body.light * { + color: #222222; +} + +.mozac-readerview-body.sepia * { + color: #5b4636; +} + +.mozac-readerview-body.dark * { + color: #eeeeee; +} + +.mozac-readerview-body.sans-serif * { + font-family: sans-serif !important; +} + +.mozac-readerview-body.serif * { + font-family: serif !important; +} + +/* Override some controls and content styles based on color scheme */ + +.mozac-readerview-body.light > .container > .header > .domain { + color: #ee7600; + border-bottom-color: #d0d0d0; +} + +.mozac-readerview-body.light > .container > .header > h1 { + color: #222222; +} + +.mozac-readerview-body.light > .container > .header > .credits { + color: #898989; +} + +.mozac-readerview-body.dark > .container > .header > .domain { + color: #ff9400; + border-bottom-color: #777777; +} + +.mozac-readerview-body.dark > .container > .header > h1 { + color: #eeeeee; +} + +.mozac-readerview-body.dark > .container > .header > .credits { + color: #aaaaaa; +} + +.mozac-readerview-body.sepia > .container > .header > .domain { + border-bottom-color: #5b4636 !important; +} + +.mozac-readerview-body.sepia > .container > .footer { + background-color: #dedad4 !important; +} + +.mozac-readerview-body.light > .container > .content .caption, +.mozac-readerview-body.light > .container > .content .wp-caption-text, +.mozac-readerview-body.light > .container > .content figcaption { + color: #898989; +} + +.mozac-readerview-body.dark > .container > .content .caption, +.mozac-readerview-body.dark > .container > .content .wp-caption-text, +.mozac-readerview-body.dark > .container > .content figcaption { + color: #aaaaaa; +} + +.mozac-readerview-body.light > .container > .content blockquote { + color: #898989 !important; + border-left-color: #d0d0d0 !important; +} + +.mozac-readerview-body.sepia blockquote { + border-inline-start: 2px solid #5b4636 !important; +} + +.mozac-readerview-body.dark > .container > .content blockquote { + color: #aaaaaa !important; + border-left-color: #777777 !important; +} + +.mozac-readerview-body > .container > hr { + margin: 0px; +} + +.mozac-readerview-body > .container > .header { + text-align: start; + padding-bottom: 10px; +} + +.mozac-readerview-body > .container > .header > .credits { + font-size: 0.9em; +} + +.mozac-readerview-body > .container > .header > .domain { + margin-top: 10px; + padding-bottom: 10px; + color: #00acff !important; + text-decoration: none; +} + +.mozac-readerview-body > .container > .header > .domain-border { + margin-top: 15px; + border-bottom: 1.5px solid #777777; + width: 50%; +} + +.mozac-readerview-body > .container > .header > h1 { + font-size: 1.33em; + font-weight: 700; + line-height: 1.1em; + width: 100%; + margin: 0px; + margin-top: 32px; + margin-bottom: 16px; + padding: 0px; +} + +.mozac-readerview-body > .container > .header > .credits { + padding: 0px; + margin: 0px; + 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-top: 10px; + padding-left: 0px; + padding-right: 0px; +} + +/*======= Article content =======*/ +.mozac-readerview-content { + font-size: 1em; +} + +.mozac-readerview-content a { + text-decoration: underline !important; + font-weight: normal; +} + +.mozac-readerview-body.dark :is( + .mozac-readerview-content a, + .mozac-readerview-content a:hover, + .mozac-readerview-content a:active + ):not(.mozac-readerview-content a:visited) { + color: #45a1ff !important; +} + +.mozac-readerview-content a, +.mozac-readerview-content a:hover, +.mozac-readerview-content a:active +:not(.mozac-readerview-content a:visited) { + color: #0060df !important; +} + +.mozac-readerview-content a:visited { + color: #b5007f !important; +} + +.mozac-readerview-content h1 { + margin-top: 16px; + margin-bottom: 16px; + font-weight: 700; + font-size: 1.6em; +} + +.mozac-readerview-content h2 { + margin-top: 16px; + margin-bottom: 16px; + font-weight: 700; + font-size: 1.2em; +} + +.mozac-readerview-content h3 { + margin-top: 16px; + margin-bottom: 16px; + font-weight: 700; + font-size: 1em; +} + +.mozac-readerview-content * { + max-width: 100% !important; + height: auto !important; +} + +.mozac-readerview-content p { + font-size: 1em !important; + line-height: 1.4em !important; + margin: 0px !important; + margin-bottom: 20px !important; +} + +/* Covers all images showing edge-to-edge using a + an optional caption text */ +.mozac-readerview-content .wp-caption, +.mozac-readerview-content figure { + display: block !important; + width: 100% !important; + margin: 0px !important; + margin-bottom: 32px !important; +} + +/* Images marked to be shown edge-to-edge with an + optional captio ntext */ +.mozac-readerview-content p > img:only-child, +.mozac-readerview-content p > a:only-child > img:only-child, +.mozac-readerview-content .wp-caption img, +.mozac-readerview-content figure img { + display: block; + margin-left: auto; + margin-right: auto; +} + +/* Account for body padding to make image full width */ +.mozac-readerview-content img[moz-reader-full-width] { + width: calc(100% + 40px); + margin-left: -20px; + margin-right: -20px; + max-width: none !important; +} + +/* Image caption text */ +.mozac-readerview-content .caption, +.mozac-readerview-content .wp-caption-text, +.mozac-readerview-content figcaption { + font-size: 0.9em; + font-family: sans-serif; + margin: 0px !important; + padding-top: 4px !important; +} + +/* Ensure all pre-formatted code inside the reader content + are properly wrapped inside content width */ +.mozac-readerview-content code, +.mozac-readerview-content pre { + white-space: pre-wrap !important; + margin-bottom: 20px !important; +} + +.mozac-readerview-content blockquote { + margin: 0px !important; + margin-bottom: 20px !important; + padding: 0px !important; + padding-inline-start: 16px !important; + border: 0px !important; + border-left: 2px solid !important; +} + +.mozac-readerview-content ul, +.mozac-readerview-content ol { + margin: 0px !important; + margin-bottom: 20px !important; + padding: 0px !important; + line-height: 1.5em; +} + +.mozac-readerview-content ul { + padding-inline-start: 30px !important; + list-style: disc !important; +} + +.mozac-readerview-content ol { + padding-inline-start: 35px !important; + list-style: decimal !important; +} + +/* Hide elements with common "hidden" class names */ +.mozac-readerview-content .visually-hidden, +.mozac-readerview-content .visuallyhidden, +.mozac-readerview-content .hidden, +.mozac-readerview-content .invisible, +.mozac-readerview-content .sr-only { +} + +/* Enforce wordpress and similar emoji/smileys aren't sized to be full-width, + * see bug 1399616 for context. */ +.mozac-readerview-content img.wp-smiley, +.mozac-readerview-content img.emoji { + display: inline-block; + border-width: 0; + /* height: auto is implied from `.mozac-readerview-content *` rule. */ + width: 1em; + margin: 0 .07em; + padding: 0; +} diff --git a/mobile/android/android-components/components/feature/readerview/src/main/assets/extensions/readerview/readerview.html b/mobile/android/android-components/components/feature/readerview/src/main/assets/extensions/readerview/readerview.html new file mode 100644 index 0000000000..b09deb7811 --- /dev/null +++ b/mobile/android/android-components/components/feature/readerview/src/main/assets/extensions/readerview/readerview.html @@ -0,0 +1,17 @@ +<!-- 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/. --> + +<html> + <head> + <meta content="text/html; charset=UTF-8" http-equiv="content-type" /> + <meta name="viewport" content="width=device-width; user-scalable=0" /> + <meta http-equiv="cache-control" content="no-store" /> + + <link rel="stylesheet" href="readerview.css" /> + + <script src="readability/JSDOMParser-0.4.2.js"></script> + <script src="readability/readability-0.4.2.js"></script> + <script src="readerview.js"></script> + </head> +</html> diff --git a/mobile/android/android-components/components/feature/readerview/src/main/assets/extensions/readerview/readerview.js b/mobile/android/android-components/components/feature/readerview/src/main/assets/extensions/readerview/readerview.js new file mode 100644 index 0000000000..23b1f35250 --- /dev/null +++ b/mobile/android/android-components/components/feature/readerview/src/main/assets/extensions/readerview/readerview.js @@ -0,0 +1,366 @@ +/* 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/. */ + +// Class names to preserve in the readerized output. We preserve these class +// names so that rules in readerview.css can match them. This list is taken from Fennec: +// https://dxr.mozilla.org/mozilla-central/rev/7d47e7fa2489550ffa83aae67715c5497048923f/toolkit/components/reader/ReaderMode.jsm#21 +const preservedClasses = [ + "caption", + "emoji", + "hidden", + "invisible", + "sr-only", + "visually-hidden", + "visuallyhidden", + "wp-caption", + "wp-caption-text", + "wp-smiley" +]; + +class ReaderView { + + static get MIN_FONT_SIZE() { + return 1; + } + + static get MAX_FONT_SIZE() { + return 9; + } + + /** + * Shows a reader view for the provided document. This method is used when activating + * reader view on the original page. In this case, we already have the DOM (passed + * through in the message from the background script) and can parse it directly. + * + * @param doc the document to make readerable. + * @param url the url of the article. + * @param options the fontSize, fontType and colorScheme to use. + */ + show(doc, url, options = {fontSize: 4, fontType: "sans-serif", colorScheme: "light"}) { + let result = new Readability(doc, {classesToPreserve: preservedClasses}).parse(); + result.language = doc.documentElement.lang; + document.title = result.title; + + let article = Object.assign( + result, + {url: new URL(url)}, + {readingTime: this.getReadingTime(result.length, result.language)}, + {byline: this.getByline(result)}, + {dir: this.getTextDirection(result)}, + {title: this.getTitle(result)} + ); + + document.body.outerHTML = this.createHtmlBody(article); + + this.setFontSize(options.fontSize); + this.setFontType(options.fontType); + this.setColorScheme(options.colorScheme); + if (options.scrollY) { + window.scrollTo({top: options.scrollY, left: 0, behavior: "instant"}); + } + } + + /** + * Allows adjusting the font size in discrete steps between ReaderView.MIN_FONT_SIZE + * and ReaderView.MAX_FONT_SIZE. + * + * @param changeAmount e.g. +1, or -1. + */ + changeFontSize(changeAmount) { + var size = Math.max(ReaderView.MIN_FONT_SIZE, Math.min(ReaderView.MAX_FONT_SIZE, this.fontSize + changeAmount)); + this.setFontSize(size); + } + + /** + * Sets the font size. + * + * @param fontSize must be value between ReaderView.MIN_FONT_SIZE + * and ReaderView.MAX_FONT_SIZE. + */ + setFontSize(fontSize) { + let size = (10 + 2 * fontSize) + "px"; + let readerView = document.getElementById("mozac-readerview-container"); + readerView.style.setProperty("font-size", size); + this.fontSize = fontSize; + } + + /** + * Sets the font type. + * + * @param fontType the font type to use. + */ + setFontType(fontType) { + let bodyClasses = document.body.classList; + + if (this.fontType) { + bodyClasses.remove(this.fontType); + } + + this.fontType = fontType; + bodyClasses.add(this.fontType); + } + + /** + * Sets the color scheme. + * + * @param colorScheme the color scheme to use, must be either light, dark + * or sepia. + */ + setColorScheme(colorScheme) { + if(!['light', 'sepia', 'dark'].includes(colorScheme)) { + console.error(`Invalid color scheme specified: ${colorScheme}`) + return; + } + + let bodyClasses = document.body.classList; + + if (this.colorScheme) { + bodyClasses.remove(this.colorScheme); + } + + this.colorScheme = colorScheme; + bodyClasses.add(this.colorScheme); + } + + /** + * Create the reader view HTML body. + * + * @param article a JSONObject representing the article to show. + */ + createHtmlBody(article) { + const safeDir = this.escapeHTML(article.dir); + const safeTitle = this.escapeHTML(article.title); + const safeByline = this.escapeHTML(article.byline); + const safeReadingTime = this.escapeHTML(article.readingTime); + return ` + <body class="mozac-readerview-body"> + <div id="mozac-readerview-container" class="container" dir="${safeDir}"> + <div class="header"> + <a class="domain" href="${article.url.href}">${article.url.hostname}</a> + <div class="domain-border"></div> + <h1>${safeTitle}</h1> + <div class="credits">${safeByline}</div> + <div> + <div>${safeReadingTime}</div> + </div> + </div> + <hr> + + <div class="content"> + <div class="mozac-readerview-content">${article.content}</div> + </div> + </div> + </body> + ` + } + + /** + * Returns the estimated reading time as localized string. + * + * @param length of the article (number of chars). + * @param optional language of the article, defaults to en. + */ + getReadingTime(length, lang = "en") { + const [readingSpeed, readingSpeedLang] = this.getReadingSpeedForLanguage(lang); + const charactersPerMinuteLow = readingSpeed.cpm - readingSpeed.variance; + const charactersPerMinuteHigh = readingSpeed.cpm + readingSpeed.variance; + const readingTimeMinsSlow = Math.ceil(length / charactersPerMinuteLow); + const readingTimeMinsFast = Math.ceil(length / charactersPerMinuteHigh); + + // Construct a localized and "humanized" reading time in minutes. + // If we have both a fast and slow reading time we'll show both e.g. + // "2 - 4 minutes", otherwise we'll just show "4 minutes". + try { + var parts = new Intl.RelativeTimeFormat(readingSpeedLang).formatToParts(readingTimeMinsSlow, 'minute'); + if (parts.length == 3) { + // No need to use part[0] which represents the literal "in". + var readingTime = parts[1].value; // reading time in minutes + var minutesLiteral = parts[2].value; // localized singular or plural literal of 'minute' + var readingTimeString = `${readingTime} ${minutesLiteral}`; + if (readingTimeMinsSlow != readingTimeMinsFast) { + readingTimeString = `${readingTimeMinsFast} - ${readingTimeString}`; + } + return readingTimeString; + } + } + catch(error) { + console.error(`Failed to format reading time: ${error}`); + } + + return ""; + } + + /** + * Returns the reading speed of a selection of languages with likely variance. + * + * Reading speed estimated from a study done on reading speeds in various languages. + * study can be found here: http://iovs.arvojournals.org/article.aspx?articleid=2166061 + * + * @return object with characters per minute and variance. Defaults to English + * if no suitable language is found in the collection. + */ + getReadingSpeedForLanguage(lang) { + const readingSpeed = new Map([ + [ "en", {cpm: 987, variance: 118 } ], + [ "ar", {cpm: 612, variance: 88 } ], + [ "de", {cpm: 920, variance: 86 } ], + [ "es", {cpm: 1025, variance: 127 } ], + [ "fi", {cpm: 1078, variance: 121 } ], + [ "fr", {cpm: 998, variance: 126 } ], + [ "he", {cpm: 833, variance: 130 } ], + [ "it", {cpm: 950, variance: 140 } ], + [ "jw", {cpm: 357, variance: 56 } ], + [ "nl", {cpm: 978, variance: 143 } ], + [ "pl", {cpm: 916, variance: 126 } ], + [ "pt", {cpm: 913, variance: 145 } ], + [ "ru", {cpm: 986, variance: 175 } ], + [ "sk", {cpm: 885, variance: 145 } ], + [ "sv", {cpm: 917, variance: 156 } ], + [ "tr", {cpm: 1054, variance: 156 } ], + [ "zh", {cpm: 255, variance: 29 } ], + ]); + + return readingSpeed.has(lang) ? [readingSpeed.get(lang), lang] : [readingSpeed.get("en"), "en"]; + } + + getByline(article) { + return article.byline || ""; + } + + /** + * Attempts to read the optional text direction from the article and uses + * language mapping to detect rtl, if missing. + */ + getTextDirection(article) { + if (article.dir) { + return article.dir; + } + + if (["ar", "fa", "he", "ug", "ur"].includes(article.language)) { + return "rtl"; + } + + return "ltr"; + } + + getTitle(article) { + return article.title || ""; + } + + escapeHTML(text) { + return text + .replace(/\&/g, "&") + .replace(/\</g, "<") + .replace(/\>/g, ">") + .replace(/\"/g, """) + .replace(/\'/g, "'"); + } +} + +function fetchDocument(url) { + return new Promise((resolve, reject) => { + let xhr = new XMLHttpRequest(); + xhr.open("GET", url, true); + xhr.onerror = evt => reject(evt.error); + xhr.responseType = "document"; + xhr.onload = evt => { + if (xhr.status !== 200) { + reject("Reader mode XHR failed with status: " + xhr.status); + return; + } + let doc = xhr.responseXML; + if (!doc) { + reject("Reader mode XHR didn't return a document"); + return; + } + resolve(doc); + }; + xhr.send(); + }); +} + +function getPreparedDocument(id, url) { + return new Promise((resolve, reject) => { + + browser.runtime.sendMessage({action: "getSerializedDoc", id: id}).then((serializedDoc) => { + if (serializedDoc) { + let doc = new JSDOMParser().parse(serializedDoc, url); + resolve(doc); + } else { + reject(); + } + } + ); + }); +} + +let readerView = new ReaderView(); +connectNativePort(); +prepareBody(); + +function connectNativePort() { + let url = new URL(window.location.href); + let articleUrl = url.searchParams.get("url"); + let id = url.searchParams.get("id"); + let baseUrl = browser.runtime.getURL("/"); + + let port = browser.runtime.connectNative("mozacReaderviewActive"); + port.onMessage.addListener((message) => { + switch (message.action) { + case 'show': + async function showAsync(options) { + try { + let doc; + if (typeof Promise.any === "function") { + doc = await Promise.any([fetchDocument(articleUrl), getPreparedDocument(id, articleUrl)]); + } else { + try { + doc = await getPreparedDocument(id, articleUrl); + } catch(e) { + doc = await fetchDocument(articleUrl); + } + } + readerView.show(doc, articleUrl, options); + } catch(e) { + console.log(e); + // We weren't able to find the prepared document and also + // failed to fetch it. Let's load the original page which + // will make sure we show an appropriate error page. + window.location.href = articleUrl; + } + } + showAsync(message.value); + break; + case 'hide': + window.location.href = articleUrl; + case 'setColorScheme': + readerView.setColorScheme(message.value.toLowerCase()); + break; + case 'changeFontSize': + readerView.changeFontSize(message.value); + break; + case 'setFontType': + readerView.setFontType(message.value.toLowerCase()); + break; + case 'checkReaderState': + port.postMessage({baseUrl: baseUrl, activeUrl: articleUrl, readerable: true}); + break; + default: + console.error(`Received invalid action ${message.action}`); + } + }); +} + +/** + * Applies the configured color scheme to the HTML body while reader view is loading. This is to + * prevent "flashes" caused by having to change the color later. + */ +function prepareBody() { + let url = new URL(window.location.href); + let colorScheme = url.searchParams.get("colorScheme"); + let body = document.createElement("body"); + body.classList.add("mozac-readerview-body"); + body.classList.add(colorScheme); + document.body = body; +} diff --git a/mobile/android/android-components/components/feature/readerview/src/main/java/mozilla/components/feature/readerview/ReaderViewFeature.kt b/mobile/android/android-components/components/feature/readerview/src/main/java/mozilla/components/feature/readerview/ReaderViewFeature.kt new file mode 100644 index 0000000000..51d6083a6c --- /dev/null +++ b/mobile/android/android-components/components/feature/readerview/src/main/java/mozilla/components/feature/readerview/ReaderViewFeature.kt @@ -0,0 +1,382 @@ +/* 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.readerview + +import android.content.Context +import androidx.annotation.VisibleForTesting +import kotlinx.coroutines.CoroutineScope +import kotlinx.coroutines.cancel +import kotlinx.coroutines.flow.collect +import kotlinx.coroutines.flow.mapNotNull +import mozilla.components.browser.state.action.EngineAction +import mozilla.components.browser.state.action.ReaderAction +import mozilla.components.browser.state.selector.selectedTab +import mozilla.components.browser.state.state.TabSessionState +import mozilla.components.browser.state.store.BrowserStore +import mozilla.components.concept.engine.Engine +import mozilla.components.concept.engine.webextension.MessageHandler +import mozilla.components.concept.engine.webextension.Port +import mozilla.components.feature.readerview.internal.ReaderViewConfig +import mozilla.components.feature.readerview.internal.ReaderViewControlsInteractor +import mozilla.components.feature.readerview.internal.ReaderViewControlsPresenter +import mozilla.components.feature.readerview.view.ReaderViewControlsView +import mozilla.components.lib.state.ext.flowScoped +import mozilla.components.support.base.feature.LifecycleAwareFeature +import mozilla.components.support.base.feature.UserInteractionHandler +import mozilla.components.support.base.log.logger.Logger +import mozilla.components.support.ktx.kotlinx.coroutines.flow.filterChanged +import mozilla.components.support.webextensions.WebExtensionController +import org.json.JSONObject +import java.lang.ref.WeakReference +import java.net.URLEncoder +import java.util.Locale +import java.util.UUID + +typealias onReaderViewStatusChange = (available: Boolean, active: Boolean) -> Unit +typealias UUIDCreator = () -> String + +/** + * Feature implementation that provides a reader view for the selected + * session, based on a web extension. + * + * @property context a reference to the context. + * @property engine a reference to the application's browser engine. + * @property store a reference to the application's [BrowserStore]. + * @param controlsView the view to use to display reader mode controls. + * @property onReaderViewStatusChange a callback invoked to indicate whether + * or not reader view is available and active for the page loaded by the + * currently selected session. The callback will be invoked when a page is + * loaded or refreshed, on any navigation (back or forward), and when the + * selected session changes. + */ +class ReaderViewFeature( + private val context: Context, + private val engine: Engine, + private val store: BrowserStore, + controlsView: ReaderViewControlsView, + private val createUUID: UUIDCreator = { UUID.randomUUID().toString() }, + private val onReaderViewStatusChange: onReaderViewStatusChange = { _, _ -> Unit }, +) : LifecycleAwareFeature, UserInteractionHandler { + + private var scope: CoroutineScope? = null + + @VisibleForTesting var readerBaseUrl: String? = null + + @VisibleForTesting + // This is an internal var to make it mutable for unit testing purposes only + internal var extensionController = WebExtensionController( + READER_VIEW_EXTENSION_ID, + READER_VIEW_EXTENSION_URL, + READER_VIEW_CONTENT_PORT, + ) + + @VisibleForTesting + internal val config = ReaderViewConfig(context) { message -> + val engineSession = store.state.selectedTab?.engineState?.engineSession + extensionController.sendContentMessage(message, engineSession, READER_VIEW_ACTIVE_CONTENT_PORT) + } + + private val controlsPresenter = ReaderViewControlsPresenter(controlsView, config) + private val controlsInteractor = ReaderViewControlsInteractor(controlsView, config) + + enum class FontType(val value: String) { SANSSERIF("sans-serif"), SERIF("serif") } + enum class ColorScheme { LIGHT, SEPIA, DARK } + + override fun start() { + ensureExtensionInstalled() + + scope = store.flowScoped { flow -> + flow.mapNotNull { state -> state.tabs } + .filterChanged { + it.readerState + } + .collect { tab -> + if (tab.readerState.connectRequired) { + connectReaderViewContentScript(tab) + } + if (tab.readerState.checkRequired) { + checkReaderState(tab) + } + if (tab.id == store.state.selectedTabId) { + maybeNotifyReaderStatusChange(tab.readerState.readerable, tab.readerState.active) + } + } + } + + controlsInteractor.start() + } + + override fun stop() { + scope?.cancel() + controlsInteractor.stop() + } + + override fun onBackPressed(): Boolean { + store.state.selectedTab?.let { + if (it.readerState.active) { + if (controlsPresenter.areControlsVisible()) { + hideControls() + } else { + hideReaderView() + } + return true + } + } + return false + } + + /** + * Shows the reader view UI. + */ + fun showReaderView(session: TabSessionState? = store.state.selectedTab) { + session?.let { + if (!it.readerState.active) { + val id = createUUID() + extensionController.sendContentMessage( + createCachePageMessage(id), + it.engineState.engineSession, + READER_VIEW_CONTENT_PORT, + ) + + val readerUrl = extensionController.createReaderUrl(it.content.url, id) ?: run { + Logger.error("FeatureReaderView unable to create ReaderUrl.") + return@let + } + + store.dispatch(EngineAction.LoadUrlAction(it.id, readerUrl)) + store.dispatch(ReaderAction.UpdateReaderActiveAction(it.id, true)) + } + } + } + + /** + * Hides the reader view UI. + */ + fun hideReaderView(session: TabSessionState? = store.state.selectedTab) { + session?.let { it -> + if (it.readerState.active) { + store.dispatch(ReaderAction.UpdateReaderActiveAction(it.id, false)) + store.dispatch(ReaderAction.UpdateReaderableAction(it.id, false)) + store.dispatch(ReaderAction.ClearReaderActiveUrlAction(it.id)) + if (it.content.canGoBack) { + it.engineState.engineSession?.goBack(false) + } else { + extensionController.sendContentMessage( + createHideReaderMessage(), + it.engineState.engineSession, + READER_VIEW_ACTIVE_CONTENT_PORT, + ) + } + } + } + } + + /** + * Shows the reader view appearance controls. + */ + fun showControls() { + controlsPresenter.show() + } + + /** + * Hides the reader view appearance controls. + */ + fun hideControls() { + controlsPresenter.hide() + } + + @VisibleForTesting + internal fun checkReaderState(session: TabSessionState? = store.state.selectedTab) { + session?.engineState?.engineSession?.let { engineSession -> + val message = createCheckReaderStateMessage() + if (extensionController.portConnected(engineSession, READER_VIEW_CONTENT_PORT)) { + extensionController.sendContentMessage(message, engineSession, READER_VIEW_CONTENT_PORT) + } + if (extensionController.portConnected(engineSession, READER_VIEW_ACTIVE_CONTENT_PORT)) { + extensionController.sendContentMessage(message, engineSession, READER_VIEW_ACTIVE_CONTENT_PORT) + } + store.dispatch(ReaderAction.UpdateReaderableCheckRequiredAction(session.id, false)) + } + } + + @VisibleForTesting + internal fun connectReaderViewContentScript(session: TabSessionState? = store.state.selectedTab) { + session?.engineState?.engineSession?.let { engineSession -> + extensionController.registerContentMessageHandler( + engineSession, + ActiveReaderViewContentMessageHandler(store, session.id, WeakReference(config)), + READER_VIEW_ACTIVE_CONTENT_PORT, + ) + extensionController.registerContentMessageHandler( + engineSession, + ReaderViewContentMessageHandler(store, session.id), + READER_VIEW_CONTENT_PORT, + ) + store.dispatch(ReaderAction.UpdateReaderConnectRequiredAction(session.id, false)) + } + } + + private var lastNotified: Pair<Boolean, Boolean>? = null + + @VisibleForTesting + internal fun maybeNotifyReaderStatusChange(readerable: Boolean = false, active: Boolean = false) { + // Make sure we only notify the UI if needed (an actual change happened) to prevent + // it from unnecessarily invalidating toolbar/menu items. + if (lastNotified == null || lastNotified != Pair(readerable, active)) { + onReaderViewStatusChange(readerable, active) + lastNotified = Pair(readerable, active) + } + } + + private fun ensureExtensionInstalled() { + val feature = WeakReference(this) + extensionController.install( + engine, + onSuccess = { + it.getMetadata()?.run { + readerBaseUrl = baseUrl + } ?: run { + Logger.error("ReaderView extension missing Metadata") + } + + feature.get()?.connectReaderViewContentScript() + }, + ) + } + + /** + * Handles content messages from regular pages. + */ + private open class ReaderViewContentMessageHandler( + protected val store: BrowserStore, + protected val sessionId: String, + ) : MessageHandler { + override fun onPortConnected(port: Port) { + port.postMessage(createCheckReaderStateMessage()) + } + + override fun onPortMessage(message: Any, port: Port) { + if (message is JSONObject) { + val readerable = message.optBoolean(READERABLE_RESPONSE_MESSAGE_KEY, false) + store.dispatch(ReaderAction.UpdateReaderableAction(sessionId, readerable)) + } + } + } + + /** + * Handles content messages from active reader pages. + */ + private class ActiveReaderViewContentMessageHandler( + store: BrowserStore, + sessionId: String, + // This needs to be a weak reference because the engine session this message handler will be + // attached to has a longer lifespan than the feature instance i.e. a tab can remain open, + // but we don't want to prevent the feature (and therefore its context/fragment) from + // being garbage collected. The config has references to both the context and feature. + private val config: WeakReference<ReaderViewConfig>, + ) : ReaderViewContentMessageHandler(store, sessionId) { + + override fun onPortMessage(message: Any, port: Port) { + super.onPortMessage(message, port) + + if (message is JSONObject) { + val baseUrl = message.getString(BASE_URL_RESPONSE_MESSAGE_KEY) + store.dispatch(ReaderAction.UpdateReaderBaseUrlAction(sessionId, baseUrl)) + + port.postMessage(createShowReaderMessage(config.get(), store.state.selectedTab?.readerState?.scrollY)) + + val activeUrl = message.getString(ACTIVE_URL_RESPONSE_MESSAGE_KEY) + store.dispatch(ReaderAction.UpdateReaderActiveUrlAction(sessionId, activeUrl)) + } + } + } + + private fun WebExtensionController.createReaderUrl(url: String, id: String): String? { + val colorScheme = config.colorScheme.name.lowercase(Locale.ROOT) + // Encode the original page url, otherwise when the readerview page will try to + // parse the url and retrieve the readerview url params (ir and colorScheme) + // the parser may get confused because the original webpage url being interpolated + // may also include its own search params non-escaped (See Bug 1860490). + val encodedUrl = URLEncoder.encode(url, "UTF-8") + return readerBaseUrl?.let { it + "readerview.html?url=$encodedUrl&id=$id&colorScheme=$colorScheme" } + } + + companion object { + private val logger = Logger("ReaderView") + + internal const val READER_VIEW_EXTENSION_ID = "readerview@mozac.org" + + // Name of the port connected to all pages for checking whether or not + // a page is readerable (see readerview_content.js). + internal const val READER_VIEW_CONTENT_PORT = "mozacReaderview" + + // Name of the port connected to active reader pages for updating + // appearance configuration (see readerview.js). + internal const val READER_VIEW_ACTIVE_CONTENT_PORT = "mozacReaderviewActive" + internal const val READER_VIEW_EXTENSION_URL = "resource://android/assets/extensions/readerview/" + + // Constants for building messages sent to the web extension: + // Change the font type: {"action": "setFontType", "value": "sans-serif"} + // Show reader view: {"action": "show", "value": {"fontSize": 3, "fontType": "serif", "colorScheme": "dark"}} + internal const val ACTION_MESSAGE_KEY = "action" + internal const val ACTION_CACHE_PAGE = "cachePage" + internal const val ACTION_SHOW = "show" + internal const val ACTION_HIDE = "hide" + internal const val ACTION_CHECK_READER_STATE = "checkReaderState" + 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_SHOW_FONT_SIZE = "fontSize" + internal const val ACTION_VALUE_SHOW_FONT_TYPE = "fontType" + internal const val ACTION_VALUE_SHOW_COLOR_SCHEME = "colorScheme" + internal const val ACTION_VALUE_SCROLLY = "scrollY" + internal const val ACTION_VALUE_ID = "id" + internal const val READERABLE_RESPONSE_MESSAGE_KEY = "readerable" + internal const val BASE_URL_RESPONSE_MESSAGE_KEY = "baseUrl" + internal const val ACTIVE_URL_RESPONSE_MESSAGE_KEY = "activeUrl" + + // Constants for storing the reader mode config in shared preferences + internal const val SHARED_PREF_NAME = "mozac_feature_reader_view" + internal const val COLOR_SCHEME_KEY = "mozac-readerview-colorscheme" + internal const val FONT_TYPE_KEY = "mozac-readerview-fonttype" + internal const val FONT_SIZE_KEY = "mozac-readerview-fontsize" + internal const val FONT_SIZE_DEFAULT = 3 + + internal fun createCheckReaderStateMessage(): JSONObject { + return JSONObject().put(ACTION_MESSAGE_KEY, ACTION_CHECK_READER_STATE) + } + + internal fun createCachePageMessage(id: String): JSONObject { + return JSONObject() + .put(ACTION_MESSAGE_KEY, ACTION_CACHE_PAGE) + .put(ACTION_VALUE_ID, id) + } + + internal fun createShowReaderMessage(config: ReaderViewConfig?, scrollY: Int? = null): JSONObject { + if (config == null) { + logger.warn("No config provided. Falling back to default values.") + } + + val fontSize = config?.fontSize ?: FONT_SIZE_DEFAULT + val fontType = config?.fontType ?: FontType.SERIF + val colorScheme = config?.colorScheme ?: ColorScheme.LIGHT + val configJson = JSONObject() + .put(ACTION_VALUE_SHOW_FONT_SIZE, fontSize) + .put(ACTION_VALUE_SHOW_FONT_TYPE, fontType.value.lowercase(Locale.ROOT)) + .put(ACTION_VALUE_SHOW_COLOR_SCHEME, colorScheme.name.lowercase(Locale.ROOT)) + if (scrollY != null) { + configJson.put(ACTION_VALUE_SCROLLY, scrollY) + } + return JSONObject() + .put(ACTION_MESSAGE_KEY, ACTION_SHOW) + .put(ACTION_VALUE, configJson) + } + + internal fun createHideReaderMessage(): JSONObject { + return JSONObject().put(ACTION_MESSAGE_KEY, ACTION_HIDE) + } + } +} diff --git a/mobile/android/android-components/components/feature/readerview/src/main/java/mozilla/components/feature/readerview/ReaderViewMiddleware.kt b/mobile/android/android-components/components/feature/readerview/src/main/java/mozilla/components/feature/readerview/ReaderViewMiddleware.kt new file mode 100644 index 0000000000..b5d602553d --- /dev/null +++ b/mobile/android/android-components/components/feature/readerview/src/main/java/mozilla/components/feature/readerview/ReaderViewMiddleware.kt @@ -0,0 +1,134 @@ +/* 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.readerview + +import androidx.annotation.VisibleForTesting +import mozilla.components.browser.state.action.BrowserAction +import mozilla.components.browser.state.action.ContentAction +import mozilla.components.browser.state.action.EngineAction +import mozilla.components.browser.state.action.ReaderAction +import mozilla.components.browser.state.action.TabListAction +import mozilla.components.browser.state.selector.findTab +import mozilla.components.browser.state.state.BrowserState +import mozilla.components.browser.state.state.TabSessionState +import mozilla.components.feature.readerview.ReaderViewFeature.Companion.READER_VIEW_CONTENT_PORT +import mozilla.components.feature.readerview.ReaderViewFeature.Companion.READER_VIEW_EXTENSION_ID +import mozilla.components.feature.readerview.ReaderViewFeature.Companion.READER_VIEW_EXTENSION_URL +import mozilla.components.lib.state.Middleware +import mozilla.components.lib.state.MiddlewareContext +import mozilla.components.support.webextensions.WebExtensionController + +/** + * [Middleware] implementation for translating [BrowserAction]s to + * [ReaderAction]s (e.g. if the URL is updated a new "readerable" + * check should be executed.) + */ +class ReaderViewMiddleware : Middleware<BrowserState, BrowserAction> { + + @VisibleForTesting + internal var extensionController = WebExtensionController( + READER_VIEW_EXTENSION_ID, + READER_VIEW_EXTENSION_URL, + READER_VIEW_CONTENT_PORT, + ) + + override fun invoke( + context: MiddlewareContext<BrowserState, BrowserAction>, + next: (BrowserAction) -> Unit, + action: BrowserAction, + ) { + if (preProcess(context, action)) { + next(action) + postProcess(context, action) + } + } + + /** + * Processes the action before it is dispatched to the store. + * + * @param context a reference to the [MiddlewareContext]. + * @param action the action to process. + * @return true if the original action should be processed, otherwise false. + */ + private fun preProcess( + context: MiddlewareContext<BrowserState, BrowserAction>, + action: BrowserAction, + ): Boolean { + return when (action) { + // We want to bind the feature instance to the lifecycle of the browser + // fragment. So it won't necessarily be active when a tab is removed + // (e.g. via a tabs tray fragment). In order to disconnect the port as + // early as possible it's best to do it here directly. + is EngineAction.UnlinkEngineSessionAction -> { + context.state.findTab(action.tabId)?.engineState?.engineSession?.let { + extensionController.disconnectPort(it, READER_VIEW_EXTENSION_ID) + } + true + } + is ContentAction.UpdateUrlAction -> { + // Activate reader view when navigating to a reader page and deactivate it + // when navigating away. In addition, we want to mask moz-extension:// + // URLs in the toolbar. So, if we detect the URL is coming from our + // extension we show the original URL instead. This is needed until + // we have a solution for: + // https://bugzilla.mozilla.org/show_bug.cgi?id=1550144 + // https://bugzilla.mozilla.org/show_bug.cgi?id=1322304 + // https://github.com/mozilla-mobile/android-components/issues/2879 + val tab = context.state.findTab(action.sessionId) + if (isReaderUrl(tab, action.url)) { + val urlReplaced = tab?.readerState?.activeUrl?.let { activeUrl -> + context.dispatch(ContentAction.UpdateUrlAction(action.sessionId, activeUrl)) + true + } ?: false + context.dispatch(ReaderAction.UpdateReaderActiveAction(action.sessionId, true)) + !urlReplaced + } else { + if (action.url != tab?.readerState?.activeUrl) { + context.dispatch(ReaderAction.UpdateReaderActiveAction(action.sessionId, false)) + context.dispatch(ReaderAction.UpdateReaderableAction(action.sessionId, false)) + context.dispatch(ReaderAction.UpdateReaderableCheckRequiredAction(action.sessionId, true)) + context.dispatch(ReaderAction.ClearReaderActiveUrlAction(action.sessionId)) + } + true + } + } + else -> true + } + } + + private fun postProcess( + context: MiddlewareContext<BrowserState, BrowserAction>, + action: BrowserAction, + ) { + when (action) { + is TabListAction.SelectTabAction -> { + context.dispatch(ReaderAction.UpdateReaderConnectRequiredAction(action.tabId, true)) + context.dispatch(ReaderAction.UpdateReaderableAction(action.tabId, false)) + context.dispatch(ReaderAction.UpdateReaderableCheckRequiredAction(action.tabId, true)) + } + is EngineAction.LinkEngineSessionAction -> { + context.dispatch(ReaderAction.UpdateReaderConnectRequiredAction(action.tabId, true)) + } + is ReaderAction.UpdateReaderActiveUrlAction -> { + // When a tab is restored, the reader page will connect, but we won't get a + // UpdateUrlAction. We still want to mask the moz-extension:// URL though + // so we update the URL here. See comment on handling UpdateUrlAction. + val tab = context.state.findTab(action.tabId) + val url = tab?.content?.url + if (url != null && isReaderUrl(tab, url)) { + context.dispatch(ContentAction.UpdateUrlAction(tab.id, url)) + } + } + else -> { + // no-op + } + } + } + + private fun isReaderUrl(tab: TabSessionState?, url: String): Boolean { + val readerViewBaseUrl = tab?.readerState?.baseUrl + return readerViewBaseUrl != null && url.startsWith(readerViewBaseUrl) + } +} diff --git a/mobile/android/android-components/components/feature/readerview/src/main/java/mozilla/components/feature/readerview/internal/ReaderViewConfig.kt b/mobile/android/android-components/components/feature/readerview/src/main/java/mozilla/components/feature/readerview/internal/ReaderViewConfig.kt new file mode 100644 index 0000000000..561d63e0e0 --- /dev/null +++ b/mobile/android/android-components/components/feature/readerview/src/main/java/mozilla/components/feature/readerview/internal/ReaderViewConfig.kt @@ -0,0 +1,108 @@ +/* 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.readerview.internal + +import android.content.Context +import android.content.Context.MODE_PRIVATE +import android.content.res.Configuration +import mozilla.components.feature.readerview.ReaderViewFeature +import mozilla.components.feature.readerview.ReaderViewFeature.Companion.ACTION_CHANGE_FONT_SIZE +import mozilla.components.feature.readerview.ReaderViewFeature.Companion.ACTION_MESSAGE_KEY +import mozilla.components.feature.readerview.ReaderViewFeature.Companion.ACTION_SET_FONT_TYPE +import mozilla.components.feature.readerview.ReaderViewFeature.Companion.ACTION_VALUE +import mozilla.components.feature.readerview.ReaderViewFeature.Companion.COLOR_SCHEME_KEY +import mozilla.components.feature.readerview.ReaderViewFeature.Companion.FONT_SIZE_DEFAULT +import mozilla.components.feature.readerview.ReaderViewFeature.Companion.FONT_SIZE_KEY +import mozilla.components.feature.readerview.ReaderViewFeature.Companion.FONT_TYPE_KEY +import mozilla.components.feature.readerview.ReaderViewFeature.Companion.SHARED_PREF_NAME +import org.json.JSONObject + +/** + * Stores the user configuration for reader view in shared prefs. + * All values are initialized lazily and cached. + * @param context Used to lazily obtain shared preferences and to check dark mode status. + * @param sendConfigMessage If the config changes, this method will be invoked + * with a JSON object which should be sent to the content script so the new + * config can be applied. + */ +internal class ReaderViewConfig( + context: Context, + private val sendConfigMessage: (JSONObject) -> Unit, +) { + + private val prefs by lazy { context.getSharedPreferences(SHARED_PREF_NAME, MODE_PRIVATE) } + private val resources = context.resources + private var colorSchemeCache: ReaderViewFeature.ColorScheme? = null + private var fontTypeCache: ReaderViewFeature.FontType? = null + private var fontSizeCache: Int? = null + + var colorScheme: ReaderViewFeature.ColorScheme + get() { + if (colorSchemeCache == null) { + // Default to a dark theme if either the system or local dark theme is active + val defaultColor = if (isNightMode()) { + ReaderViewFeature.ColorScheme.DARK + } else { + ReaderViewFeature.ColorScheme.LIGHT + } + colorSchemeCache = getEnumFromPrefs(COLOR_SCHEME_KEY, defaultColor) + } + return colorSchemeCache!! + } + set(value) { + if (colorSchemeCache != value) { + colorSchemeCache = value + prefs.edit().putString(COLOR_SCHEME_KEY, value.name).apply() + sendMessage(ReaderViewFeature.ACTION_SET_COLOR_SCHEME) { put(ACTION_VALUE, value.name) } + } + } + + var fontType: ReaderViewFeature.FontType + get() { + if (fontTypeCache == null) { + fontTypeCache = getEnumFromPrefs(FONT_TYPE_KEY, ReaderViewFeature.FontType.SERIF) + } + return fontTypeCache!! + } + set(value) { + if (fontTypeCache != value) { + fontTypeCache = value + prefs.edit().putString(FONT_TYPE_KEY, value.name).apply() + sendMessage(ACTION_SET_FONT_TYPE) { put(ACTION_VALUE, value.value) } + } + } + + var fontSize: Int + get() { + if (fontSizeCache == null) { + fontSizeCache = prefs.getInt(FONT_SIZE_KEY, FONT_SIZE_DEFAULT) + } + return fontSizeCache!! + } + set(value) { + if (fontSizeCache != value) { + val diff = value - fontSize + fontSizeCache = value + prefs.edit().putInt(FONT_SIZE_KEY, value).apply() + sendMessage(ACTION_CHANGE_FONT_SIZE) { put(ACTION_VALUE, diff) } + } + } + + private inline fun <reified T : Enum<T>> getEnumFromPrefs(key: String, default: T): T { + val enumName = prefs.getString(key, default.name) ?: default.name + return enumValueOf(enumName) + } + + private fun isNightMode(): Boolean { + val darkFlag = resources?.configuration?.uiMode?.and(Configuration.UI_MODE_NIGHT_MASK) + return darkFlag == Configuration.UI_MODE_NIGHT_YES + } + + private inline fun sendMessage(action: String, crossinline builder: JSONObject.() -> Unit) { + val message = JSONObject().put(ACTION_MESSAGE_KEY, action) + builder(message) + sendConfigMessage(message) + } +} diff --git a/mobile/android/android-components/components/feature/readerview/src/main/java/mozilla/components/feature/readerview/internal/ReaderViewControlsInteractor.kt b/mobile/android/android-components/components/feature/readerview/src/main/java/mozilla/components/feature/readerview/internal/ReaderViewControlsInteractor.kt new file mode 100644 index 0000000000..59b0f0d3d5 --- /dev/null +++ b/mobile/android/android-components/components/feature/readerview/src/main/java/mozilla/components/feature/readerview/internal/ReaderViewControlsInteractor.kt @@ -0,0 +1,51 @@ +/* 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.readerview.internal + +import mozilla.components.feature.readerview.ReaderViewFeature +import mozilla.components.feature.readerview.ReaderViewFeature.ColorScheme +import mozilla.components.feature.readerview.ReaderViewFeature.FontType +import mozilla.components.feature.readerview.view.MAX_TEXT_SIZE +import mozilla.components.feature.readerview.view.MIN_TEXT_SIZE +import mozilla.components.feature.readerview.view.ReaderViewControlsView + +/** + * Interactor that implements [ReaderViewControlsView.Listener] and notifies the feature about actions the user + * performed via the [ReaderViewFeature.Config] (e.g. "font changed"). + */ +internal class ReaderViewControlsInteractor( + private val view: ReaderViewControlsView, + private val config: ReaderViewConfig, +) : ReaderViewControlsView.Listener { + fun start() { + view.listener = this + } + + fun stop() { + view.listener = null + } + + override fun onFontChanged(font: FontType) { + config.fontType = font + } + + override fun onFontSizeIncreased(): Int { + if (config.fontSize < MAX_TEXT_SIZE) { + config.fontSize += 1 + } + return config.fontSize + } + + override fun onFontSizeDecreased(): Int { + if (config.fontSize > MIN_TEXT_SIZE) { + config.fontSize -= 1 + } + return config.fontSize + } + + override fun onColorSchemeChanged(scheme: ColorScheme) { + config.colorScheme = scheme + } +} diff --git a/mobile/android/android-components/components/feature/readerview/src/main/java/mozilla/components/feature/readerview/internal/ReaderViewControlsPresenter.kt b/mobile/android/android-components/components/feature/readerview/src/main/java/mozilla/components/feature/readerview/internal/ReaderViewControlsPresenter.kt new file mode 100644 index 0000000000..89fa48fc44 --- /dev/null +++ b/mobile/android/android-components/components/feature/readerview/src/main/java/mozilla/components/feature/readerview/internal/ReaderViewControlsPresenter.kt @@ -0,0 +1,43 @@ +/* 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.readerview.internal + +import androidx.core.view.isVisible +import mozilla.components.feature.readerview.view.ReaderViewControlsView + +/** + * Presenter implementation that will update the view whenever the feature is started. + */ +internal class ReaderViewControlsPresenter( + private val view: ReaderViewControlsView, + private val config: ReaderViewConfig, +) { + /** + * Sets the initial state of the ReaderView controls and makes the controls visible. + */ + fun show() { + view.apply { + tryInflate() + setColorScheme(config.colorScheme) + setFont(config.fontType) + setFontSize(config.fontSize) + showControls() + } + } + + /** + * Checks whether or not the ReaderView controls are visible. + */ + fun areControlsVisible(): Boolean { + return view.asView().isVisible + } + + /** + * Hides the controls. + */ + fun hide() { + view.hideControls() + } +} diff --git a/mobile/android/android-components/components/feature/readerview/src/main/java/mozilla/components/feature/readerview/view/ReaderViewControlsBar.kt b/mobile/android/android-components/components/feature/readerview/src/main/java/mozilla/components/feature/readerview/view/ReaderViewControlsBar.kt new file mode 100644 index 0000000000..3071b58323 --- /dev/null +++ b/mobile/android/android-components/components/feature/readerview/src/main/java/mozilla/components/feature/readerview/view/ReaderViewControlsBar.kt @@ -0,0 +1,174 @@ +/* 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.readerview.view + +import android.content.Context +import android.graphics.Rect +import android.util.AttributeSet +import android.view.View +import android.widget.RadioGroup +import androidx.annotation.IdRes +import androidx.appcompat.widget.AppCompatButton +import androidx.constraintlayout.widget.ConstraintLayout +import mozilla.components.feature.readerview.R +import mozilla.components.feature.readerview.ReaderViewFeature.ColorScheme +import mozilla.components.feature.readerview.ReaderViewFeature.FontType + +const val MAX_TEXT_SIZE = 9 +const val MIN_TEXT_SIZE = 1 + +/** + * A customizable ReaderView control bar implementing [ReaderViewControlsView]. + */ +class ReaderViewControlsBar @JvmOverloads constructor( + context: Context, + attrs: AttributeSet? = null, + defStyleAttr: Int = 0, +) : ConstraintLayout(context, attrs, defStyleAttr), ReaderViewControlsView { + + override var listener: ReaderViewControlsView.Listener? = null + + private lateinit var fontIncrementButton: AppCompatButton + private lateinit var fontDecrementButton: AppCompatButton + private lateinit var fontGroup: RadioGroup + private lateinit var colorSchemeGroup: RadioGroup + + private var view: View? = null + + init { + isFocusableInTouchMode = true + isClickable = true + } + + /** + * Sets the font type of the current and future ReaderView sessions. + * + * @param font The applicable font types available. + */ + override fun setFont(font: FontType) { + val selected = when (font) { + FontType.SERIF -> R.id.mozac_feature_readerview_font_serif + FontType.SANSSERIF -> R.id.mozac_feature_readerview_font_sans_serif + } + fontGroup.check(selected) + } + + /** + * Sets the font size of the current and future ReaderView sessions. + * + * Note: The readerview.js implementation under the hood scales the entire page's contents and not just + * the text size. + * + * @param size An integer value in the range [MIN_TEXT_SIZE] to [MAX_TEXT_SIZE]. + */ + override fun setFontSize(size: Int) { + val (incrementState, decrementState) = when { + size <= MIN_TEXT_SIZE -> { + Pair(first = true, second = false) + } + size >= MAX_TEXT_SIZE -> { + Pair(first = false, second = true) + } + else -> { + Pair(first = true, second = true) + } + } + fontIncrementButton.isEnabled = incrementState + fontDecrementButton.isEnabled = decrementState + } + + /** + * Sets the color scheme of the current and future ReaderView sessions. + * + * @param scheme The applicable colour schemes available. + */ + override fun setColorScheme(scheme: ColorScheme) { + val selected = when (scheme) { + ColorScheme.DARK -> R.id.mozac_feature_readerview_color_dark + ColorScheme.SEPIA -> R.id.mozac_feature_readerview_color_sepia + ColorScheme.LIGHT -> R.id.mozac_feature_readerview_color_light + } + + colorSchemeGroup.check(selected) + } + + /** + * Updates visibility to [View.VISIBLE] and requests focus for the UI controls. + */ + override fun showControls() { + visibility = View.VISIBLE + requestFocus() + } + + /** + * Updates visibility to [View.GONE] of the UI controls. + */ + override fun hideControls() { + visibility = View.GONE + } + + /** + * Tries to inflate the view if needed. + * + * See: https://github.com/mozilla-mobile/android-components/issues/5491 + * + * @return true if the inflation was completed, false if the view was already inflated. + */ + override fun tryInflate(): Boolean { + return if (view == null) { + view = View.inflate(context, R.layout.mozac_feature_readerview_view, this) + bindViews() + true + } else { + false + } + } + + override fun onFocusChanged(gainFocus: Boolean, direction: Int, previouslyFocusedRect: Rect?) { + if (!gainFocus) { + hideControls() + } + super.onFocusChanged(gainFocus, direction, previouslyFocusedRect) + } + + @Suppress("ComplexMethod") + private fun bindViews() { + fontGroup = applyCheckedListener(R.id.mozac_feature_readerview_font_group) { checkedId -> + val fontType = when (checkedId) { + R.id.mozac_feature_readerview_font_sans_serif -> FontType.SANSSERIF + R.id.mozac_feature_readerview_font_serif -> FontType.SERIF + else -> FontType.SERIF + } + listener?.onFontChanged(fontType) + } + colorSchemeGroup = applyCheckedListener(R.id.mozac_feature_readerview_color_scheme_group) { checkedId -> + val colorSchemeChoice = when (checkedId) { + R.id.mozac_feature_readerview_color_dark -> ColorScheme.DARK + R.id.mozac_feature_readerview_color_sepia -> ColorScheme.SEPIA + R.id.mozac_feature_readerview_color_light -> ColorScheme.LIGHT + else -> ColorScheme.DARK + } + listener?.onColorSchemeChanged(colorSchemeChoice) + } + fontIncrementButton = applyClickListener(R.id.mozac_feature_readerview_font_size_increase) { + listener?.onFontSizeIncreased()?.let { setFontSize(it) } + } + fontDecrementButton = applyClickListener(R.id.mozac_feature_readerview_font_size_decrease) { + listener?.onFontSizeDecreased()?.let { setFontSize(it) } + } + } + + private inline fun applyClickListener(@IdRes id: Int, crossinline block: () -> Unit): AppCompatButton { + return findViewById<AppCompatButton>(id).apply { + setOnClickListener { block() } + } + } + + private inline fun applyCheckedListener(@IdRes id: Int, crossinline block: (Int) -> Unit): RadioGroup { + return findViewById<RadioGroup>(id).apply { + setOnCheckedChangeListener { _, checkedId -> block(checkedId) } + } + } +} diff --git a/mobile/android/android-components/components/feature/readerview/src/main/java/mozilla/components/feature/readerview/view/ReaderViewControlsView.kt b/mobile/android/android-components/components/feature/readerview/src/main/java/mozilla/components/feature/readerview/view/ReaderViewControlsView.kt new file mode 100644 index 0000000000..566566e51f --- /dev/null +++ b/mobile/android/android-components/components/feature/readerview/src/main/java/mozilla/components/feature/readerview/view/ReaderViewControlsView.kt @@ -0,0 +1,63 @@ +/* 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.readerview.view + +import android.view.View +import mozilla.components.feature.readerview.ReaderViewFeature.ColorScheme +import mozilla.components.feature.readerview.ReaderViewFeature.FontType + +/** + * An interface for views that can display ReaderView appearance controls (e.g. font size, font type). + */ +interface ReaderViewControlsView { + + var listener: Listener? + + /** + * Sets the selected font option. + */ + fun setFont(font: FontType) + + /** + * Sets the selected font size. + */ + fun setFontSize(size: Int) + + /** + * Sets the selected color scheme. + */ + fun setColorScheme(scheme: ColorScheme) + + /** + * Makes the UI controls visible and requests focus. + */ + fun showControls() + + /** + * Makes the UI controls invisible. + */ + fun hideControls() + + /** + * Casts this [ReaderViewControlsView] interface to an actual Android [View] object. + */ + fun asView(): View = (this as View) + + /** + * Tries to inflate the view if needed. + * + * See: https://github.com/mozilla-mobile/android-components/issues/5491 + * + * @return true if the inflation was completed, false if the view was already inflated. + */ + fun tryInflate(): Boolean + + interface Listener { + fun onFontChanged(font: FontType) + fun onFontSizeIncreased(): Int + fun onFontSizeDecreased(): Int + fun onColorSchemeChanged(scheme: ColorScheme) + } +} diff --git a/mobile/android/android-components/components/feature/readerview/src/main/res/drawable/fontsize_controls_text_selector.xml b/mobile/android/android-components/components/feature/readerview/src/main/res/drawable/fontsize_controls_text_selector.xml new file mode 100644 index 0000000000..0e7b7319fd --- /dev/null +++ b/mobile/android/android-components/components/feature/readerview/src/main/res/drawable/fontsize_controls_text_selector.xml @@ -0,0 +1,11 @@ +<?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/. + --> + +<selector xmlns:android="http://schemas.android.com/apk/res/android"> + <item android:state_enabled="true" android:color="@color/mozac_feature_readerview_text_color" /> + <item android:state_enabled="false" android:color="@color/mozac_feature_readerview_text_color_disabled" /> +</selector>
\ No newline at end of file diff --git a/mobile/android/android-components/components/feature/readerview/src/main/res/drawable/radiobutton_color_scheme_dark_selector.xml b/mobile/android/android-components/components/feature/readerview/src/main/res/drawable/radiobutton_color_scheme_dark_selector.xml new file mode 100644 index 0000000000..0373fe5973 --- /dev/null +++ b/mobile/android/android-components/components/feature/readerview/src/main/res/drawable/radiobutton_color_scheme_dark_selector.xml @@ -0,0 +1,24 @@ +<?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/. + --> + +<selector xmlns:android="http://schemas.android.com/apk/res/android"> + <item android:state_checked="true"> + <shape> + <gradient android:angle="270" /> + <stroke android:width="2dp" android:color="@color/mozac_feature_readerview_selected"/> + <corners android:radius="4dp" /> + <solid android:color="@color/mozac_feature_readerview_dark_background"/> + </shape> + </item> + <item android:state_checked="false"> + <shape> + <gradient android:angle="270" /> + <corners android:radius="4dp" /> + <solid android:color="@color/mozac_feature_readerview_dark_background"/> + </shape> + </item> +</selector>
\ No newline at end of file diff --git a/mobile/android/android-components/components/feature/readerview/src/main/res/drawable/radiobutton_color_scheme_light_selector.xml b/mobile/android/android-components/components/feature/readerview/src/main/res/drawable/radiobutton_color_scheme_light_selector.xml new file mode 100644 index 0000000000..108205802d --- /dev/null +++ b/mobile/android/android-components/components/feature/readerview/src/main/res/drawable/radiobutton_color_scheme_light_selector.xml @@ -0,0 +1,30 @@ +<?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/. + --> + +<!-- + ~ 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/. + --> + +<selector xmlns:android="http://schemas.android.com/apk/res/android"> + <item android:state_checked="true"> + <shape> + <gradient android:angle="270" /> + <stroke android:width="2dp" android:color="@color/mozac_feature_readerview_selected"/> + <corners android:radius="4dp" /> + <solid android:color="@color/mozac_feature_readerview_light_background"/> + </shape> + </item> + <item android:state_checked="false"> + <shape> + <gradient android:angle="270" /> + <corners android:radius="4dp" /> + <solid android:color="@color/mozac_feature_readerview_light_background"/> + </shape> + </item> +</selector>
\ No newline at end of file diff --git a/mobile/android/android-components/components/feature/readerview/src/main/res/drawable/radiobutton_color_scheme_sepia_selector.xml b/mobile/android/android-components/components/feature/readerview/src/main/res/drawable/radiobutton_color_scheme_sepia_selector.xml new file mode 100644 index 0000000000..a10f0c8e24 --- /dev/null +++ b/mobile/android/android-components/components/feature/readerview/src/main/res/drawable/radiobutton_color_scheme_sepia_selector.xml @@ -0,0 +1,24 @@ +<?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/. + --> + +<selector xmlns:android="http://schemas.android.com/apk/res/android"> + <item android:state_checked="true"> + <shape> + <gradient android:angle="270" /> + <stroke android:width="2dp" android:color="@color/mozac_feature_readerview_selected"/> + <corners android:radius="4dp" /> + <solid android:color="@color/mozac_feature_readerview_sepia_background"/> + </shape> + </item> + <item android:state_checked="false"> + <shape> + <gradient android:angle="270" /> + <corners android:radius="4dp" /> + <solid android:color="@color/mozac_feature_readerview_sepia_background"/> + </shape> + </item> +</selector>
\ No newline at end of file diff --git a/mobile/android/android-components/components/feature/readerview/src/main/res/drawable/radiobutton_selected_text_selector.xml b/mobile/android/android-components/components/feature/readerview/src/main/res/drawable/radiobutton_selected_text_selector.xml new file mode 100644 index 0000000000..16854bbbc6 --- /dev/null +++ b/mobile/android/android-components/components/feature/readerview/src/main/res/drawable/radiobutton_selected_text_selector.xml @@ -0,0 +1,11 @@ +<?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/. + --> + +<selector xmlns:android="http://schemas.android.com/apk/res/android"> + <item android:state_checked="true" android:color="@color/mozac_feature_readerview_selected" /> + <item android:state_checked="false" android:color="@color/mozac_feature_readerview_text_color" /> +</selector>
\ No newline at end of file diff --git a/mobile/android/android-components/components/feature/readerview/src/main/res/layout/mozac_feature_readerview_view.xml b/mobile/android/android-components/components/feature/readerview/src/main/res/layout/mozac_feature_readerview_view.xml new file mode 100644 index 0000000000..c42e91d263 --- /dev/null +++ b/mobile/android/android-components/components/feature/readerview/src/main/res/layout/mozac_feature_readerview_view.xml @@ -0,0 +1,155 @@ +<?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/. --> + +<merge xmlns:android="http://schemas.android.com/apk/res/android" + xmlns:app="http://schemas.android.com/apk/res-auto" + xmlns:tools="http://schemas.android.com/tools" + android:layout_width="match_parent" + android:layout_height="wrap_content" + tools:background="#ffffffff" + tools:parentTag="androidx.constraintlayout.widget.ConstraintLayout"> + + <RadioGroup + android:id="@+id/mozac_feature_readerview_font_group" + android:layout_width="0dp" + android:layout_height="wrap_content" + android:minHeight="66dp" + android:orientation="horizontal" + android:gravity="center" + android:layout_marginEnd="8dp" + android:layout_marginStart="8dp" + android:layout_marginTop="8dp" + app:layout_constraintEnd_toEndOf="parent" + app:layout_constraintStart_toStartOf="parent" + app:layout_constraintTop_toTopOf="parent"> + + <androidx.appcompat.widget.AppCompatRadioButton + android:id="@+id/mozac_feature_readerview_font_sans_serif" + style="@style/RadioButtonSelectedTextStyle" + android:text="@string/mozac_feature_readerview_sans_serif_font" + android:layout_width="0dp" + android:layout_height="match_parent" + android:fontFamily="sans-serif" + android:textAlignment="center" + android:contentDescription="@string/mozac_feature_readerview_sans_serif_font_desc" + android:layout_marginStart="8dp" + android:layout_marginEnd="8dp" + android:layout_weight="1" + app:autoSizeMaxTextSize="24sp" + app:autoSizeMinTextSize="14sp" + app:autoSizeStepGranularity="2sp" + app:autoSizeTextType="uniform" /> + + <androidx.appcompat.widget.AppCompatRadioButton + android:id="@+id/mozac_feature_readerview_font_serif" + style="@style/RadioButtonSelectedTextStyle" + android:text="@string/mozac_feature_readerview_serif_font" + android:layout_width="0dp" + android:layout_height="match_parent" + android:fontFamily="serif" + android:textAlignment="center" + android:contentDescription="@string/mozac_feature_readerview_serif_font_desc" + android:layout_marginEnd="8dp" + android:layout_marginStart="8dp" + android:layout_weight="1" + app:autoSizeMaxTextSize="24sp" + app:autoSizeMinTextSize="14sp" + app:autoSizeStepGranularity="2sp" + app:autoSizeTextType="uniform" /> + </RadioGroup> + + <androidx.appcompat.widget.AppCompatButton + android:id="@+id/mozac_feature_readerview_font_size_decrease" + android:text="@string/mozac_feature_readerview_negative_sign" + android:textAlignment="center" + android:layout_width="wrap_content" + android:layout_height="wrap_content" + android:layout_marginStart="8dp" + android:layout_marginEnd="8dp" + android:contentDescription="@string/mozac_feature_readerview_font_size_decrease_desc" + android:background="?attr/selectableItemBackgroundBorderless" + android:textColor="@drawable/fontsize_controls_text_selector" + android:textSize="50sp" + android:fontFamily="sans-serif-light" + android:gravity="center" + app:layout_constraintEnd_toStartOf="@+id/mozac_feature_readerview_font_size_increase" + app:layout_constraintStart_toStartOf="parent" + app:layout_constraintTop_toBottomOf="@+id/mozac_feature_readerview_font_group" /> + + <androidx.appcompat.widget.AppCompatButton + android:id="@+id/mozac_feature_readerview_font_size_increase" + android:text="@string/mozac_feature_readerview_positive_sign" + android:textAlignment="center" + android:layout_width="wrap_content" + android:layout_height="wrap_content" + android:layout_marginEnd="8dp" + android:layout_marginStart="8dp" + android:contentDescription="@string/mozac_feature_readerview_font_size_increase_desc" + android:background="?attr/selectableItemBackgroundBorderless" + android:textColor="@drawable/fontsize_controls_text_selector" + android:textSize="50sp" + android:fontFamily="sans-serif-light" + android:gravity="center" + app:layout_constraintEnd_toEndOf="parent" + app:layout_constraintStart_toEndOf="@+id/mozac_feature_readerview_font_size_decrease" + app:layout_constraintTop_toBottomOf="@+id/mozac_feature_readerview_font_group" /> + + <RadioGroup + android:id="@+id/mozac_feature_readerview_color_scheme_group" + android:layout_width="0dp" + android:layout_height="wrap_content" + android:orientation="horizontal" + android:gravity="center" + android:layout_marginStart="8dp" + android:layout_marginEnd="8dp" + android:layout_marginTop="8dp" + android:layout_marginBottom="8dp" + app:layout_constraintBottom_toBottomOf="parent" + app:layout_constraintEnd_toEndOf="parent" + app:layout_constraintStart_toStartOf="parent" + app:layout_constraintTop_toBottomOf="@+id/mozac_feature_readerview_font_size_increase"> + + <androidx.appcompat.widget.AppCompatRadioButton + android:id="@+id/mozac_feature_readerview_color_dark" + style="@style/RadioButtonSelectedBorderStyle" + android:background="@drawable/radiobutton_color_scheme_dark_selector" + android:text="@string/mozac_feature_readerview_dark" + android:layout_width="0dp" + android:layout_height="wrap_content" + android:textColor="#F9F9FB" + android:layout_marginStart="8dp" + android:layout_marginEnd="8dp" + android:layout_weight="1" + android:textAlignment="center" + android:contentDescription="@string/mozac_feature_readerview_dark_color_scheme_desc" /> + + <androidx.appcompat.widget.AppCompatRadioButton + android:id="@+id/mozac_feature_readerview_color_sepia" + style="@style/RadioButtonSelectedBorderStyle" + android:background="@drawable/radiobutton_color_scheme_sepia_selector" + android:text="@string/mozac_feature_readerview_sephia" + android:layout_width="0dp" + android:textColor="#220033" + android:layout_height="wrap_content" + android:layout_marginEnd="8dp" + android:layout_marginStart="8dp" + android:layout_weight="1" + android:textAlignment="center" + android:contentDescription="@string/mozac_feature_readerview_sepia_color_scheme_desc" /> + + <androidx.appcompat.widget.AppCompatRadioButton + android:id="@+id/mozac_feature_readerview_color_light" + style="@style/RadioButtonSelectedBorderStyle" + android:background="@drawable/radiobutton_color_scheme_light_selector" + android:text="@string/mozac_feature_readerview_light" + android:textColor="#220033" + android:layout_width="0dp" + android:layout_height="wrap_content" + android:layout_marginStart="8dp" + android:layout_marginEnd="8dp" + android:layout_weight="1" + android:textAlignment="center" + android:contentDescription="@string/mozac_feature_readerview_light_color_scheme_desc" /> + </RadioGroup> +</merge>
\ No newline at end of file diff --git a/mobile/android/android-components/components/feature/readerview/src/main/res/values-am/strings.xml b/mobile/android/android-components/components/feature/readerview/src/main/res/values-am/strings.xml new file mode 100644 index 0000000000..bf76298450 --- /dev/null +++ b/mobile/android/android-components/components/feature/readerview/src/main/res/values-am/strings.xml @@ -0,0 +1,29 @@ +<?xml version="1.0" encoding="utf-8"?> +<resources> + <!-- Name for one of the font options available to use --> + <string name="mozac_feature_readerview_sans_serif_font">ሳንሰ ሰሪፍ</string> + <!-- Accessible description for the font option --> + <string name="mozac_feature_readerview_sans_serif_font_desc">ሳንሰ ሰሪፍ ቅርጸ-ቁምፊ</string> + <!-- Name for one of the font options available to use --> + <string name="mozac_feature_readerview_serif_font">ሰሪፍ</string> + <!-- Accessible description for the font option --> + <string name="mozac_feature_readerview_serif_font_desc">ሰሪፍ ቅርጸ-ቁምፊ</string> + + <!-- Accessible description for decreasing the font size --> + <string name="mozac_feature_readerview_font_size_decrease_desc">የቅርጸ ቁምፊ መጠን ይቀንሳል</string> + + <!-- Accessible description for increasing the font size --> + <string name="mozac_feature_readerview_font_size_increase_desc">የቅርጸ ቁምፊ መጠን መጨመር</string> + <!-- Color option for the background --> + <string name="mozac_feature_readerview_dark">ጠቆር ያለ</string> + <!-- Accessible description for the color option --> + <string name="mozac_feature_readerview_dark_color_scheme_desc">ጠቆር ያለ ቀለም ንድፍ</string> + <!-- Color option for the background --> + <string name="mozac_feature_readerview_sephia">ቀላ ያለ ቡናማ</string> + <!-- Accessible description for the color option --> + <string name="mozac_feature_readerview_sepia_color_scheme_desc">ቀላ ያለ ቡናማ ቀለም ንድፍ</string> + <!-- Color option for the background --> + <string name="mozac_feature_readerview_light">ፈካ ያለ</string> + <!-- Accessible description for the color option --> + <string name="mozac_feature_readerview_light_color_scheme_desc">ፈካ ያለ ቀለም ንድፍ</string> +</resources> diff --git a/mobile/android/android-components/components/feature/readerview/src/main/res/values-an/strings.xml b/mobile/android/android-components/components/feature/readerview/src/main/res/values-an/strings.xml new file mode 100644 index 0000000000..9a65c91b12 --- /dev/null +++ b/mobile/android/android-components/components/feature/readerview/src/main/res/values-an/strings.xml @@ -0,0 +1,29 @@ +<?xml version="1.0" encoding="utf-8"?> +<resources> + <!-- Name for one of the font options available to use --> + <string name="mozac_feature_readerview_sans_serif_font">Sans serif</string> + <!-- Accessible description for the font option --> + <string name="mozac_feature_readerview_sans_serif_font_desc">Fuent Sans Serif</string> + <!-- Name for one of the font options available to use --> + <string name="mozac_feature_readerview_serif_font">Serif</string> + <!-- Accessible description for the font option --> + <string name="mozac_feature_readerview_serif_font_desc">Fuent Serif</string> + + <!-- Accessible description for decreasing the font size --> + <string name="mozac_feature_readerview_font_size_decrease_desc">Reducción de grandaria d’a fuent</string> + + <!-- Accessible description for increasing the font size --> + <string name="mozac_feature_readerview_font_size_increase_desc">Aumento de grandaria d’a fuent</string> + <!-- Color option for the background --> + <string name="mozac_feature_readerview_dark">Fosco</string> + <!-- Accessible description for the color option --> + <string name="mozac_feature_readerview_dark_color_scheme_desc">Esquema de colors foscas</string> + <!-- Color option for the background --> + <string name="mozac_feature_readerview_sephia">Sepia</string> + <!-- Accessible description for the color option --> + <string name="mozac_feature_readerview_sepia_color_scheme_desc">Esquema de colors sepia</string> + <!-- Color option for the background --> + <string name="mozac_feature_readerview_light">Claro</string> + <!-- Accessible description for the color option --> + <string name="mozac_feature_readerview_light_color_scheme_desc">Esquema de colors claras</string> +</resources> diff --git a/mobile/android/android-components/components/feature/readerview/src/main/res/values-ar/strings.xml b/mobile/android/android-components/components/feature/readerview/src/main/res/values-ar/strings.xml new file mode 100644 index 0000000000..491da6f725 --- /dev/null +++ b/mobile/android/android-components/components/feature/readerview/src/main/res/values-ar/strings.xml @@ -0,0 +1,30 @@ +<?xml version="1.0" encoding="utf-8"?> +<resources> + <!-- Name for one of the font options available to use --> + <string name="mozac_feature_readerview_sans_serif_font">غير مذيّل</string> + <!-- Accessible description for the font option --> + <string name="mozac_feature_readerview_sans_serif_font_desc">خط غير مذيّل</string> + <!-- Name for one of the font options available to use --> + <string name="mozac_feature_readerview_serif_font">مذيّل</string> + + <!-- Accessible description for the font option --> + <string name="mozac_feature_readerview_serif_font_desc">خط مذيّل</string> + + <!-- Accessible description for decreasing the font size --> + <string name="mozac_feature_readerview_font_size_decrease_desc">إنقاص حجم الخط</string> + + <!-- Accessible description for increasing the font size --> + <string name="mozac_feature_readerview_font_size_increase_desc">زيادة حجم الخط</string> + <!-- Color option for the background --> + <string name="mozac_feature_readerview_dark">داكنة</string> + <!-- Accessible description for the color option --> + <string name="mozac_feature_readerview_dark_color_scheme_desc">مخطّط ألوان داكن</string> + <!-- Color option for the background --> + <string name="mozac_feature_readerview_sephia">بني داكن</string> + <!-- Accessible description for the color option --> + <string name="mozac_feature_readerview_sepia_color_scheme_desc">مخطّط ألوان بني داكن</string> + <!-- Color option for the background --> + <string name="mozac_feature_readerview_light">فاتح</string> + <!-- Accessible description for the color option --> + <string name="mozac_feature_readerview_light_color_scheme_desc">مخطّط ألوان فاتح</string> +</resources> diff --git a/mobile/android/android-components/components/feature/readerview/src/main/res/values-ast/strings.xml b/mobile/android/android-components/components/feature/readerview/src/main/res/values-ast/strings.xml new file mode 100644 index 0000000000..26d8c7449e --- /dev/null +++ b/mobile/android/android-components/components/feature/readerview/src/main/res/values-ast/strings.xml @@ -0,0 +1,30 @@ +<?xml version="1.0" encoding="utf-8"?> +<resources> + <!-- Name for one of the font options available to use --> + <string name="mozac_feature_readerview_sans_serif_font">Sans serif</string> + <!-- Accessible description for the font option --> + <string name="mozac_feature_readerview_sans_serif_font_desc">Fonte Sans Serif</string> + <!-- Name for one of the font options available to use --> + <string name="mozac_feature_readerview_serif_font">Serif</string> + + <!-- Accessible description for the font option --> + <string name="mozac_feature_readerview_serif_font_desc">Fonte Serif</string> + + <!-- Accessible description for decreasing the font size --> + <string name="mozac_feature_readerview_font_size_decrease_desc">Amenorgamientu del tamañu de la fonte</string> + + <!-- Accessible description for increasing the font size --> + <string name="mozac_feature_readerview_font_size_increase_desc">Aumentu del tamañu de la fonte</string> + <!-- Color option for the background --> + <string name="mozac_feature_readerview_dark">Escuridá</string> + <!-- Accessible description for the color option --> + <string name="mozac_feature_readerview_dark_color_scheme_desc">Esquema de colores escuros</string> + <!-- Color option for the background --> + <string name="mozac_feature_readerview_sephia">Sepia</string> + <!-- Accessible description for the color option --> + <string name="mozac_feature_readerview_sepia_color_scheme_desc">Esquema de colores sepia</string> + <!-- Color option for the background --> + <string name="mozac_feature_readerview_light">Claridá</string> + <!-- Accessible description for the color option --> + <string name="mozac_feature_readerview_light_color_scheme_desc">Esquema de colores claros</string> +</resources> diff --git a/mobile/android/android-components/components/feature/readerview/src/main/res/values-az/strings.xml b/mobile/android/android-components/components/feature/readerview/src/main/res/values-az/strings.xml new file mode 100644 index 0000000000..8b9781fa9e --- /dev/null +++ b/mobile/android/android-components/components/feature/readerview/src/main/res/values-az/strings.xml @@ -0,0 +1,29 @@ +<?xml version="1.0" encoding="utf-8"?> +<resources> + <!-- Name for one of the font options available to use --> + <string name="mozac_feature_readerview_sans_serif_font">Sans serif</string> + <!-- Accessible description for the font option --> + <string name="mozac_feature_readerview_sans_serif_font_desc">Sans Serif şrifti</string> + <!-- Name for one of the font options available to use --> + <string name="mozac_feature_readerview_serif_font">Serif</string> + <!-- Accessible description for the font option --> + <string name="mozac_feature_readerview_serif_font_desc">Serif şrifti</string> + + <!-- Accessible description for decreasing the font size --> + <string name="mozac_feature_readerview_font_size_decrease_desc">Şrift ölçüsünü balacalaşdırma</string> + + <!-- Accessible description for increasing the font size --> + <string name="mozac_feature_readerview_font_size_increase_desc">Şrift ölçüsünü böyütmə</string> + <!-- Color option for the background --> + <string name="mozac_feature_readerview_dark">Tünd</string> + <!-- Accessible description for the color option --> + <string name="mozac_feature_readerview_dark_color_scheme_desc">Tünd rəng sxemi</string> + <!-- Color option for the background --> + <string name="mozac_feature_readerview_sephia">Sepiya</string> + <!-- Accessible description for the color option --> + <string name="mozac_feature_readerview_sepia_color_scheme_desc">Sepiya rəng sxemi</string> + <!-- Color option for the background --> + <string name="mozac_feature_readerview_light">Açıq</string> + <!-- Accessible description for the color option --> + <string name="mozac_feature_readerview_light_color_scheme_desc">Açıq rəng sxemi</string> +</resources> diff --git a/mobile/android/android-components/components/feature/readerview/src/main/res/values-azb/strings.xml b/mobile/android/android-components/components/feature/readerview/src/main/res/values-azb/strings.xml new file mode 100644 index 0000000000..6f89a979db --- /dev/null +++ b/mobile/android/android-components/components/feature/readerview/src/main/res/values-azb/strings.xml @@ -0,0 +1,29 @@ +<?xml version="1.0" encoding="utf-8"?> +<resources> + <!-- Name for one of the font options available to use --> + <string name="mozac_feature_readerview_sans_serif_font">سریفسیز</string> + <!-- Accessible description for the font option --> + <string name="mozac_feature_readerview_sans_serif_font_desc">سریفسیز فونت</string> + <!-- Name for one of the font options available to use --> + <string name="mozac_feature_readerview_serif_font">سریفلی</string> + <!-- Accessible description for the font option --> + <string name="mozac_feature_readerview_serif_font_desc">سریفلی فونت</string> + + <!-- Accessible description for decreasing the font size --> + <string name="mozac_feature_readerview_font_size_decrease_desc">فونت اؤلچوسونون آزالماسی</string> + + <!-- Accessible description for increasing the font size --> + <string name="mozac_feature_readerview_font_size_increase_desc">فونت اؤلچوسونون چوخالماسی</string> + <!-- Color option for the background --> + <string name="mozac_feature_readerview_dark">قارانلیق</string> + <!-- Accessible description for the color option --> + <string name="mozac_feature_readerview_dark_color_scheme_desc">قارانلیق رنگ طرحی</string> + <!-- Color option for the background --> + <string name="mozac_feature_readerview_sephia">تورپاق</string> + <!-- Accessible description for the color option --> + <string name="mozac_feature_readerview_sepia_color_scheme_desc">تورپاق رنگی طرحی</string> + <!-- Color option for the background --> + <string name="mozac_feature_readerview_light">آچیق</string> + <!-- Accessible description for the color option --> + <string name="mozac_feature_readerview_light_color_scheme_desc">آچیق رنگ طرحی</string> +</resources> diff --git a/mobile/android/android-components/components/feature/readerview/src/main/res/values-be/strings.xml b/mobile/android/android-components/components/feature/readerview/src/main/res/values-be/strings.xml new file mode 100644 index 0000000000..41cd944353 --- /dev/null +++ b/mobile/android/android-components/components/feature/readerview/src/main/res/values-be/strings.xml @@ -0,0 +1,30 @@ +<?xml version="1.0" encoding="utf-8"?> +<resources> + <!-- Name for one of the font options available to use --> + <string name="mozac_feature_readerview_sans_serif_font">Без засечак</string> + <!-- Accessible description for the font option --> + <string name="mozac_feature_readerview_sans_serif_font_desc">Шрыфт без засечак</string> + <!-- Name for one of the font options available to use --> + <string name="mozac_feature_readerview_serif_font">З засечкамі</string> + + <!-- Accessible description for the font option --> + <string name="mozac_feature_readerview_serif_font_desc">Шрыфт з засечкамі</string> + + <!-- Accessible description for decreasing the font size --> + <string name="mozac_feature_readerview_font_size_decrease_desc">Паменшыць памер шрыфту</string> + + <!-- Accessible description for increasing the font size --> + <string name="mozac_feature_readerview_font_size_increase_desc">Павялічыць памер шрыфту</string> + <!-- Color option for the background --> + <string name="mozac_feature_readerview_dark">Цёмная</string> + <!-- Accessible description for the color option --> + <string name="mozac_feature_readerview_dark_color_scheme_desc">Цёмная каляровая тэма</string> + <!-- Color option for the background --> + <string name="mozac_feature_readerview_sephia">Сэпія</string> + <!-- Accessible description for the color option --> + <string name="mozac_feature_readerview_sepia_color_scheme_desc">Каляровая тэма сепія</string> + <!-- Color option for the background --> + <string name="mozac_feature_readerview_light">Светлая</string> + <!-- Accessible description for the color option --> + <string name="mozac_feature_readerview_light_color_scheme_desc">Светлая каляровая тэма</string> +</resources> diff --git a/mobile/android/android-components/components/feature/readerview/src/main/res/values-bg/strings.xml b/mobile/android/android-components/components/feature/readerview/src/main/res/values-bg/strings.xml new file mode 100644 index 0000000000..0796e1d644 --- /dev/null +++ b/mobile/android/android-components/components/feature/readerview/src/main/res/values-bg/strings.xml @@ -0,0 +1,29 @@ +<?xml version="1.0" encoding="utf-8"?> +<resources> + <!-- Name for one of the font options available to use --> + <string name="mozac_feature_readerview_sans_serif_font">Несерифен</string> + <!-- Accessible description for the font option --> + <string name="mozac_feature_readerview_sans_serif_font_desc">Несерифен шрифт</string> + <!-- Name for one of the font options available to use --> + <string name="mozac_feature_readerview_serif_font">Серифен</string> + <!-- Accessible description for the font option --> + <string name="mozac_feature_readerview_serif_font_desc">Серифен шрифт</string> + + <!-- Accessible description for decreasing the font size --> + <string name="mozac_feature_readerview_font_size_decrease_desc">Намаляване на размера на шрифта</string> + + <!-- Accessible description for increasing the font size --> + <string name="mozac_feature_readerview_font_size_increase_desc">Увеличаване на размера на шрифта</string> + <!-- Color option for the background --> + <string name="mozac_feature_readerview_dark">Тъмен</string> + <!-- Accessible description for the color option --> + <string name="mozac_feature_readerview_dark_color_scheme_desc">Тъмна цветова схема</string> + <!-- Color option for the background --> + <string name="mozac_feature_readerview_sephia">Сепия</string> + <!-- Accessible description for the color option --> + <string name="mozac_feature_readerview_sepia_color_scheme_desc">Цветна схема на Сепия</string> + <!-- Color option for the background --> + <string name="mozac_feature_readerview_light">Светла</string> + <!-- Accessible description for the color option --> + <string name="mozac_feature_readerview_light_color_scheme_desc">Светла цветова схема</string> +</resources> diff --git a/mobile/android/android-components/components/feature/readerview/src/main/res/values-bn/strings.xml b/mobile/android/android-components/components/feature/readerview/src/main/res/values-bn/strings.xml new file mode 100644 index 0000000000..01f8fc4796 --- /dev/null +++ b/mobile/android/android-components/components/feature/readerview/src/main/res/values-bn/strings.xml @@ -0,0 +1,29 @@ +<?xml version="1.0" encoding="utf-8"?> +<resources> + <!-- Name for one of the font options available to use --> + <string name="mozac_feature_readerview_sans_serif_font">Sans serif</string> + <!-- Accessible description for the font option --> + <string name="mozac_feature_readerview_sans_serif_font_desc">Sans Serif হরফ</string> + <!-- Name for one of the font options available to use --> + <string name="mozac_feature_readerview_serif_font">Serif</string> + <!-- Accessible description for the font option --> + <string name="mozac_feature_readerview_serif_font_desc">Serif হরফ</string> + + <!-- Accessible description for decreasing the font size --> + <string name="mozac_feature_readerview_font_size_decrease_desc">হরফের আকার হ্রাস করুন</string> + + <!-- Accessible description for increasing the font size --> + <string name="mozac_feature_readerview_font_size_increase_desc">হরফের আকার বৃদ্ধি করুন</string> + <!-- Color option for the background --> + <string name="mozac_feature_readerview_dark">গাঢ়</string> + <!-- Accessible description for the color option --> + <string name="mozac_feature_readerview_dark_color_scheme_desc">গাঢ় রঙের নকশা</string> + <!-- Color option for the background --> + <string name="mozac_feature_readerview_sephia">কালচে-বাদামী রং</string> + <!-- Accessible description for the color option --> + <string name="mozac_feature_readerview_sepia_color_scheme_desc">কালচে-বাদামী রঙের নকশা</string> + <!-- Color option for the background --> + <string name="mozac_feature_readerview_light">হালকা</string> + <!-- Accessible description for the color option --> + <string name="mozac_feature_readerview_light_color_scheme_desc">হালকা রঙের স্কিম</string> +</resources> diff --git a/mobile/android/android-components/components/feature/readerview/src/main/res/values-br/strings.xml b/mobile/android/android-components/components/feature/readerview/src/main/res/values-br/strings.xml new file mode 100644 index 0000000000..e976c00d45 --- /dev/null +++ b/mobile/android/android-components/components/feature/readerview/src/main/res/values-br/strings.xml @@ -0,0 +1,29 @@ +<?xml version="1.0" encoding="utf-8"?> +<resources> + <!-- Name for one of the font options available to use --> + <string name="mozac_feature_readerview_sans_serif_font">Hep Serif</string> + <!-- Accessible description for the font option --> + <string name="mozac_feature_readerview_sans_serif_font_desc">Nodrezh hep serif</string> + <!-- Name for one of the font options available to use --> + <string name="mozac_feature_readerview_serif_font">Serif</string> + <!-- Accessible description for the font option --> + <string name="mozac_feature_readerview_serif_font_desc">Nodrezh serif</string> + + <!-- Accessible description for decreasing the font size --> + <string name="mozac_feature_readerview_font_size_decrease_desc">Digresk ment an nodrezh</string> + + <!-- Accessible description for increasing the font size --> + <string name="mozac_feature_readerview_font_size_increase_desc">Kresk ment an nodrezh</string> + <!-- Color option for the background --> + <string name="mozac_feature_readerview_dark">Teñval</string> + <!-- Accessible description for the color option --> + <string name="mozac_feature_readerview_dark_color_scheme_desc">Livioù teñval</string> + <!-- Color option for the background --> + <string name="mozac_feature_readerview_sephia">Sepia</string> + <!-- Accessible description for the color option --> + <string name="mozac_feature_readerview_sepia_color_scheme_desc">Livioù sepia</string> + <!-- Color option for the background --> + <string name="mozac_feature_readerview_light">Sklaer</string> + <!-- Accessible description for the color option --> + <string name="mozac_feature_readerview_light_color_scheme_desc">Livioù sklaer</string> +</resources> diff --git a/mobile/android/android-components/components/feature/readerview/src/main/res/values-bs/strings.xml b/mobile/android/android-components/components/feature/readerview/src/main/res/values-bs/strings.xml new file mode 100644 index 0000000000..8d40e36b20 --- /dev/null +++ b/mobile/android/android-components/components/feature/readerview/src/main/res/values-bs/strings.xml @@ -0,0 +1,29 @@ +<?xml version="1.0" encoding="utf-8"?> +<resources> + <!-- Name for one of the font options available to use --> + <string name="mozac_feature_readerview_sans_serif_font">Sans serif</string> + <!-- Accessible description for the font option --> + <string name="mozac_feature_readerview_sans_serif_font_desc">Sans Serif font</string> + <!-- Name for one of the font options available to use --> + <string name="mozac_feature_readerview_serif_font">Serif</string> + <!-- Accessible description for the font option --> + <string name="mozac_feature_readerview_serif_font_desc">Serif font</string> + + <!-- Accessible description for decreasing the font size --> + <string name="mozac_feature_readerview_font_size_decrease_desc">Smanjenje veličine fonta</string> + + <!-- Accessible description for increasing the font size --> + <string name="mozac_feature_readerview_font_size_increase_desc">Povećanje veličine fonta</string> + <!-- Color option for the background --> + <string name="mozac_feature_readerview_dark">Tamna</string> + <!-- Accessible description for the color option --> + <string name="mozac_feature_readerview_dark_color_scheme_desc">Tamna shema boja</string> + <!-- Color option for the background --> + <string name="mozac_feature_readerview_sephia">Sepia</string> + <!-- Accessible description for the color option --> + <string name="mozac_feature_readerview_sepia_color_scheme_desc">Sepia shema boja</string> + <!-- Color option for the background --> + <string name="mozac_feature_readerview_light">Svijetla</string> + <!-- Accessible description for the color option --> + <string name="mozac_feature_readerview_light_color_scheme_desc">Svijetla shema boja</string> +</resources> diff --git a/mobile/android/android-components/components/feature/readerview/src/main/res/values-ca/strings.xml b/mobile/android/android-components/components/feature/readerview/src/main/res/values-ca/strings.xml new file mode 100644 index 0000000000..5c9c864d27 --- /dev/null +++ b/mobile/android/android-components/components/feature/readerview/src/main/res/values-ca/strings.xml @@ -0,0 +1,29 @@ +<?xml version="1.0" encoding="utf-8"?> +<resources> + <!-- Name for one of the font options available to use --> + <string name="mozac_feature_readerview_sans_serif_font">Sans serif</string> + <!-- Accessible description for the font option --> + <string name="mozac_feature_readerview_sans_serif_font_desc">Lletra Sans Serif</string> + <!-- Name for one of the font options available to use --> + <string name="mozac_feature_readerview_serif_font">Serif</string> + <!-- Accessible description for the font option --> + <string name="mozac_feature_readerview_serif_font_desc">Lletra Serif</string> + + <!-- Accessible description for decreasing the font size --> + <string name="mozac_feature_readerview_font_size_decrease_desc">Disminució de la mida de la lletra</string> + + <!-- Accessible description for increasing the font size --> + <string name="mozac_feature_readerview_font_size_increase_desc">Augment de la mida de la lletra</string> + <!-- Color option for the background --> + <string name="mozac_feature_readerview_dark">Fosc</string> + <!-- Accessible description for the color option --> + <string name="mozac_feature_readerview_dark_color_scheme_desc">Esquema de colors foscos</string> + <!-- Color option for the background --> + <string name="mozac_feature_readerview_sephia">Sèpia</string> + <!-- Accessible description for the color option --> + <string name="mozac_feature_readerview_sepia_color_scheme_desc">Esquema de color sèpia</string> + <!-- Color option for the background --> + <string name="mozac_feature_readerview_light">Clar</string> + <!-- Accessible description for the color option --> + <string name="mozac_feature_readerview_light_color_scheme_desc">Esquema de colors clars</string> +</resources> diff --git a/mobile/android/android-components/components/feature/readerview/src/main/res/values-cak/strings.xml b/mobile/android/android-components/components/feature/readerview/src/main/res/values-cak/strings.xml new file mode 100644 index 0000000000..650ab70746 --- /dev/null +++ b/mobile/android/android-components/components/feature/readerview/src/main/res/values-cak/strings.xml @@ -0,0 +1,29 @@ +<?xml version="1.0" encoding="utf-8"?> +<resources> + <!-- Name for one of the font options available to use --> + <string name="mozac_feature_readerview_sans_serif_font">Sans serif</string> + <!-- Accessible description for the font option --> + <string name="mozac_feature_readerview_sans_serif_font_desc">Fuente Sans Serif</string> + <!-- Name for one of the font options available to use --> + <string name="mozac_feature_readerview_serif_font">Serif</string> + <!-- Accessible description for the font option --> + <string name="mozac_feature_readerview_serif_font_desc">Fuente Serif</string> + + <!-- Accessible description for decreasing the font size --> + <string name="mozac_feature_readerview_font_size_decrease_desc">Tich\'utinisäx rupalem tz\'ib\'</string> + + <!-- Accessible description for increasing the font size --> + <string name="mozac_feature_readerview_font_size_increase_desc">Tinimirisäx rupalem tz\'ib\'</string> + <!-- Color option for the background --> + <string name="mozac_feature_readerview_dark">Q\'equ\'m</string> + <!-- Accessible description for the color option --> + <string name="mozac_feature_readerview_dark_color_scheme_desc">Q\'equ\'m rub\'onil chib\'äl</string> + <!-- Color option for the background --> + <string name="mozac_feature_readerview_sephia">Sepya</string> + <!-- Accessible description for the color option --> + <string name="mozac_feature_readerview_sepia_color_scheme_desc">Sepya rub\'onil chib\'äl</string> + <!-- Color option for the background --> + <string name="mozac_feature_readerview_light">Yuk\'unel</string> + <!-- Accessible description for the color option --> + <string name="mozac_feature_readerview_light_color_scheme_desc">yuk\'unel rub\'onil chib\'äl</string> +</resources> diff --git a/mobile/android/android-components/components/feature/readerview/src/main/res/values-ceb/strings.xml b/mobile/android/android-components/components/feature/readerview/src/main/res/values-ceb/strings.xml new file mode 100644 index 0000000000..7f18dfd315 --- /dev/null +++ b/mobile/android/android-components/components/feature/readerview/src/main/res/values-ceb/strings.xml @@ -0,0 +1,29 @@ +<?xml version="1.0" encoding="utf-8"?> +<resources> + <!-- Name for one of the font options available to use --> + <string name="mozac_feature_readerview_sans_serif_font">Sans serif</string> + <!-- Accessible description for the font option --> + <string name="mozac_feature_readerview_sans_serif_font_desc">Sans Serif nga font</string> + <!-- Name for one of the font options available to use --> + <string name="mozac_feature_readerview_serif_font">Serif</string> + <!-- Accessible description for the font option --> + <string name="mozac_feature_readerview_serif_font_desc">Serif nga font</string> + + <!-- Accessible description for decreasing the font size --> + <string name="mozac_feature_readerview_font_size_decrease_desc">Pagamyon ang font</string> + + <!-- Accessible description for increasing the font size --> + <string name="mozac_feature_readerview_font_size_increase_desc">Padakoon ang font</string> + <!-- Color option for the background --> + <string name="mozac_feature_readerview_dark">Dark</string> + <!-- Accessible description for the color option --> + <string name="mozac_feature_readerview_dark_color_scheme_desc">Dark nga color scheme</string> + <!-- Color option for the background --> + <string name="mozac_feature_readerview_sephia">Sepia</string> + <!-- Accessible description for the color option --> + <string name="mozac_feature_readerview_sepia_color_scheme_desc">Sepia nga color scheme</string> + <!-- Color option for the background --> + <string name="mozac_feature_readerview_light">Light</string> + <!-- Accessible description for the color option --> + <string name="mozac_feature_readerview_light_color_scheme_desc">Light nga color scheme</string> +</resources> diff --git a/mobile/android/android-components/components/feature/readerview/src/main/res/values-ckb/strings.xml b/mobile/android/android-components/components/feature/readerview/src/main/res/values-ckb/strings.xml new file mode 100644 index 0000000000..01fe719a52 --- /dev/null +++ b/mobile/android/android-components/components/feature/readerview/src/main/res/values-ckb/strings.xml @@ -0,0 +1,29 @@ +<?xml version="1.0" encoding="utf-8"?> +<resources> + <!-- Name for one of the font options available to use --> + <string name="mozac_feature_readerview_sans_serif_font">Sans serif</string> + <!-- Accessible description for the font option --> + <string name="mozac_feature_readerview_sans_serif_font_desc">جۆرەپیتی Sans Serif </string> + <!-- Name for one of the font options available to use --> + <string name="mozac_feature_readerview_serif_font">Serif</string> + <!-- Accessible description for the font option --> + <string name="mozac_feature_readerview_serif_font_desc">جۆرەپیتی Serif</string> + + <!-- Accessible description for decreasing the font size --> + <string name="mozac_feature_readerview_font_size_decrease_desc">کەمکردنەوەی قەبارەی جۆرەپیت(فۆنت)</string> + + <!-- Accessible description for increasing the font size --> + <string name="mozac_feature_readerview_font_size_increase_desc">گەورەکردنەوەی قەبارەی جۆرەپیت(فۆنت)</string> + <!-- Color option for the background --> + <string name="mozac_feature_readerview_dark">تاریک</string> + <!-- Accessible description for the color option --> + <string name="mozac_feature_readerview_dark_color_scheme_desc">شێوە ڕەنگی تاریک</string> + <!-- Color option for the background --> + <string name="mozac_feature_readerview_sephia">خۆڵەمێشی تۆخ</string> + <!-- Accessible description for the color option --> + <string name="mozac_feature_readerview_sepia_color_scheme_desc">شێوە ڕەنگی خۆڵەمێشی تۆخ</string> + <!-- Color option for the background --> + <string name="mozac_feature_readerview_light">ڕوون</string> + <!-- Accessible description for the color option --> + <string name="mozac_feature_readerview_light_color_scheme_desc">شێوە ڕەنگی ڕوون</string> +</resources> diff --git a/mobile/android/android-components/components/feature/readerview/src/main/res/values-co/strings.xml b/mobile/android/android-components/components/feature/readerview/src/main/res/values-co/strings.xml new file mode 100644 index 0000000000..d16f5dde27 --- /dev/null +++ b/mobile/android/android-components/components/feature/readerview/src/main/res/values-co/strings.xml @@ -0,0 +1,29 @@ +<?xml version="1.0" encoding="utf-8"?> +<resources> + <!-- Name for one of the font options available to use --> + <string name="mozac_feature_readerview_sans_serif_font">Senza patta</string> + <!-- Accessible description for the font option --> + <string name="mozac_feature_readerview_sans_serif_font_desc">Grafia senza patta</string> + <!-- Name for one of the font options available to use --> + <string name="mozac_feature_readerview_serif_font">Patta (Serif)</string> + <!-- Accessible description for the font option --> + <string name="mozac_feature_readerview_serif_font_desc">Grafia cù patta</string> + + <!-- Accessible description for decreasing the font size --> + <string name="mozac_feature_readerview_font_size_decrease_desc">Riduzzione di a dimensione di a grafia</string> + + <!-- Accessible description for increasing the font size --> + <string name="mozac_feature_readerview_font_size_increase_desc">Crescita di a dimensione di a grafia</string> + <!-- Color option for the background --> + <string name="mozac_feature_readerview_dark">Scuru</string> + <!-- Accessible description for the color option --> + <string name="mozac_feature_readerview_dark_color_scheme_desc">Ghjocu di culori scuri</string> + <!-- Color option for the background --> + <string name="mozac_feature_readerview_sephia">Sepia</string> + <!-- Accessible description for the color option --> + <string name="mozac_feature_readerview_sepia_color_scheme_desc">Ghjocu di culori sepia</string> + <!-- Color option for the background --> + <string name="mozac_feature_readerview_light">Chjaru</string> + <!-- Accessible description for the color option --> + <string name="mozac_feature_readerview_light_color_scheme_desc">Ghjocu di culori chjari</string> +</resources> diff --git a/mobile/android/android-components/components/feature/readerview/src/main/res/values-cs/strings.xml b/mobile/android/android-components/components/feature/readerview/src/main/res/values-cs/strings.xml new file mode 100644 index 0000000000..ecf4cad954 --- /dev/null +++ b/mobile/android/android-components/components/feature/readerview/src/main/res/values-cs/strings.xml @@ -0,0 +1,29 @@ +<?xml version="1.0" encoding="utf-8"?> +<resources> + <!-- Name for one of the font options available to use --> + <string name="mozac_feature_readerview_sans_serif_font">Bezpatkové</string> + <!-- Accessible description for the font option --> + <string name="mozac_feature_readerview_sans_serif_font_desc">Bezpatkové písmo</string> + <!-- Name for one of the font options available to use --> + <string name="mozac_feature_readerview_serif_font">Patkové</string> + <!-- Accessible description for the font option --> + <string name="mozac_feature_readerview_serif_font_desc">Patkové písmo</string> + + <!-- Accessible description for decreasing the font size --> + <string name="mozac_feature_readerview_font_size_decrease_desc">Zmenšení velikosti písma</string> + + <!-- Accessible description for increasing the font size --> + <string name="mozac_feature_readerview_font_size_increase_desc">Zvětšení velikosti písma</string> + <!-- Color option for the background --> + <string name="mozac_feature_readerview_dark">Tmavé</string> + <!-- Accessible description for the color option --> + <string name="mozac_feature_readerview_dark_color_scheme_desc">Tmavé barvy</string> + <!-- Color option for the background --> + <string name="mozac_feature_readerview_sephia">Sépiové</string> + <!-- Accessible description for the color option --> + <string name="mozac_feature_readerview_sepia_color_scheme_desc">Sépiové barvy</string> + <!-- Color option for the background --> + <string name="mozac_feature_readerview_light">Světlé</string> + <!-- Accessible description for the color option --> + <string name="mozac_feature_readerview_light_color_scheme_desc">Světlé barvy</string> +</resources> diff --git a/mobile/android/android-components/components/feature/readerview/src/main/res/values-cy/strings.xml b/mobile/android/android-components/components/feature/readerview/src/main/res/values-cy/strings.xml new file mode 100644 index 0000000000..7252d7b1d1 --- /dev/null +++ b/mobile/android/android-components/components/feature/readerview/src/main/res/values-cy/strings.xml @@ -0,0 +1,29 @@ +<?xml version="1.0" encoding="utf-8"?> +<resources> + <!-- Name for one of the font options available to use --> + <string name="mozac_feature_readerview_sans_serif_font">Sans serif</string> + <!-- Accessible description for the font option --> + <string name="mozac_feature_readerview_sans_serif_font_desc">Ffont Sans Serif</string> + <!-- Name for one of the font options available to use --> + <string name="mozac_feature_readerview_serif_font">Serif</string> + <!-- Accessible description for the font option --> + <string name="mozac_feature_readerview_serif_font_desc">Ffont serif</string> + + <!-- Accessible description for decreasing the font size --> + <string name="mozac_feature_readerview_font_size_decrease_desc">Lleihad maint ffont</string> + + <!-- Accessible description for increasing the font size --> + <string name="mozac_feature_readerview_font_size_increase_desc">Cynnydd maint ffont</string> + <!-- Color option for the background --> + <string name="mozac_feature_readerview_dark">Tywyll</string> + <!-- Accessible description for the color option --> + <string name="mozac_feature_readerview_dark_color_scheme_desc">Cynllun lliw tywyll</string> + <!-- Color option for the background --> + <string name="mozac_feature_readerview_sephia">Sepia</string> + <!-- Accessible description for the color option --> + <string name="mozac_feature_readerview_sepia_color_scheme_desc">Cynllun lliw tywyll</string> + <!-- Color option for the background --> + <string name="mozac_feature_readerview_light">Golau</string> + <!-- Accessible description for the color option --> + <string name="mozac_feature_readerview_light_color_scheme_desc">Cynllun lliw golau</string> +</resources> diff --git a/mobile/android/android-components/components/feature/readerview/src/main/res/values-da/strings.xml b/mobile/android/android-components/components/feature/readerview/src/main/res/values-da/strings.xml new file mode 100644 index 0000000000..f933bc4565 --- /dev/null +++ b/mobile/android/android-components/components/feature/readerview/src/main/res/values-da/strings.xml @@ -0,0 +1,30 @@ +<?xml version="1.0" encoding="utf-8"?> +<resources> + <!-- Name for one of the font options available to use --> + <string name="mozac_feature_readerview_sans_serif_font">Sans-serif</string> + <!-- Accessible description for the font option --> + <string name="mozac_feature_readerview_sans_serif_font_desc">Sans-serif-font</string> + <!-- Name for one of the font options available to use --> + <string name="mozac_feature_readerview_serif_font">Serif</string> + + <!-- Accessible description for the font option --> + <string name="mozac_feature_readerview_serif_font_desc">Serif-font</string> + + <!-- Accessible description for decreasing the font size --> + <string name="mozac_feature_readerview_font_size_decrease_desc">Gør skriftstørrelsen mindre</string> + + <!-- Accessible description for increasing the font size --> + <string name="mozac_feature_readerview_font_size_increase_desc">Gør skriftstørrelsen større</string> + <!-- Color option for the background --> + <string name="mozac_feature_readerview_dark">Mørk</string> + <!-- Accessible description for the color option --> + <string name="mozac_feature_readerview_dark_color_scheme_desc">Mørkt farveskema</string> + <!-- Color option for the background --> + <string name="mozac_feature_readerview_sephia">Sepia</string> + <!-- Accessible description for the color option --> + <string name="mozac_feature_readerview_sepia_color_scheme_desc">Sepia farveskema</string> + <!-- Color option for the background --> + <string name="mozac_feature_readerview_light">Lyst</string> + <!-- Accessible description for the color option --> + <string name="mozac_feature_readerview_light_color_scheme_desc">Lyst farveskema</string> +</resources> diff --git a/mobile/android/android-components/components/feature/readerview/src/main/res/values-de/strings.xml b/mobile/android/android-components/components/feature/readerview/src/main/res/values-de/strings.xml new file mode 100644 index 0000000000..3f973f6ae1 --- /dev/null +++ b/mobile/android/android-components/components/feature/readerview/src/main/res/values-de/strings.xml @@ -0,0 +1,29 @@ +<?xml version="1.0" encoding="utf-8"?> +<resources> + <!-- Name for one of the font options available to use --> + <string name="mozac_feature_readerview_sans_serif_font">Sans Serif</string> + <!-- Accessible description for the font option --> + <string name="mozac_feature_readerview_sans_serif_font_desc">Serifenlose Schriftart</string> + <!-- Name for one of the font options available to use --> + <string name="mozac_feature_readerview_serif_font">Serif</string> + <!-- Accessible description for the font option --> + <string name="mozac_feature_readerview_serif_font_desc">Serifenschriftart</string> + + <!-- Accessible description for decreasing the font size --> + <string name="mozac_feature_readerview_font_size_decrease_desc">Schriftgröße verringern</string> + + <!-- Accessible description for increasing the font size --> + <string name="mozac_feature_readerview_font_size_increase_desc">Schriftgröße erhöhen</string> + <!-- Color option for the background --> + <string name="mozac_feature_readerview_dark">Dunkel</string> + <!-- Accessible description for the color option --> + <string name="mozac_feature_readerview_dark_color_scheme_desc">Dunkles Farbschema</string> + <!-- Color option for the background --> + <string name="mozac_feature_readerview_sephia">Sepia</string> + <!-- Accessible description for the color option --> + <string name="mozac_feature_readerview_sepia_color_scheme_desc">Sepia-Farbschema</string> + <!-- Color option for the background --> + <string name="mozac_feature_readerview_light">Hell</string> + <!-- Accessible description for the color option --> + <string name="mozac_feature_readerview_light_color_scheme_desc">Helles Farbschema</string> +</resources> diff --git a/mobile/android/android-components/components/feature/readerview/src/main/res/values-dsb/strings.xml b/mobile/android/android-components/components/feature/readerview/src/main/res/values-dsb/strings.xml new file mode 100644 index 0000000000..84467bec78 --- /dev/null +++ b/mobile/android/android-components/components/feature/readerview/src/main/res/values-dsb/strings.xml @@ -0,0 +1,29 @@ +<?xml version="1.0" encoding="utf-8"?> +<resources> + <!-- Name for one of the font options available to use --> + <string name="mozac_feature_readerview_sans_serif_font">Sans serif</string> + <!-- Accessible description for the font option --> + <string name="mozac_feature_readerview_sans_serif_font_desc">Pismo sans serif</string> + <!-- Name for one of the font options available to use --> + <string name="mozac_feature_readerview_serif_font">Serif</string> + <!-- Accessible description for the font option --> + <string name="mozac_feature_readerview_serif_font_desc">Serifowe pismo</string> + + <!-- Accessible description for decreasing the font size --> + <string name="mozac_feature_readerview_font_size_decrease_desc">Wjelikosć pisma pómjeńšyś</string> + + <!-- Accessible description for increasing the font size --> + <string name="mozac_feature_readerview_font_size_increase_desc">Wjelikosć pisma pówětšyś</string> + <!-- Color option for the background --> + <string name="mozac_feature_readerview_dark">Śamny</string> + <!-- Accessible description for the color option --> + <string name="mozac_feature_readerview_dark_color_scheme_desc">Śamna barwowa šema</string> + <!-- Color option for the background --> + <string name="mozac_feature_readerview_sephia">Sepija</string> + <!-- Accessible description for the color option --> + <string name="mozac_feature_readerview_sepia_color_scheme_desc">Barwowa šema Sepija</string> + <!-- Color option for the background --> + <string name="mozac_feature_readerview_light">Swětły</string> + <!-- Accessible description for the color option --> + <string name="mozac_feature_readerview_light_color_scheme_desc">Swětła barwowa šema</string> +</resources> diff --git a/mobile/android/android-components/components/feature/readerview/src/main/res/values-el/strings.xml b/mobile/android/android-components/components/feature/readerview/src/main/res/values-el/strings.xml new file mode 100644 index 0000000000..abb18c3715 --- /dev/null +++ b/mobile/android/android-components/components/feature/readerview/src/main/res/values-el/strings.xml @@ -0,0 +1,30 @@ +<?xml version="1.0" encoding="utf-8"?> +<resources> + + <!-- Name for one of the font options available to use --> + <string name="mozac_feature_readerview_sans_serif_font">Sans serif</string> + <!-- Accessible description for the font option --> + <string name="mozac_feature_readerview_sans_serif_font_desc">Γραμματοσειρά Sans Serif</string> + <!-- Name for one of the font options available to use --> + <string name="mozac_feature_readerview_serif_font">Serif</string> + <!-- Accessible description for the font option --> + <string name="mozac_feature_readerview_serif_font_desc">Γραμματοσειρά Serif</string> + + <!-- Accessible description for decreasing the font size --> + <string name="mozac_feature_readerview_font_size_decrease_desc">Σμίκρυνση γραμματοσειράς</string> + + <!-- Accessible description for increasing the font size --> + <string name="mozac_feature_readerview_font_size_increase_desc">Μεγέθυνση γραμματοσειράς</string> + <!-- Color option for the background --> + <string name="mozac_feature_readerview_dark">Σκουρόχρωμο</string> + <!-- Accessible description for the color option --> + <string name="mozac_feature_readerview_dark_color_scheme_desc">Σύνολο σκούρων χρωμάτων</string> + <!-- Color option for the background --> + <string name="mozac_feature_readerview_sephia">Σέπια</string> + <!-- Accessible description for the color option --> + <string name="mozac_feature_readerview_sepia_color_scheme_desc">Σύνολο χρωμάτων σέπια</string> + <!-- Color option for the background --> + <string name="mozac_feature_readerview_light">Ανοιχτό</string> + <!-- Accessible description for the color option --> + <string name="mozac_feature_readerview_light_color_scheme_desc">Σύνολο ανοιχτών χρωμάτων</string> +</resources> diff --git a/mobile/android/android-components/components/feature/readerview/src/main/res/values-en-rCA/strings.xml b/mobile/android/android-components/components/feature/readerview/src/main/res/values-en-rCA/strings.xml new file mode 100644 index 0000000000..98a786a966 --- /dev/null +++ b/mobile/android/android-components/components/feature/readerview/src/main/res/values-en-rCA/strings.xml @@ -0,0 +1,29 @@ +<?xml version="1.0" encoding="utf-8"?> +<resources> + <!-- Name for one of the font options available to use --> + <string name="mozac_feature_readerview_sans_serif_font">Sans serif</string> + <!-- Accessible description for the font option --> + <string name="mozac_feature_readerview_sans_serif_font_desc">Sans Serif font</string> + <!-- Name for one of the font options available to use --> + <string name="mozac_feature_readerview_serif_font">Serif</string> + <!-- Accessible description for the font option --> + <string name="mozac_feature_readerview_serif_font_desc">Serif font</string> + + <!-- Accessible description for decreasing the font size --> + <string name="mozac_feature_readerview_font_size_decrease_desc">Font size decrease</string> + + <!-- Accessible description for increasing the font size --> + <string name="mozac_feature_readerview_font_size_increase_desc">Font size increase</string> + <!-- Color option for the background --> + <string name="mozac_feature_readerview_dark">Dark</string> + <!-- Accessible description for the color option --> + <string name="mozac_feature_readerview_dark_color_scheme_desc">Dark colour scheme</string> + <!-- Color option for the background --> + <string name="mozac_feature_readerview_sephia">Sepia</string> + <!-- Accessible description for the color option --> + <string name="mozac_feature_readerview_sepia_color_scheme_desc">Sepia colour scheme</string> + <!-- Color option for the background --> + <string name="mozac_feature_readerview_light">Light</string> + <!-- Accessible description for the color option --> + <string name="mozac_feature_readerview_light_color_scheme_desc">Light colour scheme</string> +</resources> diff --git a/mobile/android/android-components/components/feature/readerview/src/main/res/values-en-rGB/strings.xml b/mobile/android/android-components/components/feature/readerview/src/main/res/values-en-rGB/strings.xml new file mode 100644 index 0000000000..98a786a966 --- /dev/null +++ b/mobile/android/android-components/components/feature/readerview/src/main/res/values-en-rGB/strings.xml @@ -0,0 +1,29 @@ +<?xml version="1.0" encoding="utf-8"?> +<resources> + <!-- Name for one of the font options available to use --> + <string name="mozac_feature_readerview_sans_serif_font">Sans serif</string> + <!-- Accessible description for the font option --> + <string name="mozac_feature_readerview_sans_serif_font_desc">Sans Serif font</string> + <!-- Name for one of the font options available to use --> + <string name="mozac_feature_readerview_serif_font">Serif</string> + <!-- Accessible description for the font option --> + <string name="mozac_feature_readerview_serif_font_desc">Serif font</string> + + <!-- Accessible description for decreasing the font size --> + <string name="mozac_feature_readerview_font_size_decrease_desc">Font size decrease</string> + + <!-- Accessible description for increasing the font size --> + <string name="mozac_feature_readerview_font_size_increase_desc">Font size increase</string> + <!-- Color option for the background --> + <string name="mozac_feature_readerview_dark">Dark</string> + <!-- Accessible description for the color option --> + <string name="mozac_feature_readerview_dark_color_scheme_desc">Dark colour scheme</string> + <!-- Color option for the background --> + <string name="mozac_feature_readerview_sephia">Sepia</string> + <!-- Accessible description for the color option --> + <string name="mozac_feature_readerview_sepia_color_scheme_desc">Sepia colour scheme</string> + <!-- Color option for the background --> + <string name="mozac_feature_readerview_light">Light</string> + <!-- Accessible description for the color option --> + <string name="mozac_feature_readerview_light_color_scheme_desc">Light colour scheme</string> +</resources> diff --git a/mobile/android/android-components/components/feature/readerview/src/main/res/values-eo/strings.xml b/mobile/android/android-components/components/feature/readerview/src/main/res/values-eo/strings.xml new file mode 100644 index 0000000000..719f10262b --- /dev/null +++ b/mobile/android/android-components/components/feature/readerview/src/main/res/values-eo/strings.xml @@ -0,0 +1,29 @@ +<?xml version="1.0" encoding="utf-8"?> +<resources> + <!-- Name for one of the font options available to use --> + <string name="mozac_feature_readerview_sans_serif_font">Senserifa</string> + <!-- Accessible description for the font option --> + <string name="mozac_feature_readerview_sans_serif_font_desc">Tiparo senserifa</string> + <!-- Name for one of the font options available to use --> + <string name="mozac_feature_readerview_serif_font">Serifa</string> + <!-- Accessible description for the font option --> + <string name="mozac_feature_readerview_serif_font_desc">Tiparo serifa</string> + + <!-- Accessible description for decreasing the font size --> + <string name="mozac_feature_readerview_font_size_decrease_desc">Etigi tiparon</string> + + <!-- Accessible description for increasing the font size --> + <string name="mozac_feature_readerview_font_size_increase_desc">Grandigi tiparon</string> + <!-- Color option for the background --> + <string name="mozac_feature_readerview_dark">Malhela</string> + <!-- Accessible description for the color option --> + <string name="mozac_feature_readerview_dark_color_scheme_desc">Malhelkolora skemo</string> + <!-- Color option for the background --> + <string name="mozac_feature_readerview_sephia">Sepia</string> + <!-- Accessible description for the color option --> + <string name="mozac_feature_readerview_sepia_color_scheme_desc">Sepikolora skemo</string> + <!-- Color option for the background --> + <string name="mozac_feature_readerview_light">Hela</string> + <!-- Accessible description for the color option --> + <string name="mozac_feature_readerview_light_color_scheme_desc">Helkolora skemo</string> +</resources> diff --git a/mobile/android/android-components/components/feature/readerview/src/main/res/values-es-rAR/strings.xml b/mobile/android/android-components/components/feature/readerview/src/main/res/values-es-rAR/strings.xml new file mode 100644 index 0000000000..e37f09be5f --- /dev/null +++ b/mobile/android/android-components/components/feature/readerview/src/main/res/values-es-rAR/strings.xml @@ -0,0 +1,29 @@ +<?xml version="1.0" encoding="utf-8"?> +<resources> + <!-- Name for one of the font options available to use --> + <string name="mozac_feature_readerview_sans_serif_font">Sans-serif</string> + <!-- Accessible description for the font option --> + <string name="mozac_feature_readerview_sans_serif_font_desc">Fuente Sans Serif</string> + <!-- Name for one of the font options available to use --> + <string name="mozac_feature_readerview_serif_font">Serif</string> + <!-- Accessible description for the font option --> + <string name="mozac_feature_readerview_serif_font_desc">Fuente Serif</string> + + <!-- Accessible description for decreasing the font size --> + <string name="mozac_feature_readerview_font_size_decrease_desc">Disminución del tamaño de la fuente</string> + + <!-- Accessible description for increasing the font size --> + <string name="mozac_feature_readerview_font_size_increase_desc">Aumento del tamaño de la fuente</string> + <!-- Color option for the background --> + <string name="mozac_feature_readerview_dark">Oscuro</string> + <!-- Accessible description for the color option --> + <string name="mozac_feature_readerview_dark_color_scheme_desc">Esquema de color oscuro</string> + <!-- Color option for the background --> + <string name="mozac_feature_readerview_sephia">Sepia</string> + <!-- Accessible description for the color option --> + <string name="mozac_feature_readerview_sepia_color_scheme_desc">Esquema de color sepia</string> + <!-- Color option for the background --> + <string name="mozac_feature_readerview_light">Claro</string> + <!-- Accessible description for the color option --> + <string name="mozac_feature_readerview_light_color_scheme_desc">Esquema de color claro</string> +</resources> diff --git a/mobile/android/android-components/components/feature/readerview/src/main/res/values-es-rCL/strings.xml b/mobile/android/android-components/components/feature/readerview/src/main/res/values-es-rCL/strings.xml new file mode 100644 index 0000000000..901d2fcb3c --- /dev/null +++ b/mobile/android/android-components/components/feature/readerview/src/main/res/values-es-rCL/strings.xml @@ -0,0 +1,29 @@ +<?xml version="1.0" encoding="utf-8"?> +<resources> + <!-- Name for one of the font options available to use --> + <string name="mozac_feature_readerview_sans_serif_font">Sans serif</string> + <!-- Accessible description for the font option --> + <string name="mozac_feature_readerview_sans_serif_font_desc">Fuente Sans Serif</string> + <!-- Name for one of the font options available to use --> + <string name="mozac_feature_readerview_serif_font">Serif</string> + <!-- Accessible description for the font option --> + <string name="mozac_feature_readerview_serif_font_desc">Fuente Serif</string> + + <!-- Accessible description for decreasing the font size --> + <string name="mozac_feature_readerview_font_size_decrease_desc">Reducción del tamaño de la fuente</string> + + <!-- Accessible description for increasing the font size --> + <string name="mozac_feature_readerview_font_size_increase_desc">Incremento del tamaño de la fuente</string> + <!-- Color option for the background --> + <string name="mozac_feature_readerview_dark">Oscuro</string> + <!-- Accessible description for the color option --> + <string name="mozac_feature_readerview_dark_color_scheme_desc">Esquema de color oscuro</string> + <!-- Color option for the background --> + <string name="mozac_feature_readerview_sephia">Sepia</string> + <!-- Accessible description for the color option --> + <string name="mozac_feature_readerview_sepia_color_scheme_desc">Esquema de color sepia</string> + <!-- Color option for the background --> + <string name="mozac_feature_readerview_light">Claro</string> + <!-- Accessible description for the color option --> + <string name="mozac_feature_readerview_light_color_scheme_desc">Esquema de color claro</string> +</resources> diff --git a/mobile/android/android-components/components/feature/readerview/src/main/res/values-es-rES/strings.xml b/mobile/android/android-components/components/feature/readerview/src/main/res/values-es-rES/strings.xml new file mode 100644 index 0000000000..6ff0918f51 --- /dev/null +++ b/mobile/android/android-components/components/feature/readerview/src/main/res/values-es-rES/strings.xml @@ -0,0 +1,29 @@ +<?xml version="1.0" encoding="utf-8"?> +<resources> + <!-- Name for one of the font options available to use --> + <string name="mozac_feature_readerview_sans_serif_font">Sans serif</string> + <!-- Accessible description for the font option --> + <string name="mozac_feature_readerview_sans_serif_font_desc">Fuente Sans Serif</string> + <!-- Name for one of the font options available to use --> + <string name="mozac_feature_readerview_serif_font">Serif</string> + <!-- Accessible description for the font option --> + <string name="mozac_feature_readerview_serif_font_desc">Fuente Serif</string> + + <!-- Accessible description for decreasing the font size --> + <string name="mozac_feature_readerview_font_size_decrease_desc">Disminuir tamaño de fuente</string> + + <!-- Accessible description for increasing the font size --> + <string name="mozac_feature_readerview_font_size_increase_desc">Aumentar tamaño de fuente</string> + <!-- Color option for the background --> + <string name="mozac_feature_readerview_dark">Oscuro</string> + <!-- Accessible description for the color option --> + <string name="mozac_feature_readerview_dark_color_scheme_desc">Esquema de colores oscuros</string> + <!-- Color option for the background --> + <string name="mozac_feature_readerview_sephia">Sepia</string> + <!-- Accessible description for the color option --> + <string name="mozac_feature_readerview_sepia_color_scheme_desc">Esquema de colores sepia</string> + <!-- Color option for the background --> + <string name="mozac_feature_readerview_light">Claro</string> + <!-- Accessible description for the color option --> + <string name="mozac_feature_readerview_light_color_scheme_desc">Esquema de colores claros</string> +</resources> diff --git a/mobile/android/android-components/components/feature/readerview/src/main/res/values-es-rMX/strings.xml b/mobile/android/android-components/components/feature/readerview/src/main/res/values-es-rMX/strings.xml new file mode 100644 index 0000000000..266654f423 --- /dev/null +++ b/mobile/android/android-components/components/feature/readerview/src/main/res/values-es-rMX/strings.xml @@ -0,0 +1,29 @@ +<?xml version="1.0" encoding="utf-8"?> +<resources> + <!-- Name for one of the font options available to use --> + <string name="mozac_feature_readerview_sans_serif_font">Sans serif</string> + <!-- Accessible description for the font option --> + <string name="mozac_feature_readerview_sans_serif_font_desc">Fuente Sans Serif</string> + <!-- Name for one of the font options available to use --> + <string name="mozac_feature_readerview_serif_font">Serif</string> + <!-- Accessible description for the font option --> + <string name="mozac_feature_readerview_serif_font_desc">Fuente Serif</string> + + <!-- Accessible description for decreasing the font size --> + <string name="mozac_feature_readerview_font_size_decrease_desc">Disminuir tamaño de fuente</string> + + <!-- Accessible description for increasing the font size --> + <string name="mozac_feature_readerview_font_size_increase_desc">Aumentar tamaño de fuente</string> + <!-- Color option for the background --> + <string name="mozac_feature_readerview_dark">Oscuro</string> + <!-- Accessible description for the color option --> + <string name="mozac_feature_readerview_dark_color_scheme_desc">Esquema de color oscuro</string> + <!-- Color option for the background --> + <string name="mozac_feature_readerview_sephia">Sepia</string> + <!-- Accessible description for the color option --> + <string name="mozac_feature_readerview_sepia_color_scheme_desc">Esquema de color sepia</string> + <!-- Color option for the background --> + <string name="mozac_feature_readerview_light">Claro</string> + <!-- Accessible description for the color option --> + <string name="mozac_feature_readerview_light_color_scheme_desc">Esquema de color claro</string> +</resources> diff --git a/mobile/android/android-components/components/feature/readerview/src/main/res/values-es/strings.xml b/mobile/android/android-components/components/feature/readerview/src/main/res/values-es/strings.xml new file mode 100644 index 0000000000..6ff0918f51 --- /dev/null +++ b/mobile/android/android-components/components/feature/readerview/src/main/res/values-es/strings.xml @@ -0,0 +1,29 @@ +<?xml version="1.0" encoding="utf-8"?> +<resources> + <!-- Name for one of the font options available to use --> + <string name="mozac_feature_readerview_sans_serif_font">Sans serif</string> + <!-- Accessible description for the font option --> + <string name="mozac_feature_readerview_sans_serif_font_desc">Fuente Sans Serif</string> + <!-- Name for one of the font options available to use --> + <string name="mozac_feature_readerview_serif_font">Serif</string> + <!-- Accessible description for the font option --> + <string name="mozac_feature_readerview_serif_font_desc">Fuente Serif</string> + + <!-- Accessible description for decreasing the font size --> + <string name="mozac_feature_readerview_font_size_decrease_desc">Disminuir tamaño de fuente</string> + + <!-- Accessible description for increasing the font size --> + <string name="mozac_feature_readerview_font_size_increase_desc">Aumentar tamaño de fuente</string> + <!-- Color option for the background --> + <string name="mozac_feature_readerview_dark">Oscuro</string> + <!-- Accessible description for the color option --> + <string name="mozac_feature_readerview_dark_color_scheme_desc">Esquema de colores oscuros</string> + <!-- Color option for the background --> + <string name="mozac_feature_readerview_sephia">Sepia</string> + <!-- Accessible description for the color option --> + <string name="mozac_feature_readerview_sepia_color_scheme_desc">Esquema de colores sepia</string> + <!-- Color option for the background --> + <string name="mozac_feature_readerview_light">Claro</string> + <!-- Accessible description for the color option --> + <string name="mozac_feature_readerview_light_color_scheme_desc">Esquema de colores claros</string> +</resources> diff --git a/mobile/android/android-components/components/feature/readerview/src/main/res/values-et/strings.xml b/mobile/android/android-components/components/feature/readerview/src/main/res/values-et/strings.xml new file mode 100644 index 0000000000..672015654e --- /dev/null +++ b/mobile/android/android-components/components/feature/readerview/src/main/res/values-et/strings.xml @@ -0,0 +1,29 @@ +<?xml version="1.0" encoding="utf-8"?> +<resources> + <!-- Name for one of the font options available to use --> + <string name="mozac_feature_readerview_sans_serif_font">seriifideta</string> + <!-- Accessible description for the font option --> + <string name="mozac_feature_readerview_sans_serif_font_desc">seriifideta font</string> + <!-- Name for one of the font options available to use --> + <string name="mozac_feature_readerview_serif_font">seriifidega</string> + <!-- Accessible description for the font option --> + <string name="mozac_feature_readerview_serif_font_desc">seriifidega font</string> + + <!-- Accessible description for decreasing the font size --> + <string name="mozac_feature_readerview_font_size_decrease_desc">fondi suuruse vähendamine</string> + + <!-- Accessible description for increasing the font size --> + <string name="mozac_feature_readerview_font_size_increase_desc">fondi suuruse suurendamine</string> + <!-- Color option for the background --> + <string name="mozac_feature_readerview_dark">tume</string> + <!-- Accessible description for the color option --> + <string name="mozac_feature_readerview_dark_color_scheme_desc">tume värviskeem</string> + <!-- Color option for the background --> + <string name="mozac_feature_readerview_sephia">seepia</string> + <!-- Accessible description for the color option --> + <string name="mozac_feature_readerview_sepia_color_scheme_desc">seepia värviskeem</string> + <!-- Color option for the background --> + <string name="mozac_feature_readerview_light">hele</string> + <!-- Accessible description for the color option --> + <string name="mozac_feature_readerview_light_color_scheme_desc">hele värviskeem</string> +</resources> diff --git a/mobile/android/android-components/components/feature/readerview/src/main/res/values-eu/strings.xml b/mobile/android/android-components/components/feature/readerview/src/main/res/values-eu/strings.xml new file mode 100644 index 0000000000..df496fd857 --- /dev/null +++ b/mobile/android/android-components/components/feature/readerview/src/main/res/values-eu/strings.xml @@ -0,0 +1,29 @@ +<?xml version="1.0" encoding="utf-8"?> +<resources> + <!-- Name for one of the font options available to use --> + <string name="mozac_feature_readerview_sans_serif_font">Sans serif</string> + <!-- Accessible description for the font option --> + <string name="mozac_feature_readerview_sans_serif_font_desc">Sans Serif letra-tipoa</string> + <!-- Name for one of the font options available to use --> + <string name="mozac_feature_readerview_serif_font">Serif</string> + <!-- Accessible description for the font option --> + <string name="mozac_feature_readerview_serif_font_desc">Serif letra-tipoa</string> + + <!-- Accessible description for decreasing the font size --> + <string name="mozac_feature_readerview_font_size_decrease_desc">Txikiagotu letra tamaina</string> + + <!-- Accessible description for increasing the font size --> + <string name="mozac_feature_readerview_font_size_increase_desc">Handiagotu letra tamaina</string> + <!-- Color option for the background --> + <string name="mozac_feature_readerview_dark">Iluna</string> + <!-- Accessible description for the color option --> + <string name="mozac_feature_readerview_dark_color_scheme_desc">Kolore-eskema iluna</string> + <!-- Color option for the background --> + <string name="mozac_feature_readerview_sephia">Sepia</string> + <!-- Accessible description for the color option --> + <string name="mozac_feature_readerview_sepia_color_scheme_desc">Sepia kolore-eskema</string> + <!-- Color option for the background --> + <string name="mozac_feature_readerview_light">Argia</string> + <!-- Accessible description for the color option --> + <string name="mozac_feature_readerview_light_color_scheme_desc">Kolore-eskema argia</string> +</resources> diff --git a/mobile/android/android-components/components/feature/readerview/src/main/res/values-fa/strings.xml b/mobile/android/android-components/components/feature/readerview/src/main/res/values-fa/strings.xml new file mode 100644 index 0000000000..65abe288c6 --- /dev/null +++ b/mobile/android/android-components/components/feature/readerview/src/main/res/values-fa/strings.xml @@ -0,0 +1,29 @@ +<?xml version="1.0" encoding="utf-8"?> +<resources> + <!-- Name for one of the font options available to use --> + <string name="mozac_feature_readerview_sans_serif_font">بدون سِریف</string> + <!-- Accessible description for the font option --> + <string name="mozac_feature_readerview_sans_serif_font_desc">قلم بدون سِریف</string> + <!-- Name for one of the font options available to use --> + <string name="mozac_feature_readerview_serif_font">سِریف</string> + <!-- Accessible description for the font option --> + <string name="mozac_feature_readerview_serif_font_desc">قلم سِریف</string> + + <!-- Accessible description for decreasing the font size --> + <string name="mozac_feature_readerview_font_size_decrease_desc">کاهش اندازهٔ قلم</string> + + <!-- Accessible description for increasing the font size --> + <string name="mozac_feature_readerview_font_size_increase_desc">افزایش اندازهٔ قلم</string> + <!-- Color option for the background --> + <string name="mozac_feature_readerview_dark">تیره</string> + <!-- Accessible description for the color option --> + <string name="mozac_feature_readerview_dark_color_scheme_desc">طرح رنگ تیره</string> + <!-- Color option for the background --> + <string name="mozac_feature_readerview_sephia">سوبیایی</string> + <!-- Accessible description for the color option --> + <string name="mozac_feature_readerview_sepia_color_scheme_desc">طرح رنگی سوبیایی</string> + <!-- Color option for the background --> + <string name="mozac_feature_readerview_light">روشن</string> + <!-- Accessible description for the color option --> + <string name="mozac_feature_readerview_light_color_scheme_desc">طرح رنگ روشن</string> +</resources> diff --git a/mobile/android/android-components/components/feature/readerview/src/main/res/values-ff/strings.xml b/mobile/android/android-components/components/feature/readerview/src/main/res/values-ff/strings.xml new file mode 100644 index 0000000000..2e43edd832 --- /dev/null +++ b/mobile/android/android-components/components/feature/readerview/src/main/res/values-ff/strings.xml @@ -0,0 +1,18 @@ +<?xml version="1.0" encoding="utf-8"?> +<resources> + <!-- Name for one of the font options available to use --> + <string name="mozac_feature_readerview_sans_serif_font">Sans serif</string> + + <!-- Name for one of the font options available to use --> + <string name="mozac_feature_readerview_serif_font">Serif</string> + + <!-- Accessible description for decreasing the font size --> + <string name="mozac_feature_readerview_font_size_decrease_desc">Ustugol darnde alkule</string> + + <!-- Accessible description for increasing the font size --> + <string name="mozac_feature_readerview_font_size_increase_desc">Ɓeydugol darnde alkule</string> + <!-- Color option for the background --> + <string name="mozac_feature_readerview_dark">Niɓɓo</string> + <!-- Color option for the background --> + <string name="mozac_feature_readerview_light">Leerɗo</string> + </resources> diff --git a/mobile/android/android-components/components/feature/readerview/src/main/res/values-fi/strings.xml b/mobile/android/android-components/components/feature/readerview/src/main/res/values-fi/strings.xml new file mode 100644 index 0000000000..964c1c91cb --- /dev/null +++ b/mobile/android/android-components/components/feature/readerview/src/main/res/values-fi/strings.xml @@ -0,0 +1,30 @@ +<?xml version="1.0" encoding="utf-8"?> +<resources> + + <!-- Name for one of the font options available to use --> + <string name="mozac_feature_readerview_sans_serif_font">Päätteetön</string> + <!-- Accessible description for the font option --> + <string name="mozac_feature_readerview_sans_serif_font_desc">Päätteetön fontti</string> + <!-- Name for one of the font options available to use --> + <string name="mozac_feature_readerview_serif_font">Päätteellinen</string> + <!-- Accessible description for the font option --> + <string name="mozac_feature_readerview_serif_font_desc">Päätteellinen fontti</string> + + <!-- Accessible description for decreasing the font size --> + <string name="mozac_feature_readerview_font_size_decrease_desc">Pienennä fontin kokoa</string> + + <!-- Accessible description for increasing the font size --> + <string name="mozac_feature_readerview_font_size_increase_desc">Suurenna fontin kokoa</string> + <!-- Color option for the background --> + <string name="mozac_feature_readerview_dark">Tumma</string> + <!-- Accessible description for the color option --> + <string name="mozac_feature_readerview_dark_color_scheme_desc">Tumma väriteema</string> + <!-- Color option for the background --> + <string name="mozac_feature_readerview_sephia">Seepia</string> + <!-- Accessible description for the color option --> + <string name="mozac_feature_readerview_sepia_color_scheme_desc">Seepiamainen väriteema</string> + <!-- Color option for the background --> + <string name="mozac_feature_readerview_light">Vaalea</string> + <!-- Accessible description for the color option --> + <string name="mozac_feature_readerview_light_color_scheme_desc">Vaalea väriteema</string> +</resources> diff --git a/mobile/android/android-components/components/feature/readerview/src/main/res/values-fr/strings.xml b/mobile/android/android-components/components/feature/readerview/src/main/res/values-fr/strings.xml new file mode 100644 index 0000000000..93121bddbc --- /dev/null +++ b/mobile/android/android-components/components/feature/readerview/src/main/res/values-fr/strings.xml @@ -0,0 +1,30 @@ +<?xml version="1.0" encoding="utf-8"?> +<resources> + + <!-- Name for one of the font options available to use --> + <string name="mozac_feature_readerview_sans_serif_font">Sans empattement</string> + <!-- Accessible description for the font option --> + <string name="mozac_feature_readerview_sans_serif_font_desc">Police sans empattement</string> + <!-- Name for one of the font options available to use --> + <string name="mozac_feature_readerview_serif_font">Empattement</string> + <!-- Accessible description for the font option --> + <string name="mozac_feature_readerview_serif_font_desc">Police avec empattement</string> + + <!-- Accessible description for decreasing the font size --> + <string name="mozac_feature_readerview_font_size_decrease_desc">Réduction de la taille de la police</string> + + <!-- Accessible description for increasing the font size --> + <string name="mozac_feature_readerview_font_size_increase_desc">Augmentation de la taille de la police</string> + <!-- Color option for the background --> + <string name="mozac_feature_readerview_dark">Sombre</string> + <!-- Accessible description for the color option --> + <string name="mozac_feature_readerview_dark_color_scheme_desc">Jeu de couleurs sombres</string> + <!-- Color option for the background --> + <string name="mozac_feature_readerview_sephia">Sépia</string> + <!-- Accessible description for the color option --> + <string name="mozac_feature_readerview_sepia_color_scheme_desc">Jeu de couleurs sépia</string> + <!-- Color option for the background --> + <string name="mozac_feature_readerview_light">Clair</string> + <!-- Accessible description for the color option --> + <string name="mozac_feature_readerview_light_color_scheme_desc">Jeu de couleurs claires</string> +</resources> diff --git a/mobile/android/android-components/components/feature/readerview/src/main/res/values-fur/strings.xml b/mobile/android/android-components/components/feature/readerview/src/main/res/values-fur/strings.xml new file mode 100644 index 0000000000..1d4a29ffd7 --- /dev/null +++ b/mobile/android/android-components/components/feature/readerview/src/main/res/values-fur/strings.xml @@ -0,0 +1,29 @@ +<?xml version="1.0" encoding="utf-8"?> +<resources> + <!-- Name for one of the font options available to use --> + <string name="mozac_feature_readerview_sans_serif_font">Cence graciis (sans serif)</string> + <!-- Accessible description for the font option --> + <string name="mozac_feature_readerview_sans_serif_font_desc">Caratar cence graciis (sans serif)</string> + <!-- Name for one of the font options available to use --> + <string name="mozac_feature_readerview_serif_font">Cun graciis (serif)</string> + <!-- Accessible description for the font option --> + <string name="mozac_feature_readerview_serif_font_desc">Caratar cun graciis (serif)</string> + + <!-- Accessible description for decreasing the font size --> + <string name="mozac_feature_readerview_font_size_decrease_desc">Diminuìs dimension caratars</string> + + <!-- Accessible description for increasing the font size --> + <string name="mozac_feature_readerview_font_size_increase_desc">Aumente dimension caratars</string> + <!-- Color option for the background --> + <string name="mozac_feature_readerview_dark">Scûr</string> + <!-- Accessible description for the color option --> + <string name="mozac_feature_readerview_dark_color_scheme_desc">Scheme di colôrs scûrs</string> + <!-- Color option for the background --> + <string name="mozac_feature_readerview_sephia">Sepât</string> + <!-- Accessible description for the color option --> + <string name="mozac_feature_readerview_sepia_color_scheme_desc">Scheme di colôrs sepâts</string> + <!-- Color option for the background --> + <string name="mozac_feature_readerview_light">Clâr</string> + <!-- Accessible description for the color option --> + <string name="mozac_feature_readerview_light_color_scheme_desc">Scheme di colôrs clârs</string> +</resources> diff --git a/mobile/android/android-components/components/feature/readerview/src/main/res/values-fy-rNL/strings.xml b/mobile/android/android-components/components/feature/readerview/src/main/res/values-fy-rNL/strings.xml new file mode 100644 index 0000000000..93ff9bf9d8 --- /dev/null +++ b/mobile/android/android-components/components/feature/readerview/src/main/res/values-fy-rNL/strings.xml @@ -0,0 +1,29 @@ +<?xml version="1.0" encoding="utf-8"?> +<resources> + <!-- Name for one of the font options available to use --> + <string name="mozac_feature_readerview_sans_serif_font">Skreefleas</string> + <!-- Accessible description for the font option --> + <string name="mozac_feature_readerview_sans_serif_font_desc">Lettertype Sans Serif</string> + <!-- Name for one of the font options available to use --> + <string name="mozac_feature_readerview_serif_font">Mei skreef</string> + <!-- Accessible description for the font option --> + <string name="mozac_feature_readerview_serif_font_desc">Lettertype Serif</string> + + <!-- Accessible description for decreasing the font size --> + <string name="mozac_feature_readerview_font_size_decrease_desc">Lettergrutte ferlytsje</string> + + <!-- Accessible description for increasing the font size --> + <string name="mozac_feature_readerview_font_size_increase_desc">Lettertype fergrutsje</string> + <!-- Color option for the background --> + <string name="mozac_feature_readerview_dark">Donker</string> + <!-- Accessible description for the color option --> + <string name="mozac_feature_readerview_dark_color_scheme_desc">Donker kleureskema</string> + <!-- Color option for the background --> + <string name="mozac_feature_readerview_sephia">Sepia</string> + <!-- Accessible description for the color option --> + <string name="mozac_feature_readerview_sepia_color_scheme_desc">Sepia kleureskema</string> + <!-- Color option for the background --> + <string name="mozac_feature_readerview_light">Ljocht</string> + <!-- Accessible description for the color option --> + <string name="mozac_feature_readerview_light_color_scheme_desc">Ljocht kleureskema</string> +</resources> diff --git a/mobile/android/android-components/components/feature/readerview/src/main/res/values-ga-rIE/strings.xml b/mobile/android/android-components/components/feature/readerview/src/main/res/values-ga-rIE/strings.xml new file mode 100644 index 0000000000..eb2ec6e0b3 --- /dev/null +++ b/mobile/android/android-components/components/feature/readerview/src/main/res/values-ga-rIE/strings.xml @@ -0,0 +1,29 @@ +<?xml version="1.0" encoding="utf-8"?> +<resources> + <!-- Name for one of the font options available to use --> + <string name="mozac_feature_readerview_sans_serif_font">Sans serif</string> + <!-- Accessible description for the font option --> + <string name="mozac_feature_readerview_sans_serif_font_desc">Cló Sans Serif</string> + <!-- Name for one of the font options available to use --> + <string name="mozac_feature_readerview_serif_font">Serif</string> + <!-- Accessible description for the font option --> + <string name="mozac_feature_readerview_serif_font_desc">Cló Serif</string> + + <!-- Accessible description for decreasing the font size --> + <string name="mozac_feature_readerview_font_size_decrease_desc">Laghdaigh an cló</string> + + <!-- Accessible description for increasing the font size --> + <string name="mozac_feature_readerview_font_size_increase_desc">Méadaigh an cló</string> + <!-- Color option for the background --> + <string name="mozac_feature_readerview_dark">Dorcha</string> + <!-- Accessible description for the color option --> + <string name="mozac_feature_readerview_dark_color_scheme_desc">Scéim dathanna dorcha</string> + <!-- Color option for the background --> + <string name="mozac_feature_readerview_sephia">Dúch Cudail</string> + <!-- Accessible description for the color option --> + <string name="mozac_feature_readerview_sepia_color_scheme_desc">Scéim dathanna dúch cudail</string> + <!-- Color option for the background --> + <string name="mozac_feature_readerview_light">Sorcha</string> + <!-- Accessible description for the color option --> + <string name="mozac_feature_readerview_light_color_scheme_desc">Scéim dathanna sorcha</string> +</resources> diff --git a/mobile/android/android-components/components/feature/readerview/src/main/res/values-gd/strings.xml b/mobile/android/android-components/components/feature/readerview/src/main/res/values-gd/strings.xml new file mode 100644 index 0000000000..daf58b6252 --- /dev/null +++ b/mobile/android/android-components/components/feature/readerview/src/main/res/values-gd/strings.xml @@ -0,0 +1,29 @@ +<?xml version="1.0" encoding="utf-8"?> +<resources> + <!-- Name for one of the font options available to use --> + <string name="mozac_feature_readerview_sans_serif_font">Sans-serif</string> + <!-- Accessible description for the font option --> + <string name="mozac_feature_readerview_sans_serif_font_desc">Cruth-clò sans-serif</string> + <!-- Name for one of the font options available to use --> + <string name="mozac_feature_readerview_serif_font">Serif</string> + <!-- Accessible description for the font option --> + <string name="mozac_feature_readerview_serif_font_desc">Cruth-clò serif</string> + + <!-- Accessible description for decreasing the font size --> + <string name="mozac_feature_readerview_font_size_decrease_desc">Lùghdaich meud a’ chrutha-chlò</string> + + <!-- Accessible description for increasing the font size --> + <string name="mozac_feature_readerview_font_size_increase_desc">Meudaich an cruth-clò</string> + <!-- Color option for the background --> + <string name="mozac_feature_readerview_dark">Dorcha</string> + <!-- Accessible description for the color option --> + <string name="mozac_feature_readerview_dark_color_scheme_desc">Sgeama dhathan dorcha</string> + <!-- Color option for the background --> + <string name="mozac_feature_readerview_sephia">Sepia</string> + <!-- Accessible description for the color option --> + <string name="mozac_feature_readerview_sepia_color_scheme_desc">Sgeama dhathan sepia</string> + <!-- Color option for the background --> + <string name="mozac_feature_readerview_light">Soilleir</string> + <!-- Accessible description for the color option --> + <string name="mozac_feature_readerview_light_color_scheme_desc">Sgeama dhathan soilleir</string> +</resources> diff --git a/mobile/android/android-components/components/feature/readerview/src/main/res/values-gl/strings.xml b/mobile/android/android-components/components/feature/readerview/src/main/res/values-gl/strings.xml new file mode 100644 index 0000000000..f223fabb5f --- /dev/null +++ b/mobile/android/android-components/components/feature/readerview/src/main/res/values-gl/strings.xml @@ -0,0 +1,29 @@ +<?xml version="1.0" encoding="utf-8"?> +<resources> + <!-- Name for one of the font options available to use --> + <string name="mozac_feature_readerview_sans_serif_font">Sans-serif</string> + <!-- Accessible description for the font option --> + <string name="mozac_feature_readerview_sans_serif_font_desc">Tipo de letra sans-Serif</string> + <!-- Name for one of the font options available to use --> + <string name="mozac_feature_readerview_serif_font">Serif</string> + <!-- Accessible description for the font option --> + <string name="mozac_feature_readerview_serif_font_desc">Tipo de letra serif</string> + + <!-- Accessible description for decreasing the font size --> + <string name="mozac_feature_readerview_font_size_decrease_desc">Aumento do tamaño da letra</string> + + <!-- Accessible description for increasing the font size --> + <string name="mozac_feature_readerview_font_size_increase_desc">Redución do tamaño da letra</string> + <!-- Color option for the background --> + <string name="mozac_feature_readerview_dark">Escuro</string> + <!-- Accessible description for the color option --> + <string name="mozac_feature_readerview_dark_color_scheme_desc">Esquema de cores escuras</string> + <!-- Color option for the background --> + <string name="mozac_feature_readerview_sephia">Sepia</string> + <!-- Accessible description for the color option --> + <string name="mozac_feature_readerview_sepia_color_scheme_desc">Esquema de cores sepia</string> + <!-- Color option for the background --> + <string name="mozac_feature_readerview_light">Claro</string> + <!-- Accessible description for the color option --> + <string name="mozac_feature_readerview_light_color_scheme_desc">Esquema de cores claras</string> +</resources> diff --git a/mobile/android/android-components/components/feature/readerview/src/main/res/values-gn/strings.xml b/mobile/android/android-components/components/feature/readerview/src/main/res/values-gn/strings.xml new file mode 100644 index 0000000000..003a68394e --- /dev/null +++ b/mobile/android/android-components/components/feature/readerview/src/main/res/values-gn/strings.xml @@ -0,0 +1,30 @@ +<?xml version="1.0" encoding="utf-8"?> +<resources> + <!-- Name for one of the font options available to use --> + <string name="mozac_feature_readerview_sans_serif_font">Sans-serif</string> + + <!-- Accessible description for the font option --> + <string name="mozac_feature_readerview_sans_serif_font_desc">Sans Serif reñoiha</string> + <!-- Name for one of the font options available to use --> + <string name="mozac_feature_readerview_serif_font">Serif</string> + <!-- Accessible description for the font option --> + <string name="mozac_feature_readerview_serif_font_desc">Serif reñoiha</string> + + <!-- Accessible description for decreasing the font size --> + <string name="mozac_feature_readerview_font_size_decrease_desc">Emomichĩ tai tuichakue</string> + + <!-- Accessible description for increasing the font size --> + <string name="mozac_feature_readerview_font_size_increase_desc">Embotuicha tai tuichakue</string> + <!-- Color option for the background --> + <string name="mozac_feature_readerview_dark">Ypytũ</string> + <!-- Accessible description for the color option --> + <string name="mozac_feature_readerview_dark_color_scheme_desc">Sa’y ypytũva raity</string> + <!-- Color option for the background --> + <string name="mozac_feature_readerview_sephia">Sepia</string> + <!-- Accessible description for the color option --> + <string name="mozac_feature_readerview_sepia_color_scheme_desc">Sa’y sepia raity</string> + <!-- Color option for the background --> + <string name="mozac_feature_readerview_light">Tesakã</string> + <!-- Accessible description for the color option --> + <string name="mozac_feature_readerview_light_color_scheme_desc">Sa’y hesakãva raity</string> +</resources> diff --git a/mobile/android/android-components/components/feature/readerview/src/main/res/values-gu-rIN/strings.xml b/mobile/android/android-components/components/feature/readerview/src/main/res/values-gu-rIN/strings.xml new file mode 100644 index 0000000000..a433b5dde6 --- /dev/null +++ b/mobile/android/android-components/components/feature/readerview/src/main/res/values-gu-rIN/strings.xml @@ -0,0 +1,29 @@ +<?xml version="1.0" encoding="utf-8"?> +<resources> + <!-- Name for one of the font options available to use --> + <string name="mozac_feature_readerview_sans_serif_font">સેન્સ-શેરીફ</string> + <!-- Accessible description for the font option --> + <string name="mozac_feature_readerview_sans_serif_font_desc">સેન્સ શેરીફ ફોન્ટ</string> + <!-- Name for one of the font options available to use --> + <string name="mozac_feature_readerview_serif_font">શેરીફ</string> + <!-- Accessible description for the font option --> + <string name="mozac_feature_readerview_serif_font_desc">શેરીફ ફોન્ટ</string> + + <!-- Accessible description for decreasing the font size --> + <string name="mozac_feature_readerview_font_size_decrease_desc">ફોન્ટના કદ ઘટાડો</string> + + <!-- Accessible description for increasing the font size --> + <string name="mozac_feature_readerview_font_size_increase_desc">ફોન્ટના કદ વધારો</string> + <!-- Color option for the background --> + <string name="mozac_feature_readerview_dark">ઘટ્ટ</string> + <!-- Accessible description for the color option --> + <string name="mozac_feature_readerview_dark_color_scheme_desc">ઘાટા રંગ યોજના</string> + <!-- Color option for the background --> + <string name="mozac_feature_readerview_sephia">સેપિયા</string> + <!-- Accessible description for the color option --> + <string name="mozac_feature_readerview_sepia_color_scheme_desc">સેપિયા રંગ યોજના</string> + <!-- Color option for the background --> + <string name="mozac_feature_readerview_light">આછો</string> + <!-- Accessible description for the color option --> + <string name="mozac_feature_readerview_light_color_scheme_desc">આછા રંગ યોજના</string> +</resources> diff --git a/mobile/android/android-components/components/feature/readerview/src/main/res/values-hi-rIN/strings.xml b/mobile/android/android-components/components/feature/readerview/src/main/res/values-hi-rIN/strings.xml new file mode 100644 index 0000000000..a9e1ac2996 --- /dev/null +++ b/mobile/android/android-components/components/feature/readerview/src/main/res/values-hi-rIN/strings.xml @@ -0,0 +1,29 @@ +<?xml version="1.0" encoding="utf-8"?> +<resources> + <!-- Name for one of the font options available to use --> + <string name="mozac_feature_readerview_sans_serif_font">Sans serif</string> + <!-- Accessible description for the font option --> + <string name="mozac_feature_readerview_sans_serif_font_desc">Sans serif फ़ॉन्ट</string> + <!-- Name for one of the font options available to use --> + <string name="mozac_feature_readerview_serif_font">Serif</string> + <!-- Accessible description for the font option --> + <string name="mozac_feature_readerview_serif_font_desc">Serif फ़ॉन्ट</string> + + <!-- Accessible description for decreasing the font size --> + <string name="mozac_feature_readerview_font_size_decrease_desc">फ़ॉन्ट आकार कम करें</string> + + <!-- Accessible description for increasing the font size --> + <string name="mozac_feature_readerview_font_size_increase_desc">फ़ॉन्ट आकार बढ़ाएं</string> + <!-- Color option for the background --> + <string name="mozac_feature_readerview_dark">डार्क</string> + <!-- Accessible description for the color option --> + <string name="mozac_feature_readerview_dark_color_scheme_desc">डार्क कलर स्कीम</string> + <!-- Color option for the background --> + <string name="mozac_feature_readerview_sephia">सेपिया</string> + <!-- Accessible description for the color option --> + <string name="mozac_feature_readerview_sepia_color_scheme_desc">सेपिया कलर स्कीम</string> + <!-- Color option for the background --> + <string name="mozac_feature_readerview_light">लाइट</string> + <!-- Accessible description for the color option --> + <string name="mozac_feature_readerview_light_color_scheme_desc">लाइट कलर स्कीम</string> +</resources> diff --git a/mobile/android/android-components/components/feature/readerview/src/main/res/values-hil/strings.xml b/mobile/android/android-components/components/feature/readerview/src/main/res/values-hil/strings.xml new file mode 100644 index 0000000000..14eebfce9c --- /dev/null +++ b/mobile/android/android-components/components/feature/readerview/src/main/res/values-hil/strings.xml @@ -0,0 +1,14 @@ +<?xml version="1.0" encoding="utf-8"?> +<resources> + <!-- Name for one of the font options available to use --> + <string name="mozac_feature_readerview_sans_serif_font">Sans serif</string> + <!-- Accessible description for the font option --> + <string name="mozac_feature_readerview_sans_serif_font_desc">Sans Serif font</string> + <!-- Name for one of the font options available to use --> + <string name="mozac_feature_readerview_serif_font">Serif</string> + <!-- Accessible description for the font option --> + <string name="mozac_feature_readerview_serif_font_desc">Serif font</string> + + <!-- Color option for the background --> + <string name="mozac_feature_readerview_sephia">Sepia</string> + </resources> diff --git a/mobile/android/android-components/components/feature/readerview/src/main/res/values-hr/strings.xml b/mobile/android/android-components/components/feature/readerview/src/main/res/values-hr/strings.xml new file mode 100644 index 0000000000..6d21274a81 --- /dev/null +++ b/mobile/android/android-components/components/feature/readerview/src/main/res/values-hr/strings.xml @@ -0,0 +1,29 @@ +<?xml version="1.0" encoding="utf-8"?> +<resources> + <!-- Name for one of the font options available to use --> + <string name="mozac_feature_readerview_sans_serif_font">Bezserifni</string> + <!-- Accessible description for the font option --> + <string name="mozac_feature_readerview_sans_serif_font_desc">Bezserifni font</string> + <!-- Name for one of the font options available to use --> + <string name="mozac_feature_readerview_serif_font">Serifni</string> + <!-- Accessible description for the font option --> + <string name="mozac_feature_readerview_serif_font_desc">Serifni font</string> + + <!-- Accessible description for decreasing the font size --> + <string name="mozac_feature_readerview_font_size_decrease_desc">Smanjenje veličine fonta</string> + + <!-- Accessible description for increasing the font size --> + <string name="mozac_feature_readerview_font_size_increase_desc">Povećanje veličine fonta</string> + <!-- Color option for the background --> + <string name="mozac_feature_readerview_dark">Tamna</string> + <!-- Accessible description for the color option --> + <string name="mozac_feature_readerview_dark_color_scheme_desc">Tamna shema boja</string> + <!-- Color option for the background --> + <string name="mozac_feature_readerview_sephia">Smeđa</string> + <!-- Accessible description for the color option --> + <string name="mozac_feature_readerview_sepia_color_scheme_desc">Smeđa shema boja</string> + <!-- Color option for the background --> + <string name="mozac_feature_readerview_light">Svijetla</string> + <!-- Accessible description for the color option --> + <string name="mozac_feature_readerview_light_color_scheme_desc">Svijetla shema boja</string> +</resources> diff --git a/mobile/android/android-components/components/feature/readerview/src/main/res/values-hsb/strings.xml b/mobile/android/android-components/components/feature/readerview/src/main/res/values-hsb/strings.xml new file mode 100644 index 0000000000..34b0fb4c20 --- /dev/null +++ b/mobile/android/android-components/components/feature/readerview/src/main/res/values-hsb/strings.xml @@ -0,0 +1,30 @@ +<?xml version="1.0" encoding="utf-8"?> +<resources> + <!-- Name for one of the font options available to use --> + <string name="mozac_feature_readerview_sans_serif_font">Sans serif</string> + <!-- Accessible description for the font option --> + <string name="mozac_feature_readerview_sans_serif_font_desc">Pismo sans serif</string> + <!-- Name for one of the font options available to use --> + <string name="mozac_feature_readerview_serif_font">Serif</string> + + <!-- Accessible description for the font option --> + <string name="mozac_feature_readerview_serif_font_desc">Serifowe pismo</string> + + <!-- Accessible description for decreasing the font size --> + <string name="mozac_feature_readerview_font_size_decrease_desc">Wulkosć pisma pomjeńšić</string> + + <!-- Accessible description for increasing the font size --> + <string name="mozac_feature_readerview_font_size_increase_desc">Wulkosć pisma powjetšić</string> + <!-- Color option for the background --> + <string name="mozac_feature_readerview_dark">Ćmowy</string> + <!-- Accessible description for the color option --> + <string name="mozac_feature_readerview_dark_color_scheme_desc">Ćmowa barbowa šema</string> + <!-- Color option for the background --> + <string name="mozac_feature_readerview_sephia">Sepija</string> + <!-- Accessible description for the color option --> + <string name="mozac_feature_readerview_sepia_color_scheme_desc">Barbowa šema Sepija</string> + <!-- Color option for the background --> + <string name="mozac_feature_readerview_light">Swětły</string> + <!-- Accessible description for the color option --> + <string name="mozac_feature_readerview_light_color_scheme_desc">Swětła barbowa šema</string> +</resources> diff --git a/mobile/android/android-components/components/feature/readerview/src/main/res/values-hu/strings.xml b/mobile/android/android-components/components/feature/readerview/src/main/res/values-hu/strings.xml new file mode 100644 index 0000000000..8b1b4517da --- /dev/null +++ b/mobile/android/android-components/components/feature/readerview/src/main/res/values-hu/strings.xml @@ -0,0 +1,29 @@ +<?xml version="1.0" encoding="utf-8"?> +<resources> + <!-- Name for one of the font options available to use --> + <string name="mozac_feature_readerview_sans_serif_font">Talpatlan</string> + <!-- Accessible description for the font option --> + <string name="mozac_feature_readerview_sans_serif_font_desc">Talpatlan betűkészlet</string> + <!-- Name for one of the font options available to use --> + <string name="mozac_feature_readerview_serif_font">Talpas</string> + <!-- Accessible description for the font option --> + <string name="mozac_feature_readerview_serif_font_desc">Talpas betűtípus</string> + + <!-- Accessible description for decreasing the font size --> + <string name="mozac_feature_readerview_font_size_decrease_desc">Betűméret csökkentése</string> + + <!-- Accessible description for increasing the font size --> + <string name="mozac_feature_readerview_font_size_increase_desc">Betűméret csökkentése</string> + <!-- Color option for the background --> + <string name="mozac_feature_readerview_dark">Sötét</string> + <!-- Accessible description for the color option --> + <string name="mozac_feature_readerview_dark_color_scheme_desc">Sötét színösszeállítás</string> + <!-- Color option for the background --> + <string name="mozac_feature_readerview_sephia">Szépia</string> + <!-- Accessible description for the color option --> + <string name="mozac_feature_readerview_sepia_color_scheme_desc">Szépia színösszeállítás</string> + <!-- Color option for the background --> + <string name="mozac_feature_readerview_light">Világos</string> + <!-- Accessible description for the color option --> + <string name="mozac_feature_readerview_light_color_scheme_desc">Világos színösszeállítás</string> +</resources> diff --git a/mobile/android/android-components/components/feature/readerview/src/main/res/values-hy-rAM/strings.xml b/mobile/android/android-components/components/feature/readerview/src/main/res/values-hy-rAM/strings.xml new file mode 100644 index 0000000000..c6390958f8 --- /dev/null +++ b/mobile/android/android-components/components/feature/readerview/src/main/res/values-hy-rAM/strings.xml @@ -0,0 +1,30 @@ +<?xml version="1.0" encoding="utf-8"?> +<resources> + <!-- Name for one of the font options available to use --> + <string name="mozac_feature_readerview_sans_serif_font">Sans serif</string> + + <!-- Accessible description for the font option --> + <string name="mozac_feature_readerview_sans_serif_font_desc">Sans Serif տառատեսակ</string> + <!-- Name for one of the font options available to use --> + <string name="mozac_feature_readerview_serif_font">Serif</string> + <!-- Accessible description for the font option --> + <string name="mozac_feature_readerview_serif_font_desc">Serif տառատեսակ</string> + + <!-- Accessible description for decreasing the font size --> + <string name="mozac_feature_readerview_font_size_decrease_desc">Տառատեսակի չափի փոքրացում</string> + + <!-- Accessible description for increasing the font size --> + <string name="mozac_feature_readerview_font_size_increase_desc">Տառատեսակի չափի մեծացում</string> + <!-- Color option for the background --> + <string name="mozac_feature_readerview_dark">Մուգ</string> + <!-- Accessible description for the color option --> + <string name="mozac_feature_readerview_dark_color_scheme_desc">Մուգ գույնի ուրվակազմ</string> + <!-- Color option for the background --> + <string name="mozac_feature_readerview_sephia">Դարչնագույն</string> + <!-- Accessible description for the color option --> + <string name="mozac_feature_readerview_sepia_color_scheme_desc">Դարչնագույն գույնի ուրվակազմ</string> + <!-- Color option for the background --> + <string name="mozac_feature_readerview_light">Լուսավոր</string> + <!-- Accessible description for the color option --> + <string name="mozac_feature_readerview_light_color_scheme_desc">Բաց գույնի ուրվակազմ</string> +</resources> diff --git a/mobile/android/android-components/components/feature/readerview/src/main/res/values-ia/strings.xml b/mobile/android/android-components/components/feature/readerview/src/main/res/values-ia/strings.xml new file mode 100644 index 0000000000..b2e9faa119 --- /dev/null +++ b/mobile/android/android-components/components/feature/readerview/src/main/res/values-ia/strings.xml @@ -0,0 +1,30 @@ +<?xml version="1.0" encoding="utf-8"?> +<resources> + <!-- Name for one of the font options available to use --> + <string name="mozac_feature_readerview_sans_serif_font">Sans serif</string> + <!-- Accessible description for the font option --> + <string name="mozac_feature_readerview_sans_serif_font_desc">Typo de character Sans serif</string> + <!-- Name for one of the font options available to use --> + <string name="mozac_feature_readerview_serif_font">Serif</string> + + <!-- Accessible description for the font option --> + <string name="mozac_feature_readerview_serif_font_desc">Typo de character Serif</string> + + <!-- Accessible description for decreasing the font size --> + <string name="mozac_feature_readerview_font_size_decrease_desc">Reducer le dimension del characteres</string> + + <!-- Accessible description for increasing the font size --> + <string name="mozac_feature_readerview_font_size_increase_desc">Accrescer le dimension del characteres</string> + <!-- Color option for the background --> + <string name="mozac_feature_readerview_dark">Obscur</string> + <!-- Accessible description for the color option --> + <string name="mozac_feature_readerview_dark_color_scheme_desc">Schema de color obscur</string> + <!-- Color option for the background --> + <string name="mozac_feature_readerview_sephia">Sepia</string> + <!-- Accessible description for the color option --> + <string name="mozac_feature_readerview_sepia_color_scheme_desc">Schema de color sepia</string> + <!-- Color option for the background --> + <string name="mozac_feature_readerview_light">Clar</string> + <!-- Accessible description for the color option --> + <string name="mozac_feature_readerview_light_color_scheme_desc">Schema de color clar</string> +</resources> diff --git a/mobile/android/android-components/components/feature/readerview/src/main/res/values-in/strings.xml b/mobile/android/android-components/components/feature/readerview/src/main/res/values-in/strings.xml new file mode 100644 index 0000000000..673c931872 --- /dev/null +++ b/mobile/android/android-components/components/feature/readerview/src/main/res/values-in/strings.xml @@ -0,0 +1,30 @@ +<?xml version="1.0" encoding="utf-8"?> +<resources> + <!-- Name for one of the font options available to use --> + <string name="mozac_feature_readerview_sans_serif_font">Sans serif</string> + <!-- Accessible description for the font option --> + <string name="mozac_feature_readerview_sans_serif_font_desc">Fon Sans Serif</string> + <!-- Name for one of the font options available to use --> + <string name="mozac_feature_readerview_serif_font">Serif</string> + + <!-- Accessible description for the font option --> + <string name="mozac_feature_readerview_serif_font_desc">Fon Serif</string> + + <!-- Accessible description for decreasing the font size --> + <string name="mozac_feature_readerview_font_size_decrease_desc">Kurangi ukuran fon</string> + + <!-- Accessible description for increasing the font size --> + <string name="mozac_feature_readerview_font_size_increase_desc">Tingkatkan ukuran fon</string> + <!-- Color option for the background --> + <string name="mozac_feature_readerview_dark">Gelap</string> + <!-- Accessible description for the color option --> + <string name="mozac_feature_readerview_dark_color_scheme_desc">Skema warna gelap</string> + <!-- Color option for the background --> + <string name="mozac_feature_readerview_sephia">Sepia</string> + <!-- Accessible description for the color option --> + <string name="mozac_feature_readerview_sepia_color_scheme_desc">Skema warna sepia</string> + <!-- Color option for the background --> + <string name="mozac_feature_readerview_light">Terang</string> + <!-- Accessible description for the color option --> + <string name="mozac_feature_readerview_light_color_scheme_desc">Skema warna terang</string> +</resources> diff --git a/mobile/android/android-components/components/feature/readerview/src/main/res/values-is/strings.xml b/mobile/android/android-components/components/feature/readerview/src/main/res/values-is/strings.xml new file mode 100644 index 0000000000..ed2aadcb00 --- /dev/null +++ b/mobile/android/android-components/components/feature/readerview/src/main/res/values-is/strings.xml @@ -0,0 +1,29 @@ +<?xml version="1.0" encoding="utf-8"?> +<resources> + <!-- Name for one of the font options available to use --> + <string name="mozac_feature_readerview_sans_serif_font">Sans-serif</string> + <!-- Accessible description for the font option --> + <string name="mozac_feature_readerview_sans_serif_font_desc">Sans Serif leturgerð</string> + <!-- Name for one of the font options available to use --> + <string name="mozac_feature_readerview_serif_font">Serif</string> + <!-- Accessible description for the font option --> + <string name="mozac_feature_readerview_serif_font_desc">Serif leturgerð</string> + + <!-- Accessible description for decreasing the font size --> + <string name="mozac_feature_readerview_font_size_decrease_desc">Minnka leturstærð</string> + + <!-- Accessible description for increasing the font size --> + <string name="mozac_feature_readerview_font_size_increase_desc">Auka leturstærð</string> + <!-- Color option for the background --> + <string name="mozac_feature_readerview_dark">Dökkt</string> + <!-- Accessible description for the color option --> + <string name="mozac_feature_readerview_dark_color_scheme_desc">Dökkt litastef</string> + <!-- Color option for the background --> + <string name="mozac_feature_readerview_sephia">Sepía</string> + <!-- Accessible description for the color option --> + <string name="mozac_feature_readerview_sepia_color_scheme_desc">Sepía litastef</string> + <!-- Color option for the background --> + <string name="mozac_feature_readerview_light">Ljóst</string> + <!-- Accessible description for the color option --> + <string name="mozac_feature_readerview_light_color_scheme_desc">Ljóst litastef</string> +</resources> diff --git a/mobile/android/android-components/components/feature/readerview/src/main/res/values-it/strings.xml b/mobile/android/android-components/components/feature/readerview/src/main/res/values-it/strings.xml new file mode 100644 index 0000000000..22fea45d24 --- /dev/null +++ b/mobile/android/android-components/components/feature/readerview/src/main/res/values-it/strings.xml @@ -0,0 +1,29 @@ +<?xml version="1.0" encoding="utf-8"?> +<resources> + <!-- Name for one of the font options available to use --> + <string name="mozac_feature_readerview_sans_serif_font">Senza grazie</string> + <!-- Accessible description for the font option --> + <string name="mozac_feature_readerview_sans_serif_font_desc">Carattere senza grazie</string> + <!-- Name for one of the font options available to use --> + <string name="mozac_feature_readerview_serif_font">Con grazie</string> + <!-- Accessible description for the font option --> + <string name="mozac_feature_readerview_serif_font_desc">Carattere con grazie</string> + + <!-- Accessible description for decreasing the font size --> + <string name="mozac_feature_readerview_font_size_decrease_desc">Riduci dimensione dei caratteri</string> + + <!-- Accessible description for increasing the font size --> + <string name="mozac_feature_readerview_font_size_increase_desc">Aumenta dimensione dei caratteri</string> + <!-- Color option for the background --> + <string name="mozac_feature_readerview_dark">Scuro</string> + <!-- Accessible description for the color option --> + <string name="mozac_feature_readerview_dark_color_scheme_desc">Combinazione di colori scura</string> + <!-- Color option for the background --> + <string name="mozac_feature_readerview_sephia">Seppia</string> + <!-- Accessible description for the color option --> + <string name="mozac_feature_readerview_sepia_color_scheme_desc">Combinazione di colori seppia</string> + <!-- Color option for the background --> + <string name="mozac_feature_readerview_light">Chiaro</string> + <!-- Accessible description for the color option --> + <string name="mozac_feature_readerview_light_color_scheme_desc">Combinazione di colori chiara</string> +</resources> diff --git a/mobile/android/android-components/components/feature/readerview/src/main/res/values-iw/strings.xml b/mobile/android/android-components/components/feature/readerview/src/main/res/values-iw/strings.xml new file mode 100644 index 0000000000..2b7451b69d --- /dev/null +++ b/mobile/android/android-components/components/feature/readerview/src/main/res/values-iw/strings.xml @@ -0,0 +1,30 @@ +<?xml version="1.0" encoding="utf-8"?> +<resources> + + <!-- Name for one of the font options available to use --> + <string name="mozac_feature_readerview_sans_serif_font">ללא עיטור</string> + <!-- Accessible description for the font option --> + <string name="mozac_feature_readerview_sans_serif_font_desc">גופן ללא עיטורים</string> + <!-- Name for one of the font options available to use --> + <string name="mozac_feature_readerview_serif_font">מעוטר</string> + <!-- Accessible description for the font option --> + <string name="mozac_feature_readerview_serif_font_desc">גופן מעוטר</string> + + <!-- Accessible description for decreasing the font size --> + <string name="mozac_feature_readerview_font_size_decrease_desc">הקטנת גודל גופן</string> + + <!-- Accessible description for increasing the font size --> + <string name="mozac_feature_readerview_font_size_increase_desc">הגדלת גודל גופן</string> + <!-- Color option for the background --> + <string name="mozac_feature_readerview_dark">כהה</string> + <!-- Accessible description for the color option --> + <string name="mozac_feature_readerview_dark_color_scheme_desc">ערכת צבעים כהה</string> + <!-- Color option for the background --> + <string name="mozac_feature_readerview_sephia">חום כהה</string> + <!-- Accessible description for the color option --> + <string name="mozac_feature_readerview_sepia_color_scheme_desc">ערכת צבעים חמה</string> + <!-- Color option for the background --> + <string name="mozac_feature_readerview_light">בהיר</string> + <!-- Accessible description for the color option --> + <string name="mozac_feature_readerview_light_color_scheme_desc">ערכת צבעים בהירה</string> +</resources> diff --git a/mobile/android/android-components/components/feature/readerview/src/main/res/values-ja/strings.xml b/mobile/android/android-components/components/feature/readerview/src/main/res/values-ja/strings.xml new file mode 100644 index 0000000000..6faa51a99e --- /dev/null +++ b/mobile/android/android-components/components/feature/readerview/src/main/res/values-ja/strings.xml @@ -0,0 +1,29 @@ +<?xml version="1.0" encoding="utf-8"?> +<resources> + <!-- Name for one of the font options available to use --> + <string name="mozac_feature_readerview_sans_serif_font">ゴシック体 (Sans-serif)</string> + <!-- Accessible description for the font option --> + <string name="mozac_feature_readerview_sans_serif_font_desc">ゴシック体のフォント</string> + <!-- Name for one of the font options available to use --> + <string name="mozac_feature_readerview_serif_font">明朝体 (Serif)</string> + <!-- Accessible description for the font option --> + <string name="mozac_feature_readerview_serif_font_desc">明朝体のフォント</string> + + <!-- Accessible description for decreasing the font size --> + <string name="mozac_feature_readerview_font_size_decrease_desc">フォントサイズを小さく</string> + + <!-- Accessible description for increasing the font size --> + <string name="mozac_feature_readerview_font_size_increase_desc">フォントサイズを大きく</string> + <!-- Color option for the background --> + <string name="mozac_feature_readerview_dark">Dark</string> + <!-- Accessible description for the color option --> + <string name="mozac_feature_readerview_dark_color_scheme_desc">暗いカラースキーム</string> + <!-- Color option for the background --> + <string name="mozac_feature_readerview_sephia">Sepia</string> + <!-- Accessible description for the color option --> + <string name="mozac_feature_readerview_sepia_color_scheme_desc">セピアのカラースキーム</string> + <!-- Color option for the background --> + <string name="mozac_feature_readerview_light">Light</string> + <!-- Accessible description for the color option --> + <string name="mozac_feature_readerview_light_color_scheme_desc">明るいカラースキーム</string> +</resources> diff --git a/mobile/android/android-components/components/feature/readerview/src/main/res/values-ka/strings.xml b/mobile/android/android-components/components/feature/readerview/src/main/res/values-ka/strings.xml new file mode 100644 index 0000000000..40d031d4c7 --- /dev/null +++ b/mobile/android/android-components/components/feature/readerview/src/main/res/values-ka/strings.xml @@ -0,0 +1,29 @@ +<?xml version="1.0" encoding="utf-8"?> +<resources> + <!-- Name for one of the font options available to use --> + <string name="mozac_feature_readerview_sans_serif_font">უნაჭდევო</string> + <!-- Accessible description for the font option --> + <string name="mozac_feature_readerview_sans_serif_font_desc">უნაჭდევო შრიფტი</string> + <!-- Name for one of the font options available to use --> + <string name="mozac_feature_readerview_serif_font">ნაჭდევებით</string> + <!-- Accessible description for the font option --> + <string name="mozac_feature_readerview_serif_font_desc">ნაჭდევებიანი შრიფტი</string> + + <!-- Accessible description for decreasing the font size --> + <string name="mozac_feature_readerview_font_size_decrease_desc">შრიფტის ზომის შემცირება</string> + + <!-- Accessible description for increasing the font size --> + <string name="mozac_feature_readerview_font_size_increase_desc">შრიფტის ზომის მომატება</string> + <!-- Color option for the background --> + <string name="mozac_feature_readerview_dark">მუქი</string> + <!-- Accessible description for the color option --> + <string name="mozac_feature_readerview_dark_color_scheme_desc">მუქი ფერები</string> + <!-- Color option for the background --> + <string name="mozac_feature_readerview_sephia">სეპია</string> + <!-- Accessible description for the color option --> + <string name="mozac_feature_readerview_sepia_color_scheme_desc">მოყავისფრო ფერები</string> + <!-- Color option for the background --> + <string name="mozac_feature_readerview_light">ნათელი</string> + <!-- Accessible description for the color option --> + <string name="mozac_feature_readerview_light_color_scheme_desc">ნათელი ფერები</string> +</resources> diff --git a/mobile/android/android-components/components/feature/readerview/src/main/res/values-kaa/strings.xml b/mobile/android/android-components/components/feature/readerview/src/main/res/values-kaa/strings.xml new file mode 100644 index 0000000000..9a90792a6d --- /dev/null +++ b/mobile/android/android-components/components/feature/readerview/src/main/res/values-kaa/strings.xml @@ -0,0 +1,30 @@ +<?xml version="1.0" encoding="utf-8"?> +<resources> + <!-- Name for one of the font options available to use --> + <string name="mozac_feature_readerview_sans_serif_font">Sans serif</string> + <!-- Accessible description for the font option --> + <string name="mozac_feature_readerview_sans_serif_font_desc">Sans serif shrifti</string> + <!-- Name for one of the font options available to use --> + <string name="mozac_feature_readerview_serif_font">Serif</string> + + <!-- Accessible description for the font option --> + <string name="mozac_feature_readerview_serif_font_desc">Serif shrifti</string> + + <!-- Accessible description for decreasing the font size --> + <string name="mozac_feature_readerview_font_size_decrease_desc">Shrift ólshemin kishireytiw</string> + + <!-- Accessible description for increasing the font size --> + <string name="mozac_feature_readerview_font_size_increase_desc">Shrift ólshemin úlkeytiw</string> + <!-- Color option for the background --> + <string name="mozac_feature_readerview_dark">Qarańǵı</string> + <!-- Accessible description for the color option --> + <string name="mozac_feature_readerview_dark_color_scheme_desc">Qara reń gamması</string> + <!-- Color option for the background --> + <string name="mozac_feature_readerview_sephia">Sepia</string> + <!-- Accessible description for the color option --> + <string name="mozac_feature_readerview_sepia_color_scheme_desc">Sepiya reń gamması</string> + <!-- Color option for the background --> + <string name="mozac_feature_readerview_light">Jaqtı</string> + <!-- Accessible description for the color option --> + <string name="mozac_feature_readerview_light_color_scheme_desc">Ashıq reń gamması</string> +</resources> diff --git a/mobile/android/android-components/components/feature/readerview/src/main/res/values-kab/strings.xml b/mobile/android/android-components/components/feature/readerview/src/main/res/values-kab/strings.xml new file mode 100644 index 0000000000..edbcc1d2dd --- /dev/null +++ b/mobile/android/android-components/components/feature/readerview/src/main/res/values-kab/strings.xml @@ -0,0 +1,30 @@ +<?xml version="1.0" encoding="utf-8"?> +<resources> + + <!-- Name for one of the font options available to use --> + <string name="mozac_feature_readerview_sans_serif_font">Sans serif</string> + <!-- Accessible description for the font option --> + <string name="mozac_feature_readerview_sans_serif_font_desc">Tasefsit Sans Serif</string> + <!-- Name for one of the font options available to use --> + <string name="mozac_feature_readerview_serif_font">Serif</string> + <!-- Accessible description for the font option --> + <string name="mozac_feature_readerview_serif_font_desc">Tasefsit Serif</string> + + <!-- Accessible description for decreasing the font size --> + <string name="mozac_feature_readerview_font_size_decrease_desc">Asenɣes n teɣzi n tsefsit</string> + + <!-- Accessible description for increasing the font size --> + <string name="mozac_feature_readerview_font_size_increase_desc">Timerna n teɣzi n tsefsit</string> + <!-- Color option for the background --> + <string name="mozac_feature_readerview_dark">Aberkan</string> + <!-- Accessible description for the color option --> + <string name="mozac_feature_readerview_dark_color_scheme_desc">Initen ubriken</string> + <!-- Color option for the background --> + <string name="mozac_feature_readerview_sephia">Sepia</string> + <!-- Accessible description for the color option --> + <string name="mozac_feature_readerview_sepia_color_scheme_desc">Initen Sepia</string> + <!-- Color option for the background --> + <string name="mozac_feature_readerview_light">Aceɛlal</string> + <!-- Accessible description for the color option --> + <string name="mozac_feature_readerview_light_color_scheme_desc">Initen iceɛlalen</string> +</resources> diff --git a/mobile/android/android-components/components/feature/readerview/src/main/res/values-kk/strings.xml b/mobile/android/android-components/components/feature/readerview/src/main/res/values-kk/strings.xml new file mode 100644 index 0000000000..64e057d5ff --- /dev/null +++ b/mobile/android/android-components/components/feature/readerview/src/main/res/values-kk/strings.xml @@ -0,0 +1,29 @@ +<?xml version="1.0" encoding="utf-8"?> +<resources> + <!-- Name for one of the font options available to use --> + <string name="mozac_feature_readerview_sans_serif_font">Sans serif</string> + <!-- Accessible description for the font option --> + <string name="mozac_feature_readerview_sans_serif_font_desc">Sans Serif қарібі</string> + <!-- Name for one of the font options available to use --> + <string name="mozac_feature_readerview_serif_font">Serif</string> + <!-- Accessible description for the font option --> + <string name="mozac_feature_readerview_serif_font_desc">Serif қарібі</string> + + <!-- Accessible description for decreasing the font size --> + <string name="mozac_feature_readerview_font_size_decrease_desc">Қаріп өлшемін кішірейту</string> + + <!-- Accessible description for increasing the font size --> + <string name="mozac_feature_readerview_font_size_increase_desc">Қаріп өлшемін үлкейту</string> + <!-- Color option for the background --> + <string name="mozac_feature_readerview_dark">Күңгірт</string> + <!-- Accessible description for the color option --> + <string name="mozac_feature_readerview_dark_color_scheme_desc">Күңгірт түстер схемасы</string> + <!-- Color option for the background --> + <string name="mozac_feature_readerview_sephia">Сепия</string> + <!-- Accessible description for the color option --> + <string name="mozac_feature_readerview_sepia_color_scheme_desc">Сепия түстер схемасы</string> + <!-- Color option for the background --> + <string name="mozac_feature_readerview_light">Ашық түсті</string> + <!-- Accessible description for the color option --> + <string name="mozac_feature_readerview_light_color_scheme_desc">Ашық түсті түстер схемасы</string> +</resources> diff --git a/mobile/android/android-components/components/feature/readerview/src/main/res/values-kmr/strings.xml b/mobile/android/android-components/components/feature/readerview/src/main/res/values-kmr/strings.xml new file mode 100644 index 0000000000..06f53cd051 --- /dev/null +++ b/mobile/android/android-components/components/feature/readerview/src/main/res/values-kmr/strings.xml @@ -0,0 +1,29 @@ +<?xml version="1.0" encoding="utf-8"?> +<resources> + <!-- Name for one of the font options available to use --> + <string name="mozac_feature_readerview_sans_serif_font">Sans serif</string> + <!-- Accessible description for the font option --> + <string name="mozac_feature_readerview_sans_serif_font_desc">Fonta Sans Serifê</string> + <!-- Name for one of the font options available to use --> + <string name="mozac_feature_readerview_serif_font">Serif</string> + <!-- Accessible description for the font option --> + <string name="mozac_feature_readerview_serif_font_desc">Fonta Serifê</string> + + <!-- Accessible description for decreasing the font size --> + <string name="mozac_feature_readerview_font_size_decrease_desc">Biçûkkirina mezinahiya fontê</string> + + <!-- Accessible description for increasing the font size --> + <string name="mozac_feature_readerview_font_size_increase_desc">Mezinkirina mezinahiya fontê</string> + <!-- Color option for the background --> + <string name="mozac_feature_readerview_dark">Tarî</string> + <!-- Accessible description for the color option --> + <string name="mozac_feature_readerview_dark_color_scheme_desc">Şemaya rengê tarî</string> + <!-- Color option for the background --> + <string name="mozac_feature_readerview_sephia">Sepya</string> + <!-- Accessible description for the color option --> + <string name="mozac_feature_readerview_sepia_color_scheme_desc">Şemaya rengê sepyayî</string> + <!-- Color option for the background --> + <string name="mozac_feature_readerview_light">Ronî</string> + <!-- Accessible description for the color option --> + <string name="mozac_feature_readerview_light_color_scheme_desc">Şemaya rengê ronî</string> +</resources> diff --git a/mobile/android/android-components/components/feature/readerview/src/main/res/values-kn/strings.xml b/mobile/android/android-components/components/feature/readerview/src/main/res/values-kn/strings.xml new file mode 100644 index 0000000000..5ca8fe9639 --- /dev/null +++ b/mobile/android/android-components/components/feature/readerview/src/main/res/values-kn/strings.xml @@ -0,0 +1,30 @@ +<?xml version="1.0" encoding="utf-8"?> +<resources> + <!-- Name for one of the font options available to use --> + <string name="mozac_feature_readerview_sans_serif_font">ಸಾನ್ಸ್ ಸೆರಿಫ್</string> + <!-- Accessible description for the font option --> + <string name="mozac_feature_readerview_sans_serif_font_desc">ಸಾನ್ಸ್ ಸೆರಿಫ್ ಫಾಂಟ್</string> + <!-- Name for one of the font options available to use --> + <string name="mozac_feature_readerview_serif_font">ಸೆರಿಫ್</string> + + <!-- Accessible description for the font option --> + <string name="mozac_feature_readerview_serif_font_desc">ಸೆರಿಫ್ ಫಾಂಟ್</string> + + <!-- Accessible description for decreasing the font size --> + <string name="mozac_feature_readerview_font_size_decrease_desc">ಫಾಂಟ್ ಗಾತ್ರ ಕಡಿಮೆಯಾಗುತ್ತದೆ</string> + + <!-- Accessible description for increasing the font size --> + <string name="mozac_feature_readerview_font_size_increase_desc">ಫಾಂಟ್ ಗಾತ್ರ ಹೆಚ್ಚಳ</string> + <!-- Color option for the background --> + <string name="mozac_feature_readerview_dark">ಗಾಢ</string> + <!-- Accessible description for the color option --> + <string name="mozac_feature_readerview_dark_color_scheme_desc">ಗಾಢ ಬಣ್ಣದ ಯೋಜನೆ</string> + <!-- Color option for the background --> + <string name="mozac_feature_readerview_sephia">ಸೆಪಿಯ</string> + <!-- Accessible description for the color option --> + <string name="mozac_feature_readerview_sepia_color_scheme_desc">ಸೆಪಿಯಾ ಬಣ್ಣದ ಯೋಜನೆ</string> + <!-- Color option for the background --> + <string name="mozac_feature_readerview_light">ತಿಳಿ</string> + <!-- Accessible description for the color option --> + <string name="mozac_feature_readerview_light_color_scheme_desc">ತಿಳಿ ಬಣ್ಣದ ಯೋಜನೆ</string> +</resources> diff --git a/mobile/android/android-components/components/feature/readerview/src/main/res/values-ko/strings.xml b/mobile/android/android-components/components/feature/readerview/src/main/res/values-ko/strings.xml new file mode 100644 index 0000000000..3189ebf4c3 --- /dev/null +++ b/mobile/android/android-components/components/feature/readerview/src/main/res/values-ko/strings.xml @@ -0,0 +1,29 @@ +<?xml version="1.0" encoding="utf-8"?> +<resources> + <!-- Name for one of the font options available to use --> + <string name="mozac_feature_readerview_sans_serif_font">산세리프</string> + <!-- Accessible description for the font option --> + <string name="mozac_feature_readerview_sans_serif_font_desc">산세리프 글꼴</string> + <!-- Name for one of the font options available to use --> + <string name="mozac_feature_readerview_serif_font">세리프</string> + <!-- Accessible description for the font option --> + <string name="mozac_feature_readerview_serif_font_desc">세리프 글꼴</string> + + <!-- Accessible description for decreasing the font size --> + <string name="mozac_feature_readerview_font_size_decrease_desc">글꼴 크기 작게</string> + + <!-- Accessible description for increasing the font size --> + <string name="mozac_feature_readerview_font_size_increase_desc">글꼴 크기 크게</string> + <!-- Color option for the background --> + <string name="mozac_feature_readerview_dark">어둡게</string> + <!-- Accessible description for the color option --> + <string name="mozac_feature_readerview_dark_color_scheme_desc">어두운 색 구성표</string> + <!-- Color option for the background --> + <string name="mozac_feature_readerview_sephia">세피아</string> + <!-- Accessible description for the color option --> + <string name="mozac_feature_readerview_sepia_color_scheme_desc">세피아 색 구성표</string> + <!-- Color option for the background --> + <string name="mozac_feature_readerview_light">밝게</string> + <!-- Accessible description for the color option --> + <string name="mozac_feature_readerview_light_color_scheme_desc">밝은 색 구성표</string> +</resources> diff --git a/mobile/android/android-components/components/feature/readerview/src/main/res/values-lij/strings.xml b/mobile/android/android-components/components/feature/readerview/src/main/res/values-lij/strings.xml new file mode 100644 index 0000000000..f2e20c0a7f --- /dev/null +++ b/mobile/android/android-components/components/feature/readerview/src/main/res/values-lij/strings.xml @@ -0,0 +1,29 @@ +<?xml version="1.0" encoding="utf-8"?> +<resources> + <!-- Name for one of the font options available to use --> + <string name="mozac_feature_readerview_sans_serif_font">Sans serif</string> + <!-- Accessible description for the font option --> + <string name="mozac_feature_readerview_sans_serif_font_desc">Sans Serif font</string> + <!-- Name for one of the font options available to use --> + <string name="mozac_feature_readerview_serif_font">Serif</string> + <!-- Accessible description for the font option --> + <string name="mozac_feature_readerview_serif_font_desc">Serif font</string> + + <!-- Accessible description for decreasing the font size --> + <string name="mozac_feature_readerview_font_size_decrease_desc">Carateri ciù picin</string> + + <!-- Accessible description for increasing the font size --> + <string name="mozac_feature_readerview_font_size_increase_desc">Carateri ciù grendi</string> + <!-- Color option for the background --> + <string name="mozac_feature_readerview_dark">Scuo</string> + <!-- Accessible description for the color option --> + <string name="mozac_feature_readerview_dark_color_scheme_desc">Conbinaçion scua</string> + <!-- Color option for the background --> + <string name="mozac_feature_readerview_sephia">Sepia</string> + <!-- Accessible description for the color option --> + <string name="mozac_feature_readerview_sepia_color_scheme_desc">Conbinaçion sepia</string> + <!-- Color option for the background --> + <string name="mozac_feature_readerview_light">Ciæo</string> + <!-- Accessible description for the color option --> + <string name="mozac_feature_readerview_light_color_scheme_desc">Conbinaçion Ciæa</string> +</resources> diff --git a/mobile/android/android-components/components/feature/readerview/src/main/res/values-lo/strings.xml b/mobile/android/android-components/components/feature/readerview/src/main/res/values-lo/strings.xml new file mode 100644 index 0000000000..42b88f3741 --- /dev/null +++ b/mobile/android/android-components/components/feature/readerview/src/main/res/values-lo/strings.xml @@ -0,0 +1,29 @@ +<?xml version="1.0" encoding="utf-8"?> +<resources> + <!-- Name for one of the font options available to use --> + <string name="mozac_feature_readerview_sans_serif_font">Sans serif</string> + <!-- Accessible description for the font option --> + <string name="mozac_feature_readerview_sans_serif_font_desc">ຟັອນ Sans Serif</string> + <!-- Name for one of the font options available to use --> + <string name="mozac_feature_readerview_serif_font">Serif</string> + <!-- Accessible description for the font option --> + <string name="mozac_feature_readerview_serif_font_desc">ຟັອນ Serif</string> + + <!-- Accessible description for decreasing the font size --> + <string name="mozac_feature_readerview_font_size_decrease_desc">ຂະໜາດຟັອນຫຼຸດລົງ</string> + + <!-- Accessible description for increasing the font size --> + <string name="mozac_feature_readerview_font_size_increase_desc">ຂະໜາດຟັອນເພີ່ມຂື້ນ</string> + <!-- Color option for the background --> + <string name="mozac_feature_readerview_dark">ມືດ</string> + <!-- Accessible description for the color option --> + <string name="mozac_feature_readerview_dark_color_scheme_desc">ຊຸດສີມືດດຳ</string> + <!-- Color option for the background --> + <string name="mozac_feature_readerview_sephia">ສີນ້ຳຕານດຳ</string> + <!-- Accessible description for the color option --> + <string name="mozac_feature_readerview_sepia_color_scheme_desc">ຊຸດສີນ້ຳຕານດຳ</string> + <!-- Color option for the background --> + <string name="mozac_feature_readerview_light">ແຈ້ງ</string> + <!-- Accessible description for the color option --> + <string name="mozac_feature_readerview_light_color_scheme_desc">ຊຸດສີແຈ້ງສະຫວ່າງ</string> +</resources> diff --git a/mobile/android/android-components/components/feature/readerview/src/main/res/values-lt/strings.xml b/mobile/android/android-components/components/feature/readerview/src/main/res/values-lt/strings.xml new file mode 100644 index 0000000000..a98b68ae91 --- /dev/null +++ b/mobile/android/android-components/components/feature/readerview/src/main/res/values-lt/strings.xml @@ -0,0 +1,29 @@ +<?xml version="1.0" encoding="utf-8"?> +<resources> + <!-- Name for one of the font options available to use --> + <string name="mozac_feature_readerview_sans_serif_font">Be užraitų</string> + <!-- Accessible description for the font option --> + <string name="mozac_feature_readerview_sans_serif_font_desc">Šriftas be užraitų</string> + <!-- Name for one of the font options available to use --> + <string name="mozac_feature_readerview_serif_font">Su užraitas</string> + <!-- Accessible description for the font option --> + <string name="mozac_feature_readerview_serif_font_desc">Šriftas su užraitais</string> + + <!-- Accessible description for decreasing the font size --> + <string name="mozac_feature_readerview_font_size_decrease_desc">Šrifto dydžio mažinimas</string> + + <!-- Accessible description for increasing the font size --> + <string name="mozac_feature_readerview_font_size_increase_desc">Šrifto dydžio didinimas</string> + <!-- Color option for the background --> + <string name="mozac_feature_readerview_dark">Tamsus</string> + <!-- Accessible description for the color option --> + <string name="mozac_feature_readerview_dark_color_scheme_desc">Tamsi spalvų aibė</string> + <!-- Color option for the background --> + <string name="mozac_feature_readerview_sephia">Sepija</string> + <!-- Accessible description for the color option --> + <string name="mozac_feature_readerview_sepia_color_scheme_desc">Sepijos spalvų aibė</string> + <!-- Color option for the background --> + <string name="mozac_feature_readerview_light">Šviesus</string> + <!-- Accessible description for the color option --> + <string name="mozac_feature_readerview_light_color_scheme_desc">Šviesi spalvų aibė</string> +</resources> diff --git a/mobile/android/android-components/components/feature/readerview/src/main/res/values-ml/strings.xml b/mobile/android/android-components/components/feature/readerview/src/main/res/values-ml/strings.xml new file mode 100644 index 0000000000..bcf0cabcc1 --- /dev/null +++ b/mobile/android/android-components/components/feature/readerview/src/main/res/values-ml/strings.xml @@ -0,0 +1,29 @@ +<?xml version="1.0" encoding="utf-8"?> +<resources> + <!-- Name for one of the font options available to use --> + <string name="mozac_feature_readerview_sans_serif_font">സാൻസ് സെരിഫ്</string> + <!-- Accessible description for the font option --> + <string name="mozac_feature_readerview_sans_serif_font_desc">സാൻസ് സെരിഫ് ഫോണ്ട്</string> + <!-- Name for one of the font options available to use --> + <string name="mozac_feature_readerview_serif_font">സെരിഫ്</string> + <!-- Accessible description for the font option --> + <string name="mozac_feature_readerview_serif_font_desc">സെരിഫ് ഫോണ്ട്</string> + + <!-- Accessible description for decreasing the font size --> + <string name="mozac_feature_readerview_font_size_decrease_desc">ഫോണ്ട് വലുപ്പം കുറയ്ക്കുക</string> + + <!-- Accessible description for increasing the font size --> + <string name="mozac_feature_readerview_font_size_increase_desc">ഫോണ്ട് വലുപ്പം കൂട്ടുക</string> + <!-- Color option for the background --> + <string name="mozac_feature_readerview_dark">കടും</string> + <!-- Accessible description for the color option --> + <string name="mozac_feature_readerview_dark_color_scheme_desc">ഡാർക്ക് കളർ സ്കീം</string> + <!-- Color option for the background --> + <string name="mozac_feature_readerview_sephia">സെപിയ</string> + <!-- Accessible description for the color option --> + <string name="mozac_feature_readerview_sepia_color_scheme_desc">സെപിയ കളർ സ്കീം</string> + <!-- Color option for the background --> + <string name="mozac_feature_readerview_light">ലൈറ്റ്</string> + <!-- Accessible description for the color option --> + <string name="mozac_feature_readerview_light_color_scheme_desc">ലൈറ്റ് കളർ സ്കീം</string> +</resources> diff --git a/mobile/android/android-components/components/feature/readerview/src/main/res/values-mr/strings.xml b/mobile/android/android-components/components/feature/readerview/src/main/res/values-mr/strings.xml new file mode 100644 index 0000000000..a98aeb3639 --- /dev/null +++ b/mobile/android/android-components/components/feature/readerview/src/main/res/values-mr/strings.xml @@ -0,0 +1,29 @@ +<?xml version="1.0" encoding="utf-8"?> +<resources> + <!-- Name for one of the font options available to use --> + <string name="mozac_feature_readerview_sans_serif_font">सॅन्स सेरिफ</string> + <!-- Accessible description for the font option --> + <string name="mozac_feature_readerview_sans_serif_font_desc">सॅन्स सेरिफ टंक</string> + <!-- Name for one of the font options available to use --> + <string name="mozac_feature_readerview_serif_font">सेरिफ</string> + <!-- Accessible description for the font option --> + <string name="mozac_feature_readerview_serif_font_desc">सेरिफ टंक</string> + + <!-- Accessible description for decreasing the font size --> + <string name="mozac_feature_readerview_font_size_decrease_desc">टंकाचा आकार कमी करा</string> + + <!-- Accessible description for increasing the font size --> + <string name="mozac_feature_readerview_font_size_increase_desc">टंकाचा आकार वाढावा</string> + <!-- Color option for the background --> + <string name="mozac_feature_readerview_dark">गडद</string> + <!-- Accessible description for the color option --> + <string name="mozac_feature_readerview_dark_color_scheme_desc">गडद रंग योजना</string> + <!-- Color option for the background --> + <string name="mozac_feature_readerview_sephia">दाट तपकिरी</string> + <!-- Accessible description for the color option --> + <string name="mozac_feature_readerview_sepia_color_scheme_desc">दाट तपकिरी रंग योजना</string> + <!-- Color option for the background --> + <string name="mozac_feature_readerview_light">फिकट</string> + <!-- Accessible description for the color option --> + <string name="mozac_feature_readerview_light_color_scheme_desc">फिकट रंग योजना</string> +</resources> diff --git a/mobile/android/android-components/components/feature/readerview/src/main/res/values-my/strings.xml b/mobile/android/android-components/components/feature/readerview/src/main/res/values-my/strings.xml new file mode 100644 index 0000000000..be83dbe7e1 --- /dev/null +++ b/mobile/android/android-components/components/feature/readerview/src/main/res/values-my/strings.xml @@ -0,0 +1,30 @@ +<?xml version="1.0" encoding="utf-8"?> +<resources> + + <!-- Name for one of the font options available to use --> + <string name="mozac_feature_readerview_sans_serif_font">Sans serif</string> + <!-- Accessible description for the font option --> + <string name="mozac_feature_readerview_sans_serif_font_desc">Sans Serif ဖောင့်</string> + <!-- Name for one of the font options available to use --> + <string name="mozac_feature_readerview_serif_font">Serif</string> + <!-- Accessible description for the font option --> + <string name="mozac_feature_readerview_serif_font_desc">serif ဖောင့်</string> + + <!-- Accessible description for decreasing the font size --> + <string name="mozac_feature_readerview_font_size_decrease_desc">စာလုံးအရွယ်အစားလျှေ့ာပါ</string> + + <!-- Accessible description for increasing the font size --> + <string name="mozac_feature_readerview_font_size_increase_desc">စာလုံးအရွယ်အစားတိုးပါ</string> + <!-- Color option for the background --> + <string name="mozac_feature_readerview_dark">အမှောင်</string> + <!-- Accessible description for the color option --> + <string name="mozac_feature_readerview_dark_color_scheme_desc">မှောင်မိုက်အရောင်အစီအစဉ်</string> + <!-- Color option for the background --> + <string name="mozac_feature_readerview_sephia">နီညိုရောင်</string> + <!-- Accessible description for the color option --> + <string name="mozac_feature_readerview_sepia_color_scheme_desc">နီညိုရောင် ပြသမှု</string> + <!-- Color option for the background --> + <string name="mozac_feature_readerview_light">အလင်း</string> + <!-- Accessible description for the color option --> + <string name="mozac_feature_readerview_light_color_scheme_desc">အလင်းအရောင်အစီအစဉ်</string> +</resources> diff --git a/mobile/android/android-components/components/feature/readerview/src/main/res/values-nb-rNO/strings.xml b/mobile/android/android-components/components/feature/readerview/src/main/res/values-nb-rNO/strings.xml new file mode 100644 index 0000000000..21758154bd --- /dev/null +++ b/mobile/android/android-components/components/feature/readerview/src/main/res/values-nb-rNO/strings.xml @@ -0,0 +1,29 @@ +<?xml version="1.0" encoding="utf-8"?> +<resources> + <!-- Name for one of the font options available to use --> + <string name="mozac_feature_readerview_sans_serif_font">Seriffløs skrift</string> + <!-- Accessible description for the font option --> + <string name="mozac_feature_readerview_sans_serif_font_desc">Seriffløs skrift</string> + <!-- Name for one of the font options available to use --> + <string name="mozac_feature_readerview_serif_font">Seriffskrift</string> + <!-- Accessible description for the font option --> + <string name="mozac_feature_readerview_serif_font_desc">Seriffskrift</string> + + <!-- Accessible description for decreasing the font size --> + <string name="mozac_feature_readerview_font_size_decrease_desc">Skriftstørrelse reduseres</string> + + <!-- Accessible description for increasing the font size --> + <string name="mozac_feature_readerview_font_size_increase_desc">Skriftstørrelse økes</string> + <!-- Color option for the background --> + <string name="mozac_feature_readerview_dark">Mørk</string> + <!-- Accessible description for the color option --> + <string name="mozac_feature_readerview_dark_color_scheme_desc">Mørkt fargevalg</string> + <!-- Color option for the background --> + <string name="mozac_feature_readerview_sephia">Sepia</string> + <!-- Accessible description for the color option --> + <string name="mozac_feature_readerview_sepia_color_scheme_desc">Sepia fargevalg</string> + <!-- Color option for the background --> + <string name="mozac_feature_readerview_light">Lys</string> + <!-- Accessible description for the color option --> + <string name="mozac_feature_readerview_light_color_scheme_desc">Lyst fargevalg</string> +</resources> diff --git a/mobile/android/android-components/components/feature/readerview/src/main/res/values-ne-rNP/strings.xml b/mobile/android/android-components/components/feature/readerview/src/main/res/values-ne-rNP/strings.xml new file mode 100644 index 0000000000..08f2ddb96d --- /dev/null +++ b/mobile/android/android-components/components/feature/readerview/src/main/res/values-ne-rNP/strings.xml @@ -0,0 +1,31 @@ +<?xml version="1.0" encoding="utf-8"?> +<resources> + + <!-- Name for one of the font options available to use --> + <string name="mozac_feature_readerview_sans_serif_font">सानस्-सेरिफ</string> + <!-- Accessible description for the font option --> + <string name="mozac_feature_readerview_sans_serif_font_desc">सानस्-सेरिफ फन्ट</string> + <!-- Name for one of the font options available to use --> + <string name="mozac_feature_readerview_serif_font">सेरिफ</string> + + <!-- Accessible description for the font option --> + <string name="mozac_feature_readerview_serif_font_desc">सेरिफ फन्ट</string> + + <!-- Accessible description for decreasing the font size --> + <string name="mozac_feature_readerview_font_size_decrease_desc">फन्टको आकार घटाउनुहोस्</string> + + <!-- Accessible description for increasing the font size --> + <string name="mozac_feature_readerview_font_size_increase_desc">फन्टको आकार बढाउनुहोस्</string> + <!-- Color option for the background --> + <string name="mozac_feature_readerview_dark">अँध्यारो</string> + <!-- Accessible description for the color option --> + <string name="mozac_feature_readerview_dark_color_scheme_desc">अँध्यारो रङ्ग योजना</string> + <!-- Color option for the background --> + <string name="mozac_feature_readerview_sephia">सेपिया</string> + <!-- Accessible description for the color option --> + <string name="mozac_feature_readerview_sepia_color_scheme_desc">सेपिया रंग योजना</string> + <!-- Color option for the background --> + <string name="mozac_feature_readerview_light">उज्यालो</string> + <!-- Accessible description for the color option --> + <string name="mozac_feature_readerview_light_color_scheme_desc">उज्यालो रङ्ग योजना</string> +</resources> diff --git a/mobile/android/android-components/components/feature/readerview/src/main/res/values-nl/strings.xml b/mobile/android/android-components/components/feature/readerview/src/main/res/values-nl/strings.xml new file mode 100644 index 0000000000..5cd24cbf48 --- /dev/null +++ b/mobile/android/android-components/components/feature/readerview/src/main/res/values-nl/strings.xml @@ -0,0 +1,30 @@ +<?xml version="1.0" encoding="utf-8"?> +<resources> + + <!-- Name for one of the font options available to use --> + <string name="mozac_feature_readerview_sans_serif_font">Schreefloos</string> + <!-- Accessible description for the font option --> + <string name="mozac_feature_readerview_sans_serif_font_desc">Lettertype Sans Serif</string> + <!-- Name for one of the font options available to use --> + <string name="mozac_feature_readerview_serif_font">Met schreef</string> + <!-- Accessible description for the font option --> + <string name="mozac_feature_readerview_serif_font_desc">Lettertype Serif</string> + + <!-- Accessible description for decreasing the font size --> + <string name="mozac_feature_readerview_font_size_decrease_desc">Lettergrootte verkleinen</string> + + <!-- Accessible description for increasing the font size --> + <string name="mozac_feature_readerview_font_size_increase_desc">Lettergrootte vergroten</string> + <!-- Color option for the background --> + <string name="mozac_feature_readerview_dark">Donker</string> + <!-- Accessible description for the color option --> + <string name="mozac_feature_readerview_dark_color_scheme_desc">Donker kleurenschema</string> + <!-- Color option for the background --> + <string name="mozac_feature_readerview_sephia">Sepia</string> + <!-- Accessible description for the color option --> + <string name="mozac_feature_readerview_sepia_color_scheme_desc">Sepia kleurenschema</string> + <!-- Color option for the background --> + <string name="mozac_feature_readerview_light">Licht</string> + <!-- Accessible description for the color option --> + <string name="mozac_feature_readerview_light_color_scheme_desc">Licht kleurenschema</string> +</resources> diff --git a/mobile/android/android-components/components/feature/readerview/src/main/res/values-nn-rNO/strings.xml b/mobile/android/android-components/components/feature/readerview/src/main/res/values-nn-rNO/strings.xml new file mode 100644 index 0000000000..7060f25970 --- /dev/null +++ b/mobile/android/android-components/components/feature/readerview/src/main/res/values-nn-rNO/strings.xml @@ -0,0 +1,29 @@ +<?xml version="1.0" encoding="utf-8"?> +<resources> + <!-- Name for one of the font options available to use --> + <string name="mozac_feature_readerview_sans_serif_font">Serifflaus skrift</string> + <!-- Accessible description for the font option --> + <string name="mozac_feature_readerview_sans_serif_font_desc">Serifflaus skrift</string> + <!-- Name for one of the font options available to use --> + <string name="mozac_feature_readerview_serif_font">Seriffskrift</string> + <!-- Accessible description for the font option --> + <string name="mozac_feature_readerview_serif_font_desc">Seriffskrift</string> + + <!-- Accessible description for decreasing the font size --> + <string name="mozac_feature_readerview_font_size_decrease_desc">Skriftstørrelse minskar</string> + + <!-- Accessible description for increasing the font size --> + <string name="mozac_feature_readerview_font_size_increase_desc">Skriftstørrelse aukar</string> + <!-- Color option for the background --> + <string name="mozac_feature_readerview_dark">Mørkt</string> + <!-- Accessible description for the color option --> + <string name="mozac_feature_readerview_dark_color_scheme_desc">Mørkt fargeval</string> + <!-- Color option for the background --> + <string name="mozac_feature_readerview_sephia">Sepia</string> + <!-- Accessible description for the color option --> + <string name="mozac_feature_readerview_sepia_color_scheme_desc">Sepia fargeval</string> + <!-- Color option for the background --> + <string name="mozac_feature_readerview_light">Lyst</string> + <!-- Accessible description for the color option --> + <string name="mozac_feature_readerview_light_color_scheme_desc">Lyst fargeval</string> +</resources> diff --git a/mobile/android/android-components/components/feature/readerview/src/main/res/values-oc/strings.xml b/mobile/android/android-components/components/feature/readerview/src/main/res/values-oc/strings.xml new file mode 100644 index 0000000000..b914e4e856 --- /dev/null +++ b/mobile/android/android-components/components/feature/readerview/src/main/res/values-oc/strings.xml @@ -0,0 +1,31 @@ +<?xml version="1.0" encoding="utf-8"?> +<resources> + + <!-- Name for one of the font options available to use --> + <string name="mozac_feature_readerview_sans_serif_font">Sans serif</string> + + <!-- Accessible description for the font option --> + <string name="mozac_feature_readerview_sans_serif_font_desc">Polissa Sans Serif</string> + <!-- Name for one of the font options available to use --> + <string name="mozac_feature_readerview_serif_font">Serif</string> + <!-- Accessible description for the font option --> + <string name="mozac_feature_readerview_serif_font_desc">Polissa Serif</string> + + <!-- Accessible description for decreasing the font size --> + <string name="mozac_feature_readerview_font_size_decrease_desc">Reduccion de la talha de la polissa</string> + + <!-- Accessible description for increasing the font size --> + <string name="mozac_feature_readerview_font_size_increase_desc">Agrandiment de la talha de la polissa</string> + <!-- Color option for the background --> + <string name="mozac_feature_readerview_dark">Fosc</string> + <!-- Accessible description for the color option --> + <string name="mozac_feature_readerview_dark_color_scheme_desc">Jòc de colors foscas</string> + <!-- Color option for the background --> + <string name="mozac_feature_readerview_sephia">Sèpia</string> + <!-- Accessible description for the color option --> + <string name="mozac_feature_readerview_sepia_color_scheme_desc">Jòc de colors sépia</string> + <!-- Color option for the background --> + <string name="mozac_feature_readerview_light">Clar</string> + <!-- Accessible description for the color option --> + <string name="mozac_feature_readerview_light_color_scheme_desc">Jòc de color claras</string> +</resources> diff --git a/mobile/android/android-components/components/feature/readerview/src/main/res/values-or/strings.xml b/mobile/android/android-components/components/feature/readerview/src/main/res/values-or/strings.xml new file mode 100644 index 0000000000..617dade600 --- /dev/null +++ b/mobile/android/android-components/components/feature/readerview/src/main/res/values-or/strings.xml @@ -0,0 +1,16 @@ +<?xml version="1.0" encoding="utf-8"?> +<resources> + + <!-- Color option for the background --> + <string name="mozac_feature_readerview_dark">ଗାଢ଼</string> + <!-- Accessible description for the color option --> + <string name="mozac_feature_readerview_dark_color_scheme_desc">ଗାଢ଼ ରଙ୍ଗ ଯୋଜନା</string> + <!-- Color option for the background --> + <string name="mozac_feature_readerview_sephia">ସେପିଆ</string> + <!-- Accessible description for the color option --> + <string name="mozac_feature_readerview_sepia_color_scheme_desc">ସେପିଆ ରଙ୍ଗ ଯୋଜନା</string> + <!-- Color option for the background --> + <string name="mozac_feature_readerview_light">ହାଲୁକା</string> + <!-- Accessible description for the color option --> + <string name="mozac_feature_readerview_light_color_scheme_desc">ହାଲୁକା ରଙ୍ଗ ଯୋଜନା</string> +</resources> diff --git a/mobile/android/android-components/components/feature/readerview/src/main/res/values-pa-rIN/strings.xml b/mobile/android/android-components/components/feature/readerview/src/main/res/values-pa-rIN/strings.xml new file mode 100644 index 0000000000..07052c8e4c --- /dev/null +++ b/mobile/android/android-components/components/feature/readerview/src/main/res/values-pa-rIN/strings.xml @@ -0,0 +1,30 @@ +<?xml version="1.0" encoding="utf-8"?> +<resources> + + <!-- Name for one of the font options available to use --> + <string name="mozac_feature_readerview_sans_serif_font">ਸਨਜ ਸੈਰੀਫ਼</string> + <!-- Accessible description for the font option --> + <string name="mozac_feature_readerview_sans_serif_font_desc">ਸਨਜ ਸੈਰੀਫ਼ ਫ਼ੋਂਟ</string> + <!-- Name for one of the font options available to use --> + <string name="mozac_feature_readerview_serif_font">ਸੈਰੀਫ਼</string> + <!-- Accessible description for the font option --> + <string name="mozac_feature_readerview_serif_font_desc">ਸੈਰੀਫ਼ ਫ਼ੋਂਟ</string> + + <!-- Accessible description for decreasing the font size --> + <string name="mozac_feature_readerview_font_size_decrease_desc">ਫ਼ੋਂਟ ਆਕਾਰ ਘਟਾਓ</string> + + <!-- Accessible description for increasing the font size --> + <string name="mozac_feature_readerview_font_size_increase_desc">ਫ਼ੋਂਟ ਆਕਾਰ ਵਧਾਓ</string> + <!-- Color option for the background --> + <string name="mozac_feature_readerview_dark">ਗੂੜ੍ਹਾ</string> + <!-- Accessible description for the color option --> + <string name="mozac_feature_readerview_dark_color_scheme_desc">ਗੂੜ੍ਹੇ ਰੰਗ ਦੀ ਰੂਪ-ਰੇਖਾ</string> + <!-- Color option for the background --> + <string name="mozac_feature_readerview_sephia">ਭੂਰਾ</string> + <!-- Accessible description for the color option --> + <string name="mozac_feature_readerview_sepia_color_scheme_desc">ਭੂਰੇ ਰੰਗ ਦੀ ਰੂਪ-ਰੇਖਾ</string> + <!-- Color option for the background --> + <string name="mozac_feature_readerview_light">ਹਲਕਾ</string> + <!-- Accessible description for the color option --> + <string name="mozac_feature_readerview_light_color_scheme_desc">ਹਲਕੀ ਰੰਗ ਸਕੀਮ</string> +</resources> diff --git a/mobile/android/android-components/components/feature/readerview/src/main/res/values-pa-rPK/strings.xml b/mobile/android/android-components/components/feature/readerview/src/main/res/values-pa-rPK/strings.xml new file mode 100644 index 0000000000..0b8fa4d685 --- /dev/null +++ b/mobile/android/android-components/components/feature/readerview/src/main/res/values-pa-rPK/strings.xml @@ -0,0 +1,30 @@ +<?xml version="1.0" encoding="utf-8"?> +<resources> + <!-- Name for one of the font options available to use --> + <string name="mozac_feature_readerview_sans_serif_font">سیرف بغیر</string> + <!-- Accessible description for the font option --> + <string name="mozac_feature_readerview_sans_serif_font_desc">سیرف بغیر فونٹ</string> + <!-- Name for one of the font options available to use --> + <string name="mozac_feature_readerview_serif_font">سیرف</string> + + <!-- Accessible description for the font option --> + <string name="mozac_feature_readerview_serif_font_desc">سیرف فونٹ</string> + + <!-- Accessible description for decreasing the font size --> + <string name="mozac_feature_readerview_font_size_decrease_desc">فونٹ آکار گھٹاؤ</string> + + <!-- Accessible description for increasing the font size --> + <string name="mozac_feature_readerview_font_size_increase_desc">فونٹ آکار ودھاؤ</string> + <!-- Color option for the background --> + <string name="mozac_feature_readerview_dark">گوڑھا</string> + <!-- Accessible description for the color option --> + <string name="mozac_feature_readerview_dark_color_scheme_desc">گوڑھے رنگ دی روپ ریکھا</string> + <!-- Color option for the background --> + <string name="mozac_feature_readerview_sephia">بھورا</string> + <!-- Accessible description for the color option --> + <string name="mozac_feature_readerview_sepia_color_scheme_desc">بھورا رنگ دی روپ ریکھا</string> + <!-- Color option for the background --> + <string name="mozac_feature_readerview_light">ہلکا</string> + <!-- Accessible description for the color option --> + <string name="mozac_feature_readerview_light_color_scheme_desc">ہلکے رنگ دی روپ ریکھا</string> +</resources> diff --git a/mobile/android/android-components/components/feature/readerview/src/main/res/values-pl/strings.xml b/mobile/android/android-components/components/feature/readerview/src/main/res/values-pl/strings.xml new file mode 100644 index 0000000000..0c07108257 --- /dev/null +++ b/mobile/android/android-components/components/feature/readerview/src/main/res/values-pl/strings.xml @@ -0,0 +1,29 @@ +<?xml version="1.0" encoding="utf-8"?> +<resources> + <!-- Name for one of the font options available to use --> + <string name="mozac_feature_readerview_sans_serif_font">Bezszeryfowa</string> + <!-- Accessible description for the font option --> + <string name="mozac_feature_readerview_sans_serif_font_desc">Czcionka bezszeryfowa</string> + <!-- Name for one of the font options available to use --> + <string name="mozac_feature_readerview_serif_font">Szeryfowa</string> + <!-- Accessible description for the font option --> + <string name="mozac_feature_readerview_serif_font_desc">Czcionka szeryfowa</string> + + <!-- Accessible description for decreasing the font size --> + <string name="mozac_feature_readerview_font_size_decrease_desc">Zmniejsz rozmiar czcionki</string> + + <!-- Accessible description for increasing the font size --> + <string name="mozac_feature_readerview_font_size_increase_desc">Zwiększ rozmiar czcionki</string> + <!-- Color option for the background --> + <string name="mozac_feature_readerview_dark">Ciemny</string> + <!-- Accessible description for the color option --> + <string name="mozac_feature_readerview_dark_color_scheme_desc">Ciemny schemat kolorów</string> + <!-- Color option for the background --> + <string name="mozac_feature_readerview_sephia">Sepia</string> + <!-- Accessible description for the color option --> + <string name="mozac_feature_readerview_sepia_color_scheme_desc">Schemat kolorów sepii</string> + <!-- Color option for the background --> + <string name="mozac_feature_readerview_light">Jasny</string> + <!-- Accessible description for the color option --> + <string name="mozac_feature_readerview_light_color_scheme_desc">Jasny schemat kolorów</string> +</resources> diff --git a/mobile/android/android-components/components/feature/readerview/src/main/res/values-pt-rBR/strings.xml b/mobile/android/android-components/components/feature/readerview/src/main/res/values-pt-rBR/strings.xml new file mode 100644 index 0000000000..f0d45e960f --- /dev/null +++ b/mobile/android/android-components/components/feature/readerview/src/main/res/values-pt-rBR/strings.xml @@ -0,0 +1,29 @@ +<?xml version="1.0" encoding="utf-8"?> +<resources> + <!-- Name for one of the font options available to use --> + <string name="mozac_feature_readerview_sans_serif_font">Sem serifa</string> + <!-- Accessible description for the font option --> + <string name="mozac_feature_readerview_sans_serif_font_desc">Fonte sem serifa</string> + <!-- Name for one of the font options available to use --> + <string name="mozac_feature_readerview_serif_font">Com serifa</string> + <!-- Accessible description for the font option --> + <string name="mozac_feature_readerview_serif_font_desc">Fonte com serifa</string> + + <!-- Accessible description for decreasing the font size --> + <string name="mozac_feature_readerview_font_size_decrease_desc">Reduzir tamanho da fonte</string> + + <!-- Accessible description for increasing the font size --> + <string name="mozac_feature_readerview_font_size_increase_desc">Aumentar tamanho da fonte</string> + <!-- Color option for the background --> + <string name="mozac_feature_readerview_dark">Escuro</string> + <!-- Accessible description for the color option --> + <string name="mozac_feature_readerview_dark_color_scheme_desc">Esquema de cores escuras</string> + <!-- Color option for the background --> + <string name="mozac_feature_readerview_sephia">Sépia</string> + <!-- Accessible description for the color option --> + <string name="mozac_feature_readerview_sepia_color_scheme_desc">Esquema de cores sépia</string> + <!-- Color option for the background --> + <string name="mozac_feature_readerview_light">Claro</string> + <!-- Accessible description for the color option --> + <string name="mozac_feature_readerview_light_color_scheme_desc">Esquema de cores claras</string> +</resources> diff --git a/mobile/android/android-components/components/feature/readerview/src/main/res/values-pt-rPT/strings.xml b/mobile/android/android-components/components/feature/readerview/src/main/res/values-pt-rPT/strings.xml new file mode 100644 index 0000000000..aba390c250 --- /dev/null +++ b/mobile/android/android-components/components/feature/readerview/src/main/res/values-pt-rPT/strings.xml @@ -0,0 +1,29 @@ +<?xml version="1.0" encoding="utf-8"?> +<resources> + <!-- Name for one of the font options available to use --> + <string name="mozac_feature_readerview_sans_serif_font">Sem serifa</string> + <!-- Accessible description for the font option --> + <string name="mozac_feature_readerview_sans_serif_font_desc">Tipo de letra sem serifa</string> + <!-- Name for one of the font options available to use --> + <string name="mozac_feature_readerview_serif_font">Serifa</string> + <!-- Accessible description for the font option --> + <string name="mozac_feature_readerview_serif_font_desc">Tipo de letra com serifa</string> + + <!-- Accessible description for decreasing the font size --> + <string name="mozac_feature_readerview_font_size_decrease_desc">Redução do tamanho da letra</string> + + <!-- Accessible description for increasing the font size --> + <string name="mozac_feature_readerview_font_size_increase_desc">Aumento do tamanho da letra</string> + <!-- Color option for the background --> + <string name="mozac_feature_readerview_dark">Escuro</string> + <!-- Accessible description for the color option --> + <string name="mozac_feature_readerview_dark_color_scheme_desc">Esquema de cores escuro</string> + <!-- Color option for the background --> + <string name="mozac_feature_readerview_sephia">Sépia</string> + <!-- Accessible description for the color option --> + <string name="mozac_feature_readerview_sepia_color_scheme_desc">Esquema de cores sépia</string> + <!-- Color option for the background --> + <string name="mozac_feature_readerview_light">Claro</string> + <!-- Accessible description for the color option --> + <string name="mozac_feature_readerview_light_color_scheme_desc">Esquema de cores claras</string> +</resources> diff --git a/mobile/android/android-components/components/feature/readerview/src/main/res/values-rm/strings.xml b/mobile/android/android-components/components/feature/readerview/src/main/res/values-rm/strings.xml new file mode 100644 index 0000000000..b0753955e5 --- /dev/null +++ b/mobile/android/android-components/components/feature/readerview/src/main/res/values-rm/strings.xml @@ -0,0 +1,29 @@ +<?xml version="1.0" encoding="utf-8"?> +<resources> + <!-- Name for one of the font options available to use --> + <string name="mozac_feature_readerview_sans_serif_font">Senza serifas</string> + <!-- Accessible description for the font option --> + <string name="mozac_feature_readerview_sans_serif_font_desc">Scrittira senza serifas</string> + <!-- Name for one of the font options available to use --> + <string name="mozac_feature_readerview_serif_font">Cun serifas</string> + <!-- Accessible description for the font option --> + <string name="mozac_feature_readerview_serif_font_desc">Scrittira cun serifas</string> + + <!-- Accessible description for decreasing the font size --> + <string name="mozac_feature_readerview_font_size_decrease_desc">Empitschnir la scrittira</string> + + <!-- Accessible description for increasing the font size --> + <string name="mozac_feature_readerview_font_size_increase_desc">Engrondir la scrittira</string> + <!-- Color option for the background --> + <string name="mozac_feature_readerview_dark">Stgir</string> + <!-- Accessible description for the color option --> + <string name="mozac_feature_readerview_dark_color_scheme_desc">Schema stgir</string> + <!-- Color option for the background --> + <string name="mozac_feature_readerview_sephia">Sepia</string> + <!-- Accessible description for the color option --> + <string name="mozac_feature_readerview_sepia_color_scheme_desc">Schema da sepia</string> + <!-- Color option for the background --> + <string name="mozac_feature_readerview_light">Cler</string> + <!-- Accessible description for the color option --> + <string name="mozac_feature_readerview_light_color_scheme_desc">Schema cler</string> +</resources> diff --git a/mobile/android/android-components/components/feature/readerview/src/main/res/values-ro/strings.xml b/mobile/android/android-components/components/feature/readerview/src/main/res/values-ro/strings.xml new file mode 100644 index 0000000000..808f367aa5 --- /dev/null +++ b/mobile/android/android-components/components/feature/readerview/src/main/res/values-ro/strings.xml @@ -0,0 +1,29 @@ +<?xml version="1.0" encoding="utf-8"?> +<resources> + <!-- Name for one of the font options available to use --> + <string name="mozac_feature_readerview_sans_serif_font">Sans serif</string> + <!-- Accessible description for the font option --> + <string name="mozac_feature_readerview_sans_serif_font_desc">Font Sans Serif</string> + <!-- Name for one of the font options available to use --> + <string name="mozac_feature_readerview_serif_font">Serif</string> + <!-- Accessible description for the font option --> + <string name="mozac_feature_readerview_serif_font_desc">Font Serif </string> + + <!-- Accessible description for decreasing the font size --> + <string name="mozac_feature_readerview_font_size_decrease_desc">Scăderea dimensiunii fontului</string> + + <!-- Accessible description for increasing the font size --> + <string name="mozac_feature_readerview_font_size_increase_desc">Creșterea dimensiunii fontului</string> + <!-- Color option for the background --> + <string name="mozac_feature_readerview_dark">Întunecat</string> + <!-- Accessible description for the color option --> + <string name="mozac_feature_readerview_dark_color_scheme_desc">Paletă de culori închise</string> + <!-- Color option for the background --> + <string name="mozac_feature_readerview_sephia">Sepia</string> + <!-- Accessible description for the color option --> + <string name="mozac_feature_readerview_sepia_color_scheme_desc">Paletă de culori sepia</string> + <!-- Color option for the background --> + <string name="mozac_feature_readerview_light">Deschis</string> + <!-- Accessible description for the color option --> + <string name="mozac_feature_readerview_light_color_scheme_desc">Paletă de culori deschise</string> +</resources> diff --git a/mobile/android/android-components/components/feature/readerview/src/main/res/values-ru/strings.xml b/mobile/android/android-components/components/feature/readerview/src/main/res/values-ru/strings.xml new file mode 100644 index 0000000000..4d4a49469a --- /dev/null +++ b/mobile/android/android-components/components/feature/readerview/src/main/res/values-ru/strings.xml @@ -0,0 +1,29 @@ +<?xml version="1.0" encoding="utf-8"?> +<resources> + <!-- Name for one of the font options available to use --> + <string name="mozac_feature_readerview_sans_serif_font">Без засечек</string> + <!-- Accessible description for the font option --> + <string name="mozac_feature_readerview_sans_serif_font_desc">Шрифт без засечек</string> + <!-- Name for one of the font options available to use --> + <string name="mozac_feature_readerview_serif_font">С засечками</string> + <!-- Accessible description for the font option --> + <string name="mozac_feature_readerview_serif_font_desc">Шрифт с засечками</string> + + <!-- Accessible description for decreasing the font size --> + <string name="mozac_feature_readerview_font_size_decrease_desc">Уменьшить размер шрифта</string> + + <!-- Accessible description for increasing the font size --> + <string name="mozac_feature_readerview_font_size_increase_desc">Увеличить размер шрифта</string> + <!-- Color option for the background --> + <string name="mozac_feature_readerview_dark">Тёмная</string> + <!-- Accessible description for the color option --> + <string name="mozac_feature_readerview_dark_color_scheme_desc">Цветовая гамма «Тёмная»</string> + <!-- Color option for the background --> + <string name="mozac_feature_readerview_sephia">Сепия</string> + <!-- Accessible description for the color option --> + <string name="mozac_feature_readerview_sepia_color_scheme_desc">Цветовая гамма «Сепия»</string> + <!-- Color option for the background --> + <string name="mozac_feature_readerview_light">Светлая</string> + <!-- Accessible description for the color option --> + <string name="mozac_feature_readerview_light_color_scheme_desc">Цветовая гамма «Светлая»</string> +</resources> diff --git a/mobile/android/android-components/components/feature/readerview/src/main/res/values-sat/strings.xml b/mobile/android/android-components/components/feature/readerview/src/main/res/values-sat/strings.xml new file mode 100644 index 0000000000..d96b1fb8cd --- /dev/null +++ b/mobile/android/android-components/components/feature/readerview/src/main/res/values-sat/strings.xml @@ -0,0 +1,29 @@ +<?xml version="1.0" encoding="utf-8"?> +<resources> + <!-- Name for one of the font options available to use --> + <string name="mozac_feature_readerview_sans_serif_font">ᱥᱮᱱᱥ ᱥᱮᱨᱤᱯᱷ</string> + <!-- Accessible description for the font option --> + <string name="mozac_feature_readerview_sans_serif_font_desc">ᱥᱮᱱᱥ ᱥᱮᱨᱤᱯᱷ ᱪᱤᱠᱤ</string> + <!-- Name for one of the font options available to use --> + <string name="mozac_feature_readerview_serif_font">ᱥᱮᱨᱤᱯᱷ</string> + <!-- Accessible description for the font option --> + <string name="mozac_feature_readerview_serif_font_desc">ᱥᱮᱨᱤᱯᱷ ᱪᱤᱠᱤ</string> + + <!-- Accessible description for decreasing the font size --> + <string name="mozac_feature_readerview_font_size_decrease_desc">ᱪᱤᱠᱤ ᱢᱟᱯ ᱠᱚᱢ ᱢᱮ</string> + + <!-- Accessible description for increasing the font size --> + <string name="mozac_feature_readerview_font_size_increase_desc">ᱪᱤᱠᱤ ᱢᱟᱯ ᱢᱟᱨᱟᱝ ᱢᱮ</string> + <!-- Color option for the background --> + <string name="mozac_feature_readerview_dark">ᱧᱩᱛ</string> + <!-- Accessible description for the color option --> + <string name="mozac_feature_readerview_dark_color_scheme_desc">ᱧᱩᱛ ᱨᱚᱝ ᱯᱚᱱᱛᱷᱟ</string> + <!-- Color option for the background --> + <string name="mozac_feature_readerview_sephia">ᱥᱮᱯᱤᱭᱟ</string> + <!-- Accessible description for the color option --> + <string name="mozac_feature_readerview_sepia_color_scheme_desc">ᱥᱮᱯᱤᱭᱟ ᱨᱚᱝ ᱯᱚᱱᱛᱷᱟ</string> + <!-- Color option for the background --> + <string name="mozac_feature_readerview_light">ᱢᱟᱨᱥᱟᱞ</string> + <!-- Accessible description for the color option --> + <string name="mozac_feature_readerview_light_color_scheme_desc">ᱢᱟᱨᱥᱟᱞ ᱨᱚᱝ ᱯᱚᱱᱛᱷᱟ</string> +</resources> diff --git a/mobile/android/android-components/components/feature/readerview/src/main/res/values-sc/strings.xml b/mobile/android/android-components/components/feature/readerview/src/main/res/values-sc/strings.xml new file mode 100644 index 0000000000..e49917faef --- /dev/null +++ b/mobile/android/android-components/components/feature/readerview/src/main/res/values-sc/strings.xml @@ -0,0 +1,29 @@ +<?xml version="1.0" encoding="utf-8"?> +<resources> + <!-- Name for one of the font options available to use --> + <string name="mozac_feature_readerview_sans_serif_font">Sans serif</string> + <!-- Accessible description for the font option --> + <string name="mozac_feature_readerview_sans_serif_font_desc">Tipografia Sans Serif</string> + <!-- Name for one of the font options available to use --> + <string name="mozac_feature_readerview_serif_font">Serif</string> + <!-- Accessible description for the font option --> + <string name="mozac_feature_readerview_serif_font_desc">Tipografia Serif</string> + + <!-- Accessible description for decreasing the font size --> + <string name="mozac_feature_readerview_font_size_decrease_desc">Mìnima sa mannària de sa tipografia</string> + + <!-- Accessible description for increasing the font size --> + <string name="mozac_feature_readerview_font_size_increase_desc">Cresche sa mannària de sa tipografia</string> + <!-- Color option for the background --> + <string name="mozac_feature_readerview_dark">Iscuru</string> + <!-- Accessible description for the color option --> + <string name="mozac_feature_readerview_dark_color_scheme_desc">Tema de colores iscuru</string> + <!-- Color option for the background --> + <string name="mozac_feature_readerview_sephia">Sèpia</string> + <!-- Accessible description for the color option --> + <string name="mozac_feature_readerview_sepia_color_scheme_desc">Tema de colores sèpia</string> + <!-- Color option for the background --> + <string name="mozac_feature_readerview_light">Craru</string> + <!-- Accessible description for the color option --> + <string name="mozac_feature_readerview_light_color_scheme_desc">Tema de colores craru</string> +</resources> diff --git a/mobile/android/android-components/components/feature/readerview/src/main/res/values-si/strings.xml b/mobile/android/android-components/components/feature/readerview/src/main/res/values-si/strings.xml new file mode 100644 index 0000000000..c3ee32bdea --- /dev/null +++ b/mobile/android/android-components/components/feature/readerview/src/main/res/values-si/strings.xml @@ -0,0 +1,29 @@ +<?xml version="1.0" encoding="utf-8"?> +<resources> + <!-- Name for one of the font options available to use --> + <string name="mozac_feature_readerview_sans_serif_font">සාන්ස් සෙරිෆ්</string> + <!-- Accessible description for the font option --> + <string name="mozac_feature_readerview_sans_serif_font_desc">සාන්ස් සෙරිෆ් අකුර</string> + <!-- Name for one of the font options available to use --> + <string name="mozac_feature_readerview_serif_font">සෙරිෆ්</string> + <!-- Accessible description for the font option --> + <string name="mozac_feature_readerview_serif_font_desc">සෙරිෆ් අකුර</string> + + <!-- Accessible description for decreasing the font size --> + <string name="mozac_feature_readerview_font_size_decrease_desc">අකුරේ තරම අඩු කිරීම</string> + + <!-- Accessible description for increasing the font size --> + <string name="mozac_feature_readerview_font_size_increase_desc">අකුරේ තරම වැඩි කිරීම</string> + <!-- Color option for the background --> + <string name="mozac_feature_readerview_dark">අඳුරු</string> + <!-- Accessible description for the color option --> + <string name="mozac_feature_readerview_dark_color_scheme_desc">අඳුරු වර්ණ රටාව</string> + <!-- Color option for the background --> + <string name="mozac_feature_readerview_sephia">ලා දුඹුරු</string> + <!-- Accessible description for the color option --> + <string name="mozac_feature_readerview_sepia_color_scheme_desc">ලා දුඹුරු වර්ණ රටාව</string> + <!-- Color option for the background --> + <string name="mozac_feature_readerview_light">දීප්ත</string> + <!-- Accessible description for the color option --> + <string name="mozac_feature_readerview_light_color_scheme_desc">දීප්ත වර්ණ රටාව</string> +</resources> diff --git a/mobile/android/android-components/components/feature/readerview/src/main/res/values-sk/strings.xml b/mobile/android/android-components/components/feature/readerview/src/main/res/values-sk/strings.xml new file mode 100644 index 0000000000..77f7c9a80a --- /dev/null +++ b/mobile/android/android-components/components/feature/readerview/src/main/res/values-sk/strings.xml @@ -0,0 +1,30 @@ +<?xml version="1.0" encoding="utf-8"?> +<resources> + <!-- Name for one of the font options available to use --> + <string name="mozac_feature_readerview_sans_serif_font">Bezpätkové</string> + <!-- Accessible description for the font option --> + <string name="mozac_feature_readerview_sans_serif_font_desc">Bezpätkové písmo</string> + <!-- Name for one of the font options available to use --> + <string name="mozac_feature_readerview_serif_font">Pätkové</string> + + <!-- Accessible description for the font option --> + <string name="mozac_feature_readerview_serif_font_desc">Pätkové písmo</string> + + <!-- Accessible description for decreasing the font size --> + <string name="mozac_feature_readerview_font_size_decrease_desc">Zmenšiť písmo</string> + + <!-- Accessible description for increasing the font size --> + <string name="mozac_feature_readerview_font_size_increase_desc">Zväčšiť písmo</string> + <!-- Color option for the background --> + <string name="mozac_feature_readerview_dark">Tmavá</string> + <!-- Accessible description for the color option --> + <string name="mozac_feature_readerview_dark_color_scheme_desc">Tmavá farebná schéma</string> + <!-- Color option for the background --> + <string name="mozac_feature_readerview_sephia">Sépia</string> + <!-- Accessible description for the color option --> + <string name="mozac_feature_readerview_sepia_color_scheme_desc">Farebná schéma sépia</string> + <!-- Color option for the background --> + <string name="mozac_feature_readerview_light">Svetlá</string> + <!-- Accessible description for the color option --> + <string name="mozac_feature_readerview_light_color_scheme_desc">Svetlá farebná schéma</string> +</resources> diff --git a/mobile/android/android-components/components/feature/readerview/src/main/res/values-skr/strings.xml b/mobile/android/android-components/components/feature/readerview/src/main/res/values-skr/strings.xml new file mode 100644 index 0000000000..34b1b31c0d --- /dev/null +++ b/mobile/android/android-components/components/feature/readerview/src/main/res/values-skr/strings.xml @@ -0,0 +1,29 @@ +<?xml version="1.0" encoding="utf-8"?> +<resources> + <!-- Name for one of the font options available to use --> + <string name="mozac_feature_readerview_sans_serif_font">سینس سیرف</string> + <!-- Accessible description for the font option --> + <string name="mozac_feature_readerview_sans_serif_font_desc">سانس سیرف فونٹ</string> + <!-- Name for one of the font options available to use --> + <string name="mozac_feature_readerview_serif_font">سیرف</string> + <!-- Accessible description for the font option --> + <string name="mozac_feature_readerview_serif_font_desc">سیرف فونٹ</string> + + <!-- Accessible description for decreasing the font size --> + <string name="mozac_feature_readerview_font_size_decrease_desc">فونٹ سائز گھٹاؤ</string> + + <!-- Accessible description for increasing the font size --> + <string name="mozac_feature_readerview_font_size_increase_desc">فونٹ سائز ودھاؤ</string> + <!-- Color option for the background --> + <string name="mozac_feature_readerview_dark">شوخ</string> + <!-- Accessible description for the color option --> + <string name="mozac_feature_readerview_dark_color_scheme_desc">شوخ رنگ سکیم</string> + <!-- Color option for the background --> + <string name="mozac_feature_readerview_sephia">سیپیا</string> + <!-- Accessible description for the color option --> + <string name="mozac_feature_readerview_sepia_color_scheme_desc">سیپیا رنگ سکیم</string> + <!-- Color option for the background --> + <string name="mozac_feature_readerview_light">پھکا</string> + <!-- Accessible description for the color option --> + <string name="mozac_feature_readerview_light_color_scheme_desc">پھکا رنگ سکیم</string> +</resources> diff --git a/mobile/android/android-components/components/feature/readerview/src/main/res/values-sl/strings.xml b/mobile/android/android-components/components/feature/readerview/src/main/res/values-sl/strings.xml new file mode 100644 index 0000000000..f4f1048988 --- /dev/null +++ b/mobile/android/android-components/components/feature/readerview/src/main/res/values-sl/strings.xml @@ -0,0 +1,29 @@ +<?xml version="1.0" encoding="utf-8"?> +<resources> + <!-- Name for one of the font options available to use --> + <string name="mozac_feature_readerview_sans_serif_font">Sans serif</string> + <!-- Accessible description for the font option --> + <string name="mozac_feature_readerview_sans_serif_font_desc">Pisava Sans Serif</string> + <!-- Name for one of the font options available to use --> + <string name="mozac_feature_readerview_serif_font">Serif</string> + <!-- Accessible description for the font option --> + <string name="mozac_feature_readerview_serif_font_desc">Pisava Serif</string> + + <!-- Accessible description for decreasing the font size --> + <string name="mozac_feature_readerview_font_size_decrease_desc">Zmanjšanje velikosti pisave</string> + + <!-- Accessible description for increasing the font size --> + <string name="mozac_feature_readerview_font_size_increase_desc">Povečanje velikosti pisave</string> + <!-- Color option for the background --> + <string name="mozac_feature_readerview_dark">Temna</string> + <!-- Accessible description for the color option --> + <string name="mozac_feature_readerview_dark_color_scheme_desc">Temna barvna shema</string> + <!-- Color option for the background --> + <string name="mozac_feature_readerview_sephia">Sepia</string> + <!-- Accessible description for the color option --> + <string name="mozac_feature_readerview_sepia_color_scheme_desc">Sepia barvna shema</string> + <!-- Color option for the background --> + <string name="mozac_feature_readerview_light">Svetla</string> + <!-- Accessible description for the color option --> + <string name="mozac_feature_readerview_light_color_scheme_desc">Svetla barvna shema</string> +</resources> diff --git a/mobile/android/android-components/components/feature/readerview/src/main/res/values-sq/strings.xml b/mobile/android/android-components/components/feature/readerview/src/main/res/values-sq/strings.xml new file mode 100644 index 0000000000..df876b388e --- /dev/null +++ b/mobile/android/android-components/components/feature/readerview/src/main/res/values-sq/strings.xml @@ -0,0 +1,29 @@ +<?xml version="1.0" encoding="utf-8"?> +<resources> + <!-- Name for one of the font options available to use --> + <string name="mozac_feature_readerview_sans_serif_font">Sans serif</string> + <!-- Accessible description for the font option --> + <string name="mozac_feature_readerview_sans_serif_font_desc">Shkronja Sans Serif</string> + <!-- Name for one of the font options available to use --> + <string name="mozac_feature_readerview_serif_font">Serif</string> + <!-- Accessible description for the font option --> + <string name="mozac_feature_readerview_serif_font_desc">Shkronja Serif</string> + + <!-- Accessible description for decreasing the font size --> + <string name="mozac_feature_readerview_font_size_decrease_desc">Zvogëlim madhësie shkronjash</string> + + <!-- Accessible description for increasing the font size --> + <string name="mozac_feature_readerview_font_size_increase_desc">Zmadhim madhësie shkronjash</string> + <!-- Color option for the background --> + <string name="mozac_feature_readerview_dark">I errët</string> + <!-- Accessible description for the color option --> + <string name="mozac_feature_readerview_dark_color_scheme_desc">Skemë ngjyrash të errëta</string> + <!-- Color option for the background --> + <string name="mozac_feature_readerview_sephia">Sepia</string> + <!-- Accessible description for the color option --> + <string name="mozac_feature_readerview_sepia_color_scheme_desc">Skemë ngjyrash sepia</string> + <!-- Color option for the background --> + <string name="mozac_feature_readerview_light">I çelët</string> + <!-- Accessible description for the color option --> + <string name="mozac_feature_readerview_light_color_scheme_desc">Skemë ngjyrash të çelëta</string> +</resources> diff --git a/mobile/android/android-components/components/feature/readerview/src/main/res/values-sr/strings.xml b/mobile/android/android-components/components/feature/readerview/src/main/res/values-sr/strings.xml new file mode 100644 index 0000000000..baf441de8d --- /dev/null +++ b/mobile/android/android-components/components/feature/readerview/src/main/res/values-sr/strings.xml @@ -0,0 +1,29 @@ +<?xml version="1.0" encoding="utf-8"?> +<resources> + <!-- Name for one of the font options available to use --> + <string name="mozac_feature_readerview_sans_serif_font">Бесерифни</string> + <!-- Accessible description for the font option --> + <string name="mozac_feature_readerview_sans_serif_font_desc">Бесерифни фонт</string> + <!-- Name for one of the font options available to use --> + <string name="mozac_feature_readerview_serif_font">Серифни</string> + <!-- Accessible description for the font option --> + <string name="mozac_feature_readerview_serif_font_desc">Серифни фонт</string> + + <!-- Accessible description for decreasing the font size --> + <string name="mozac_feature_readerview_font_size_decrease_desc">Смањење величине фонта</string> + + <!-- Accessible description for increasing the font size --> + <string name="mozac_feature_readerview_font_size_increase_desc">Повећање величине фонта</string> + <!-- Color option for the background --> + <string name="mozac_feature_readerview_dark">Тамна</string> + <!-- Accessible description for the color option --> + <string name="mozac_feature_readerview_dark_color_scheme_desc">Тамна шема боја</string> + <!-- Color option for the background --> + <string name="mozac_feature_readerview_sephia">Сепиа</string> + <!-- Accessible description for the color option --> + <string name="mozac_feature_readerview_sepia_color_scheme_desc">Сепиа шема боја</string> + <!-- Color option for the background --> + <string name="mozac_feature_readerview_light">Светла</string> + <!-- Accessible description for the color option --> + <string name="mozac_feature_readerview_light_color_scheme_desc">Светла шема боја</string> +</resources> diff --git a/mobile/android/android-components/components/feature/readerview/src/main/res/values-su/strings.xml b/mobile/android/android-components/components/feature/readerview/src/main/res/values-su/strings.xml new file mode 100644 index 0000000000..de0dbbb3a0 --- /dev/null +++ b/mobile/android/android-components/components/feature/readerview/src/main/res/values-su/strings.xml @@ -0,0 +1,29 @@ +<?xml version="1.0" encoding="utf-8"?> +<resources> + <!-- Name for one of the font options available to use --> + <string name="mozac_feature_readerview_sans_serif_font">Sans-serif</string> + <!-- Accessible description for the font option --> + <string name="mozac_feature_readerview_sans_serif_font_desc">Font Sans Serif</string> + <!-- Name for one of the font options available to use --> + <string name="mozac_feature_readerview_serif_font">Serif</string> + <!-- Accessible description for the font option --> + <string name="mozac_feature_readerview_serif_font_desc">Font Serif</string> + + <!-- Accessible description for decreasing the font size --> + <string name="mozac_feature_readerview_font_size_decrease_desc">Ukuran font ngaalitan</string> + + <!-- Accessible description for increasing the font size --> + <string name="mozac_feature_readerview_font_size_increase_desc">Ukuran font ngaageungan</string> + <!-- Color option for the background --> + <string name="mozac_feature_readerview_dark">Poék</string> + <!-- Accessible description for the color option --> + <string name="mozac_feature_readerview_dark_color_scheme_desc">Skéma warna poék</string> + <!-- Color option for the background --> + <string name="mozac_feature_readerview_sephia">Sepia</string> + <!-- Accessible description for the color option --> + <string name="mozac_feature_readerview_sepia_color_scheme_desc">Skéma warna sépia</string> + <!-- Color option for the background --> + <string name="mozac_feature_readerview_light">Caang</string> + <!-- Accessible description for the color option --> + <string name="mozac_feature_readerview_light_color_scheme_desc">Skéma warna caang</string> +</resources> diff --git a/mobile/android/android-components/components/feature/readerview/src/main/res/values-sv-rSE/strings.xml b/mobile/android/android-components/components/feature/readerview/src/main/res/values-sv-rSE/strings.xml new file mode 100644 index 0000000000..f740efd80e --- /dev/null +++ b/mobile/android/android-components/components/feature/readerview/src/main/res/values-sv-rSE/strings.xml @@ -0,0 +1,29 @@ +<?xml version="1.0" encoding="utf-8"?> +<resources> + <!-- Name for one of the font options available to use --> + <string name="mozac_feature_readerview_sans_serif_font">Sans serif</string> + <!-- Accessible description for the font option --> + <string name="mozac_feature_readerview_sans_serif_font_desc">Sans Serif typsnitt</string> + <!-- Name for one of the font options available to use --> + <string name="mozac_feature_readerview_serif_font">Serif</string> + <!-- Accessible description for the font option --> + <string name="mozac_feature_readerview_serif_font_desc">Serif typsnitt</string> + + <!-- Accessible description for decreasing the font size --> + <string name="mozac_feature_readerview_font_size_decrease_desc">Teckenstorlek minskar</string> + + <!-- Accessible description for increasing the font size --> + <string name="mozac_feature_readerview_font_size_increase_desc">Teckenstorlek ökar</string> + <!-- Color option for the background --> + <string name="mozac_feature_readerview_dark">Mörkt</string> + <!-- Accessible description for the color option --> + <string name="mozac_feature_readerview_dark_color_scheme_desc">Mörkt färgschema</string> + <!-- Color option for the background --> + <string name="mozac_feature_readerview_sephia">Sepia</string> + <!-- Accessible description for the color option --> + <string name="mozac_feature_readerview_sepia_color_scheme_desc">Sepia färgschema</string> + <!-- Color option for the background --> + <string name="mozac_feature_readerview_light">Ljust</string> + <!-- Accessible description for the color option --> + <string name="mozac_feature_readerview_light_color_scheme_desc">Ljust färgschema</string> +</resources> diff --git a/mobile/android/android-components/components/feature/readerview/src/main/res/values-ta/strings.xml b/mobile/android/android-components/components/feature/readerview/src/main/res/values-ta/strings.xml new file mode 100644 index 0000000000..6249d73494 --- /dev/null +++ b/mobile/android/android-components/components/feature/readerview/src/main/res/values-ta/strings.xml @@ -0,0 +1,29 @@ +<?xml version="1.0" encoding="utf-8"?> +<resources> + <!-- Name for one of the font options available to use --> + <string name="mozac_feature_readerview_sans_serif_font">சான்ஸ் செரிஃப்</string> + <!-- Accessible description for the font option --> + <string name="mozac_feature_readerview_sans_serif_font_desc">சான்ஸ் செரிஃப் எழுத்துரு</string> + <!-- Name for one of the font options available to use --> + <string name="mozac_feature_readerview_serif_font">ஷெரிஃப்</string> + <!-- Accessible description for the font option --> + <string name="mozac_feature_readerview_serif_font_desc">செரிஃப் எழுத்துரு</string> + + <!-- Accessible description for decreasing the font size --> + <string name="mozac_feature_readerview_font_size_decrease_desc">எழுத்துரு அளவு குறைகிறது</string> + + <!-- Accessible description for increasing the font size --> + <string name="mozac_feature_readerview_font_size_increase_desc">எழுத்துரு அளவு அதிகரிக்கும்</string> + <!-- Color option for the background --> + <string name="mozac_feature_readerview_dark">கருமை</string> + <!-- Accessible description for the color option --> + <string name="mozac_feature_readerview_dark_color_scheme_desc">இருண்ட வண்ண திட்டம்</string> + <!-- Color option for the background --> + <string name="mozac_feature_readerview_sephia">செபியா</string> + <!-- Accessible description for the color option --> + <string name="mozac_feature_readerview_sepia_color_scheme_desc">செபியா வண்ணத் திட்டம்</string> + <!-- Color option for the background --> + <string name="mozac_feature_readerview_light">வெளிர்</string> + <!-- Accessible description for the color option --> + <string name="mozac_feature_readerview_light_color_scheme_desc">வெளிர் வண்ணத் திட்டம்</string> +</resources> diff --git a/mobile/android/android-components/components/feature/readerview/src/main/res/values-te/strings.xml b/mobile/android/android-components/components/feature/readerview/src/main/res/values-te/strings.xml new file mode 100644 index 0000000000..9a367ee164 --- /dev/null +++ b/mobile/android/android-components/components/feature/readerview/src/main/res/values-te/strings.xml @@ -0,0 +1,31 @@ +<?xml version="1.0" encoding="utf-8"?> +<resources> + <!-- Name for one of the font options available to use --> + <string name="mozac_feature_readerview_sans_serif_font">సాన్స్ సెరీఫ్</string> + + <!-- Accessible description for the font option --> + <string name="mozac_feature_readerview_sans_serif_font_desc">సాన్స్ సెరిఫ్ ఫాంటు</string> + <!-- Name for one of the font options available to use --> + <string name="mozac_feature_readerview_serif_font">సెరిఫ్</string> + + <!-- Accessible description for the font option --> + <string name="mozac_feature_readerview_serif_font_desc">సెరిఫ్ ఫాంటు</string> + + <!-- Accessible description for decreasing the font size --> + <string name="mozac_feature_readerview_font_size_decrease_desc">ఫాంటు పరిమాణం తగ్గించు</string> + + <!-- Accessible description for increasing the font size --> + <string name="mozac_feature_readerview_font_size_increase_desc">ఫాంటు పరిమాణం పెంచు</string> + <!-- Color option for the background --> + <string name="mozac_feature_readerview_dark">నల్లని</string> + <!-- Accessible description for the color option --> + <string name="mozac_feature_readerview_dark_color_scheme_desc">నల్లని రంగు స్కీము</string> + <!-- Color option for the background --> + <string name="mozac_feature_readerview_sephia">సేపియా</string> + <!-- Accessible description for the color option --> + <string name="mozac_feature_readerview_sepia_color_scheme_desc">సేపియా రంగు స్కీము</string> + <!-- Color option for the background --> + <string name="mozac_feature_readerview_light">లేత</string> + <!-- Accessible description for the color option --> + <string name="mozac_feature_readerview_light_color_scheme_desc">లేత రంగు</string> +</resources> diff --git a/mobile/android/android-components/components/feature/readerview/src/main/res/values-tg/strings.xml b/mobile/android/android-components/components/feature/readerview/src/main/res/values-tg/strings.xml new file mode 100644 index 0000000000..c54770b1e0 --- /dev/null +++ b/mobile/android/android-components/components/feature/readerview/src/main/res/values-tg/strings.xml @@ -0,0 +1,29 @@ +<?xml version="1.0" encoding="utf-8"?> +<resources> + <!-- Name for one of the font options available to use --> + <string name="mozac_feature_readerview_sans_serif_font">Ғайрибарҷаста</string> + <!-- Accessible description for the font option --> + <string name="mozac_feature_readerview_sans_serif_font_desc">Ҳуруфи ғайрибарҷаста</string> + <!-- Name for one of the font options available to use --> + <string name="mozac_feature_readerview_serif_font">Барҷаста</string> + <!-- Accessible description for the font option --> + <string name="mozac_feature_readerview_serif_font_desc">Ҳуруфи барҷаста</string> + + <!-- Accessible description for decreasing the font size --> + <string name="mozac_feature_readerview_font_size_decrease_desc">Хурд кардани андозаи ҳуруф</string> + + <!-- Accessible description for increasing the font size --> + <string name="mozac_feature_readerview_font_size_increase_desc">Калон кардани андозаи ҳуруф</string> + <!-- Color option for the background --> + <string name="mozac_feature_readerview_dark">Торик</string> + <!-- Accessible description for the color option --> + <string name="mozac_feature_readerview_dark_color_scheme_desc">Нақшаи рангии торик</string> + <!-- Color option for the background --> + <string name="mozac_feature_readerview_sephia">Сепия</string> + <!-- Accessible description for the color option --> + <string name="mozac_feature_readerview_sepia_color_scheme_desc">Нақшаи рангии сепия</string> + <!-- Color option for the background --> + <string name="mozac_feature_readerview_light">Равшан</string> + <!-- Accessible description for the color option --> + <string name="mozac_feature_readerview_light_color_scheme_desc">Нақшаи рангии равшан</string> +</resources> diff --git a/mobile/android/android-components/components/feature/readerview/src/main/res/values-th/strings.xml b/mobile/android/android-components/components/feature/readerview/src/main/res/values-th/strings.xml new file mode 100644 index 0000000000..9f55fc255b --- /dev/null +++ b/mobile/android/android-components/components/feature/readerview/src/main/res/values-th/strings.xml @@ -0,0 +1,29 @@ +<?xml version="1.0" encoding="utf-8"?> +<resources> + <!-- Name for one of the font options available to use --> + <string name="mozac_feature_readerview_sans_serif_font">ไม่มีเชิง</string> + <!-- Accessible description for the font option --> + <string name="mozac_feature_readerview_sans_serif_font_desc">แบบอักษรไม่มีเชิง</string> + <!-- Name for one of the font options available to use --> + <string name="mozac_feature_readerview_serif_font">มีเชิง</string> + <!-- Accessible description for the font option --> + <string name="mozac_feature_readerview_serif_font_desc">แบบอักษรมีเชิง</string> + + <!-- Accessible description for decreasing the font size --> + <string name="mozac_feature_readerview_font_size_decrease_desc">ลดขนาดแบบอักษร</string> + + <!-- Accessible description for increasing the font size --> + <string name="mozac_feature_readerview_font_size_increase_desc">เพิ่มขนาดแบบอักษร</string> + <!-- Color option for the background --> + <string name="mozac_feature_readerview_dark">มืด</string> + <!-- Accessible description for the color option --> + <string name="mozac_feature_readerview_dark_color_scheme_desc">แบบแผนสีมืด</string> + <!-- Color option for the background --> + <string name="mozac_feature_readerview_sephia">ซีเปีย</string> + <!-- Accessible description for the color option --> + <string name="mozac_feature_readerview_sepia_color_scheme_desc">แบบแผนสีซีเปีย</string> + <!-- Color option for the background --> + <string name="mozac_feature_readerview_light">สว่าง</string> + <!-- Accessible description for the color option --> + <string name="mozac_feature_readerview_light_color_scheme_desc">แบบแผนสีสว่าง</string> +</resources> diff --git a/mobile/android/android-components/components/feature/readerview/src/main/res/values-tl/strings.xml b/mobile/android/android-components/components/feature/readerview/src/main/res/values-tl/strings.xml new file mode 100644 index 0000000000..b67303393c --- /dev/null +++ b/mobile/android/android-components/components/feature/readerview/src/main/res/values-tl/strings.xml @@ -0,0 +1,29 @@ +<?xml version="1.0" encoding="utf-8"?> +<resources> + <!-- Name for one of the font options available to use --> + <string name="mozac_feature_readerview_sans_serif_font">Sans serif</string> + <!-- Accessible description for the font option --> + <string name="mozac_feature_readerview_sans_serif_font_desc">Sans Serif font</string> + <!-- Name for one of the font options available to use --> + <string name="mozac_feature_readerview_serif_font">Serif</string> + <!-- Accessible description for the font option --> + <string name="mozac_feature_readerview_serif_font_desc">Serif font</string> + + <!-- Accessible description for decreasing the font size --> + <string name="mozac_feature_readerview_font_size_decrease_desc">Liitan ang font size</string> + + <!-- Accessible description for increasing the font size --> + <string name="mozac_feature_readerview_font_size_increase_desc">Lakihan ang font size</string> + <!-- Color option for the background --> + <string name="mozac_feature_readerview_dark">Madilim</string> + <!-- Accessible description for the color option --> + <string name="mozac_feature_readerview_dark_color_scheme_desc">Madilim na color scheme</string> + <!-- Color option for the background --> + <string name="mozac_feature_readerview_sephia">Sepia</string> + <!-- Accessible description for the color option --> + <string name="mozac_feature_readerview_sepia_color_scheme_desc">Sepia color scheme</string> + <!-- Color option for the background --> + <string name="mozac_feature_readerview_light">Maliwanag</string> + <!-- Accessible description for the color option --> + <string name="mozac_feature_readerview_light_color_scheme_desc">Maliwanag na color scheme</string> +</resources> diff --git a/mobile/android/android-components/components/feature/readerview/src/main/res/values-tok/strings.xml b/mobile/android/android-components/components/feature/readerview/src/main/res/values-tok/strings.xml new file mode 100644 index 0000000000..e4cd63a232 --- /dev/null +++ b/mobile/android/android-components/components/feature/readerview/src/main/res/values-tok/strings.xml @@ -0,0 +1,30 @@ +<?xml version="1.0" encoding="utf-8"?> +<resources> + + <!-- Name for one of the font options available to use --> + <string name="mozac_feature_readerview_sans_serif_font">linja lili ala</string> + <!-- Accessible description for the font option --> + <string name="mozac_feature_readerview_sans_serif_font_desc">nasin sitelen pi linja lili ala</string> + <!-- Name for one of the font options available to use --> + <string name="mozac_feature_readerview_serif_font">linja lili</string> + <!-- Accessible description for the font option --> + <string name="mozac_feature_readerview_serif_font_desc">nasin sitelen pi linja lili</string> + + <!-- Accessible description for decreasing the font size --> + <string name="mozac_feature_readerview_font_size_decrease_desc">sitelen li kama lili</string> + + <!-- Accessible description for increasing the font size --> + <string name="mozac_feature_readerview_font_size_increase_desc">sitelen li kama suli</string> + <!-- Color option for the background --> + <string name="mozac_feature_readerview_dark">pimeja</string> + <!-- Accessible description for the color option --> + <string name="mozac_feature_readerview_dark_color_scheme_desc">nasin lukin pimeja</string> + <!-- Color option for the background --> + <string name="mozac_feature_readerview_sephia">loje lili</string> + <!-- Accessible description for the color option --> + <string name="mozac_feature_readerview_sepia_color_scheme_desc">nasin lukin pi loje lili</string> + <!-- Color option for the background --> + <string name="mozac_feature_readerview_light">suno</string> + <!-- Accessible description for the color option --> + <string name="mozac_feature_readerview_light_color_scheme_desc">nasin lukin suno</string> +</resources> diff --git a/mobile/android/android-components/components/feature/readerview/src/main/res/values-tr/strings.xml b/mobile/android/android-components/components/feature/readerview/src/main/res/values-tr/strings.xml new file mode 100644 index 0000000000..3f8430132c --- /dev/null +++ b/mobile/android/android-components/components/feature/readerview/src/main/res/values-tr/strings.xml @@ -0,0 +1,30 @@ +<?xml version="1.0" encoding="utf-8"?> +<resources> + <!-- Name for one of the font options available to use --> + <string name="mozac_feature_readerview_sans_serif_font">Sans serif</string> + <!-- Accessible description for the font option --> + <string name="mozac_feature_readerview_sans_serif_font_desc">Sans serif yazı tipi</string> + + <!-- Name for one of the font options available to use --> + <string name="mozac_feature_readerview_serif_font">Serif</string> + <!-- Accessible description for the font option --> + <string name="mozac_feature_readerview_serif_font_desc">Serif yazı tipi</string> + + <!-- Accessible description for decreasing the font size --> + <string name="mozac_feature_readerview_font_size_decrease_desc">Yazı tipi boyutu küçültme</string> + + <!-- Accessible description for increasing the font size --> + <string name="mozac_feature_readerview_font_size_increase_desc">Yazı tipi boyutu büyütme</string> + <!-- Color option for the background --> + <string name="mozac_feature_readerview_dark">Koyu</string> + <!-- Accessible description for the color option --> + <string name="mozac_feature_readerview_dark_color_scheme_desc">Koyu renk şeması</string> + <!-- Color option for the background --> + <string name="mozac_feature_readerview_sephia">Sepya</string> + <!-- Accessible description for the color option --> + <string name="mozac_feature_readerview_sepia_color_scheme_desc">Sepya renk şeması</string> + <!-- Color option for the background --> + <string name="mozac_feature_readerview_light">Açık</string> + <!-- Accessible description for the color option --> + <string name="mozac_feature_readerview_light_color_scheme_desc">Açık renk şeması</string> +</resources> diff --git a/mobile/android/android-components/components/feature/readerview/src/main/res/values-trs/strings.xml b/mobile/android/android-components/components/feature/readerview/src/main/res/values-trs/strings.xml new file mode 100644 index 0000000000..b494597f2f --- /dev/null +++ b/mobile/android/android-components/components/feature/readerview/src/main/res/values-trs/strings.xml @@ -0,0 +1,30 @@ +<?xml version="1.0" encoding="utf-8"?> +<resources> + <!-- Name for one of the font options available to use --> + <string name="mozac_feature_readerview_sans_serif_font">Sans serif</string> + + <!-- Accessible description for the font option --> + <string name="mozac_feature_readerview_sans_serif_font_desc">Lechrâ Sans Serif</string> + <!-- Name for one of the font options available to use --> + <string name="mozac_feature_readerview_serif_font">Serif</string> + <!-- Accessible description for the font option --> + <string name="mozac_feature_readerview_serif_font_desc">Lechrâ Serif</string> + + <!-- Accessible description for decreasing the font size --> + <string name="mozac_feature_readerview_font_size_decrease_desc">Nāgi\'iaj lij lêchra</string> + + <!-- Accessible description for increasing the font size --> + <string name="mozac_feature_readerview_font_size_increase_desc">Nāgi\'iaj yāchìj lêchra</string> + <!-- Color option for the background --> + <string name="mozac_feature_readerview_dark">Gā rūmin\' man</string> + <!-- Accessible description for the color option --> + <string name="mozac_feature_readerview_dark_color_scheme_desc">Nej dūguì\' kolô rūmin\'in</string> + <!-- Color option for the background --> + <string name="mozac_feature_readerview_sephia">Rūguì\'i</string> + <!-- Accessible description for the color option --> + <string name="mozac_feature_readerview_sepia_color_scheme_desc">Nej dūguì\' kolô rūguì\'i</string> + <!-- Color option for the background --> + <string name="mozac_feature_readerview_light">Sa yigìïn</string> + <!-- Accessible description for the color option --> + <string name="mozac_feature_readerview_light_color_scheme_desc">Nej dūguì\' kolô yigìïn</string> +</resources> diff --git a/mobile/android/android-components/components/feature/readerview/src/main/res/values-tt/strings.xml b/mobile/android/android-components/components/feature/readerview/src/main/res/values-tt/strings.xml new file mode 100644 index 0000000000..9b3aa9dcb3 --- /dev/null +++ b/mobile/android/android-components/components/feature/readerview/src/main/res/values-tt/strings.xml @@ -0,0 +1,29 @@ +<?xml version="1.0" encoding="utf-8"?> +<resources> + <!-- Name for one of the font options available to use --> + <string name="mozac_feature_readerview_sans_serif_font">Sans serif</string> + <!-- Accessible description for the font option --> + <string name="mozac_feature_readerview_sans_serif_font_desc">Sans Serif шрифты</string> + <!-- Name for one of the font options available to use --> + <string name="mozac_feature_readerview_serif_font">Serif</string> + <!-- Accessible description for the font option --> + <string name="mozac_feature_readerview_serif_font_desc">Serif шрифты</string> + + <!-- Accessible description for decreasing the font size --> + <string name="mozac_feature_readerview_font_size_decrease_desc">Шрифт үлчәмен кечерәйтү</string> + + <!-- Accessible description for increasing the font size --> + <string name="mozac_feature_readerview_font_size_increase_desc">Шрифт үлчәмен зурайту</string> + <!-- Color option for the background --> + <string name="mozac_feature_readerview_dark">Караңгы</string> + <!-- Accessible description for the color option --> + <string name="mozac_feature_readerview_dark_color_scheme_desc">Караңгы төсле схема</string> + <!-- Color option for the background --> + <string name="mozac_feature_readerview_sephia">Сепия</string> + <!-- Accessible description for the color option --> + <string name="mozac_feature_readerview_sepia_color_scheme_desc">Сепия төсләр схемасы</string> + <!-- Color option for the background --> + <string name="mozac_feature_readerview_light">Ачык</string> + <!-- Accessible description for the color option --> + <string name="mozac_feature_readerview_light_color_scheme_desc">Ачык төсле схема</string> +</resources> diff --git a/mobile/android/android-components/components/feature/readerview/src/main/res/values-tzm/strings.xml b/mobile/android/android-components/components/feature/readerview/src/main/res/values-tzm/strings.xml new file mode 100644 index 0000000000..c684bc547f --- /dev/null +++ b/mobile/android/android-components/components/feature/readerview/src/main/res/values-tzm/strings.xml @@ -0,0 +1,13 @@ +<?xml version="1.0" encoding="utf-8"?> +<resources> + + <!-- Name for one of the font options available to use --> + <string name="mozac_feature_readerview_serif_font">Serif</string> + <!-- Accessible description for the font option --> + <string name="mozac_feature_readerview_serif_font_desc">Tasefsit Serif</string> + + <!-- Color option for the background --> + <string name="mozac_feature_readerview_dark">Adeɣmum</string> + <!-- Color option for the background --> + <string name="mozac_feature_readerview_light">Anafaw</string> + </resources> diff --git a/mobile/android/android-components/components/feature/readerview/src/main/res/values-ug/strings.xml b/mobile/android/android-components/components/feature/readerview/src/main/res/values-ug/strings.xml new file mode 100644 index 0000000000..164979ae7a --- /dev/null +++ b/mobile/android/android-components/components/feature/readerview/src/main/res/values-ug/strings.xml @@ -0,0 +1,30 @@ +<?xml version="1.0" encoding="utf-8"?> +<resources> + + <!-- Name for one of the font options available to use --> + <string name="mozac_feature_readerview_sans_serif_font">Sans serif</string> + <!-- Accessible description for the font option --> + <string name="mozac_feature_readerview_sans_serif_font_desc">Sans Serif خەت نۇسخا</string> + <!-- Name for one of the font options available to use --> + <string name="mozac_feature_readerview_serif_font">Serif</string> + <!-- Accessible description for the font option --> + <string name="mozac_feature_readerview_serif_font_desc">Serif خەت نۇسخا</string> + + <!-- Accessible description for decreasing the font size --> + <string name="mozac_feature_readerview_font_size_decrease_desc">خەت كىچىكلىتىش</string> + + <!-- Accessible description for increasing the font size --> + <string name="mozac_feature_readerview_font_size_increase_desc">خەت چوڭايتىش</string> + <!-- Color option for the background --> + <string name="mozac_feature_readerview_dark">قاراڭغۇ</string> + <!-- Accessible description for the color option --> + <string name="mozac_feature_readerview_dark_color_scheme_desc">قېنىق رەڭ لايىھەسى</string> + <!-- Color option for the background --> + <string name="mozac_feature_readerview_sephia">قېنىق قوڭۇر رەڭ</string> + <!-- Accessible description for the color option --> + <string name="mozac_feature_readerview_sepia_color_scheme_desc">قېنىق قوڭۇر رەڭ لايىھەسى</string> + <!-- Color option for the background --> + <string name="mozac_feature_readerview_light">يورۇق</string> + <!-- Accessible description for the color option --> + <string name="mozac_feature_readerview_light_color_scheme_desc">سۇس رەڭ لايىھەسى</string> +</resources> diff --git a/mobile/android/android-components/components/feature/readerview/src/main/res/values-uk/strings.xml b/mobile/android/android-components/components/feature/readerview/src/main/res/values-uk/strings.xml new file mode 100644 index 0000000000..43ff1f3b0c --- /dev/null +++ b/mobile/android/android-components/components/feature/readerview/src/main/res/values-uk/strings.xml @@ -0,0 +1,29 @@ +<?xml version="1.0" encoding="utf-8"?> +<resources> + <!-- Name for one of the font options available to use --> + <string name="mozac_feature_readerview_sans_serif_font">Без зарубок</string> + <!-- Accessible description for the font option --> + <string name="mozac_feature_readerview_sans_serif_font_desc">Шрифт без зарубок</string> + <!-- Name for one of the font options available to use --> + <string name="mozac_feature_readerview_serif_font">Із зарубками</string> + <!-- Accessible description for the font option --> + <string name="mozac_feature_readerview_serif_font_desc">Шрифт із зарубками</string> + + <!-- Accessible description for decreasing the font size --> + <string name="mozac_feature_readerview_font_size_decrease_desc">Зменшити розмір шрифту</string> + + <!-- Accessible description for increasing the font size --> + <string name="mozac_feature_readerview_font_size_increase_desc">Збільшити розмір шрифту</string> + <!-- Color option for the background --> + <string name="mozac_feature_readerview_dark">Темна</string> + <!-- Accessible description for the color option --> + <string name="mozac_feature_readerview_dark_color_scheme_desc">Темна колірна тема</string> + <!-- Color option for the background --> + <string name="mozac_feature_readerview_sephia">Сепія</string> + <!-- Accessible description for the color option --> + <string name="mozac_feature_readerview_sepia_color_scheme_desc">Колірна тема сепія</string> + <!-- Color option for the background --> + <string name="mozac_feature_readerview_light">Світла</string> + <!-- Accessible description for the color option --> + <string name="mozac_feature_readerview_light_color_scheme_desc">Світла колірна тема</string> +</resources> diff --git a/mobile/android/android-components/components/feature/readerview/src/main/res/values-ur/strings.xml b/mobile/android/android-components/components/feature/readerview/src/main/res/values-ur/strings.xml new file mode 100644 index 0000000000..3da140cb4e --- /dev/null +++ b/mobile/android/android-components/components/feature/readerview/src/main/res/values-ur/strings.xml @@ -0,0 +1,29 @@ +<?xml version="1.0" encoding="utf-8"?> +<resources> + <!-- Name for one of the font options available to use --> + <string name="mozac_feature_readerview_sans_serif_font">سینس سیرف</string> + <!-- Accessible description for the font option --> + <string name="mozac_feature_readerview_sans_serif_font_desc">سانس سیرف فونٹ</string> + <!-- Name for one of the font options available to use --> + <string name="mozac_feature_readerview_serif_font">سیرف</string> + <!-- Accessible description for the font option --> + <string name="mozac_feature_readerview_serif_font_desc">سیرف فونٹ</string> + + <!-- Accessible description for decreasing the font size --> + <string name="mozac_feature_readerview_font_size_decrease_desc">فونٹ سائز کم کریں</string> + + <!-- Accessible description for increasing the font size --> + <string name="mozac_feature_readerview_font_size_increase_desc">فونٹ سائز اضافہ کریں</string> + <!-- Color option for the background --> + <string name="mozac_feature_readerview_dark">گہرا</string> + <!-- Accessible description for the color option --> + <string name="mozac_feature_readerview_dark_color_scheme_desc">گہرے رنگ کی اسکیم</string> + <!-- Color option for the background --> + <string name="mozac_feature_readerview_sephia">سیپیا</string> + <!-- Accessible description for the color option --> + <string name="mozac_feature_readerview_sepia_color_scheme_desc">سیپیا رنگ سکیم</string> + <!-- Color option for the background --> + <string name="mozac_feature_readerview_light">ہلکا</string> + <!-- Accessible description for the color option --> + <string name="mozac_feature_readerview_light_color_scheme_desc">ہلکے رنگ کی اسکیم</string> +</resources> diff --git a/mobile/android/android-components/components/feature/readerview/src/main/res/values-uz/strings.xml b/mobile/android/android-components/components/feature/readerview/src/main/res/values-uz/strings.xml new file mode 100644 index 0000000000..d0ac181290 --- /dev/null +++ b/mobile/android/android-components/components/feature/readerview/src/main/res/values-uz/strings.xml @@ -0,0 +1,29 @@ +<?xml version="1.0" encoding="utf-8"?> +<resources> + <!-- Name for one of the font options available to use --> + <string name="mozac_feature_readerview_sans_serif_font">Sans serif</string> + <!-- Accessible description for the font option --> + <string name="mozac_feature_readerview_sans_serif_font_desc">Sans Serif shrifti</string> + <!-- Name for one of the font options available to use --> + <string name="mozac_feature_readerview_serif_font">Serif</string> + <!-- Accessible description for the font option --> + <string name="mozac_feature_readerview_serif_font_desc">Serif shrifti</string> + + <!-- Accessible description for decreasing the font size --> + <string name="mozac_feature_readerview_font_size_decrease_desc">Shrift hajmini kichraytirish</string> + + <!-- Accessible description for increasing the font size --> + <string name="mozac_feature_readerview_font_size_increase_desc">Shrift hajmini kattalashtirish</string> + <!-- Color option for the background --> + <string name="mozac_feature_readerview_dark">Qora</string> + <!-- Accessible description for the color option --> + <string name="mozac_feature_readerview_dark_color_scheme_desc">“Qora” rang sxemasi</string> + <!-- Color option for the background --> + <string name="mozac_feature_readerview_sephia">Sepia</string> + <!-- Accessible description for the color option --> + <string name="mozac_feature_readerview_sepia_color_scheme_desc">“Sepiya” rang sxemasi</string> + <!-- Color option for the background --> + <string name="mozac_feature_readerview_light">Yorqin</string> + <!-- Accessible description for the color option --> + <string name="mozac_feature_readerview_light_color_scheme_desc">“Yorqin” rang sxemasi</string> +</resources> diff --git a/mobile/android/android-components/components/feature/readerview/src/main/res/values-vec/strings.xml b/mobile/android/android-components/components/feature/readerview/src/main/res/values-vec/strings.xml new file mode 100644 index 0000000000..5802794118 --- /dev/null +++ b/mobile/android/android-components/components/feature/readerview/src/main/res/values-vec/strings.xml @@ -0,0 +1,29 @@ +<?xml version="1.0" encoding="utf-8"?> +<resources> + <!-- Name for one of the font options available to use --> + <string name="mozac_feature_readerview_sans_serif_font">Sans serif</string> + <!-- Accessible description for the font option --> + <string name="mozac_feature_readerview_sans_serif_font_desc">Sans Serif font</string> + <!-- Name for one of the font options available to use --> + <string name="mozac_feature_readerview_serif_font">Serif</string> + <!-- Accessible description for the font option --> + <string name="mozac_feature_readerview_serif_font_desc">Serif font</string> + + <!-- Accessible description for decreasing the font size --> + <string name="mozac_feature_readerview_font_size_decrease_desc">Ridusi dimension de i caratari</string> + + <!-- Accessible description for increasing the font size --> + <string name="mozac_feature_readerview_font_size_increase_desc">Aumenta dimension de i caratari</string> + <!-- Color option for the background --> + <string name="mozac_feature_readerview_dark">Scuro</string> + <!-- Accessible description for the color option --> + <string name="mozac_feature_readerview_dark_color_scheme_desc">Conbinasion de coƚori scura</string> + <!-- Color option for the background --> + <string name="mozac_feature_readerview_sephia">Sepa</string> + <!-- Accessible description for the color option --> + <string name="mozac_feature_readerview_sepia_color_scheme_desc">Conbinasion de coƚori sepa</string> + <!-- Color option for the background --> + <string name="mozac_feature_readerview_light">Ciaro</string> + <!-- Accessible description for the color option --> + <string name="mozac_feature_readerview_light_color_scheme_desc">Conbinasion de coƚori ciara</string> +</resources> diff --git a/mobile/android/android-components/components/feature/readerview/src/main/res/values-vi/strings.xml b/mobile/android/android-components/components/feature/readerview/src/main/res/values-vi/strings.xml new file mode 100644 index 0000000000..116ecd027d --- /dev/null +++ b/mobile/android/android-components/components/feature/readerview/src/main/res/values-vi/strings.xml @@ -0,0 +1,29 @@ +<?xml version="1.0" encoding="utf-8"?> +<resources> + <!-- Name for one of the font options available to use --> + <string name="mozac_feature_readerview_sans_serif_font">Sans serif</string> + <!-- Accessible description for the font option --> + <string name="mozac_feature_readerview_sans_serif_font_desc">Phông Sans Serif</string> + <!-- Name for one of the font options available to use --> + <string name="mozac_feature_readerview_serif_font">Serif</string> + <!-- Accessible description for the font option --> + <string name="mozac_feature_readerview_serif_font_desc">Phông Serif</string> + + <!-- Accessible description for decreasing the font size --> + <string name="mozac_feature_readerview_font_size_decrease_desc">Giảm cỡ chữ</string> + + <!-- Accessible description for increasing the font size --> + <string name="mozac_feature_readerview_font_size_increase_desc">Tăng cỡ chữ</string> + <!-- Color option for the background --> + <string name="mozac_feature_readerview_dark">Tối</string> + <!-- Accessible description for the color option --> + <string name="mozac_feature_readerview_dark_color_scheme_desc">Phối màu tối</string> + <!-- Color option for the background --> + <string name="mozac_feature_readerview_sephia">Nâu đen</string> + <!-- Accessible description for the color option --> + <string name="mozac_feature_readerview_sepia_color_scheme_desc">Phối màu nâu đen</string> + <!-- Color option for the background --> + <string name="mozac_feature_readerview_light">Sáng</string> + <!-- Accessible description for the color option --> + <string name="mozac_feature_readerview_light_color_scheme_desc">Phối màu sáng</string> +</resources> diff --git a/mobile/android/android-components/components/feature/readerview/src/main/res/values-yo/strings.xml b/mobile/android/android-components/components/feature/readerview/src/main/res/values-yo/strings.xml new file mode 100644 index 0000000000..135da6274e --- /dev/null +++ b/mobile/android/android-components/components/feature/readerview/src/main/res/values-yo/strings.xml @@ -0,0 +1,29 @@ +<?xml version="1.0" encoding="utf-8"?> +<resources> + <!-- Name for one of the font options available to use --> + <string name="mozac_feature_readerview_sans_serif_font">Sans serif</string> + <!-- Accessible description for the font option --> + <string name="mozac_feature_readerview_sans_serif_font_desc">Sans Serif font</string> + <!-- Name for one of the font options available to use --> + <string name="mozac_feature_readerview_serif_font">Serif</string> + <!-- Accessible description for the font option --> + <string name="mozac_feature_readerview_serif_font_desc">Fọ́ǹtì sérọ̀fù</string> + + <!-- Accessible description for decreasing the font size --> + <string name="mozac_feature_readerview_font_size_decrease_desc">Ìwọn fọ́ǹtì dínkù</string> + + <!-- Accessible description for increasing the font size --> + <string name="mozac_feature_readerview_font_size_increase_desc">Ìwọn fọ́ǹtì pọ̀ si</string> + <!-- Color option for the background --> + <string name="mozac_feature_readerview_dark">Dúdú</string> + <!-- Accessible description for the color option --> + <string name="mozac_feature_readerview_dark_color_scheme_desc">Ìlànà aláwọ̀ dúdú</string> + <!-- Color option for the background --> + <string name="mozac_feature_readerview_sephia">Sepia</string> + <!-- Accessible description for the color option --> + <string name="mozac_feature_readerview_sepia_color_scheme_desc">Ìlànà aláwọ̀ Sepia</string> + <!-- Color option for the background --> + <string name="mozac_feature_readerview_light">Mọ́lẹ̀</string> + <!-- Accessible description for the color option --> + <string name="mozac_feature_readerview_light_color_scheme_desc">Ìlànà aláwọ̀ tó mọ́lẹ̀</string> +</resources> diff --git a/mobile/android/android-components/components/feature/readerview/src/main/res/values-zh-rCN/strings.xml b/mobile/android/android-components/components/feature/readerview/src/main/res/values-zh-rCN/strings.xml new file mode 100644 index 0000000000..059aab3a88 --- /dev/null +++ b/mobile/android/android-components/components/feature/readerview/src/main/res/values-zh-rCN/strings.xml @@ -0,0 +1,29 @@ +<?xml version="1.0" encoding="utf-8"?> +<resources> + <!-- Name for one of the font options available to use --> + <string name="mozac_feature_readerview_sans_serif_font">无衬线</string> + <!-- Accessible description for the font option --> + <string name="mozac_feature_readerview_sans_serif_font_desc">无衬线字体</string> + <!-- Name for one of the font options available to use --> + <string name="mozac_feature_readerview_serif_font">衬线</string> + <!-- Accessible description for the font option --> + <string name="mozac_feature_readerview_serif_font_desc">衬线字体</string> + + <!-- Accessible description for decreasing the font size --> + <string name="mozac_feature_readerview_font_size_decrease_desc">缩小文字</string> + + <!-- Accessible description for increasing the font size --> + <string name="mozac_feature_readerview_font_size_increase_desc">增大文字</string> + <!-- Color option for the background --> + <string name="mozac_feature_readerview_dark">深邃</string> + <!-- Accessible description for the color option --> + <string name="mozac_feature_readerview_dark_color_scheme_desc">深邃配色</string> + <!-- Color option for the background --> + <string name="mozac_feature_readerview_sephia">纸墨</string> + <!-- Accessible description for the color option --> + <string name="mozac_feature_readerview_sepia_color_scheme_desc">纸墨配色</string> + <!-- Color option for the background --> + <string name="mozac_feature_readerview_light">明亮</string> + <!-- Accessible description for the color option --> + <string name="mozac_feature_readerview_light_color_scheme_desc">明亮配色</string> +</resources> diff --git a/mobile/android/android-components/components/feature/readerview/src/main/res/values-zh-rTW/strings.xml b/mobile/android/android-components/components/feature/readerview/src/main/res/values-zh-rTW/strings.xml new file mode 100644 index 0000000000..21f91277d3 --- /dev/null +++ b/mobile/android/android-components/components/feature/readerview/src/main/res/values-zh-rTW/strings.xml @@ -0,0 +1,30 @@ +<?xml version="1.0" encoding="utf-8"?> +<resources> + + <!-- Name for one of the font options available to use --> + <string name="mozac_feature_readerview_sans_serif_font">無襯線</string> + <!-- Accessible description for the font option --> + <string name="mozac_feature_readerview_sans_serif_font_desc">無襯線字型</string> + <!-- Name for one of the font options available to use --> + <string name="mozac_feature_readerview_serif_font">襯線字</string> + <!-- Accessible description for the font option --> + <string name="mozac_feature_readerview_serif_font_desc">襯線字型</string> + + <!-- Accessible description for decreasing the font size --> + <string name="mozac_feature_readerview_font_size_decrease_desc">縮小字體</string> + + <!-- Accessible description for increasing the font size --> + <string name="mozac_feature_readerview_font_size_increase_desc">放大字體</string> + <!-- Color option for the background --> + <string name="mozac_feature_readerview_dark">暗色</string> + <!-- Accessible description for the color option --> + <string name="mozac_feature_readerview_dark_color_scheme_desc">暗色佈景主題</string> + <!-- Color option for the background --> + <string name="mozac_feature_readerview_sephia">懷舊風</string> + <!-- Accessible description for the color option --> + <string name="mozac_feature_readerview_sepia_color_scheme_desc">棕褐色調佈景主題</string> + <!-- Color option for the background --> + <string name="mozac_feature_readerview_light">亮色</string> + <!-- Accessible description for the color option --> + <string name="mozac_feature_readerview_light_color_scheme_desc">亮色佈景主題</string> +</resources> diff --git a/mobile/android/android-components/components/feature/readerview/src/main/res/values/attrs.xml b/mobile/android/android-components/components/feature/readerview/src/main/res/values/attrs.xml new file mode 100644 index 0000000000..71637e8804 --- /dev/null +++ b/mobile/android/android-components/components/feature/readerview/src/main/res/values/attrs.xml @@ -0,0 +1,26 @@ +<?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/. + --> + +<resources> + <declare-styleable name="ReaderViewControlsBar"> + <attr name="readerViewControlsBackgroundColor" format="reference|color" /> + <attr name="readerViewControlsSelectedColor" format="reference|color" /> + <attr name="readerViewControlsTextColor" format="reference|color" /> + </declare-styleable> + + <style name="RadioButtonSelectedTextStyle" parent="@android:style/Widget.CompoundButton"> + <item name="android:textColor">@drawable/radiobutton_selected_text_selector</item> + <item name="android:button">@null</item> + <item name="android:padding">16dp</item> + </style> + + <style name="RadioButtonSelectedBorderStyle" parent="@android:style/Widget.CompoundButton"> + <item name="android:button">@null</item> + <item name="android:padding">16dp</item> + <item name="android:textStyle">bold</item> + </style> +</resources> diff --git a/mobile/android/android-components/components/feature/readerview/src/main/res/values/colors.xml b/mobile/android/android-components/components/feature/readerview/src/main/res/values/colors.xml new file mode 100644 index 0000000000..94136325dd --- /dev/null +++ b/mobile/android/android-components/components/feature/readerview/src/main/res/values/colors.xml @@ -0,0 +1,15 @@ +<?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/. + --> + +<resources> + <color name="mozac_feature_readerview_selected">#592ACB</color> + <color name="mozac_feature_readerview_text_color">#220033</color> + <color name="mozac_feature_readerview_text_color_disabled">#B1B1B3</color> + <color name="mozac_feature_readerview_dark_background">#32313B</color> + <color name="mozac_feature_readerview_light_background">#EFEFF2</color> + <color name="mozac_feature_readerview_sepia_background">#FDF4E0</color> +</resources>
\ No newline at end of file diff --git a/mobile/android/android-components/components/feature/readerview/src/main/res/values/mozac_feature_readerview_strings.xml b/mobile/android/android-components/components/feature/readerview/src/main/res/values/mozac_feature_readerview_strings.xml new file mode 100644 index 0000000000..cca9a042ff --- /dev/null +++ b/mobile/android/android-components/components/feature/readerview/src/main/res/values/mozac_feature_readerview_strings.xml @@ -0,0 +1,11 @@ +<?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/. + --> + +<resources> + <string name="mozac_feature_readerview_negative_sign" translatable="false">−</string> + <string name="mozac_feature_readerview_positive_sign" translatable="false">+</string> +</resources>
\ No newline at end of file diff --git a/mobile/android/android-components/components/feature/readerview/src/main/res/values/strings.xml b/mobile/android/android-components/components/feature/readerview/src/main/res/values/strings.xml new file mode 100644 index 0000000000..bd212d57fe --- /dev/null +++ b/mobile/android/android-components/components/feature/readerview/src/main/res/values/strings.xml @@ -0,0 +1,35 @@ +<?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/. + --> + +<resources> + <!-- Name for one of the font options available to use --> + <string name="mozac_feature_readerview_sans_serif_font">Sans serif</string> + <!-- Accessible description for the font option --> + <string name="mozac_feature_readerview_sans_serif_font_desc">Sans Serif font</string> + <!-- Name for one of the font options available to use --> + <string name="mozac_feature_readerview_serif_font">Serif</string> + <!-- Accessible description for the font option --> + <string name="mozac_feature_readerview_serif_font_desc">Serif font</string> + + <!-- Accessible description for decreasing the font size --> + <string name="mozac_feature_readerview_font_size_decrease_desc">Font size decrease</string> + + <!-- Accessible description for increasing the font size --> + <string name="mozac_feature_readerview_font_size_increase_desc">Font size increase</string> + <!-- Color option for the background --> + <string name="mozac_feature_readerview_dark">Dark</string> + <!-- Accessible description for the color option --> + <string name="mozac_feature_readerview_dark_color_scheme_desc">Dark color scheme</string> + <!-- Color option for the background --> + <string name="mozac_feature_readerview_sephia">Sepia</string> + <!-- Accessible description for the color option --> + <string name="mozac_feature_readerview_sepia_color_scheme_desc">Sepia color scheme</string> + <!-- Color option for the background --> + <string name="mozac_feature_readerview_light">Light</string> + <!-- Accessible description for the color option --> + <string name="mozac_feature_readerview_light_color_scheme_desc">Light color scheme</string> +</resources>
\ No newline at end of file diff --git a/mobile/android/android-components/components/feature/readerview/src/test/java/mozilla/components/feature/readerview/ReaderViewFeatureTest.kt b/mobile/android/android-components/components/feature/readerview/src/test/java/mozilla/components/feature/readerview/ReaderViewFeatureTest.kt new file mode 100644 index 0000000000..a4b2536ad5 --- /dev/null +++ b/mobile/android/android-components/components/feature/readerview/src/test/java/mozilla/components/feature/readerview/ReaderViewFeatureTest.kt @@ -0,0 +1,608 @@ +/* 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.readerview + +import android.content.Context +import android.view.View +import androidx.test.ext.junit.runners.AndroidJUnit4 +import mozilla.components.browser.state.action.ContentAction +import mozilla.components.browser.state.action.EngineAction +import mozilla.components.browser.state.action.ReaderAction +import mozilla.components.browser.state.action.TabListAction +import mozilla.components.browser.state.state.BrowserState +import mozilla.components.browser.state.state.ReaderState +import mozilla.components.browser.state.state.TabSessionState +import mozilla.components.browser.state.state.createTab +import mozilla.components.browser.state.store.BrowserStore +import mozilla.components.concept.engine.Engine +import mozilla.components.concept.engine.EngineSession +import mozilla.components.concept.engine.webextension.MessageHandler +import mozilla.components.concept.engine.webextension.Port +import mozilla.components.concept.engine.webextension.WebExtension +import mozilla.components.feature.readerview.ReaderViewFeature.Companion.FONT_SIZE_DEFAULT +import mozilla.components.feature.readerview.ReaderViewFeature.Companion.READER_VIEW_ACTIVE_CONTENT_PORT +import mozilla.components.feature.readerview.ReaderViewFeature.Companion.READER_VIEW_CONTENT_PORT +import mozilla.components.feature.readerview.ReaderViewFeature.Companion.READER_VIEW_EXTENSION_ID +import mozilla.components.feature.readerview.ReaderViewFeature.Companion.READER_VIEW_EXTENSION_URL +import mozilla.components.feature.readerview.view.ReaderViewControlsBar +import mozilla.components.feature.readerview.view.ReaderViewControlsView +import mozilla.components.support.test.any +import mozilla.components.support.test.argumentCaptor +import mozilla.components.support.test.eq +import mozilla.components.support.test.ext.joinBlocking +import mozilla.components.support.test.mock +import mozilla.components.support.test.robolectric.testContext +import mozilla.components.support.test.rule.MainCoroutineRule +import mozilla.components.support.test.whenever +import mozilla.components.support.webextensions.WebExtensionController +import mozilla.ext.appCompatContext +import org.json.JSONObject +import org.junit.Assert.assertEquals +import org.junit.Assert.assertFalse +import org.junit.Assert.assertNotNull +import org.junit.Assert.assertNull +import org.junit.Assert.assertTrue +import org.junit.Before +import org.junit.Rule +import org.junit.Test +import org.junit.runner.RunWith +import org.mockito.ArgumentMatchers.anyInt +import org.mockito.Mockito.never +import org.mockito.Mockito.spy +import org.mockito.Mockito.times +import org.mockito.Mockito.verify +import java.util.Locale + +@RunWith(AndroidJUnit4::class) +class ReaderViewFeatureTest { + + @get:Rule + val coroutinesTestRule = MainCoroutineRule() + + @Before + fun setup() { + WebExtensionController.installedExtensions.clear() + } + + @Test + fun `start installs webextension`() { + val engine: Engine = mock() + val store = BrowserStore() + val readerViewFeature = ReaderViewFeature(testContext, engine, store, mock()) + + readerViewFeature.start() + + val onSuccess = argumentCaptor<((WebExtension) -> Unit)>() + val onError = argumentCaptor<((Throwable) -> Unit)>() + verify(engine, times(1)).installBuiltInWebExtension( + eq(ReaderViewFeature.READER_VIEW_EXTENSION_ID), + eq(ReaderViewFeature.READER_VIEW_EXTENSION_URL), + onSuccess.capture(), + onError.capture(), + ) + + onSuccess.value.invoke(mock()) + + // Already installed, should not try to install again. + readerViewFeature.start() + verify(engine, times(1)).installBuiltInWebExtension( + eq(ReaderViewFeature.READER_VIEW_EXTENSION_ID), + eq(ReaderViewFeature.READER_VIEW_EXTENSION_URL), + any(), + any(), + ) + } + + @Test + fun `start registers content message handlers for selected session`() { + val engine: Engine = mock() + val view: ReaderViewControlsView = mock() + val engineSession: EngineSession = mock() + val controller = spy( + WebExtensionController( + READER_VIEW_EXTENSION_ID, + READER_VIEW_EXTENSION_URL, + READER_VIEW_CONTENT_PORT, + ), + ) + val tab = createTab( + url = "https://www.mozilla.org", + id = "test-tab", + engineSession = engineSession, + ) + val store = BrowserStore( + initialState = BrowserState( + tabs = listOf(tab), + selectedTabId = tab.id, + ), + ) + + val readerViewFeature = ReaderViewFeature(testContext, engine, store, view) + readerViewFeature.extensionController = controller + readerViewFeature.start() + + val onSuccess = argumentCaptor<((WebExtension) -> Unit)>() + val onError = argumentCaptor<((Throwable) -> Unit)>() + verify(engine, times(1)).installBuiltInWebExtension( + eq(ReaderViewFeature.READER_VIEW_EXTENSION_ID), + eq(ReaderViewFeature.READER_VIEW_EXTENSION_URL), + onSuccess.capture(), + onError.capture(), + ) + onSuccess.value.invoke(mock()) + verify(controller).registerContentMessageHandler(eq(engineSession), any(), eq(READER_VIEW_ACTIVE_CONTENT_PORT)) + verify(controller).registerContentMessageHandler(eq(engineSession), any(), eq(READER_VIEW_CONTENT_PORT)) + } + + @Test + fun `start also starts controls interactor`() { + val engine: Engine = mock() + val store = BrowserStore() + val view: ReaderViewControlsView = ReaderViewControlsBar(appCompatContext) + + val readerViewFeature = spy(ReaderViewFeature(testContext, engine, store, view)) + readerViewFeature.start() + + assertNotNull(view.listener) + } + + @Test + fun `stop also stops controls interactor`() { + val engine: Engine = mock() + val store = BrowserStore() + val view: ReaderViewControlsView = ReaderViewControlsBar(appCompatContext) + + val readerViewFeature = spy(ReaderViewFeature(testContext, engine, store, view)) + readerViewFeature.stop() + + assertNull(view.listener) + } + + @Test + fun `showControls invokes the controls presenter`() { + val view: ReaderViewControlsView = mock() + val feature = spy(ReaderViewFeature(testContext, mock(), mock(), view)) + + feature.showControls() + + verify(view).setColorScheme(any()) + verify(view).setFont(any()) + verify(view).setFontSize(anyInt()) + verify(view).showControls() + } + + @Test + fun `hideControls invokes the controls presenter`() { + val view: ReaderViewControlsView = mock() + val feature = spy(ReaderViewFeature(testContext, mock(), mock(), view)) + + feature.hideControls() + + verify(view).hideControls() + } + + @Test + fun `triggers readerable check when required`() { + val engine: Engine = mock() + val tab = createTab("https://www.mozilla.org", id = "test-tab") + val store = BrowserStore(initialState = BrowserState(tabs = listOf(tab))) + val readerViewFeature = spy(ReaderViewFeature(testContext, engine, store, mock())) + readerViewFeature.start() + + store.dispatch(ReaderAction.UpdateReaderableCheckRequiredAction(tab.id, true)).joinBlocking() + + val tabCaptor = argumentCaptor<TabSessionState>() + verify(readerViewFeature).checkReaderState(tabCaptor.capture()) + assertEquals(tab.id, tabCaptor.value.id) + } + + @Test + fun `connects content script port when required`() { + val engine: Engine = mock() + val tab = createTab("https://www.mozilla.org", id = "test-tab") + val store = BrowserStore(initialState = BrowserState(tabs = listOf(tab), selectedTabId = tab.id)) + val readerViewFeature = spy(ReaderViewFeature(testContext, engine, store, mock())) + readerViewFeature.start() + + store.dispatch(ReaderAction.UpdateReaderConnectRequiredAction(tab.id, true)).joinBlocking() + val tabCaptor = argumentCaptor<TabSessionState>() + verify(readerViewFeature).connectReaderViewContentScript(tabCaptor.capture()) + assertEquals(tab.id, tabCaptor.value.id) + } + + @Test + fun `notifies readerable state changes of selected tab`() { + val readerViewStatusChanges = mutableListOf<Pair<Boolean, Boolean>>() + val onReaderViewStatusChange: onReaderViewStatusChange = { + readerable, active -> + readerViewStatusChanges.add(Pair(readerable, active)) + } + + val engine: Engine = mock() + val tab = createTab("https://www.mozilla.org", id = "test-tab") + val store = BrowserStore(initialState = BrowserState(tabs = listOf(tab))) + val readerViewFeature = spy(ReaderViewFeature(testContext, engine, store, mock(), { "test-uuid" }, onReaderViewStatusChange)) + readerViewFeature.start() + assertTrue(readerViewStatusChanges.isEmpty()) + + store.dispatch(TabListAction.SelectTabAction(tab.id)).joinBlocking() + store.dispatch(ReaderAction.UpdateReaderableAction(tab.id, true)).joinBlocking() + assertEquals(1, readerViewStatusChanges.size) + assertEquals(Pair(true, false), readerViewStatusChanges[0]) + + store.dispatch(ReaderAction.UpdateReaderActiveAction(tab.id, true)).joinBlocking() + assertEquals(2, readerViewStatusChanges.size) + assertEquals(Pair(true, true), readerViewStatusChanges[1]) + + store.dispatch(ReaderAction.UpdateReaderableAction(tab.id, true)).joinBlocking() + // No change -> No notification should have been sent + assertEquals(2, readerViewStatusChanges.size) + + store.dispatch(ReaderAction.UpdateReaderActiveAction(tab.id, false)).joinBlocking() + assertEquals(3, readerViewStatusChanges.size) + assertEquals(Pair(true, false), readerViewStatusChanges[2]) + + store.dispatch(ReaderAction.UpdateReaderableAction(tab.id, false)).joinBlocking() + assertEquals(4, readerViewStatusChanges.size) + assertEquals(Pair(false, false), readerViewStatusChanges[3]) + } + + @Test + fun `show reader view sends message to web extension`() { + val port = mock<Port>() + val message = argumentCaptor<JSONObject>() + val readerViewFeature = prepareFeatureForTest(port, createUUID = { "test-uuid" }) + + readerViewFeature.showReaderView() + verify(port).postMessage(message.capture()) + assertEquals(ReaderViewFeature.ACTION_CACHE_PAGE, message.value[ReaderViewFeature.ACTION_MESSAGE_KEY]) + assertEquals("test-uuid", message.value[ReaderViewFeature.ACTION_VALUE_ID]) + } + + @Test + fun `show reader view dispatches LoadUrlAction and UpdateReaderActiveAction`() { + val engine: Engine = mock() + val engineSession: EngineSession = mock() + val tab = createTab( + url = "https://www.mozilla.org", + id = "test-tab", + engineSession = engineSession, + ) + val store = spy( + BrowserStore( + initialState = BrowserState( + tabs = listOf(tab), + selectedTabId = tab.id, + ), + ), + ) + + val readerViewFeature = ReaderViewFeature(testContext, engine, store, mock(), { "bbbbf5ce-3b0f-4f74-8a1f-986d89bffea7" }) + readerViewFeature.readerBaseUrl = "moz-extension://012345/" + readerViewFeature.showReaderView() + verify(store).dispatch(EngineAction.LoadUrlAction(tab.id, "moz-extension://012345/readerview.html?url=https%3A%2F%2Fwww.mozilla.org&id=bbbbf5ce-3b0f-4f74-8a1f-986d89bffea7&colorScheme=light")) + verify(store).dispatch(ReaderAction.UpdateReaderActiveAction(tab.id, true)) + } + + @Test + fun `default values used for showing reader view if no config is present`() { + val message = ReaderViewFeature.createShowReaderMessage(null) + assertEquals(ReaderViewFeature.ACTION_SHOW, message[ReaderViewFeature.ACTION_MESSAGE_KEY]) + val config = message[ReaderViewFeature.ACTION_VALUE] as JSONObject? + assertNotNull(config) + assertEquals(FONT_SIZE_DEFAULT, config!![ReaderViewFeature.ACTION_VALUE_SHOW_FONT_SIZE]) + assertEquals( + ReaderViewFeature.FontType.SERIF.value.lowercase(Locale.ROOT), + config[ReaderViewFeature.ACTION_VALUE_SHOW_FONT_TYPE], + ) + assertEquals( + ReaderViewFeature.ColorScheme.LIGHT.name.lowercase(Locale.ROOT), + config[ReaderViewFeature.ACTION_VALUE_SHOW_COLOR_SCHEME], + ) + assertFalse(config.has(ReaderViewFeature.ACTION_VALUE_SCROLLY)) + } + + @Test + fun `pass scrollY for showing reader view`() { + val message = ReaderViewFeature.createShowReaderMessage(null, 1234) + assertEquals(ReaderViewFeature.ACTION_SHOW, message[ReaderViewFeature.ACTION_MESSAGE_KEY]) + val config = message[ReaderViewFeature.ACTION_VALUE] as JSONObject? + assertNotNull(config) + assertEquals(1234, config!![ReaderViewFeature.ACTION_VALUE_SCROLLY]) + } + + @Test + fun `hide reader view navigates back if possible`() { + val engine: Engine = mock() + val engineSession: EngineSession = mock() + val tab = createTab("https://www.mozilla.org", id = "test-tab", readerState = ReaderState(active = true)) + val store = BrowserStore(initialState = BrowserState(tabs = listOf(tab))) + val readerViewFeature = ReaderViewFeature(testContext, engine, store, mock()) + store.dispatch(EngineAction.LinkEngineSessionAction(tab.id, engineSession)).joinBlocking() + store.dispatch(TabListAction.SelectTabAction(tab.id)).joinBlocking() + store.dispatch(ContentAction.UpdateBackNavigationStateAction(tab.id, true)).joinBlocking() + + readerViewFeature.hideReaderView() + verify(engineSession).goBack(false) + } + + @Test + fun `hide reader view sends message to web extension`() { + val port = mock<Port>() + val message = argumentCaptor<JSONObject>() + val readerViewFeature = prepareFeatureForTest( + readerActivePort = port, + tab = createTab("https://www.mozilla.org", id = "test-tab", readerState = ReaderState(active = true)), + ) + + readerViewFeature.hideReaderView() + verify(port, times(1)).postMessage(message.capture()) + assertEquals(ReaderViewFeature.ACTION_HIDE, message.value[ReaderViewFeature.ACTION_MESSAGE_KEY]) + } + + @Test + fun `hide reader view updates state`() { + val engine: Engine = mock() + val tab = createTab( + url = "https://www.mozilla.org", + id = "test-tab", + readerState = ReaderState(active = true), + ) + + val store = spy( + BrowserStore( + initialState = BrowserState( + tabs = listOf(tab), + selectedTabId = tab.id, + ), + ), + ) + val readerViewFeature = ReaderViewFeature(testContext, engine, store, mock()) + readerViewFeature.hideReaderView() + verify(store).dispatch(ReaderAction.UpdateReaderActiveAction(tab.id, false)) + verify(store).dispatch(ReaderAction.UpdateReaderableAction(tab.id, false)) + verify(store).dispatch(ReaderAction.ClearReaderActiveUrlAction(tab.id)) + } + + @Test + fun `reader state check sends message to web extension`() { + val port = mock<Port>() + val message = argumentCaptor<JSONObject>() + val readerViewFeature = prepareFeatureForTest(port) + + readerViewFeature.checkReaderState() + verify(port, times(1)).postMessage(message.capture()) + assertEquals(ReaderViewFeature.ACTION_CHECK_READER_STATE, message.value[ReaderViewFeature.ACTION_MESSAGE_KEY]) + } + + @Test + fun `color scheme config change persists and is sent to web extension`() { + val port = mock<Port>() + val message = argumentCaptor<JSONObject>() + + val readerViewFeature = prepareFeatureForTest(readerActivePort = port) + val prefs = testContext.getSharedPreferences(ReaderViewFeature.SHARED_PREF_NAME, Context.MODE_PRIVATE) + + readerViewFeature.config.colorScheme = ReaderViewFeature.ColorScheme.DARK + assertEquals(ReaderViewFeature.ColorScheme.DARK.name, prefs.getString(ReaderViewFeature.COLOR_SCHEME_KEY, null)) + + verify(port, times(1)).postMessage(message.capture()) + assertEquals(ReaderViewFeature.ACTION_SET_COLOR_SCHEME, message.value[ReaderViewFeature.ACTION_MESSAGE_KEY]) + assertEquals(ReaderViewFeature.ColorScheme.DARK.name, message.value[ReaderViewFeature.ACTION_VALUE]) + + // Setting to the same value should not cause another message to be sent + readerViewFeature.config.colorScheme = ReaderViewFeature.ColorScheme.DARK + verify(port, times(1)).postMessage(message.capture()) + } + + @Test + fun `font type config change persists and is sent to web extension`() { + val port = mock<Port>() + val message = argumentCaptor<JSONObject>() + + val readerViewFeature = prepareFeatureForTest(readerActivePort = port) + val prefs = testContext.getSharedPreferences(ReaderViewFeature.SHARED_PREF_NAME, Context.MODE_PRIVATE) + + readerViewFeature.config.fontType = ReaderViewFeature.FontType.SANSSERIF + assertEquals(ReaderViewFeature.FontType.SANSSERIF.name, prefs.getString(ReaderViewFeature.FONT_TYPE_KEY, null)) + + verify(port, times(1)).postMessage(message.capture()) + assertEquals(ReaderViewFeature.ACTION_SET_FONT_TYPE, message.value[ReaderViewFeature.ACTION_MESSAGE_KEY]) + assertEquals(ReaderViewFeature.FontType.SANSSERIF.value, message.value[ReaderViewFeature.ACTION_VALUE]) + + // Setting to the same value should not cause another message to be sent + readerViewFeature.config.fontType = ReaderViewFeature.FontType.SANSSERIF + verify(port, times(1)).postMessage(message.capture()) + } + + @Test + fun `font size config change persists and is sent to web extension`() { + val port = mock<Port>() + val message = argumentCaptor<JSONObject>() + + val readerViewFeature = prepareFeatureForTest(readerActivePort = port) + val prefs = testContext.getSharedPreferences(ReaderViewFeature.SHARED_PREF_NAME, Context.MODE_PRIVATE) + + readerViewFeature.config.fontSize = 4 + assertEquals(4, prefs.getInt(ReaderViewFeature.FONT_SIZE_KEY, 0)) + + verify(port, times(1)).postMessage(message.capture()) + assertEquals(ReaderViewFeature.ACTION_CHANGE_FONT_SIZE, message.value[ReaderViewFeature.ACTION_MESSAGE_KEY]) + assertEquals(1, message.value[ReaderViewFeature.ACTION_VALUE]) + + // Setting to the same value should not cause another message to be sent + readerViewFeature.config.fontSize = 4 + verify(port, times(1)).postMessage(message.capture()) + } + + @Test + fun `on back pressed hides controls`() { + val engine: Engine = mock() + val engineSession: EngineSession = mock() + + val tab = createTab("https://www.mozilla.org", id = "test-tab") + val store = BrowserStore(BrowserState(tabs = listOf(tab))) + store.dispatch(EngineAction.LinkEngineSessionAction(tab.id, engineSession)).joinBlocking() + store.dispatch(TabListAction.SelectTabAction(tab.id)).joinBlocking() + + val controlsView: ReaderViewControlsView = mock() + val view: View = mock() + whenever(controlsView.asView()).thenReturn(view) + + val readerViewFeature = spy(ReaderViewFeature(testContext, engine, store, controlsView)) + assertFalse(readerViewFeature.onBackPressed()) + + store.dispatch(ReaderAction.UpdateReaderActiveAction(tab.id, true)).joinBlocking() + whenever(view.visibility).thenReturn(View.VISIBLE) + assertTrue(readerViewFeature.onBackPressed()) + verify(readerViewFeature, never()).hideReaderView(any()) + verify(readerViewFeature, times(1)).hideControls() + + whenever(view.visibility).thenReturn(View.GONE) + assertTrue(readerViewFeature.onBackPressed()) + verify(readerViewFeature, times(1)).hideReaderView(any()) + verify(readerViewFeature, times(1)).hideControls() + } + + @Test + fun `state is updated when reader state arrives`() { + val engine: Engine = mock() + val view: ReaderViewControlsView = mock() + val engineSession: EngineSession = mock() + val ext: WebExtension = mock() + val controller: WebExtensionController = mock() + val tab = createTab( + url = "https://www.mozilla.org", + id = "test-tab", + engineSession = engineSession, + ) + val store = spy( + BrowserStore( + initialState = BrowserState( + tabs = listOf(tab), + selectedTabId = tab.id, + ), + ), + ) + + WebExtensionController.installedExtensions[ReaderViewFeature.READER_VIEW_EXTENSION_ID] = ext + + val port: Port = mock() + whenever(port.engineSession).thenReturn(engineSession) + whenever(ext.getConnectedPort(any(), any())).thenReturn(port) + + whenever(controller.portConnected(any(), any())).thenReturn(true) + val readerViewFeature = spy(ReaderViewFeature(testContext, engine, store, view)) + readerViewFeature.extensionController = controller + + val messageHandler = argumentCaptor<MessageHandler>() + val message = argumentCaptor<JSONObject>() + readerViewFeature.start() + store.dispatch(ReaderAction.UpdateReaderConnectRequiredAction(tab.id, true)).joinBlocking() + verify(controller).registerContentMessageHandler( + eq(engineSession), + messageHandler.capture(), + eq(READER_VIEW_ACTIVE_CONTENT_PORT), + ) + + messageHandler.value.onPortConnected(port) + verify(port).postMessage(message.capture()) + assertEquals(ReaderViewFeature.ACTION_CHECK_READER_STATE, message.value[ReaderViewFeature.ACTION_MESSAGE_KEY]) + + val readerStateMessage = JSONObject() + .put("readerable", true) + .put("baseUrl", "moz-extension://") + .put("activeUrl", "http://mozilla.org/article") + messageHandler.value.onPortMessage(readerStateMessage, port) + verify(store).dispatch(ReaderAction.UpdateReaderableAction(tab.id, true)) + verify(store).dispatch(ReaderAction.UpdateReaderBaseUrlAction(tab.id, "moz-extension://")) + verify(store).dispatch(ReaderAction.UpdateReaderActiveUrlAction(tab.id, "http://mozilla.org/article")) + } + + @Test + fun `reader is shown when state arrives from reader page`() { + val engine: Engine = mock() + val view: ReaderViewControlsView = mock() + val engineSession: EngineSession = mock() + val ext: WebExtension = mock() + val controller: WebExtensionController = mock() + val tab = createTab( + url = "https://www.mozilla.org", + id = "test-tab", + engineSession = engineSession, + ) + val store = spy( + BrowserStore( + initialState = BrowserState( + tabs = listOf(tab), + selectedTabId = tab.id, + ), + ), + ) + + WebExtensionController.installedExtensions[READER_VIEW_EXTENSION_ID] = ext + + val port: Port = mock() + whenever(port.engineSession).thenReturn(engineSession) + whenever(ext.getConnectedPort(any(), any())).thenReturn(port) + + whenever(controller.portConnected(any(), any())).thenReturn(true) + val readerViewFeature = spy(ReaderViewFeature(testContext, engine, store, view)) + readerViewFeature.extensionController = controller + + val messageHandler = argumentCaptor<MessageHandler>() + val message = argumentCaptor<JSONObject>() + readerViewFeature.start() + + store.dispatch(ReaderAction.UpdateReaderConnectRequiredAction(tab.id, true)).joinBlocking() + verify(controller).registerContentMessageHandler( + eq(engineSession), + messageHandler.capture(), + eq(READER_VIEW_ACTIVE_CONTENT_PORT), + ) + messageHandler.value.onPortConnected(port) + + val readerStateMessage = JSONObject() + .put("readerable", true) + .put("baseUrl", "moz-extension://") + .put("activeUrl", "http://mozilla.org/article") + messageHandler.value.onPortMessage(readerStateMessage, port) + verify(port, times(2)).postMessage(message.capture()) + assertEquals(ReaderViewFeature.ACTION_CHECK_READER_STATE, message.allValues[0][ReaderViewFeature.ACTION_MESSAGE_KEY]) + assertEquals(ReaderViewFeature.ACTION_SHOW, message.allValues[1][ReaderViewFeature.ACTION_MESSAGE_KEY]) + } + + private fun prepareFeatureForTest( + contentPort: Port? = null, + readerActivePort: Port? = null, + tab: TabSessionState = createTab("https://www.mozilla.org", id = "test-tab"), + engineSession: EngineSession = mock(), + controller: WebExtensionController? = null, + createUUID: UUIDCreator = { "" }, + ): ReaderViewFeature { + val engine: Engine = mock() + + val store = BrowserStore(BrowserState(tabs = listOf(tab))) + store.dispatch(EngineAction.LinkEngineSessionAction(tab.id, engineSession)).joinBlocking() + store.dispatch(TabListAction.SelectTabAction(tab.id)).joinBlocking() + + val ext: WebExtension = mock() + contentPort?.let { + whenever(ext.getConnectedPort(eq(ReaderViewFeature.READER_VIEW_CONTENT_PORT), any())) + .thenReturn(contentPort) + } + readerActivePort?.let { + whenever(ext.getConnectedPort(eq(ReaderViewFeature.READER_VIEW_ACTIVE_CONTENT_PORT), any())) + .thenReturn(readerActivePort) + } + WebExtensionController.installedExtensions[ReaderViewFeature.READER_VIEW_EXTENSION_ID] = ext + + val feature = ReaderViewFeature(testContext, engine, store, mock(), createUUID) + if (controller != null) { + feature.extensionController = controller + } + return feature + } +} diff --git a/mobile/android/android-components/components/feature/readerview/src/test/java/mozilla/components/feature/readerview/ReaderViewMiddlewareTest.kt b/mobile/android/android-components/components/feature/readerview/src/test/java/mozilla/components/feature/readerview/ReaderViewMiddlewareTest.kt new file mode 100644 index 0000000000..5573954e86 --- /dev/null +++ b/mobile/android/android-components/components/feature/readerview/src/test/java/mozilla/components/feature/readerview/ReaderViewMiddlewareTest.kt @@ -0,0 +1,187 @@ +/* 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.readerview + +import androidx.test.ext.junit.runners.AndroidJUnit4 +import mozilla.components.browser.state.action.ContentAction +import mozilla.components.browser.state.action.EngineAction +import mozilla.components.browser.state.action.ReaderAction +import mozilla.components.browser.state.action.TabListAction +import mozilla.components.browser.state.selector.findTab +import mozilla.components.browser.state.state.BrowserState +import mozilla.components.browser.state.state.ReaderState +import mozilla.components.browser.state.state.createTab +import mozilla.components.browser.state.store.BrowserStore +import mozilla.components.concept.engine.EngineSession +import mozilla.components.feature.readerview.ReaderViewFeature.Companion.READER_VIEW_EXTENSION_ID +import mozilla.components.support.test.ext.joinBlocking +import mozilla.components.support.test.mock +import mozilla.components.support.webextensions.WebExtensionController +import org.junit.Assert.assertEquals +import org.junit.Assert.assertFalse +import org.junit.Assert.assertNull +import org.junit.Assert.assertSame +import org.junit.Assert.assertTrue +import org.junit.Test +import org.junit.runner.RunWith +import org.mockito.Mockito.never +import org.mockito.Mockito.verify + +@RunWith(AndroidJUnit4::class) +class ReaderViewMiddlewareTest { + + @Test + fun `state is updated to connect content script port when tab is added and engine session linked`() { + val store = BrowserStore( + initialState = BrowserState(), + middleware = listOf(ReaderViewMiddleware()), + ) + + val tab = createTab("https://www.mozilla.org", id = "test-tab") + store.dispatch(TabListAction.AddTabAction(tab)).joinBlocking() + store.dispatch(EngineAction.LinkEngineSessionAction(tab.id, mock())).joinBlocking() + + val readerState = store.state.findTab(tab.id)!!.readerState + assertTrue(readerState.connectRequired) + } + + @Test + fun `state is updated to disconnect content script port when tab is removed`() { + val tab = createTab("https://www.mozilla.org", id = "test-tab") + val engineSession: EngineSession = mock() + val controller: WebExtensionController = mock() + val middleware = ReaderViewMiddleware().apply { + extensionController = controller + } + val store = BrowserStore( + initialState = BrowserState(tabs = listOf(tab)), + middleware = listOf(middleware), + ) + + store.dispatch(EngineAction.LinkEngineSessionAction(tab.id, engineSession)).joinBlocking() + assertSame(engineSession, store.state.findTab(tab.id)?.engineState?.engineSession) + verify(controller, never()).disconnectPort(engineSession, READER_VIEW_EXTENSION_ID) + + store.dispatch(EngineAction.UnlinkEngineSessionAction(tab.id)).joinBlocking() + assertNull(store.state.findTab(tab.id)?.engineState?.engineSession) + verify(controller).disconnectPort(engineSession, READER_VIEW_EXTENSION_ID) + } + + @Test + fun `state is updated to reconnect and trigger readerable check when new tab is selected`() { + val tab1 = createTab("https://www.mozilla.org", id = "test-tab1") + val tab2 = createTab( + "https://www.firefox.com", + id = "test-tab2", + readerState = ReaderState(readerable = true), + ) + val store = BrowserStore( + initialState = BrowserState(tabs = listOf(tab1, tab2)), + middleware = listOf(ReaderViewMiddleware()), + ) + assertFalse(store.state.findTab(tab1.id)!!.readerState.connectRequired) + assertFalse(store.state.findTab(tab1.id)!!.readerState.checkRequired) + assertFalse(store.state.findTab(tab1.id)!!.readerState.readerable) + assertFalse(store.state.findTab(tab2.id)!!.readerState.connectRequired) + assertFalse(store.state.findTab(tab2.id)!!.readerState.checkRequired) + assertTrue(store.state.findTab(tab2.id)!!.readerState.readerable) + + store.dispatch(TabListAction.SelectTabAction(tab2.id)).joinBlocking() + assertFalse(store.state.findTab(tab1.id)!!.readerState.readerable) + assertFalse(store.state.findTab(tab1.id)!!.readerState.checkRequired) + assertFalse(store.state.findTab(tab1.id)!!.readerState.connectRequired) + assertFalse(store.state.findTab(tab2.id)!!.readerState.readerable) + assertTrue(store.state.findTab(tab2.id)!!.readerState.checkRequired) + assertTrue(store.state.findTab(tab2.id)!!.readerState.connectRequired) + } + + @Test + fun `state is updated to trigger readerable check when URL changes`() { + val tab = createTab("https://www.mozilla.org", id = "test-tab1") + val store = BrowserStore( + initialState = BrowserState(tabs = listOf(tab)), + middleware = listOf(ReaderViewMiddleware()), + ) + assertFalse(store.state.findTab(tab.id)!!.readerState.checkRequired) + + store.dispatch(ContentAction.UpdateUrlAction(tab.id, "https://www.firefox.com")).joinBlocking() + assertTrue(store.state.findTab(tab.id)!!.readerState.checkRequired) + } + + @Test + fun `state is updated to enter and leave reader view when URL changes`() { + val tab = createTab( + "https://www.mozilla.org", + id = "test-tab1", + readerState = ReaderState(active = false, baseUrl = "moz-extension://123"), + ) + val store = BrowserStore( + initialState = BrowserState(tabs = listOf(tab)), + middleware = listOf(ReaderViewMiddleware()), + ) + assertFalse(store.state.findTab(tab.id)!!.readerState.active) + + store.dispatch(ContentAction.UpdateUrlAction(tab.id, "moz-extension://123?url=articleLink")).joinBlocking() + assertTrue(store.state.findTab(tab.id)!!.readerState.active) + + store.dispatch(ContentAction.UpdateUrlAction(tab.id, "https://www.firefox.com")).joinBlocking() + assertFalse(store.state.findTab(tab.id)!!.readerState.active) + assertFalse(store.state.findTab(tab.id)!!.readerState.readerable) + assertTrue(store.state.findTab(tab.id)!!.readerState.checkRequired) + assertNull(store.state.findTab(tab.id)!!.readerState.activeUrl) + } + + @Test + fun `state is updated to mask extension page URL when navigating to reader view page`() { + val tab = createTab( + "https://www.mozilla.org", + id = "test-tab1", + readerState = ReaderState( + active = true, + baseUrl = "moz-extension://123", + activeUrl = "https://mozilla.org/article1", + ), + ) + val store = BrowserStore( + initialState = BrowserState(tabs = listOf(tab)), + middleware = listOf(ReaderViewMiddleware()), + ) + + store.dispatch( + ContentAction.UpdateUrlAction( + tab.id, + "moz-extension://123?url=https%3A%2F%2Fmozilla.org%2Farticle1", + ), + ).joinBlocking() + + assertTrue(store.state.findTab(tab.id)!!.readerState.active) + assertEquals("https://mozilla.org/article1", store.state.findTab(tab.id)!!.content.url) + } + + @Test + fun `state is updated to mask extension page URL when reader view connects`() { + val tab = createTab( + "moz-extension://123?url=https%3A%2F%2Fmozilla.org%2Farticle1", + id = "test-tab1", + readerState = ReaderState( + active = true, + baseUrl = "moz-extension://123", + ), + ) + val store = BrowserStore( + initialState = BrowserState(tabs = listOf(tab)), + middleware = listOf(ReaderViewMiddleware()), + ) + + store.dispatch( + ReaderAction.UpdateReaderActiveUrlAction( + tab.id, + activeUrl = "https://mozilla.org/article1", + ), + ).joinBlocking() + + assertEquals("https://mozilla.org/article1", store.state.findTab(tab.id)!!.content.url) + } +} diff --git a/mobile/android/android-components/components/feature/readerview/src/test/java/mozilla/components/feature/readerview/internal/ReaderViewConfigTest.kt b/mobile/android/android-components/components/feature/readerview/src/test/java/mozilla/components/feature/readerview/internal/ReaderViewConfigTest.kt new file mode 100644 index 0000000000..13b0ec9b25 --- /dev/null +++ b/mobile/android/android-components/components/feature/readerview/src/test/java/mozilla/components/feature/readerview/internal/ReaderViewConfigTest.kt @@ -0,0 +1,108 @@ +/* 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.readerview.internal + +import android.content.Context +import android.content.SharedPreferences +import android.content.res.Configuration +import android.content.res.Resources +import mozilla.components.feature.readerview.ReaderViewFeature +import mozilla.components.feature.readerview.ReaderViewFeature.Companion.COLOR_SCHEME_KEY +import mozilla.components.feature.readerview.ReaderViewFeature.Companion.FONT_SIZE_DEFAULT +import mozilla.components.feature.readerview.ReaderViewFeature.Companion.FONT_SIZE_KEY +import mozilla.components.feature.readerview.ReaderViewFeature.Companion.FONT_TYPE_KEY +import mozilla.components.support.test.eq +import mozilla.components.support.test.whenever +import org.json.JSONObject +import org.junit.Assert.assertEquals +import org.junit.Before +import org.junit.Test +import org.mockito.ArgumentMatchers.anyInt +import org.mockito.Mock +import org.mockito.Mockito.anyString +import org.mockito.Mockito.never +import org.mockito.Mockito.times +import org.mockito.Mockito.verify +import org.mockito.MockitoAnnotations + +class ReaderViewConfigTest { + + @Mock private lateinit var context: Context + + @Mock private lateinit var prefs: SharedPreferences + + @Mock private lateinit var editor: SharedPreferences.Editor + + @Mock private lateinit var sendConfigMessage: (JSONObject) -> Unit + + @Mock private lateinit var resources: Resources + + @Mock private lateinit var configuration: Configuration + + private lateinit var config: ReaderViewConfig + + @Before + fun setup() { + MockitoAnnotations.openMocks(this) + whenever(context.getSharedPreferences(anyString(), anyInt())).thenReturn(prefs) + whenever(prefs.edit()).thenReturn(editor) + whenever(context.resources).thenReturn(resources) + whenever(resources.configuration).thenReturn(configuration) + configuration.uiMode = Configuration.UI_MODE_NIGHT_UNDEFINED + + config = ReaderViewConfig(context, sendConfigMessage) + } + + @Test + fun `color scheme should read from shared prefs`() { + whenever(prefs.getString(COLOR_SCHEME_KEY, "LIGHT")).thenReturn("SEPIA") + verify(prefs, never()).getString(eq(COLOR_SCHEME_KEY), anyString()) + + assertEquals(ReaderViewFeature.ColorScheme.SEPIA, config.colorScheme) + verify(prefs, times(1)).getString(eq(COLOR_SCHEME_KEY), anyString()) + + assertEquals(ReaderViewFeature.ColorScheme.SEPIA, config.colorScheme) + verify(prefs, times(1)).getString(eq(COLOR_SCHEME_KEY), anyString()) + } + + @Test + fun `color scheme default should respect active dark mode`() { + whenever(prefs.getString(COLOR_SCHEME_KEY, "LIGHT")).thenReturn("LIGHT") + whenever(prefs.getString(COLOR_SCHEME_KEY, "DARK")).thenReturn("DARK") + // reset config and test for a default of DARK for dark mode + configuration.uiMode = Configuration.UI_MODE_NIGHT_YES + config = ReaderViewConfig(context, sendConfigMessage) + assertEquals(ReaderViewFeature.ColorScheme.DARK, config.colorScheme) + // reset config and test for a default of LIGHT for explicit non-dark (light) mode + configuration.uiMode = Configuration.UI_MODE_NIGHT_NO + config = ReaderViewConfig(context, sendConfigMessage) + assertEquals(ReaderViewFeature.ColorScheme.LIGHT, config.colorScheme) + // test for UI_MODE_NIGHT_UNDEFINED was already done in the above test function + } + + @Test + fun `font type should read from shared prefs`() { + whenever(prefs.getString(FONT_TYPE_KEY, "SERIF")).thenReturn("SANSSERIF") + verify(prefs, never()).getString(eq(FONT_TYPE_KEY), anyString()) + + assertEquals(ReaderViewFeature.FontType.SANSSERIF, config.fontType) + verify(prefs, times(1)).getString(eq(FONT_TYPE_KEY), anyString()) + + assertEquals(ReaderViewFeature.FontType.SANSSERIF, config.fontType) + verify(prefs, times(1)).getString(eq(FONT_TYPE_KEY), anyString()) + } + + @Test + fun `font size should read from shared prefs`() { + whenever(prefs.getInt(FONT_SIZE_KEY, FONT_SIZE_DEFAULT)).thenReturn(4) + verify(prefs, never()).getInt(eq(FONT_SIZE_KEY), anyInt()) + + assertEquals(4, config.fontSize) + verify(prefs, times(1)).getInt(eq(FONT_SIZE_KEY), anyInt()) + + assertEquals(4, config.fontSize) + verify(prefs, times(1)).getInt(eq(FONT_SIZE_KEY), anyInt()) + } +} diff --git a/mobile/android/android-components/components/feature/readerview/src/test/java/mozilla/components/feature/readerview/internal/ReaderViewControlsInteractorTest.kt b/mobile/android/android-components/components/feature/readerview/src/test/java/mozilla/components/feature/readerview/internal/ReaderViewControlsInteractorTest.kt new file mode 100644 index 0000000000..0db805b7c3 --- /dev/null +++ b/mobile/android/android-components/components/feature/readerview/src/test/java/mozilla/components/feature/readerview/internal/ReaderViewControlsInteractorTest.kt @@ -0,0 +1,90 @@ +/* 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.readerview.internal + +import mozilla.components.feature.readerview.ReaderViewFeature +import mozilla.components.feature.readerview.view.ReaderViewControlsView +import mozilla.components.support.test.mock +import mozilla.components.support.test.whenever +import org.junit.Test +import org.mockito.Mockito.verify +import org.mockito.Mockito.verifyNoMoreInteractions + +class ReaderViewControlsInteractorTest { + + @Test + fun `interactor assigns listener to self`() { + val view = mock<ReaderViewControlsView>() + val interactor = ReaderViewControlsInteractor(view, mock()) + + interactor.start() + + verify(view).listener = interactor + verifyNoMoreInteractions(view) + } + + @Test + fun `interactor un-assigns self from listener`() { + val view = mock<ReaderViewControlsView>() + val interactor = ReaderViewControlsInteractor(view, mock()) + + interactor.stop() + + verify(view).listener = null + verifyNoMoreInteractions(view) + } + + @Test + fun `update config on change`() { + val config: ReaderViewConfig = mock() + val interactor = ReaderViewControlsInteractor(mock(), config) + + interactor.onFontChanged(ReaderViewFeature.FontType.SANSSERIF) + + verify(config).fontType = ReaderViewFeature.FontType.SANSSERIF + + interactor.onColorSchemeChanged(ReaderViewFeature.ColorScheme.SEPIA) + + verify(config).colorScheme = ReaderViewFeature.ColorScheme.SEPIA + } + + @Test + fun `update config when font size increased`() { + val config: ReaderViewConfig = mock() + val interactor = ReaderViewControlsInteractor(mock(), config) + + whenever(config.fontSize).thenReturn(7) + interactor.onFontSizeIncreased() + verify(config).fontSize = 8 + + whenever(config.fontSize).thenReturn(8) + interactor.onFontSizeIncreased() + verify(config).fontSize = 9 + + // Can't increase past MAX_FONT_SIZE + whenever(config.fontSize).thenReturn(9) + interactor.onFontSizeIncreased() + verify(config).fontSize = 9 + } + + @Test + fun `update config when font size decreased`() { + val config: ReaderViewConfig = mock() + val interactor = ReaderViewControlsInteractor(mock(), config) + + whenever(config.fontSize).thenReturn(3) + interactor.onFontSizeDecreased() + verify(config).fontSize = 2 + + whenever(config.fontSize).thenReturn(2) + interactor.onFontSizeDecreased() + verify(config).fontSize = 1 + + // Can't decrease below MIN_FONT_SIZE + whenever(config.fontSize).thenReturn(1) + interactor.onFontSizeDecreased() + verify(config).fontSize = 1 + } +} diff --git a/mobile/android/android-components/components/feature/readerview/src/test/java/mozilla/components/feature/readerview/internal/ReaderViewControlsPresenterTest.kt b/mobile/android/android-components/components/feature/readerview/src/test/java/mozilla/components/feature/readerview/internal/ReaderViewControlsPresenterTest.kt new file mode 100644 index 0000000000..399bceaea6 --- /dev/null +++ b/mobile/android/android-components/components/feature/readerview/src/test/java/mozilla/components/feature/readerview/internal/ReaderViewControlsPresenterTest.kt @@ -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.readerview.internal + +import android.view.View +import mozilla.components.feature.readerview.view.ReaderViewControlsView +import mozilla.components.support.test.any +import mozilla.components.support.test.mock +import mozilla.components.support.test.whenever +import org.junit.Assert.assertFalse +import org.junit.Assert.assertTrue +import org.junit.Test +import org.mockito.Mockito.verify + +class ReaderViewControlsPresenterTest { + + @Test + fun `start applies config to view`() { + val config: ReaderViewConfig = mock() + val view = mock<ReaderViewControlsView>() + val presenter = ReaderViewControlsPresenter(view, config) + + whenever(config.colorScheme).thenReturn(mock()) + whenever(config.fontSize).thenReturn(5) + whenever(config.fontType).thenReturn(mock()) + + presenter.show() + + verify(view).tryInflate() + verify(view).setColorScheme(any()) + verify(view).setFontSize(5) + verify(view).setFont(any()) + verify(view).showControls() + } + + @Test + fun `are controls visible`() { + val controlsView = mock<ReaderViewControlsView>() + val view = mock<View>() + whenever(controlsView.asView()).thenReturn(view) + val presenter = ReaderViewControlsPresenter(controlsView, mock()) + + whenever(view.visibility).thenReturn(View.GONE) + assertFalse(presenter.areControlsVisible()) + + whenever(view.visibility).thenReturn(View.VISIBLE) + assertTrue(presenter.areControlsVisible()) + } + + @Test + fun `hide updates the visibility of the controls`() { + val view = mock<ReaderViewControlsView>() + val presenter = ReaderViewControlsPresenter(view, mock()) + + presenter.hide() + + verify(view).hideControls() + } +} diff --git a/mobile/android/android-components/components/feature/readerview/src/test/java/mozilla/components/feature/readerview/view/ReaderViewControlsBarTest.kt b/mobile/android/android-components/components/feature/readerview/src/test/java/mozilla/components/feature/readerview/view/ReaderViewControlsBarTest.kt new file mode 100644 index 0000000000..0af16267b9 --- /dev/null +++ b/mobile/android/android-components/components/feature/readerview/src/test/java/mozilla/components/feature/readerview/view/ReaderViewControlsBarTest.kt @@ -0,0 +1,204 @@ +/* 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.readerview.view + +import android.view.View +import androidx.appcompat.widget.AppCompatButton +import androidx.appcompat.widget.AppCompatRadioButton +import androidx.test.ext.junit.runners.AndroidJUnit4 +import mozilla.components.feature.readerview.R +import mozilla.components.feature.readerview.ReaderViewFeature +import mozilla.components.support.test.mock +import mozilla.ext.appCompatContext +import org.junit.Assert.assertFalse +import org.junit.Assert.assertNull +import org.junit.Assert.assertTrue +import org.junit.Test +import org.junit.runner.RunWith +import org.mockito.Mockito.never +import org.mockito.Mockito.spy +import org.mockito.Mockito.verify + +@RunWith(AndroidJUnit4::class) +class ReaderViewControlsBarTest { + + @Test + fun `flags are set on UI init`() { + val bar = spy(ReaderViewControlsBar(appCompatContext)) + + assertTrue(bar.isFocusableInTouchMode) + assertTrue(bar.isClickable) + } + + @Test + fun `font options are set`() { + val bar = ReaderViewControlsBar(appCompatContext) + bar.tryInflate() + + val serifButton = bar.findViewById<AppCompatRadioButton>(R.id.mozac_feature_readerview_font_serif) + val sansSerifButton = bar.findViewById<AppCompatRadioButton>(R.id.mozac_feature_readerview_font_sans_serif) + + assertFalse(serifButton.isChecked) + + bar.setFont(ReaderViewFeature.FontType.SERIF) + + assertTrue(serifButton.isChecked) + + assertFalse(sansSerifButton.isChecked) + + bar.setFont(ReaderViewFeature.FontType.SANSSERIF) + + assertTrue(sansSerifButton.isChecked) + } + + @Test + fun `font size buttons are enabled or disabled`() { + val bar = ReaderViewControlsBar(appCompatContext) + bar.tryInflate() + + val sizeDecreaseButton = bar.findViewById<AppCompatButton>(R.id.mozac_feature_readerview_font_size_decrease) + val sizeIncreaseButton = bar.findViewById<AppCompatButton>(R.id.mozac_feature_readerview_font_size_increase) + + bar.setFontSize(5) + + assertTrue(sizeDecreaseButton.isEnabled) + assertTrue(sizeIncreaseButton.isEnabled) + + bar.setFontSize(1) + + assertFalse(sizeDecreaseButton.isEnabled) + assertTrue(sizeIncreaseButton.isEnabled) + + bar.setFontSize(0) + + assertFalse(sizeDecreaseButton.isEnabled) + assertTrue(sizeIncreaseButton.isEnabled) + + bar.setFontSize(9) + + assertTrue(sizeDecreaseButton.isEnabled) + assertFalse(sizeIncreaseButton.isEnabled) + + bar.setFontSize(10) + + assertTrue(sizeDecreaseButton.isEnabled) + assertFalse(sizeIncreaseButton.isEnabled) + } + + @Test + fun `color scheme is set`() { + val bar = ReaderViewControlsBar(appCompatContext) + bar.tryInflate() + + val colorOptionDark = bar.findViewById<AppCompatRadioButton>(R.id.mozac_feature_readerview_color_dark) + val colorOptionSepia = bar.findViewById<AppCompatRadioButton>(R.id.mozac_feature_readerview_color_sepia) + val colorOptionLight = bar.findViewById<AppCompatRadioButton>(R.id.mozac_feature_readerview_color_light) + + bar.setColorScheme(ReaderViewFeature.ColorScheme.DARK) + + assertTrue(colorOptionDark.isChecked) + assertFalse(colorOptionSepia.isChecked) + assertFalse(colorOptionLight.isChecked) + + bar.setColorScheme(ReaderViewFeature.ColorScheme.SEPIA) + + assertFalse(colorOptionDark.isChecked) + assertTrue(colorOptionSepia.isChecked) + assertFalse(colorOptionLight.isChecked) + + bar.setColorScheme(ReaderViewFeature.ColorScheme.LIGHT) + + assertFalse(colorOptionDark.isChecked) + assertFalse(colorOptionSepia.isChecked) + assertTrue(colorOptionLight.isChecked) + } + + @Test + fun `showControls updates visibility and requests focus`() { + val bar = spy(ReaderViewControlsBar(appCompatContext)) + + bar.showControls() + + verify(bar).visibility = View.VISIBLE + verify(bar).requestFocus() + } + + @Test + fun `hideControls updates visibility`() { + val bar = spy(ReaderViewControlsBar(appCompatContext)) + + bar.hideControls() + + verify(bar).visibility = View.GONE + } + + @Test + fun `when focus is lost, hide controls`() { + val bar = spy(ReaderViewControlsBar(appCompatContext)) + + bar.clearFocus() + + verify(bar, never()).hideControls() + + bar.requestFocus() + + bar.clearFocus() + + verify(bar).hideControls() + } + + @Test + fun `listener is invoked when clicking a font option`() { + val bar = ReaderViewControlsBar(appCompatContext) + val listener: ReaderViewControlsView.Listener = mock() + + assertNull(bar.listener) + + bar.listener = listener + bar.tryInflate() + + bar.findViewById<AppCompatRadioButton>(R.id.mozac_feature_readerview_font_sans_serif).performClick() + + verify(listener).onFontChanged(ReaderViewFeature.FontType.SANSSERIF) + } + + @Test + fun `listener is invoked when clicking a font size option`() { + val bar = ReaderViewControlsBar(appCompatContext) + val listener: ReaderViewControlsView.Listener = mock() + + assertNull(bar.listener) + + bar.listener = listener + bar.tryInflate() + + bar.findViewById<AppCompatButton>(R.id.mozac_feature_readerview_font_size_increase).performClick() + + verify(listener).onFontSizeIncreased() + } + + @Test + fun `listener is invoked when clicking a color scheme`() { + val bar = ReaderViewControlsBar(appCompatContext) + val listener: ReaderViewControlsView.Listener = mock() + + assertNull(bar.listener) + + bar.listener = listener + bar.tryInflate() + + bar.findViewById<AppCompatRadioButton>(R.id.mozac_feature_readerview_color_sepia).performClick() + + verify(listener).onColorSchemeChanged(ReaderViewFeature.ColorScheme.SEPIA) + } + + @Test + fun `tryInflate is only successfully once`() { + val bar = ReaderViewControlsBar(appCompatContext) + + assertTrue(bar.tryInflate()) + assertFalse(bar.tryInflate()) + } +} diff --git a/mobile/android/android-components/components/feature/readerview/src/test/java/mozilla/ext/context.kt b/mobile/android/android-components/components/feature/readerview/src/test/java/mozilla/ext/context.kt new file mode 100644 index 0000000000..64575ce095 --- /dev/null +++ b/mobile/android/android-components/components/feature/readerview/src/test/java/mozilla/ext/context.kt @@ -0,0 +1,18 @@ +/* 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.ext + +import android.content.Context +import androidx.appcompat.R +import androidx.appcompat.view.ContextThemeWrapper +import mozilla.components.support.test.robolectric.testContext + +/** + * `testContext` wrapped with AppCompat theme. + * + * Useful for views that uses theme attributes, for example. + */ +internal val appCompatContext: Context + get() = ContextThemeWrapper(testContext, R.style.Theme_AppCompat) diff --git a/mobile/android/android-components/components/feature/readerview/src/test/resources/mockito-extensions/org.mockito.plugins.MockMaker b/mobile/android/android-components/components/feature/readerview/src/test/resources/mockito-extensions/org.mockito.plugins.MockMaker new file mode 100644 index 0000000000..49324d83c5 --- /dev/null +++ b/mobile/android/android-components/components/feature/readerview/src/test/resources/mockito-extensions/org.mockito.plugins.MockMaker @@ -0,0 +1,3 @@ +mock-maker-inline +// This allows mocking final classes (classes are final by default in Kotlin) + diff --git a/mobile/android/android-components/components/feature/readerview/src/test/resources/robolectric.properties b/mobile/android/android-components/components/feature/readerview/src/test/resources/robolectric.properties new file mode 100644 index 0000000000..932b01b9eb --- /dev/null +++ b/mobile/android/android-components/components/feature/readerview/src/test/resources/robolectric.properties @@ -0,0 +1 @@ +sdk=28 |