major update
This commit is contained in:
+40
@@ -0,0 +1,40 @@
|
||||
const FEED_MIME_TYPES = [
|
||||
"application/atom",
|
||||
"application/rss"
|
||||
];
|
||||
|
||||
// Listen for any webRequest that might be a feed
|
||||
browser.webRequest.onHeadersReceived.addListener(
|
||||
function (details) {
|
||||
let isFeed = false;
|
||||
|
||||
for (let header of details.responseHeaders) {
|
||||
if (header.name.toLowerCase() === "content-type") {
|
||||
const contentType = header.value.toLowerCase();
|
||||
|
||||
for (const mimeType of FEED_MIME_TYPES) {
|
||||
if (contentType.includes(mimeType)) {
|
||||
isFeed = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (isFeed) break;
|
||||
}
|
||||
}
|
||||
|
||||
if (isFeed) {
|
||||
port.postMessage({
|
||||
"type": "feedRequest",
|
||||
"url": details.url
|
||||
});
|
||||
|
||||
return { cancel: true };
|
||||
}
|
||||
|
||||
// Not a feed, let the browser handle it normally
|
||||
return { responseHeaders: details.responseHeaders };
|
||||
},
|
||||
{ urls: ["<all_urls>"] },
|
||||
["blocking", "responseHeaders"]
|
||||
);
|
||||
+7
-3
@@ -2,20 +2,24 @@
|
||||
"manifest_version": 2,
|
||||
"browser_specific_settings": {
|
||||
"gecko": {
|
||||
"id": "turndown@movenext.me"
|
||||
"id": "browser_extension@movenext.me"
|
||||
}
|
||||
},
|
||||
"name": "Converts html to markdown",
|
||||
"name": "Misc extensions",
|
||||
"version": "1.0",
|
||||
"background": {
|
||||
"scripts": [
|
||||
"readability.min.js",
|
||||
"background.js"
|
||||
"port.js",
|
||||
"turndown.js",
|
||||
"feed.js"
|
||||
]
|
||||
},
|
||||
"permissions": [
|
||||
"geckoViewAddons",
|
||||
"nativeMessaging",
|
||||
"webRequest",
|
||||
"webRequestBlocking",
|
||||
"<all_urls>"
|
||||
]
|
||||
}
|
||||
+1
@@ -0,0 +1 @@
|
||||
const port = browser.runtime.connectNative("mozacBrowserExtension");
|
||||
+1
-1
@@ -1,4 +1,3 @@
|
||||
const port = browser.runtime.connectNative("mozacTurndownHtml");
|
||||
const parser = new DOMParser();
|
||||
|
||||
port.onMessage.addListener(message => {
|
||||
@@ -15,6 +14,7 @@ port.onMessage.addListener(message => {
|
||||
});
|
||||
|
||||
port.postMessage({
|
||||
"type": "turndown",
|
||||
"id": requestId,
|
||||
"status": "success",
|
||||
"result": results
|
||||
+5
-3
@@ -8,6 +8,7 @@ import eu.lensai.flutter_mozilla_components.components.Features
|
||||
import eu.lensai.flutter_mozilla_components.components.Search
|
||||
import eu.lensai.flutter_mozilla_components.components.Services
|
||||
import eu.lensai.flutter_mozilla_components.components.UseCases
|
||||
import eu.lensai.flutter_mozilla_components.pigeons.BrowserExtensionEvents
|
||||
import eu.lensai.flutter_mozilla_components.pigeons.GeckoAddonEvents
|
||||
import eu.lensai.flutter_mozilla_components.pigeons.GeckoStateEvents
|
||||
import eu.lensai.flutter_mozilla_components.pigeons.GeckoSuggestionEvents
|
||||
@@ -21,10 +22,11 @@ class Components(private val context: Context,
|
||||
val flutterEvents: GeckoStateEvents,
|
||||
val readerViewController: ReaderViewController,
|
||||
val selectionAction: SelectionActionDelegate,
|
||||
val addonEvents: GeckoAddonEvents,
|
||||
val tabContentEvents: GeckoTabContentEvents
|
||||
private val addonEvents: GeckoAddonEvents,
|
||||
private val tabContentEvents: GeckoTabContentEvents,
|
||||
private val extensionEvents: BrowserExtensionEvents
|
||||
) {
|
||||
val core by lazy { Core(context, this, flutterEvents) }
|
||||
val core by lazy { Core(context, this, flutterEvents, extensionEvents) }
|
||||
val events by lazy { Events(flutterEvents) }
|
||||
val useCases by lazy { UseCases(context, core.engine, core.store) }
|
||||
val services by lazy { Services(context, useCases.tabsUseCases) }
|
||||
|
||||
+4
-3
@@ -8,7 +8,8 @@ import android.content.Context
|
||||
import eu.lensai.flutter_mozilla_components.feature.ContainerProxyFeature
|
||||
import eu.lensai.flutter_mozilla_components.feature.CookieManagerFeature
|
||||
import eu.lensai.flutter_mozilla_components.feature.PrefManagerFeature
|
||||
import eu.lensai.flutter_mozilla_components.feature.TurndownFeature
|
||||
import eu.lensai.flutter_mozilla_components.feature.BrowserExtensionFeature
|
||||
import eu.lensai.flutter_mozilla_components.pigeons.BrowserExtensionEvents
|
||||
import mozilla.components.browser.engine.gecko.GeckoEngine
|
||||
import mozilla.components.browser.engine.gecko.fetch.GeckoViewFetchClient
|
||||
import mozilla.components.concept.engine.DefaultSettings
|
||||
@@ -44,7 +45,7 @@ object EngineProvider {
|
||||
return runtime!!
|
||||
}
|
||||
|
||||
fun createEngine(context: Context, defaultSettings: DefaultSettings): Engine {
|
||||
fun createEngine(context: Context, defaultSettings: DefaultSettings, extensionEvents: BrowserExtensionEvents): Engine {
|
||||
Logger.debug("Creating Engine")
|
||||
val runtime = getOrCreateRuntime(context)
|
||||
|
||||
@@ -53,7 +54,7 @@ object EngineProvider {
|
||||
CookieManagerFeature.install(it)
|
||||
PrefManagerFeature.install(it)
|
||||
ContainerProxyFeature.install(it)
|
||||
TurndownFeature.install(it)
|
||||
BrowserExtensionFeature.install(it, extensionEvents)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
+24
-17
@@ -2,6 +2,7 @@ package eu.lensai.flutter_mozilla_components
|
||||
|
||||
import android.app.Activity
|
||||
import android.content.Intent
|
||||
import android.view.View
|
||||
import androidx.fragment.app.FragmentActivity
|
||||
import eu.lensai.flutter_mozilla_components.activities.NotificationActivity
|
||||
import eu.lensai.flutter_mozilla_components.api.GeckoAddonsApiImpl
|
||||
@@ -18,11 +19,13 @@ import eu.lensai.flutter_mozilla_components.api.GeckoSelectionActionControllerIm
|
||||
import eu.lensai.flutter_mozilla_components.api.GeckoSessionApiImpl
|
||||
import eu.lensai.flutter_mozilla_components.api.GeckoSuggestionApiImpl
|
||||
import eu.lensai.flutter_mozilla_components.api.GeckoTabsApiImpl
|
||||
import eu.lensai.flutter_mozilla_components.api.GeckoTurndownApiImpl
|
||||
import eu.lensai.flutter_mozilla_components.api.GeckoBrowserExtensionApiImpl
|
||||
import eu.lensai.flutter_mozilla_components.feature.DefaultSelectionActionDelegate
|
||||
import eu.lensai.flutter_mozilla_components.pigeons.BrowserExtensionEvents
|
||||
import eu.lensai.flutter_mozilla_components.pigeons.GeckoAddonEvents
|
||||
import eu.lensai.flutter_mozilla_components.pigeons.GeckoAddonsApi
|
||||
import eu.lensai.flutter_mozilla_components.pigeons.GeckoBrowserApi
|
||||
import eu.lensai.flutter_mozilla_components.pigeons.GeckoBrowserExtensionApi
|
||||
import eu.lensai.flutter_mozilla_components.pigeons.GeckoContainerProxyApi
|
||||
import eu.lensai.flutter_mozilla_components.pigeons.GeckoCookieApi
|
||||
import eu.lensai.flutter_mozilla_components.pigeons.GeckoDeleteBrowsingDataController
|
||||
@@ -39,7 +42,6 @@ import eu.lensai.flutter_mozilla_components.pigeons.GeckoSuggestionApi
|
||||
import eu.lensai.flutter_mozilla_components.pigeons.GeckoSuggestionEvents
|
||||
import eu.lensai.flutter_mozilla_components.pigeons.GeckoTabContentEvents
|
||||
import eu.lensai.flutter_mozilla_components.pigeons.GeckoTabsApi
|
||||
import eu.lensai.flutter_mozilla_components.pigeons.GeckoTurndownApi
|
||||
import eu.lensai.flutter_mozilla_components.pigeons.ReaderViewController
|
||||
import eu.lensai.flutter_mozilla_components.pigeons.ReaderViewEvents
|
||||
|
||||
@@ -68,7 +70,6 @@ class FlutterMozillaComponentsPlugin: FlutterPlugin, ActivityAware {
|
||||
private lateinit var _flutterEvents : GeckoStateEvents
|
||||
|
||||
private var isPlatformViewRegistered = false
|
||||
private var pendingFragmentShow = false
|
||||
|
||||
override fun onAttachedToEngine(flutterPluginBinding: FlutterPlugin.FlutterPluginBinding) {
|
||||
synchronized(this) {
|
||||
@@ -92,6 +93,8 @@ class FlutterMozillaComponentsPlugin: FlutterPlugin, ActivityAware {
|
||||
val readerViewController =
|
||||
ReaderViewController(_flutterPluginBinding.binaryMessenger)
|
||||
|
||||
val extensionEvents = BrowserExtensionEvents(_flutterPluginBinding.binaryMessenger)
|
||||
|
||||
val addonEvents = GeckoAddonEvents(_flutterPluginBinding.binaryMessenger)
|
||||
val tabContentEvents = GeckoTabContentEvents(_flutterPluginBinding.binaryMessenger)
|
||||
|
||||
@@ -105,6 +108,7 @@ class FlutterMozillaComponentsPlugin: FlutterPlugin, ActivityAware {
|
||||
selectionActionDelegate,
|
||||
addonEvents,
|
||||
tabContentEvents,
|
||||
extensionEvents
|
||||
)
|
||||
|
||||
GeckoBrowserApi.setUp(_flutterPluginBinding.binaryMessenger, GeckoBrowserApiImpl {
|
||||
@@ -125,7 +129,7 @@ class FlutterMozillaComponentsPlugin: FlutterPlugin, ActivityAware {
|
||||
))
|
||||
GeckoDeleteBrowsingDataController.setUp(_flutterPluginBinding.binaryMessenger, GeckoDeleteBrowsingDataControllerImpl())
|
||||
GeckoDownloadsApi.setUp(_flutterPluginBinding.binaryMessenger, GeckoDownloadsApiImpl())
|
||||
GeckoTurndownApi.setUp(_flutterPluginBinding.binaryMessenger, GeckoTurndownApiImpl())
|
||||
GeckoBrowserExtensionApi.setUp(_flutterPluginBinding.binaryMessenger, GeckoBrowserExtensionApiImpl())
|
||||
|
||||
ReaderViewEvents.setUp(
|
||||
_flutterPluginBinding.binaryMessenger,
|
||||
@@ -137,21 +141,31 @@ class FlutterMozillaComponentsPlugin: FlutterPlugin, ActivityAware {
|
||||
flutterPluginBinding.applicationContext.startActivity(intent)
|
||||
}
|
||||
|
||||
private fun showNativeFragment() {
|
||||
private fun showNativeFragment(): Boolean {
|
||||
if (!isPlatformViewRegistered) {
|
||||
pendingFragmentShow = true
|
||||
return
|
||||
return false
|
||||
}
|
||||
|
||||
if (activity == null) {
|
||||
return
|
||||
if (activity == null || activity !is FragmentActivity) {
|
||||
return false
|
||||
}
|
||||
|
||||
val fragmentActivity = activity as FragmentActivity
|
||||
|
||||
// Check if the container view exists in the view hierarchy
|
||||
val container = fragmentActivity.findViewById<View>(FRAGMENT_CONTAINER_ID)
|
||||
if (container == null) {
|
||||
// Container doesn't exist yet, retry later
|
||||
return false
|
||||
}
|
||||
|
||||
val nativeFragment = BrowserFragment.create()
|
||||
val fm = (activity as FragmentActivity).supportFragmentManager
|
||||
val fm = fragmentActivity.supportFragmentManager
|
||||
fm.beginTransaction()
|
||||
.replace(FRAGMENT_CONTAINER_ID, nativeFragment)
|
||||
.commitAllowingStateLoss()
|
||||
|
||||
return true
|
||||
}
|
||||
|
||||
override fun onDetachedFromEngine(binding: FlutterPlugin.FlutterPluginBinding) {
|
||||
@@ -170,12 +184,6 @@ class FlutterMozillaComponentsPlugin: FlutterPlugin, ActivityAware {
|
||||
)
|
||||
|
||||
isPlatformViewRegistered = true
|
||||
|
||||
// Process any pending fragment show request
|
||||
if (pendingFragmentShow) {
|
||||
pendingFragmentShow = false
|
||||
showNativeFragment()
|
||||
}
|
||||
}
|
||||
|
||||
override fun onDetachedFromActivityForConfigChanges() {
|
||||
@@ -189,6 +197,5 @@ class FlutterMozillaComponentsPlugin: FlutterPlugin, ActivityAware {
|
||||
override fun onDetachedFromActivity() {
|
||||
this.activity = null
|
||||
isPlatformViewRegistered = false
|
||||
pendingFragmentShow = false
|
||||
}
|
||||
}
|
||||
|
||||
+9
-3
@@ -37,7 +37,13 @@ private class NativeFragmentView(
|
||||
FrameLayout.LayoutParams(
|
||||
ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.MATCH_PARENT
|
||||
)
|
||||
container = FrameLayout(activity!!)
|
||||
|
||||
// Ensure activity is not null before creating the container
|
||||
if (activity == null) {
|
||||
throw IllegalStateException("Activity cannot be null when creating NativeFragmentView")
|
||||
}
|
||||
|
||||
container = FrameLayout(activity)
|
||||
container.layoutParams = vParams
|
||||
container.id = containerId
|
||||
}
|
||||
@@ -45,8 +51,8 @@ private class NativeFragmentView(
|
||||
override fun onFlutterViewAttached(flutterView: View) {
|
||||
super.onFlutterViewAttached(flutterView)
|
||||
|
||||
components.engineReportedInitialized = false;
|
||||
flutterEvents.onViewReadyStateChange(System.currentTimeMillis(),true) { _ -> }
|
||||
components.engineReportedInitialized = false
|
||||
flutterEvents.onViewReadyStateChange(System.currentTimeMillis(), true) { _ -> }
|
||||
}
|
||||
|
||||
override fun getView(): View {
|
||||
|
||||
+5
-2
@@ -1,6 +1,7 @@
|
||||
package eu.lensai.flutter_mozilla_components
|
||||
|
||||
import android.content.Context
|
||||
import eu.lensai.flutter_mozilla_components.pigeons.BrowserExtensionEvents
|
||||
import eu.lensai.flutter_mozilla_components.pigeons.GeckoAddonEvents
|
||||
import eu.lensai.flutter_mozilla_components.pigeons.GeckoStateEvents
|
||||
import eu.lensai.flutter_mozilla_components.pigeons.GeckoSuggestionEvents
|
||||
@@ -44,7 +45,8 @@ object GlobalComponents {
|
||||
readerViewController: ReaderViewController,
|
||||
selectionAction: SelectionActionDelegate,
|
||||
addonEvents: GeckoAddonEvents,
|
||||
tabContentEvents: GeckoTabContentEvents
|
||||
tabContentEvents: GeckoTabContentEvents,
|
||||
extensionEvents: BrowserExtensionEvents
|
||||
) {
|
||||
Logger.debug("Creating new components")
|
||||
|
||||
@@ -54,7 +56,8 @@ object GlobalComponents {
|
||||
readerViewController,
|
||||
selectionAction,
|
||||
addonEvents,
|
||||
tabContentEvents
|
||||
tabContentEvents,
|
||||
extensionEvents
|
||||
)
|
||||
|
||||
//newComponents.crashReporter.install(applicationContext)
|
||||
|
||||
+5
-3
@@ -9,17 +9,19 @@ import mozilla.components.feature.addons.logger
|
||||
* Implementation of GeckoBrowserApi that handles browser-related operations
|
||||
* @param showFragmentCallback Callback function to show native fragment
|
||||
*/
|
||||
class GeckoBrowserApiImpl(private val showFragmentCallback: () -> Unit) : GeckoBrowserApi {
|
||||
class GeckoBrowserApiImpl(private val showFragmentCallback: () -> Boolean) : GeckoBrowserApi {
|
||||
companion object {
|
||||
private const val TAG = "GeckoBrowserApiImpl"
|
||||
}
|
||||
|
||||
override fun showNativeFragment() {
|
||||
override fun showNativeFragment(): Boolean {
|
||||
try {
|
||||
showFragmentCallback()
|
||||
return showFragmentCallback()
|
||||
} catch (e: Exception) {
|
||||
logger.error("Failed to show native fragment", e)
|
||||
}
|
||||
|
||||
return false
|
||||
}
|
||||
|
||||
override fun onTrimMemory(level: Long) {
|
||||
|
||||
+4
-4
@@ -1,12 +1,12 @@
|
||||
package eu.lensai.flutter_mozilla_components.api
|
||||
|
||||
import eu.lensai.flutter_mozilla_components.feature.ResultConsumer
|
||||
import eu.lensai.flutter_mozilla_components.feature.TurndownFeature
|
||||
import eu.lensai.flutter_mozilla_components.pigeons.GeckoTurndownApi
|
||||
import eu.lensai.flutter_mozilla_components.feature.BrowserExtensionFeature
|
||||
import eu.lensai.flutter_mozilla_components.pigeons.GeckoBrowserExtensionApi
|
||||
import org.json.JSONArray
|
||||
import org.json.JSONObject
|
||||
|
||||
class GeckoTurndownApiImpl : GeckoTurndownApi {
|
||||
class GeckoBrowserExtensionApiImpl : GeckoBrowserExtensionApi {
|
||||
private fun JSONObject.toMap(): Map<String, Any> {
|
||||
val map = mutableMapOf<String, Any>()
|
||||
val keys = this.keys()
|
||||
@@ -38,7 +38,7 @@ class GeckoTurndownApiImpl : GeckoTurndownApi {
|
||||
}
|
||||
|
||||
override fun getMarkdown(htmlList: List<String>, callback: (Result<List<Any>>) -> Unit) {
|
||||
TurndownFeature.scheduleRequest("turndown", htmlList, object :
|
||||
BrowserExtensionFeature.scheduleRequest("turndown", htmlList, object :
|
||||
ResultConsumer<JSONObject> {
|
||||
override fun success(result: JSONObject) {
|
||||
val resultArray = result.getJSONArray("result")
|
||||
+3
-1
@@ -14,6 +14,7 @@ import eu.lensai.flutter_mozilla_components.activities.NotificationActivity
|
||||
import eu.lensai.flutter_mozilla_components.R
|
||||
import eu.lensai.flutter_mozilla_components.ext.getPreferenceKey
|
||||
import eu.lensai.flutter_mozilla_components.middleware.FlutterEventMiddleware
|
||||
import eu.lensai.flutter_mozilla_components.pigeons.BrowserExtensionEvents
|
||||
import eu.lensai.flutter_mozilla_components.pigeons.GeckoStateEvents
|
||||
import kotlinx.coroutines.FlowPreview
|
||||
import mozilla.components.browser.engine.gecko.permission.GeckoSitePermissionsStorage
|
||||
@@ -54,6 +55,7 @@ private const val DAY_IN_MINUTES = 24 * 60L
|
||||
class Core(private val context: Context,
|
||||
private val components: Components,
|
||||
private val flutterEvents: GeckoStateEvents,
|
||||
private val extensionEvents: BrowserExtensionEvents
|
||||
) {
|
||||
val prefs by lazy {
|
||||
PreferenceManager.getDefaultSharedPreferences(context)
|
||||
@@ -95,7 +97,7 @@ class Core(private val context: Context,
|
||||
}
|
||||
|
||||
val engine: Engine by lazy {
|
||||
EngineProvider.createEngine(context, engineSettings)
|
||||
EngineProvider.createEngine(context, engineSettings, extensionEvents)
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
+49
-26
@@ -1,6 +1,7 @@
|
||||
package eu.lensai.flutter_mozilla_components.feature
|
||||
|
||||
import androidx.annotation.VisibleForTesting
|
||||
import eu.lensai.flutter_mozilla_components.pigeons.BrowserExtensionEvents
|
||||
import kotlinx.coroutines.Dispatchers
|
||||
import kotlinx.coroutines.runBlocking
|
||||
import kotlinx.coroutines.sync.Mutex
|
||||
@@ -13,13 +14,15 @@ import mozilla.components.support.base.log.logger.Logger
|
||||
import mozilla.components.support.webextensions.WebExtensionController
|
||||
import org.json.JSONArray
|
||||
import org.json.JSONObject
|
||||
import org.mozilla.gecko.util.ThreadUtils.runOnUiThread
|
||||
|
||||
object TurndownFeature {
|
||||
private val logger = Logger("turndown")
|
||||
object BrowserExtensionFeature {
|
||||
private val logger = Logger("browser_extension")
|
||||
|
||||
private const val PREF_MANAGER_REPORTER_EXTENSION_ID = "turndown@movenext.me"
|
||||
private const val PREF_MANAGER_REPORTER_EXTENSION_URL = "resource://android/assets/extensions/turndown/"
|
||||
private const val PREF_MANAGER_REPORTER_MESSAGING_ID = "mozacTurndownHtml"
|
||||
private const val PREF_MANAGER_REPORTER_EXTENSION_ID = "browser_extension@movenext.me"
|
||||
private const val PREF_MANAGER_REPORTER_EXTENSION_URL =
|
||||
"resource://android/assets/extensions/browser_extension/"
|
||||
private const val PREF_MANAGER_REPORTER_MESSAGING_ID = "mozacBrowserExtension"
|
||||
|
||||
private var nextRequestId: Int = 0
|
||||
private val requestHandlers = HashMap<Int, ResultConsumer<JSONObject>>()
|
||||
@@ -30,16 +33,22 @@ object TurndownFeature {
|
||||
internal var extensionController = WebExtensionController(
|
||||
PREF_MANAGER_REPORTER_EXTENSION_ID,
|
||||
PREF_MANAGER_REPORTER_EXTENSION_URL,
|
||||
PREF_MANAGER_REPORTER_MESSAGING_ID,
|
||||
PREF_MANAGER_REPORTER_MESSAGING_ID
|
||||
)
|
||||
|
||||
fun scheduleRequest(command: String, args: Any, callback: ResultConsumer<JSONObject>) {
|
||||
fun scheduleRequest(
|
||||
command: String,
|
||||
args: Any,
|
||||
callback: ResultConsumer<JSONObject>
|
||||
) {
|
||||
val message = JSONObject()
|
||||
message.put("action", command);
|
||||
message.put("args", when (args) {
|
||||
is List<*> -> JSONArray(args)
|
||||
else -> args
|
||||
})
|
||||
message.put(
|
||||
"args", when (args) {
|
||||
is List<*> -> JSONArray(args)
|
||||
else -> args
|
||||
}
|
||||
)
|
||||
|
||||
runBlocking {
|
||||
withContext(Dispatchers.Default) {
|
||||
@@ -56,23 +65,37 @@ object TurndownFeature {
|
||||
}
|
||||
}
|
||||
|
||||
private class TurndownBackgroundMessageHandler() : MessageHandler {
|
||||
private class ExtensionBackgroundMessageHandler(
|
||||
private val extensionEvents: BrowserExtensionEvents
|
||||
) : MessageHandler {
|
||||
override fun onPortMessage(message: Any, port: Port) {
|
||||
runBlocking {
|
||||
withContext(Dispatchers.Default) {
|
||||
mutex.withLock {
|
||||
val messageJSON = message as JSONObject;
|
||||
val type = messageJSON.getString("type")
|
||||
|
||||
val requestId = messageJSON.getInt("id")
|
||||
val status = messageJSON.getString("status")
|
||||
if (status == "success") {
|
||||
requestHandlers[requestId]?.success(message)
|
||||
} else {
|
||||
requestHandlers[requestId]?.error(
|
||||
"Pref Manager",
|
||||
"Failed to perform operation",
|
||||
message.getString("error")
|
||||
)
|
||||
if (type == "feedRequest") {
|
||||
val url = messageJSON.getString("url")
|
||||
|
||||
runOnUiThread {
|
||||
extensionEvents.onFeedRequested(
|
||||
System.currentTimeMillis(),
|
||||
url
|
||||
) { _ -> }
|
||||
}
|
||||
} else if (type == "turndown") {
|
||||
val requestId = messageJSON.getInt("id")
|
||||
val status = messageJSON.getString("status")
|
||||
if (status == "success") {
|
||||
requestHandlers[requestId]?.success(message)
|
||||
} else {
|
||||
requestHandlers[requestId]?.error(
|
||||
"Pref Manager",
|
||||
"Failed to perform operation",
|
||||
message.getString("error")
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -87,17 +110,17 @@ object TurndownFeature {
|
||||
* @param productName a custom product name used to automatically label reports. Defaults to
|
||||
* "android-components".
|
||||
*/
|
||||
fun install(runtime: WebExtensionRuntime) {
|
||||
fun install(runtime: WebExtensionRuntime, extensionEvents: BrowserExtensionEvents) {
|
||||
extensionController.registerBackgroundMessageHandler(
|
||||
TurndownBackgroundMessageHandler(),
|
||||
ExtensionBackgroundMessageHandler(extensionEvents)
|
||||
)
|
||||
extensionController.install(
|
||||
runtime,
|
||||
onSuccess = {
|
||||
logger.debug("Installed Turndown webextension: ${it.id}")
|
||||
logger.debug("Installed browser_extension webextension: ${it.id}")
|
||||
},
|
||||
onError = { throwable ->
|
||||
logger.error("Failed to install Turndown webextension: ", throwable)
|
||||
logger.error("Failed to install browser_extension webextension: ", throwable)
|
||||
},
|
||||
)
|
||||
}
|
||||
+33
-8
@@ -1994,7 +1994,7 @@ private open class GeckoPigeonCodec : StandardMessageCodec() {
|
||||
|
||||
/** Generated interface from Pigeon that represents a handler of messages from Flutter. */
|
||||
interface GeckoBrowserApi {
|
||||
fun showNativeFragment()
|
||||
fun showNativeFragment(): Boolean
|
||||
fun onTrimMemory(level: Long)
|
||||
|
||||
companion object {
|
||||
@@ -2011,8 +2011,7 @@ interface GeckoBrowserApi {
|
||||
if (api != null) {
|
||||
channel.setMessageHandler { _, reply ->
|
||||
val wrapped: List<Any?> = try {
|
||||
api.showNativeFragment()
|
||||
listOf(null)
|
||||
listOf(api.showNativeFragment())
|
||||
} catch (exception: Throwable) {
|
||||
wrapError(exception)
|
||||
}
|
||||
@@ -2980,20 +2979,20 @@ interface GeckoPrefApi {
|
||||
}
|
||||
}
|
||||
/** Generated interface from Pigeon that represents a handler of messages from Flutter. */
|
||||
interface GeckoTurndownApi {
|
||||
interface GeckoBrowserExtensionApi {
|
||||
fun getMarkdown(htmlList: List<String>, callback: (Result<List<Any>>) -> Unit)
|
||||
|
||||
companion object {
|
||||
/** The codec used by GeckoTurndownApi. */
|
||||
/** The codec used by GeckoBrowserExtensionApi. */
|
||||
val codec: MessageCodec<Any?> by lazy {
|
||||
GeckoPigeonCodec()
|
||||
}
|
||||
/** Sets up an instance of `GeckoTurndownApi` to handle messages through the `binaryMessenger`. */
|
||||
/** Sets up an instance of `GeckoBrowserExtensionApi` to handle messages through the `binaryMessenger`. */
|
||||
@JvmOverloads
|
||||
fun setUp(binaryMessenger: BinaryMessenger, api: GeckoTurndownApi?, messageChannelSuffix: String = "") {
|
||||
fun setUp(binaryMessenger: BinaryMessenger, api: GeckoBrowserExtensionApi?, messageChannelSuffix: String = "") {
|
||||
val separatedMessageChannelSuffix = if (messageChannelSuffix.isNotEmpty()) ".$messageChannelSuffix" else ""
|
||||
run {
|
||||
val channel = BasicMessageChannel<Any?>(binaryMessenger, "dev.flutter.pigeon.flutter_mozilla_components.GeckoTurndownApi.getMarkdown$separatedMessageChannelSuffix", codec)
|
||||
val channel = BasicMessageChannel<Any?>(binaryMessenger, "dev.flutter.pigeon.flutter_mozilla_components.GeckoBrowserExtensionApi.getMarkdown$separatedMessageChannelSuffix", codec)
|
||||
if (api != null) {
|
||||
channel.setMessageHandler { message, reply ->
|
||||
val args = message as List<Any?>
|
||||
@@ -4010,3 +4009,29 @@ interface GeckoDownloadsApi {
|
||||
}
|
||||
}
|
||||
}
|
||||
/** Generated class from Pigeon that represents Flutter messages that can be called from Kotlin. */
|
||||
class BrowserExtensionEvents(private val binaryMessenger: BinaryMessenger, private val messageChannelSuffix: String = "") {
|
||||
companion object {
|
||||
/** The codec used by BrowserExtensionEvents. */
|
||||
val codec: MessageCodec<Any?> by lazy {
|
||||
GeckoPigeonCodec()
|
||||
}
|
||||
}
|
||||
fun onFeedRequested(timestampArg: Long, urlArg: String, callback: (Result<Unit>) -> Unit)
|
||||
{
|
||||
val separatedMessageChannelSuffix = if (messageChannelSuffix.isNotEmpty()) ".$messageChannelSuffix" else ""
|
||||
val channelName = "dev.flutter.pigeon.flutter_mozilla_components.BrowserExtensionEvents.onFeedRequested$separatedMessageChannelSuffix"
|
||||
val channel = BasicMessageChannel<Any?>(binaryMessenger, channelName, codec)
|
||||
channel.send(listOf(timestampArg, urlArg)) {
|
||||
if (it is List<*>) {
|
||||
if (it.size > 1) {
|
||||
callback(Result.failure(FlutterError(it[0] as String, it[1] as String, it[2] as String?)))
|
||||
} else {
|
||||
callback(Result.success(Unit))
|
||||
}
|
||||
} else {
|
||||
callback(Result.failure(createConnectionError(channelName)))
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -18,7 +18,7 @@ export 'src/domain/services/gecko_session.dart';
|
||||
export 'src/domain/services/gecko_suggestions.dart';
|
||||
export 'src/domain/services/gecko_tab.dart';
|
||||
export 'src/domain/services/gecko_tab_content.dart';
|
||||
export 'src/domain/services/gecko_turndown.dart';
|
||||
export 'src/domain/services/gecko_browser_extension.dart';
|
||||
export 'src/geckoview_widget.dart';
|
||||
export 'src/pigeons/gecko.g.dart'
|
||||
show
|
||||
|
||||
@@ -7,7 +7,7 @@ class GeckoBrowserService {
|
||||
|
||||
GeckoBrowserService({GeckoBrowserApi? api}) : _api = api ?? _apiInstance;
|
||||
|
||||
Future<void> showNativeFragment() {
|
||||
Future<bool> showNativeFragment() {
|
||||
return _api.showNativeFragment();
|
||||
}
|
||||
|
||||
|
||||
+56
@@ -0,0 +1,56 @@
|
||||
import 'dart:async';
|
||||
|
||||
import 'package:flutter/services.dart';
|
||||
import 'package:flutter_mozilla_components/src/domain/entities/turndown_result.dart';
|
||||
import 'package:flutter_mozilla_components/src/extensions/subject.dart';
|
||||
import 'package:flutter_mozilla_components/src/pigeons/gecko.g.dart';
|
||||
import 'package:rxdart/rxdart.dart';
|
||||
|
||||
final _apiInstance = GeckoBrowserExtensionApi();
|
||||
|
||||
class GeckoBrowserExtensionService extends BrowserExtensionEvents {
|
||||
final _feedRequest = BehaviorSubject<String>();
|
||||
|
||||
Stream<String> get feedRequested => _feedRequest.stream;
|
||||
|
||||
static Future<List<TurndownResults>> turndownHtml(
|
||||
List<String> htmlList,
|
||||
) async {
|
||||
final markdownResult = await _apiInstance.getMarkdown(htmlList);
|
||||
|
||||
final results =
|
||||
markdownResult
|
||||
.cast()
|
||||
.map(
|
||||
(result) => TurndownResults(
|
||||
// ignore: avoid_dynamic_calls valid
|
||||
markdown: result['fullContentMarkdown'] as String,
|
||||
// ignore: avoid_dynamic_calls valid
|
||||
plain: result['fullContentPlain'] as String,
|
||||
),
|
||||
)
|
||||
.toList();
|
||||
|
||||
return results;
|
||||
}
|
||||
|
||||
GeckoBrowserExtensionService.setUp({
|
||||
BinaryMessenger? binaryMessenger,
|
||||
String messageChannelSuffix = '',
|
||||
}) {
|
||||
BrowserExtensionEvents.setUp(
|
||||
this,
|
||||
binaryMessenger: binaryMessenger,
|
||||
messageChannelSuffix: messageChannelSuffix,
|
||||
);
|
||||
}
|
||||
|
||||
@override
|
||||
void onFeedRequested(int timestamp, String url) {
|
||||
_feedRequest.addWhenMoreRecent(timestamp, null, url);
|
||||
}
|
||||
|
||||
void dispose() {
|
||||
unawaited(_feedRequest.close());
|
||||
}
|
||||
}
|
||||
@@ -1,25 +0,0 @@
|
||||
import 'package:flutter_mozilla_components/src/domain/entities/turndown_result.dart';
|
||||
import 'package:flutter_mozilla_components/src/pigeons/gecko.g.dart';
|
||||
|
||||
final _apiInstance = GeckoTurndownApi();
|
||||
|
||||
class GeckoTurndownService {
|
||||
Future<List<TurndownResults>> turndownHtml(List<String> htmlList) async {
|
||||
final markdownResult = await _apiInstance.getMarkdown(htmlList);
|
||||
|
||||
final results =
|
||||
markdownResult
|
||||
.cast()
|
||||
.map(
|
||||
(result) => TurndownResults(
|
||||
// ignore: avoid_dynamic_calls valid
|
||||
markdown: result['fullContentMarkdown'] as String,
|
||||
// ignore: avoid_dynamic_calls valid
|
||||
plain: result['fullContentPlain'] as String,
|
||||
),
|
||||
)
|
||||
.toList();
|
||||
|
||||
return results;
|
||||
}
|
||||
}
|
||||
@@ -43,6 +43,29 @@ class _GeckoViewState extends State<GeckoView> {
|
||||
});
|
||||
}
|
||||
|
||||
Future<bool> _showNativeFragment({
|
||||
int maxRetries = 100,
|
||||
|
||||
/// Default ist about one frame
|
||||
Duration retryDelay = const Duration(milliseconds: 1000 ~/ 60),
|
||||
}) async {
|
||||
for (int attempt = 0; attempt < maxRetries; attempt++) {
|
||||
final result = await browserService.showNativeFragment();
|
||||
|
||||
if (result) {
|
||||
debugPrint('Fragment ATTACHED after $attempt tries');
|
||||
return true;
|
||||
}
|
||||
|
||||
if (attempt < maxRetries - 1) {
|
||||
await Future.delayed(retryDelay);
|
||||
}
|
||||
}
|
||||
|
||||
debugPrint('Fragment FAILED after $maxRetries tries');
|
||||
return false;
|
||||
}
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return PlatformViewLink(
|
||||
@@ -68,13 +91,8 @@ class _GeckoViewState extends State<GeckoView> {
|
||||
SchedulerBinding.instance.addPostFrameCallback((_) async {
|
||||
await widget.preInitializationStep?.call();
|
||||
|
||||
await Future.delayed(
|
||||
//Wait for two more frames just to be sure view has been initialized
|
||||
Duration(milliseconds: ((1000 / 60) * 2).toInt()),
|
||||
).whenComplete(() async {
|
||||
await browserService.showNativeFragment();
|
||||
await widget.postInitializationStep?.call();
|
||||
});
|
||||
await _showNativeFragment();
|
||||
await widget.postInitializationStep?.call();
|
||||
});
|
||||
})
|
||||
// ignore: discarded_futures that hos it is done in docs
|
||||
|
||||
@@ -1983,7 +1983,7 @@ class GeckoBrowserApi {
|
||||
|
||||
final String pigeonVar_messageChannelSuffix;
|
||||
|
||||
Future<void> showNativeFragment() async {
|
||||
Future<bool> showNativeFragment() async {
|
||||
final String pigeonVar_channelName = 'dev.flutter.pigeon.flutter_mozilla_components.GeckoBrowserApi.showNativeFragment$pigeonVar_messageChannelSuffix';
|
||||
final BasicMessageChannel<Object?> pigeonVar_channel = BasicMessageChannel<Object?>(
|
||||
pigeonVar_channelName,
|
||||
@@ -2001,8 +2001,13 @@ class GeckoBrowserApi {
|
||||
message: pigeonVar_replyList[1] as String?,
|
||||
details: pigeonVar_replyList[2],
|
||||
);
|
||||
} else if (pigeonVar_replyList[0] == null) {
|
||||
throw PlatformException(
|
||||
code: 'null-error',
|
||||
message: 'Host platform returned null value for non-null return value.',
|
||||
);
|
||||
} else {
|
||||
return;
|
||||
return (pigeonVar_replyList[0] as bool?)!;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -3123,11 +3128,11 @@ class GeckoPrefApi {
|
||||
}
|
||||
}
|
||||
|
||||
class GeckoTurndownApi {
|
||||
/// Constructor for [GeckoTurndownApi]. The [binaryMessenger] named argument is
|
||||
class GeckoBrowserExtensionApi {
|
||||
/// Constructor for [GeckoBrowserExtensionApi]. The [binaryMessenger] named argument is
|
||||
/// available for dependency injection. If it is left null, the default
|
||||
/// BinaryMessenger will be used which routes to the host platform.
|
||||
GeckoTurndownApi({BinaryMessenger? binaryMessenger, String messageChannelSuffix = ''})
|
||||
GeckoBrowserExtensionApi({BinaryMessenger? binaryMessenger, String messageChannelSuffix = ''})
|
||||
: pigeonVar_binaryMessenger = binaryMessenger,
|
||||
pigeonVar_messageChannelSuffix = messageChannelSuffix.isNotEmpty ? '.$messageChannelSuffix' : '';
|
||||
final BinaryMessenger? pigeonVar_binaryMessenger;
|
||||
@@ -3137,7 +3142,7 @@ class GeckoTurndownApi {
|
||||
final String pigeonVar_messageChannelSuffix;
|
||||
|
||||
Future<List<Object>> getMarkdown(List<String> htmlList) async {
|
||||
final String pigeonVar_channelName = 'dev.flutter.pigeon.flutter_mozilla_components.GeckoTurndownApi.getMarkdown$pigeonVar_messageChannelSuffix';
|
||||
final String pigeonVar_channelName = 'dev.flutter.pigeon.flutter_mozilla_components.GeckoBrowserExtensionApi.getMarkdown$pigeonVar_messageChannelSuffix';
|
||||
final BasicMessageChannel<Object?> pigeonVar_channel = BasicMessageChannel<Object?>(
|
||||
pigeonVar_channelName,
|
||||
pigeonChannelCodec,
|
||||
@@ -4529,3 +4534,41 @@ class GeckoDownloadsApi {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
abstract class BrowserExtensionEvents {
|
||||
static const MessageCodec<Object?> pigeonChannelCodec = _PigeonCodec();
|
||||
|
||||
void onFeedRequested(int timestamp, String url);
|
||||
|
||||
static void setUp(BrowserExtensionEvents? api, {BinaryMessenger? binaryMessenger, String messageChannelSuffix = '',}) {
|
||||
messageChannelSuffix = messageChannelSuffix.isNotEmpty ? '.$messageChannelSuffix' : '';
|
||||
{
|
||||
final BasicMessageChannel<Object?> pigeonVar_channel = BasicMessageChannel<Object?>(
|
||||
'dev.flutter.pigeon.flutter_mozilla_components.BrowserExtensionEvents.onFeedRequested$messageChannelSuffix', pigeonChannelCodec,
|
||||
binaryMessenger: binaryMessenger);
|
||||
if (api == null) {
|
||||
pigeonVar_channel.setMessageHandler(null);
|
||||
} else {
|
||||
pigeonVar_channel.setMessageHandler((Object? message) async {
|
||||
assert(message != null,
|
||||
'Argument for dev.flutter.pigeon.flutter_mozilla_components.BrowserExtensionEvents.onFeedRequested was null.');
|
||||
final List<Object?> args = (message as List<Object?>?)!;
|
||||
final int? arg_timestamp = (args[0] as int?);
|
||||
assert(arg_timestamp != null,
|
||||
'Argument for dev.flutter.pigeon.flutter_mozilla_components.BrowserExtensionEvents.onFeedRequested was null, expected non-null int.');
|
||||
final String? arg_url = (args[1] as String?);
|
||||
assert(arg_url != null,
|
||||
'Argument for dev.flutter.pigeon.flutter_mozilla_components.BrowserExtensionEvents.onFeedRequested was null, expected non-null String.');
|
||||
try {
|
||||
api.onFeedRequested(arg_timestamp!, arg_url!);
|
||||
return wrapResponse(empty: true);
|
||||
} on PlatformException catch (e) {
|
||||
return wrapResponse(error: e);
|
||||
} catch (e) {
|
||||
return wrapResponse(error: PlatformException(code: 'error', message: e.toString()));
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -736,7 +736,7 @@ class ShareInternetResourceState {
|
||||
)
|
||||
@HostApi()
|
||||
abstract class GeckoBrowserApi {
|
||||
void showNativeFragment();
|
||||
bool showNativeFragment();
|
||||
void onTrimMemory(int level);
|
||||
}
|
||||
|
||||
@@ -943,7 +943,7 @@ abstract class GeckoPrefApi {
|
||||
}
|
||||
|
||||
@HostApi()
|
||||
abstract class GeckoTurndownApi {
|
||||
abstract class GeckoBrowserExtensionApi {
|
||||
@async
|
||||
List<Object> getMarkdown(List<String> htmlList);
|
||||
}
|
||||
@@ -1122,3 +1122,8 @@ abstract class GeckoDownloadsApi {
|
||||
void copyInternetResource(String tabId, ShareInternetResourceState state);
|
||||
void shareInternetResource(String tabId, ShareInternetResourceState state);
|
||||
}
|
||||
|
||||
@FlutterApi()
|
||||
abstract class BrowserExtensionEvents {
|
||||
void onFeedRequested(int timestamp, String url);
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user