added PrintJobController to manage print jobs

This commit is contained in:
Lorenzo Pichilli 2022-05-12 00:19:43 +02:00
parent 0a16e1babe
commit 0dd9dfbff6
72 changed files with 3464 additions and 127 deletions

View File

@ -3,6 +3,7 @@
- Deprecated old classes/properties/methods to make them eventually compatible with other Platforms and WebView engines.
- Added Web support
- Added `ProxyController` for Android
- Added `PrintJobController` to manage print jobs
- Added `WebAuthenticationSession` for iOS
- Added `pauseAllMediaPlayback`, `setAllMediaPlaybackSuspended`, `closeAllMediaPresentations`, `requestMediaPlaybackState`, `isInFullscreen`, `getCameraCaptureState`, `setCameraCaptureState`, `getMicrophoneCaptureState`, `setMicrophoneCaptureState` WebView controller methods
- Added `underPageBackgroundColor`, `isTextInteractionEnabled`, `isSiteSpecificQuirksModeEnabled`, `upgradeKnownHostsToHTTPS`, `forceDarkStrategy` WebView settings

View File

@ -1,9 +1,11 @@
package com.pichillilorenzo.flutter_inappwebview;
import androidx.annotation.NonNull;
import java.util.Map;
public interface ISettings<T> {
public ISettings parse(Map<String, Object> settings);
public Map<String, Object> toMap();
public Map<String, Object> getRealSettings(T obj);
@NonNull ISettings<T> parse(@NonNull Map<String, Object> settings);
@NonNull Map<String, Object> toMap();
@NonNull Map<String, Object> getRealSettings(@NonNull T obj);
}

View File

@ -6,12 +6,14 @@ import android.net.Uri;
import android.os.Build;
import android.webkit.ValueCallback;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import com.pichillilorenzo.flutter_inappwebview.chrome_custom_tabs.ChromeSafariBrowserManager;
import com.pichillilorenzo.flutter_inappwebview.credential_database.CredentialDatabaseHandler;
import com.pichillilorenzo.flutter_inappwebview.in_app_browser.InAppBrowserManager;
import com.pichillilorenzo.flutter_inappwebview.headless_in_app_webview.HeadlessInAppWebViewManager;
import com.pichillilorenzo.flutter_inappwebview.print_job.PrintJobManager;
import com.pichillilorenzo.flutter_inappwebview.proxy.ProxyManager;
import com.pichillilorenzo.flutter_inappwebview.service_worker.ServiceWorkerManager;
@ -27,21 +29,36 @@ public class InAppWebViewFlutterPlugin implements FlutterPlugin, ActivityAware {
protected static final String LOG_TAG = "InAppWebViewFlutterPL";
@Nullable
public PlatformUtil platformUtil;
@Nullable
public InAppBrowserManager inAppBrowserManager;
@Nullable
public HeadlessInAppWebViewManager headlessInAppWebViewManager;
@Nullable
public ChromeSafariBrowserManager chromeSafariBrowserManager;
@Nullable
public InAppWebViewStatic inAppWebViewStatic;
@Nullable
public MyCookieManager myCookieManager;
@Nullable
public CredentialDatabaseHandler credentialDatabaseHandler;
@Nullable
public MyWebStorage myWebStorage;
@Nullable
public ServiceWorkerManager serviceWorkerManager;
@Nullable
public WebViewFeatureManager webViewFeatureManager;
@Nullable
public ProxyManager proxyManager;
public FlutterWebViewFactory flutterWebViewFactory;
@Nullable
public PrintJobManager printJobManager;
@Nullable
public static ValueCallback<Uri> filePathCallbackLegacy;
@Nullable
public static ValueCallback<Uri[]> filePathCallback;
public FlutterWebViewFactory flutterWebViewFactory;
public Context applicationContext;
public PluginRegistry.Registrar registrar;
public BinaryMessenger messenger;
@ -101,10 +118,13 @@ public class InAppWebViewFlutterPlugin implements FlutterPlugin, ActivityAware {
}
webViewFeatureManager = new WebViewFeatureManager(this);
proxyManager = new ProxyManager(this);
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT) {
printJobManager = new PrintJobManager();
}
}
@Override
public void onDetachedFromEngine(FlutterPluginBinding binding) {
public void onDetachedFromEngine(@NonNull FlutterPluginBinding binding) {
if (platformUtil != null) {
platformUtil.dispose();
platformUtil = null;
@ -149,6 +169,10 @@ public class InAppWebViewFlutterPlugin implements FlutterPlugin, ActivityAware {
proxyManager.dispose();
proxyManager = null;
}
if (printJobManager != null && Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT) {
printJobManager.dispose();
printJobManager = null;
}
filePathCallbackLegacy = null;
filePathCallback = null;
}

View File

@ -2,6 +2,7 @@ package com.pichillilorenzo.flutter_inappwebview.chrome_custom_tabs;
import android.content.Intent;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import androidx.browser.customtabs.CustomTabsIntent;
import androidx.browser.trusted.ScreenOrientation;
@ -35,8 +36,9 @@ public class ChromeCustomTabsSettings implements ISettings<ChromeCustomTabsActiv
public TrustedWebActivityDisplayMode displayMode = null;
public Integer screenOrientation = ScreenOrientation.DEFAULT;
@NonNull
@Override
public ChromeCustomTabsSettings parse(Map<String, Object> options) {
public ChromeCustomTabsSettings parse(@NonNull Map<String, Object> options) {
for (Map.Entry<String, Object> pair : options.entrySet()) {
String key = pair.getKey();
Object value = pair.getValue();
@ -104,6 +106,7 @@ public class ChromeCustomTabsSettings implements ISettings<ChromeCustomTabsActiv
return this;
}
@NonNull
@Override
public Map<String, Object> toMap() {
Map<String, Object> options = new HashMap<>();
@ -122,8 +125,9 @@ public class ChromeCustomTabsSettings implements ISettings<ChromeCustomTabsActiv
return options;
}
@NonNull
@Override
public Map<String, Object> getRealSettings(ChromeCustomTabsActivity chromeCustomTabsActivity) {
public Map<String, Object> getRealSettings(@NonNull ChromeCustomTabsActivity chromeCustomTabsActivity) {
Map<String, Object> realOptions = toMap();
if (chromeCustomTabsActivity != null) {
Intent intent = chromeCustomTabsActivity.getIntent();

View File

@ -1,5 +1,6 @@
package com.pichillilorenzo.flutter_inappwebview.in_app_browser;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import com.pichillilorenzo.flutter_inappwebview.ISettings;
@ -26,8 +27,9 @@ public class InAppBrowserSettings implements ISettings<InAppBrowserActivity> {
public Boolean allowGoBackWithBackButton = true;
public Boolean shouldCloseOnBackButtonPressed = false;
@NonNull
@Override
public InAppBrowserSettings parse(Map<String, Object> settings) {
public InAppBrowserSettings parse(@NonNull Map<String, Object> settings) {
for (Map.Entry<String, Object> pair : settings.entrySet()) {
String key = pair.getKey();
Object value = pair.getValue();
@ -72,6 +74,7 @@ public class InAppBrowserSettings implements ISettings<InAppBrowserActivity> {
return this;
}
@NonNull
@Override
public Map<String, Object> toMap() {
Map<String, Object> settings = new HashMap<>();
@ -88,12 +91,13 @@ public class InAppBrowserSettings implements ISettings<InAppBrowserActivity> {
return settings;
}
@NonNull
@Override
public Map<String, Object> getRealSettings(InAppBrowserActivity inAppBrowserActivity) {
public Map<String, Object> getRealSettings(@NonNull InAppBrowserActivity inAppBrowserActivity) {
Map<String, Object> realSettings = toMap();
realSettings.put("hideToolbarTop", !inAppBrowserActivity.actionBar.isShowing());
realSettings.put("hideUrlBar", !inAppBrowserActivity.menu.findItem(R.id.menu_search).isVisible());
realSettings.put("hideProgressBar", inAppBrowserActivity.progressBar.getMax() == 0);
realSettings.put("hideToolbarTop", inAppBrowserActivity.actionBar == null || !inAppBrowserActivity.actionBar.isShowing());
realSettings.put("hideUrlBar", inAppBrowserActivity.menu == null || !inAppBrowserActivity.menu.findItem(R.id.menu_search).isVisible());
realSettings.put("hideProgressBar", inAppBrowserActivity.progressBar == null || inAppBrowserActivity.progressBar.getMax() == 0);
return realSettings;
}
}

View File

@ -15,7 +15,7 @@ public class PrintJS {
public static final String PRINT_JS_SOURCE = "window.print = function() {" +
" if (window.top == null || window.top === window) {" +
" window." + JavaScriptBridgeJS.JAVASCRIPT_BRIDGE_NAME + ".callHandler('onPrint', window.location.href);" +
" window." + JavaScriptBridgeJS.JAVASCRIPT_BRIDGE_NAME + ".callHandler('onPrintRequest', window.location.href);" +
" } else {" +
" window.top.print();" +
" }" +

View File

@ -0,0 +1,70 @@
package com.pichillilorenzo.flutter_inappwebview.print_job;
import android.os.Build;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import androidx.annotation.RequiresApi;
import com.pichillilorenzo.flutter_inappwebview.types.ChannelDelegateImpl;
import com.pichillilorenzo.flutter_inappwebview.types.PrintJobInfoExt;
import io.flutter.plugin.common.MethodCall;
import io.flutter.plugin.common.MethodChannel;
@RequiresApi(api = Build.VERSION_CODES.KITKAT)
public class PrintJobChannelDelegate extends ChannelDelegateImpl {
@Nullable
private PrintJobController printJobController;
public PrintJobChannelDelegate(@NonNull PrintJobController printJobController, @NonNull MethodChannel channel) {
super(channel);
this.printJobController = printJobController;
}
@Override
public void onMethodCall(@NonNull MethodCall call, @NonNull MethodChannel.Result result) {
switch (call.method) {
case "cancel":
if (printJobController != null) {
printJobController.cancel();
result.success(true);
} else {
result.success(false);
}
break;
case "restart":
if (printJobController != null) {
printJobController.restart();
result.success(true);
} else {
result.success(false);
}
break;
case "getInfo":
if (printJobController != null) {
PrintJobInfoExt info = printJobController.getInfo();
result.success(info != null ? info.toMap() : null);
} else {
result.success(null);
}
break;
case "dispose":
if (printJobController != null) {
printJobController.dispose();
result.success(true);
} else {
result.success(false);
}
break;
default:
result.notImplemented();
}
}
@Override
public void dispose() {
super.dispose();
printJobController = null;
}
}

View File

@ -0,0 +1,86 @@
package com.pichillilorenzo.flutter_inappwebview.print_job;
import android.os.Build;
import android.util.Log;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import androidx.annotation.RequiresApi;
import com.pichillilorenzo.flutter_inappwebview.InAppWebViewFlutterPlugin;
import com.pichillilorenzo.flutter_inappwebview.types.Disposable;
import com.pichillilorenzo.flutter_inappwebview.types.PrintJobInfoExt;
import io.flutter.plugin.common.MethodChannel;
@RequiresApi(api = Build.VERSION_CODES.KITKAT)
public class PrintJobController implements Disposable {
protected static final String LOG_TAG = "PrintJob";
public static final String METHOD_CHANNEL_NAME_PREFIX = "com.pichillilorenzo/flutter_inappwebview_printjobcontroller_";
@NonNull
public String id;
@Nullable
public PrintJobChannelDelegate channelDelegate;
@Nullable
public android.print.PrintJob job;
@Nullable
public PrintJobSettings settings;
public PrintJobController(@NonNull String id, @NonNull android.print.PrintJob job,
@Nullable PrintJobSettings settings, @NonNull InAppWebViewFlutterPlugin plugin) {
this.id = id;
this.job = job;
this.settings = settings;
final MethodChannel channel = new MethodChannel(plugin.messenger, METHOD_CHANNEL_NAME_PREFIX + id);
this.channelDelegate = new PrintJobChannelDelegate(this, channel);
}
public void cancel() {
if (this.job != null) {
this.job.cancel();
}
}
public void restart() {
if (this.job != null) {
this.job.restart();
}
}
@Nullable
public PrintJobInfoExt getInfo() {
if (this.job != null) {
return PrintJobInfoExt.fromPrintJobInfo(this.job.getInfo());
}
return null;
}
public void disposeNoCancel() {
if (channelDelegate != null) {
channelDelegate.dispose();
channelDelegate = null;
}
if (PrintJobManager.jobs.containsKey(id)) {
PrintJobManager.jobs.put(id, null);
}
if (job != null) {
job = null;
}
}
@Override
public void dispose() {
if (channelDelegate != null) {
channelDelegate.dispose();
channelDelegate = null;
}
if (PrintJobManager.jobs.containsKey(id)) {
PrintJobManager.jobs.put(id, null);
}
if (job != null) {
job.cancel();
job = null;
}
}
}

View File

@ -0,0 +1,53 @@
/*
*
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you 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.
*
*/
package com.pichillilorenzo.flutter_inappwebview.print_job;
import android.os.Build;
import androidx.annotation.RequiresApi;
import com.pichillilorenzo.flutter_inappwebview.types.Disposable;
import java.util.Collection;
import java.util.HashMap;
import java.util.Map;
@RequiresApi(api = Build.VERSION_CODES.KITKAT)
public class PrintJobManager implements Disposable {
protected static final String LOG_TAG = "PrintJobManager";
public static final Map<String, PrintJobController> jobs = new HashMap<>();
public PrintJobManager() {
super();
}
public void dispose() {
Collection<PrintJobController> printJobControllers = jobs.values();
for (PrintJobController job : printJobControllers) {
if (job != null) {
job.dispose();
}
}
jobs.clear();
}
}

View File

@ -0,0 +1,99 @@
package com.pichillilorenzo.flutter_inappwebview.print_job;
import android.os.Build;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import androidx.annotation.RequiresApi;
import com.pichillilorenzo.flutter_inappwebview.ISettings;
import com.pichillilorenzo.flutter_inappwebview.types.MediaSizeExt;
import com.pichillilorenzo.flutter_inappwebview.types.ResolutionExt;
import java.util.HashMap;
import java.util.Map;
@RequiresApi(api = Build.VERSION_CODES.KITKAT)
public class PrintJobSettings implements ISettings<PrintJobController> {
public static final String LOG_TAG = "PrintJobSettings";
public Boolean handledByClient = false;
@Nullable
public String jobName;
@Nullable
public Integer orientation;
// @Nullable
// public MarginsExt margins;
@Nullable
public MediaSizeExt mediaSize;
@Nullable
public Integer colorMode;
@Nullable
public Integer duplexMode;
@Nullable
public ResolutionExt resolution;
@NonNull
@Override
public PrintJobSettings parse(@NonNull Map<String, Object> settings) {
for (Map.Entry<String, Object> pair : settings.entrySet()) {
String key = pair.getKey();
Object value = pair.getValue();
if (value == null) {
continue;
}
switch (key) {
case "handledByClient":
handledByClient = (Boolean) value;
break;
case "jobName":
jobName = (String) value;
break;
case "orientation":
orientation = (Integer) value;
break;
// case "margins":
// margins = MarginsExt.fromMap((Map<String, Object>) value);
// break;
case "mediaSize":
mediaSize = MediaSizeExt.fromMap((Map<String, Object>) value);
break;
case "colorMode":
colorMode = (Integer) value;
break;
case "duplexMode":
duplexMode = (Integer) value;
break;
case "resolution":
resolution = ResolutionExt.fromMap((Map<String, Object>) value);
break;
}
}
return this;
}
@NonNull
@Override
public Map<String, Object> toMap() {
Map<String, Object> settings = new HashMap<>();
settings.put("handledByClient", handledByClient);
settings.put("jobName", jobName);
settings.put("orientation", orientation);
// settings.put("margins", margins != null ? margins.toMap() : null);
settings.put("mediaSize", mediaSize != null ? mediaSize.toMap() : null);
settings.put("colorMode", colorMode);
settings.put("duplexMode", duplexMode);
settings.put("resolution", resolution != null ? resolution.toMap() : null);
return settings;
}
@NonNull
@Override
public Map<String, Object> getRealSettings(@NonNull PrintJobController printJobController) {
Map<String, Object> realSettings = toMap();
return realSettings;
}
}

View File

@ -1,5 +1,6 @@
package com.pichillilorenzo.flutter_inappwebview.proxy;
import androidx.annotation.NonNull;
import androidx.webkit.ProxyConfig;
import com.pichillilorenzo.flutter_inappwebview.ISettings;
@ -17,8 +18,9 @@ public class ProxySettings implements ISettings<ProxyConfig> {
Boolean bypassSimpleHostnames = null;
Boolean removeImplicitRules = null;
@NonNull
@Override
public ProxySettings parse(Map<String, Object> settings) {
public ProxySettings parse(@NonNull Map<String, Object> settings) {
for (Map.Entry<String, Object> pair : settings.entrySet()) {
String key = pair.getKey();
Object value = pair.getValue();
@ -55,6 +57,7 @@ public class ProxySettings implements ISettings<ProxyConfig> {
return this;
}
@NonNull
@Override
public Map<String, Object> toMap() {
List<Map<String, String>> proxyRuleMapList = new ArrayList<>();
@ -70,8 +73,9 @@ public class ProxySettings implements ISettings<ProxyConfig> {
return settings;
}
@NonNull
@Override
public Map<String, Object> getRealSettings(ProxyConfig proxyConfig) {
public Map<String, Object> getRealSettings(@NonNull ProxyConfig proxyConfig) {
Map<String, Object> realSettings = toMap();
List<Map<String, String>> proxyRuleMapList = new ArrayList<>();
List<ProxyConfig.ProxyRule> proxyRules = proxyConfig.getProxyRules();

View File

@ -1,5 +1,6 @@
package com.pichillilorenzo.flutter_inappwebview.pull_to_refresh;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import com.pichillilorenzo.flutter_inappwebview.ISettings;
@ -22,7 +23,9 @@ public class PullToRefreshSettings implements ISettings<PullToRefreshLayout> {
@Nullable
public Integer size;
public PullToRefreshSettings parse(Map<String, Object> settings) {
@NonNull
@Override
public PullToRefreshSettings parse(@NonNull Map<String, Object> settings) {
for (Map.Entry<String, Object> pair : settings.entrySet()) {
String key = pair.getKey();
Object value = pair.getValue();
@ -55,6 +58,7 @@ public class PullToRefreshSettings implements ISettings<PullToRefreshLayout> {
return this;
}
@NonNull
public Map<String, Object> toMap() {
Map<String, Object> settings = new HashMap<>();
settings.put("enabled", enabled);
@ -66,8 +70,9 @@ public class PullToRefreshSettings implements ISettings<PullToRefreshLayout> {
return settings;
}
@NonNull
@Override
public Map<String, Object> getRealSettings(PullToRefreshLayout pullToRefreshLayout) {
public Map<String, Object> getRealSettings(@NonNull PullToRefreshLayout pullToRefreshLayout) {
Map<String, Object> realSettings = toMap();
return realSettings;
}

View File

@ -0,0 +1,150 @@
package com.pichillilorenzo.flutter_inappwebview.types;
import android.os.Build;
import android.print.PrintAttributes;
import androidx.annotation.Nullable;
import androidx.annotation.RequiresApi;
import java.util.HashMap;
import java.util.Map;
@RequiresApi(api = Build.VERSION_CODES.KITKAT)
public class MarginsExt {
private double top;
private double right;
private double bottom;
private double left;
public MarginsExt() {}
public MarginsExt(double top, double right, double bottom, double left) {
this.top = top;
this.right = right;
this.bottom = bottom;
this.left = left;
}
@Nullable
public static MarginsExt fromMargins(@Nullable PrintAttributes.Margins margins) {
if (margins == null) {
return null;
}
MarginsExt marginsExt = new MarginsExt();
marginsExt.top = milsToPixels(margins.getTopMils());
marginsExt.right = milsToPixels(margins.getRightMils());
marginsExt.bottom = milsToPixels(margins.getBottomMils());
marginsExt.left = milsToPixels(margins.getLeftMils());
return marginsExt;
}
@Nullable
public static MarginsExt fromMap(@Nullable Map<String, Object> map) {
if (map == null) {
return null;
}
return new MarginsExt(
(double) map.get("top"),
(double) map.get("right"),
(double) map.get("bottom"),
(double) map.get("left"));
}
public PrintAttributes.Margins toMargins() {
return new PrintAttributes.Margins(
pixelsToMils(left),
pixelsToMils(top),
pixelsToMils(right),
pixelsToMils(bottom)
);
}
// from mils to pixels
private static double milsToPixels(int mils) {
return mils * 0.09600001209449;
}
// from pixels to mils
private static int pixelsToMils(double pixels) {
return (int) Math.round(pixels * 10.416665354331);
}
public Map<String, Object> toMap() {
Map<String, Object> obj = new HashMap<>();
obj.put("top", top);
obj.put("right", right);
obj.put("bottom", bottom);
obj.put("left", left);
return obj;
}
public double getTop() {
return top;
}
public void setTop(double top) {
this.top = top;
}
public double getRight() {
return right;
}
public void setRight(double right) {
this.right = right;
}
public double getBottom() {
return bottom;
}
public void setBottom(double bottom) {
this.bottom = bottom;
}
public double getLeft() {
return left;
}
public void setLeft(double left) {
this.left = left;
}
@Override
public boolean equals(Object o) {
if (this == o) return true;
if (o == null || getClass() != o.getClass()) return false;
MarginsExt that = (MarginsExt) o;
if (Double.compare(that.top, top) != 0) return false;
if (Double.compare(that.right, right) != 0) return false;
if (Double.compare(that.bottom, bottom) != 0) return false;
return Double.compare(that.left, left) == 0;
}
@Override
public int hashCode() {
int result;
long temp;
temp = Double.doubleToLongBits(top);
result = (int) (temp ^ (temp >>> 32));
temp = Double.doubleToLongBits(right);
result = 31 * result + (int) (temp ^ (temp >>> 32));
temp = Double.doubleToLongBits(bottom);
result = 31 * result + (int) (temp ^ (temp >>> 32));
temp = Double.doubleToLongBits(left);
result = 31 * result + (int) (temp ^ (temp >>> 32));
return result;
}
@Override
public String toString() {
return "MarginsExt{" +
"top=" + top +
", right=" + right +
", bottom=" + bottom +
", left=" + left +
'}';
}
}

View File

@ -0,0 +1,134 @@
package com.pichillilorenzo.flutter_inappwebview.types;
import android.os.Build;
import android.print.PrintAttributes;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import androidx.annotation.RequiresApi;
import java.util.HashMap;
import java.util.Map;
@RequiresApi(api = Build.VERSION_CODES.KITKAT)
public class MediaSizeExt {
@NonNull
private String id;
@Nullable
private String label;
private int widthMils;
private int heightMils;
public MediaSizeExt(@NonNull String id, @Nullable String label, int widthMils, int heightMils) {
this.id = id;
this.label = label;
this.widthMils = widthMils;
this.heightMils = heightMils;
}
@Nullable
public static MediaSizeExt fromMediaSize(@Nullable PrintAttributes.MediaSize mediaSize) {
if (mediaSize == null) {
return null;
}
return new MediaSizeExt(
mediaSize.getId(),
null,
mediaSize.getHeightMils(),
mediaSize.getWidthMils()
);
}
@Nullable
public static MediaSizeExt fromMap(@Nullable Map<String, Object> map) {
if (map == null) {
return null;
}
String id = (String) map.get("id");
String label = (String) map.get("label");
int widthMils = (int) map.get("widthMils");
int heightMils = (int) map.get("heightMils");
return new MediaSizeExt(id, label, widthMils, heightMils);
}
public PrintAttributes.MediaSize toMediaSize() {
return new PrintAttributes.MediaSize(
id, "Custom", widthMils, heightMils
);
}
public Map<String, Object> toMap() {
Map<String, Object> obj = new HashMap<>();
obj.put("id", id);
obj.put("label", label);
obj.put("heightMils", heightMils);
obj.put("widthMils", widthMils);
return obj;
}
@NonNull
public String getId() {
return id;
}
public void setId(@NonNull String id) {
this.id = id;
}
@Nullable
public String getLabel() {
return label;
}
public void setLabel(@Nullable String label) {
this.label = label;
}
public int getWidthMils() {
return widthMils;
}
public void setWidthMils(int widthMils) {
this.widthMils = widthMils;
}
public int getHeightMils() {
return heightMils;
}
public void setHeightMils(int heightMils) {
this.heightMils = heightMils;
}
@Override
public boolean equals(Object o) {
if (this == o) return true;
if (o == null || getClass() != o.getClass()) return false;
MediaSizeExt that = (MediaSizeExt) o;
if (widthMils != that.widthMils) return false;
if (heightMils != that.heightMils) return false;
if (!id.equals(that.id)) return false;
return label != null ? label.equals(that.label) : that.label == null;
}
@Override
public int hashCode() {
int result = id.hashCode();
result = 31 * result + (label != null ? label.hashCode() : 0);
result = 31 * result + widthMils;
result = 31 * result + heightMils;
return result;
}
@Override
public String toString() {
return "MediaSizeExt{" +
"id='" + id + '\'' +
", label='" + label + '\'' +
", widthMils=" + widthMils +
", heightMils=" + heightMils +
'}';
}
}

View File

@ -0,0 +1,56 @@
package com.pichillilorenzo.flutter_inappwebview.types;
import android.os.Build;
import android.print.PrintAttributes;
import androidx.annotation.Nullable;
import androidx.annotation.RequiresApi;
import java.util.HashMap;
import java.util.Map;
@RequiresApi(api = Build.VERSION_CODES.KITKAT)
public class PrintAttributesExt {
private int colorMode;
@Nullable
private Integer duplex;
@Nullable
private Integer orientation;
@Nullable
private MediaSizeExt mediaSize;
@Nullable
private ResolutionExt resolution;
@Nullable
private MarginsExt margins;
@Nullable
public static PrintAttributesExt fromPrintAttributes(@Nullable PrintAttributes attributes) {
if (attributes == null) {
return null;
}
PrintAttributesExt attributesExt = new PrintAttributesExt();
attributesExt.colorMode = attributes.getColorMode();
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
attributesExt.duplex = attributes.getDuplexMode();
}
PrintAttributes.MediaSize mediaSize = attributes.getMediaSize();
if (mediaSize != null) {
attributesExt.mediaSize = MediaSizeExt.fromMediaSize(mediaSize);
attributesExt.orientation = mediaSize.isPortrait() ? 0 : 1;
}
attributesExt.resolution = ResolutionExt.fromResolution(attributes.getResolution());
attributesExt.margins = MarginsExt.fromMargins(attributes.getMinMargins());
return attributesExt;
}
public Map<String, Object> toMap() {
Map<String, Object> obj = new HashMap<>();
obj.put("colorMode", colorMode);
obj.put("duplex", duplex);
obj.put("orientation", orientation);
obj.put("mediaSize", mediaSize != null ? mediaSize.toMap() : null);
obj.put("resolution", resolution != null ? resolution.toMap() : null);
obj.put("margins", margins != null ? margins.toMap() : null);
return obj;
}
}

View File

@ -0,0 +1,55 @@
package com.pichillilorenzo.flutter_inappwebview.types;
import android.os.Build;
import android.print.PrintAttributes;
import android.print.PrintJobInfo;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import androidx.annotation.RequiresApi;
import java.util.HashMap;
import java.util.Map;
@RequiresApi(api = Build.VERSION_CODES.KITKAT)
public class PrintJobInfoExt {
private int state;
private int copies;
@Nullable
private Integer numberOfPages;
private long creationTime;
@NonNull
private String label;
@Nullable
private String printerId;
@Nullable
private PrintAttributesExt attributes;
@Nullable
public static PrintJobInfoExt fromPrintJobInfo(@Nullable PrintJobInfo info) {
if (info == null) {
return null;
}
PrintJobInfoExt printJobInfoExt = new PrintJobInfoExt();
printJobInfoExt.state = info.getState();
printJobInfoExt.copies = info.getCopies();
printJobInfoExt.numberOfPages = info.getPages() != null ? info.getPages().length : null;
printJobInfoExt.creationTime = info.getCreationTime();
printJobInfoExt.label = info.getLabel();
printJobInfoExt.printerId = info.getPrinterId() != null ? info.getPrinterId().getLocalId() : null;
printJobInfoExt.attributes = PrintAttributesExt.fromPrintAttributes(info.getAttributes());
return printJobInfoExt;
}
public Map<String, Object> toMap() {
Map<String, Object> obj = new HashMap<>();
obj.put("state", state);
obj.put("copies", copies);
obj.put("numberOfPages", numberOfPages);
obj.put("creationTime", creationTime);
obj.put("label", label);
obj.put("printerId", printerId);
obj.put("attributes", attributes != null ? attributes.toMap() : null);
return obj;
}
}

View File

@ -0,0 +1,134 @@
package com.pichillilorenzo.flutter_inappwebview.types;
import android.os.Build;
import android.print.PrintAttributes;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import androidx.annotation.RequiresApi;
import java.util.HashMap;
import java.util.Map;
@RequiresApi(api = Build.VERSION_CODES.KITKAT)
public class ResolutionExt {
@NonNull
private String id;
@NonNull
private String label;
private int verticalDpi;
private int horizontalDpi;
public ResolutionExt(@NonNull String id, @NonNull String label, int verticalDpi, int horizontalDpi) {
this.id = id;
this.label = label;
this.verticalDpi = verticalDpi;
this.horizontalDpi = horizontalDpi;
}
@Nullable
public static ResolutionExt fromResolution(@Nullable PrintAttributes.Resolution resolution) {
if (resolution == null) {
return null;
}
return new ResolutionExt(
resolution.getId(),
resolution.getLabel(),
resolution.getVerticalDpi(),
resolution.getHorizontalDpi()
);
}
@Nullable
public static ResolutionExt fromMap(@Nullable Map<String, Object> map) {
if (map == null) {
return null;
}
String id = (String) map.get("id");
String label = (String) map.get("label");
int verticalDpi = (int) map.get("verticalDpi");
int horizontalDpi = (int) map.get("horizontalDpi");
return new ResolutionExt(id, label, verticalDpi, horizontalDpi);
}
public PrintAttributes.Resolution toResolution() {
return new PrintAttributes.Resolution(
id, label, horizontalDpi, verticalDpi
);
}
public Map<String, Object> toMap() {
Map<String, Object> obj = new HashMap<>();
obj.put("id", id);
obj.put("label", label);
obj.put("verticalDpi", verticalDpi);
obj.put("horizontalDpi", horizontalDpi);
return obj;
}
@NonNull
public String getId() {
return id;
}
public void setId(@NonNull String id) {
this.id = id;
}
@NonNull
public String getLabel() {
return label;
}
public void setLabel(@NonNull String label) {
this.label = label;
}
public int getVerticalDpi() {
return verticalDpi;
}
public void setVerticalDpi(int verticalDpi) {
this.verticalDpi = verticalDpi;
}
public int getHorizontalDpi() {
return horizontalDpi;
}
public void setHorizontalDpi(int horizontalDpi) {
this.horizontalDpi = horizontalDpi;
}
@Override
public boolean equals(Object o) {
if (this == o) return true;
if (o == null || getClass() != o.getClass()) return false;
ResolutionExt that = (ResolutionExt) o;
if (verticalDpi != that.verticalDpi) return false;
if (horizontalDpi != that.horizontalDpi) return false;
if (!id.equals(that.id)) return false;
return label.equals(that.label);
}
@Override
public int hashCode() {
int result = id.hashCode();
result = 31 * result + label.hashCode();
result = 31 * result + verticalDpi;
result = 31 * result + horizontalDpi;
return result;
}
@Override
public String toString() {
return "ResolutionExt{" +
"id='" + id + '\'' +
", label='" + label + '\'' +
", verticalDpi=" + verticalDpi +
", horizontalDpi=" + horizontalDpi +
'}';
}
}

View File

@ -1,5 +1,7 @@
package com.pichillilorenzo.flutter_inappwebview.webview;
import androidx.annotation.NonNull;
import com.pichillilorenzo.flutter_inappwebview.ISettings;
import java.util.HashMap;
@ -10,7 +12,9 @@ public class ContextMenuSettings implements ISettings<Object> {
public Boolean hideDefaultSystemContextMenuItems = false;
public ContextMenuSettings parse(Map<String, Object> options) {
@NonNull
@Override
public ContextMenuSettings parse(@NonNull Map<String, Object> options) {
for (Map.Entry<String, Object> pair : options.entrySet()) {
String key = pair.getKey();
Object value = pair.getValue();
@ -28,14 +32,16 @@ public class ContextMenuSettings implements ISettings<Object> {
return this;
}
@NonNull
public Map<String, Object> toMap() {
Map<String, Object> options = new HashMap<>();
options.put("hideDefaultSystemContextMenuItems", hideDefaultSystemContextMenuItems);
return options;
}
@NonNull
@Override
public Map<String, Object> getRealSettings(Object obj) {
public Map<String, Object> getRealSettings(@NonNull Object obj) {
Map<String, Object> realOptions = toMap();
return realOptions;
}

View File

@ -12,6 +12,7 @@ import androidx.annotation.Nullable;
import com.pichillilorenzo.flutter_inappwebview.InAppWebViewFlutterPlugin;
import com.pichillilorenzo.flutter_inappwebview.in_app_browser.InAppBrowserDelegate;
import com.pichillilorenzo.flutter_inappwebview.print_job.PrintJobSettings;
import com.pichillilorenzo.flutter_inappwebview.types.ContentWorld;
import com.pichillilorenzo.flutter_inappwebview.types.HitTestResult;
import com.pichillilorenzo.flutter_inappwebview.types.URLRequest;
@ -64,7 +65,8 @@ public interface InAppWebViewInterface {
void onResume();
void pauseTimers();
void resumeTimers();
void printCurrentPage();
@Nullable
String printCurrentPage(@Nullable PrintJobSettings settings);
int getContentHeight();
void getContentHeight(ValueCallback<Integer> callback);
void zoomBy(float zoomFactor);

View File

@ -6,11 +6,16 @@ import android.util.Log;
import android.webkit.JavascriptInterface;
import android.webkit.ValueCallback;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import com.pichillilorenzo.flutter_inappwebview.print_job.PrintJobController;
import com.pichillilorenzo.flutter_inappwebview.print_job.PrintJobManager;
import com.pichillilorenzo.flutter_inappwebview.print_job.PrintJobSettings;
import com.pichillilorenzo.flutter_inappwebview.webview.WebViewChannelDelegate;
import com.pichillilorenzo.flutter_inappwebview.webview.in_app_webview.InAppWebView;
import com.pichillilorenzo.flutter_inappwebview.plugin_scripts_js.JavaScriptBridgeJS;
import com.pichillilorenzo.flutter_inappwebview.webview.in_app_webview.InAppWebViewChromeClient;
import org.json.JSONArray;
import org.json.JSONException;
@ -58,8 +63,33 @@ public class JavaScriptBridgeInterface {
return;
}
if (handlerName.equals("onPrint") && Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
inAppWebView.printCurrentPage();
if (handlerName.equals("onPrintRequest") && Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
PrintJobSettings settings = new PrintJobSettings();
settings.handledByClient = true;
final String printJobId = inAppWebView.printCurrentPage(settings);
if (inAppWebView != null && inAppWebView.channelDelegate != null) {
inAppWebView.channelDelegate.onPrintRequest(inAppWebView.getUrl(), printJobId, new WebViewChannelDelegate.PrintRequestCallback() {
@Override
public boolean nonNullSuccess(@NonNull Boolean handledByClient) {
return !handledByClient;
}
@Override
public void defaultBehaviour(@Nullable Boolean handledByClient) {
PrintJobController printJobController = PrintJobManager.jobs.get(printJobId);
if (printJobController != null) {
printJobController.disposeNoCancel();
}
}
@Override
public void error(String errorCode, @Nullable String errorMessage, @Nullable Object errorDetails) {
Log.e(LOG_TAG, errorCode + ", " + ((errorMessage != null) ? errorMessage : ""));
defaultBehaviour(null);
}
});
}
return;
} else if (handlerName.equals("callAsyncJavaScript")) {
try {
JSONArray arguments = new JSONArray(args);

View File

@ -15,6 +15,7 @@ import androidx.webkit.WebViewFeature;
import com.pichillilorenzo.flutter_inappwebview.Util;
import com.pichillilorenzo.flutter_inappwebview.in_app_browser.InAppBrowserActivity;
import com.pichillilorenzo.flutter_inappwebview.in_app_browser.InAppBrowserSettings;
import com.pichillilorenzo.flutter_inappwebview.print_job.PrintJobSettings;
import com.pichillilorenzo.flutter_inappwebview.types.BaseCallbackResultImpl;
import com.pichillilorenzo.flutter_inappwebview.types.ChannelDelegateImpl;
import com.pichillilorenzo.flutter_inappwebview.types.ClientCertChallenge;
@ -344,9 +345,15 @@ public class WebViewChannelDelegate extends ChannelDelegateImpl {
break;
case "printCurrentPage":
if (webView != null && Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
webView.printCurrentPage();
PrintJobSettings settings = new PrintJobSettings();
Map<String, Object> settingsMap = (Map<String, Object>) call.argument("settings");
if (settingsMap != null) {
settings.parse(settingsMap);
}
result.success(webView.printCurrentPage(settings));
} else {
result.success(null);
}
result.success(true);
break;
case "getContentHeight":
if (webView instanceof InAppWebView) {
@ -1230,6 +1237,26 @@ public class WebViewChannelDelegate extends ChannelDelegateImpl {
channel.invokeMethod("onCallJsHandler", obj, callback);
}
public static class PrintRequestCallback extends BaseCallbackResultImpl<Boolean> {
@Nullable
@Override
public Boolean decodeResult(@Nullable Object obj) {
return (obj instanceof Boolean) && (boolean) obj;
}
}
public void onPrintRequest(String url, String printJobId, @NonNull PrintRequestCallback callback) {
MethodChannel channel = getChannel();
if (channel == null) {
callback.defaultBehaviour(null);
return;
}
Map<String, Object> obj = new HashMap<>();
obj.put("url", url);
obj.put("printJobId", printJobId);
channel.invokeMethod("onPrintRequest", obj, callback);
}
@Override
public void dispose() {
super.dispose();

View File

@ -55,6 +55,9 @@ import androidx.webkit.WebViewCompat;
import androidx.webkit.WebViewFeature;
import com.pichillilorenzo.flutter_inappwebview.InAppWebViewFlutterPlugin;
import com.pichillilorenzo.flutter_inappwebview.print_job.PrintJobController;
import com.pichillilorenzo.flutter_inappwebview.print_job.PrintJobManager;
import com.pichillilorenzo.flutter_inappwebview.print_job.PrintJobSettings;
import com.pichillilorenzo.flutter_inappwebview.webview.JavaScriptBridgeInterface;
import com.pichillilorenzo.flutter_inappwebview.R;
import com.pichillilorenzo.flutter_inappwebview.Util;
@ -1251,25 +1254,75 @@ final public class InAppWebView extends InputAwareWebView implements InAppWebVie
webSettings.setBuiltInZoomControls(enabled);
}
@RequiresApi(api = Build.VERSION_CODES.LOLLIPOP)
public void printCurrentPage() {
@RequiresApi(api = Build.VERSION_CODES.KITKAT)
@Nullable
public String printCurrentPage(@Nullable PrintJobSettings settings) {
if (plugin != null && plugin.activity != null) {
// Get a PrintManager instance
PrintManager printManager = (PrintManager) plugin.activity.getSystemService(Context.PRINT_SERVICE);
if (printManager != null) {
String jobName = getTitle() + " Document";
PrintAttributes.Builder builder = new PrintAttributes.Builder();
String jobName = (getTitle() != null ? getTitle() : getUrl()) + " Document";
if (settings != null) {
if (settings.jobName != null && !settings.jobName.isEmpty()) {
jobName = settings.jobName;
}
if (settings.orientation != null) {
int orientation = settings.orientation;
switch (orientation) {
case 0:
// PORTRAIT
builder.setMediaSize(PrintAttributes.MediaSize.UNKNOWN_PORTRAIT);
break;
case 1:
// LANDSCAPE
builder.setMediaSize(PrintAttributes.MediaSize.UNKNOWN_LANDSCAPE);
break;
}
}
// if (settings.margins != null) {
// // for some reason, Android doesn't set the margins
// builder.setMinMargins(settings.margins.toMargins());
// }
if (settings.mediaSize != null) {
builder.setMediaSize(settings.mediaSize.toMediaSize());
}
if (settings.colorMode != null) {
builder.setColorMode(settings.colorMode);
}
if (settings.duplexMode != null && Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
builder.setDuplexMode(settings.duplexMode);
}
if (settings.resolution != null) {
builder.setResolution(settings.resolution.toResolution());
}
}
// Get a printCurrentPage adapter instance
PrintDocumentAdapter printAdapter = createPrintDocumentAdapter(jobName);
PrintDocumentAdapter printAdapter;
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
printAdapter = createPrintDocumentAdapter(jobName);
} else {
printAdapter = createPrintDocumentAdapter();
}
// Create a printCurrentPage job with name and adapter instance
printManager.print(jobName, printAdapter,
new PrintAttributes.Builder().build());
android.print.PrintJob job = printManager.print(jobName, printAdapter, builder.build());
if (settings != null && settings.handledByClient) {
String id = UUID.randomUUID().toString();
PrintJobController printJobController = new PrintJobController(id, job, settings, plugin);
PrintJobManager.jobs.put(printJobController.id, printJobController);
return id;
}
} else {
Log.e(LOG_TAG, "No PrintManager available");
}
}
return null;
}
@Override

View File

@ -4,6 +4,7 @@ import android.os.Build;
import android.view.View;
import android.webkit.WebSettings;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import androidx.webkit.WebSettingsCompat;
import androidx.webkit.WebViewFeature;
@ -112,8 +113,9 @@ public class InAppWebViewSettings implements ISettings<InAppWebViewInterface> {
@Nullable
public String horizontalScrollbarTrackColor;
@NonNull
@Override
public InAppWebViewSettings parse(Map<String, Object> settings) {
public InAppWebViewSettings parse(@NonNull Map<String, Object> settings) {
for (Map.Entry<String, Object> pair : settings.entrySet()) {
String key = pair.getKey();
Object value = pair.getValue();
@ -374,6 +376,7 @@ public class InAppWebViewSettings implements ISettings<InAppWebViewInterface> {
return this;
}
@NonNull
@Override
public Map<String, Object> toMap() {
Map<String, Object> settings = new HashMap<>();
@ -462,8 +465,9 @@ public class InAppWebViewSettings implements ISettings<InAppWebViewInterface> {
return settings;
}
@NonNull
@Override
public Map<String, Object> getRealSettings(InAppWebViewInterface inAppWebView) {
public Map<String, Object> getRealSettings(@NonNull InAppWebViewInterface inAppWebView) {
Map<String, Object> realSettings = toMap();
if (inAppWebView instanceof InAppWebView) {
InAppWebView webView = (InAppWebView) inAppWebView;

View File

@ -27,7 +27,8 @@ import io.flutter.plugin.common.MethodChannel;
public class WebMessageChannel implements Disposable {
protected static final String LOG_TAG = "WebMessageChannel";
public static final String METHOD_CHANNEL_NAME_PREFIX = "com.pichillilorenzo/flutter_inappwebview_web_message_channel_";
@NonNull
public String id;
@Nullable
public WebMessageChannelChannelDelegate channelDelegate;

View File

@ -30,7 +30,7 @@ public class WebMessageChannelChannelDelegate extends ChannelDelegateImpl {
final Integer index = (Integer) call.argument("index");
webMessageChannel.setWebMessageCallbackForInAppWebView(index, result);
} else {
result.success(true);
result.success(false);
}
break;
case "postMessage":
@ -39,7 +39,7 @@ public class WebMessageChannelChannelDelegate extends ChannelDelegateImpl {
Map<String, Object> message = (Map<String, Object>) call.argument("message");
webMessageChannel.postMessageForInAppWebView(index, message, result);
} else {
result.success(true);
result.success(false);
}
break;
case "close":
@ -47,7 +47,7 @@ public class WebMessageChannelChannelDelegate extends ChannelDelegateImpl {
Integer index = (Integer) call.argument("index");
webMessageChannel.closeForInAppWebView(index, result);
} else {
result.success(true);
result.success(false);
}
break;
default:

View File

@ -31,7 +31,7 @@ public class WebMessageListenerChannelDelegate extends ChannelDelegateImpl {
String message = (String) call.argument("message");
webMessageListener.postMessageForInAppWebView(message, result);
} else {
result.success(true);
result.success(false);
}
break;
default:

View File

@ -29,8 +29,9 @@ void onPrint() {
onLoadStop: (controller, url) async {
await controller.evaluateJavascript(source: "window.print();");
},
onPrint: (controller, url) {
onPrintRequest: (controller, url, printJob) async {
onPrintCompleter.complete(url?.toString());
return false;
},
),
),

View File

@ -114,7 +114,7 @@ class _InAppWebViewExampleScreenState extends State<InAppWebViewExampleScreen> {
InAppWebView(
key: webViewKey,
initialUrlRequest:
URLRequest(url: Uri.parse('https://flutter.dev/')),
URLRequest(url: Uri.parse('https://github.com/flutter/')),
// initialUrlRequest:
// URLRequest(url: Uri.parse(Uri.base.toString().replaceFirst("/#/", "/") + 'page.html')),
// initialFile: "assets/index.html",

View File

@ -14,10 +14,14 @@ public class ISettings<T>: NSObject {
super.init()
}
func parse(settings: [String: Any?]) -> ISettings {
func parse(settings: [String: Any?]) -> ISettings<T> {
for (key, value) in settings {
if !(value is NSNull), value != nil, self.responds(to: Selector(key)) {
self.setValue(value, forKey: key)
if !(value is NSNull), value != nil {
if self.responds(to: Selector(key)) {
self.setValue(value, forKey: key)
} else if self.responds(to: Selector("_" + key)) {
self.setValue(value, forKey: "_" + key)
}
}
}
return self
@ -25,15 +29,15 @@ public class ISettings<T>: NSObject {
func toMap() -> [String: Any?] {
var settings: [String: Any?] = [:]
var counts = UInt32();
let properties = class_copyPropertyList(object_getClass(self), &counts);
var counts = UInt32()
let properties = class_copyPropertyList(object_getClass(self), &counts)
for i in 0..<counts {
let property = properties?.advanced(by: Int(i)).pointee;
let cName = property_getName(property!);
let name = String(cString: cName)
settings[name] = self.value(forKey: name)
if let property = properties?.advanced(by: Int(i)).pointee {
let cName = property_getName(property)
let name = String(cString: cName)
let key = !name.hasPrefix("_") ? name : String(name.suffix(from: name.index(name.startIndex, offsetBy: 1)))
settings[key] = self.value(forKey: key)
}
}
free(properties)
return settings

View File

@ -724,7 +724,7 @@ public class InAppWebView: WKWebView, UIScrollViewDelegate, WKUIDelegate,
if let with = with {
snapshotConfiguration = WKSnapshotConfiguration()
if let rect = with["rect"] as? [String: Double] {
snapshotConfiguration!.rect = CGRect(x: rect["x"]!, y: rect["y"]!, width: rect["width"]!, height: rect["height"]!)
snapshotConfiguration!.rect = CGRect.fromMap(map: rect)
}
if let snapshotWidth = with["snapshotWidth"] as? Double {
snapshotConfiguration!.snapshotWidth = NSNumber(value: snapshotWidth)
@ -762,7 +762,7 @@ public class InAppWebView: WKWebView, UIScrollViewDelegate, WKUIDelegate,
let pdfConfiguration: WKPDFConfiguration = .init()
if let configuration = configuration {
if let rect = configuration["rect"] as? [String: Double] {
pdfConfiguration.rect = CGRect(x: rect["x"]!, y: rect["y"]!, width: rect["width"]!, height: rect["height"]!)
pdfConfiguration.rect = CGRect.fromMap(map: rect)
}
}
createPDF(configuration: pdfConfiguration) { (result) in
@ -2584,9 +2584,29 @@ public class InAppWebView: WKWebView, UIScrollViewDelegate, WKUIDelegate,
} else if message.name == "callHandler" {
let body = message.body as! [String: Any?]
let handlerName = body["handlerName"] as! String
if handlerName == "onPrint" {
printCurrentPage(printCompletionHandler: nil)
if handlerName == "onPrintRequest" {
let settings = PrintJobSettings()
settings.handledByClient = true
if let printJobId = printCurrentPage(settings: settings) {
let callback = WebViewChannelDelegate.PrintRequestCallback()
callback.nonNullSuccess = { (handledByClient: Bool) in
return !handledByClient
}
callback.defaultBehaviour = { (handledByClient: Bool?) in
if let printJob = PrintJobManager.jobs[printJobId] {
printJob?.disposeNoDismiss()
}
}
callback.error = { [weak callback] (code: String, message: String?, details: Any?) in
print(code + ", " + (message ?? ""))
callback?.defaultBehaviour(nil)
}
channelDelegate?.onPrintRequest(url: url, printJobId: printJobId, callback: callback)
}
return
}
let _callHandlerID = body["_callHandlerID"] as! Int64
let args = body["args"] as! String
@ -2724,25 +2744,79 @@ if(window.\(JAVASCRIPT_BRIDGE_NAME)[\(_callHandlerID)] != null) {
}
}
public func printCurrentPage(printCompletionHandler: ((_ completed: Bool, _ error: Error?) -> Void)?) {
public func printCurrentPage(settings: PrintJobSettings? = nil,
completionHandler: UIPrintInteractionController.CompletionHandler? = nil) -> String? {
var printJobId: String? = nil
if let settings = settings, settings.handledByClient {
printJobId = NSUUID().uuidString
}
let printController = UIPrintInteractionController.shared
let printFormatter = self.viewPrintFormatter()
if let settings = settings {
if let margins = settings.margins {
printFormatter.perPageContentInsets = margins
}
if let maximumContentHeight = settings.maximumContentHeight {
printFormatter.maximumContentHeight = maximumContentHeight
}
if let maximumContentWidth = settings.maximumContentWidth {
printFormatter.maximumContentWidth = maximumContentWidth
}
}
printController.printFormatter = printFormatter
let completionHandler: UIPrintInteractionController.CompletionHandler = { (printController, completed, error) in
if !completed {
if let e = error {
print("[PRINT] Failed: \(e.localizedDescription)")
} else {
print("[PRINT] Canceled")
printController.printInfo = UIPrintInfo(dictionary: nil)
if let printInfo = printController.printInfo {
printInfo.jobName = settings?.jobName ?? (title ?? url?.absoluteString ?? "") + " Document"
if let settings = settings {
if let orientationValue = settings.orientation,
let orientation = UIPrintInfo.Orientation.init(rawValue: orientationValue) {
printInfo.orientation = orientation
}
if let duplexModeValue = settings.duplexMode,
let duplexMode = UIPrintInfo.Duplex.init(rawValue: duplexModeValue) {
printInfo.duplex = duplexMode
}
if let outputTypeValue = settings.outputType,
let outputType = UIPrintInfo.OutputType.init(rawValue: outputTypeValue) {
printInfo.outputType = outputType
}
}
if let callback = printCompletionHandler {
callback(completed, error)
}
}
printController.present(animated: true, completionHandler: completionHandler)
// initialize print renderer and set its formatter
let printRenderer = CustomUIPrintPageRenderer(numberOfPage: settings?.numberOfPages,
forceRenderingQuality: settings?.forceRenderingQuality)
printRenderer.addPrintFormatter(printFormatter, startingAtPageAt: 0)
if let settings = settings {
if let footerHeight = settings.footerHeight {
printRenderer.footerHeight = footerHeight
}
if let headerHeight = settings.headerHeight {
printRenderer.headerHeight = headerHeight
}
}
printController.printPageRenderer = printRenderer
if let settings = settings {
printController.showsNumberOfCopies = settings.showsNumberOfCopies
printController.showsPaperSelectionForLoadedPapers = settings.showsPaperSelectionForLoadedPapers
if #available(iOS 15.0, *) {
printController.showsPaperOrientation = settings.showsPaperOrientation
}
}
let animated = settings?.animated ?? true
if let id = printJobId {
let printJob = PrintJobController(id: id, job: printController, settings: settings)
PrintJobManager.jobs[id] = printJob
printJob.present(animated: animated, completionHandler: completionHandler)
} else {
printController.present(animated: animated, completionHandler: completionHandler)
}
return printJobId
}
public func getContentHeight() -> Int64 {

View File

@ -46,8 +46,8 @@ public class InAppWebViewSettings: ISettings<InAppWebView> {
var ignoresViewportScaleLimits = false
var allowsInlineMediaPlayback = false
var allowsPictureInPictureMediaPlayback = true
var isFraudulentWebsiteWarningEnabled = true;
var selectionGranularity = 0;
var isFraudulentWebsiteWarningEnabled = true
var selectionGranularity = 0
var dataDetectorTypes: [String] = ["NONE"] // WKDataDetectorTypeNone
var preferredContentMode = 0
var sharedCookiesEnabled = false

View File

@ -273,16 +273,13 @@ public class WebViewChannelDelegate : ChannelDelegate {
break
case "printCurrentPage":
if let webView = webView {
webView.printCurrentPage(printCompletionHandler: {(completed, error) in
if !completed, let err = error {
print(err.localizedDescription)
result(false)
return
}
result(true)
})
let settings = PrintJobSettings()
if let settingsMap = arguments!["settings"] as? [String: Any?] {
let _ = settings.parse(settings: settingsMap)
}
result(webView.printCurrentPage(settings: settings))
} else {
result(false)
result(nil)
}
break
case "getContentHeight":
@ -1054,6 +1051,27 @@ public class WebViewChannelDelegate : ChannelDelegate {
channel?.invokeMethod("onMicrophoneCaptureStateChanged", arguments: arguments)
}
public class PrintRequestCallback : BaseCallbackResult<Bool> {
override init() {
super.init()
self.decodeResult = { (obj: Any?) in
return obj is Bool && obj as! Bool
}
}
}
public func onPrintRequest(url: URL?, printJobId: String?, callback: PrintRequestCallback) {
guard let channel = channel else {
callback.defaultBehaviour(nil)
return
}
let arguments = [
"url": url?.absoluteString,
"printJobId": printJobId,
]
channel.invokeMethod("onPrintRequest", arguments: arguments, callback: callback)
}
public override func dispose() {
super.dispose()
webView = nil

View File

@ -19,6 +19,6 @@ let PRINT_JS_PLUGIN_SCRIPT = PluginScript(
let PRINT_JS_SOURCE = """
window.print = function() {
window.\(JAVASCRIPT_BRIDGE_NAME).callHandler("onPrint", window.location.href);
window.\(JAVASCRIPT_BRIDGE_NAME).callHandler("onPrintRequest", window.location.href);
}
"""

View File

@ -0,0 +1,34 @@
//
// CustomUIPrintPageRenderer.swift
// flutter_inappwebview
//
// Created by Lorenzo Pichilli on 10/05/22.
//
import Foundation
public class CustomUIPrintPageRenderer : UIPrintPageRenderer {
private var _numberOfPages: Int?
private var forceRenderingQuality: Int?
public init(numberOfPage: Int? = nil, forceRenderingQuality: Int? = nil) {
super.init()
self._numberOfPages = numberOfPage
self.forceRenderingQuality = forceRenderingQuality
}
open override var numberOfPages: Int {
get {
return _numberOfPages ?? super.numberOfPages
}
}
@available(iOS 14.5, *)
open override func currentRenderingQuality(forRequested requestedRenderingQuality: UIPrintRenderingQuality) -> UIPrintRenderingQuality {
if let forceRenderingQuality = forceRenderingQuality,
let quality = UIPrintRenderingQuality.init(rawValue: forceRenderingQuality) {
return quality
}
return super.currentRenderingQuality(forRequested: requestedRenderingQuality)
}
}

View File

@ -0,0 +1,56 @@
//
// PrintAttributes.swift
// flutter_inappwebview
//
// Created by Lorenzo Pichilli on 10/05/22.
//
import Foundation
public class PrintAttributes : NSObject {
var orientation: UIPrintInfo.Orientation?
var duplex: UIPrintInfo.Duplex?
var outputType: UIPrintInfo.OutputType?
var margins: UIEdgeInsets?
var footerHeight: Double?
var headerHeight: Double?
var paperRect: CGRect?
var printableRect: CGRect?
var maximumContentHeight: Double?
var maximumContentWidth: Double?
public init(fromPrintJobController: PrintJobController) {
super.init()
if let printPageRenderer = fromPrintJobController.printPageRenderer {
footerHeight = printPageRenderer.footerHeight
headerHeight = printPageRenderer.headerHeight
paperRect = printPageRenderer.paperRect
printableRect = printPageRenderer.printableRect
}
if let printFormatter = fromPrintJobController.printFormatter {
maximumContentHeight = printFormatter.maximumContentHeight
maximumContentWidth = printFormatter.maximumContentWidth
margins = printFormatter.perPageContentInsets
}
if let job = fromPrintJobController.job, let printInfo = job.printInfo {
orientation = printInfo.orientation
duplex = printInfo.duplex
outputType = printInfo.outputType
}
}
public func toMap () -> [String:Any?] {
return [
"footerHeight": footerHeight,
"headerHeight": headerHeight,
"paperRect": paperRect?.toMap(),
"printableRect": printableRect?.toMap(),
"margins": margins?.toMap(),
"maximumContentHeight": maximumContentHeight,
"maximumContentWidth": maximumContentWidth,
"orientation": orientation?.rawValue,
"duplex": duplex?.rawValue,
"outputType": outputType?.rawValue
]
}
}

View File

@ -0,0 +1,68 @@
//
// PrintJobChannelDelegate.swift
// flutter_inappwebview
//
// Created by Lorenzo Pichilli on 09/05/22.
//
import Foundation
public class PrintJobChannelDelegate : ChannelDelegate {
private weak var printJobController: PrintJobController?
public init(printJobController: PrintJobController, channel: FlutterMethodChannel) {
super.init(channel: channel)
self.printJobController = printJobController
}
public override func handle(_ call: FlutterMethodCall, result: @escaping FlutterResult) {
let arguments = call.arguments as? NSDictionary
switch call.method {
case "dismiss":
if let job = printJobController?.job {
let animated = arguments!["animated"] as! Bool
job.dismiss(animated: animated)
result(true)
} else {
result(false)
}
break
case "getInfo":
if let printJobController = printJobController {
result(printJobController.getInfo()?.toMap())
} else {
result(false)
}
break
case "dispose":
if let printJobController = printJobController {
printJobController.dispose()
result(true)
} else {
result(false)
}
break
default:
result(FlutterMethodNotImplemented)
break
}
}
public func onComplete(completed: Bool, error: Error?) {
let arguments: [String: Any?] = [
"completed": completed,
"error": error?.localizedDescription
]
channel?.invokeMethod("onComplete", arguments: arguments)
}
public override func dispose() {
super.dispose()
printJobController = nil
}
deinit {
dispose()
}
}

View File

@ -0,0 +1,96 @@
//
// PrintJob.swift
// flutter_inappwebview
//
// Created by Lorenzo Pichilli on 09/05/22.
//
import Foundation
public enum PrintJobState: Int {
case created = 1
case started = 3
case completed = 5
case failed = 6
case canceled = 7
}
public class PrintJobController : NSObject, Disposable, UIPrintInteractionControllerDelegate {
static let METHOD_CHANNEL_NAME_PREFIX = "com.pichillilorenzo/flutter_inappwebview_printjobcontroller_"
var id: String
var job: UIPrintInteractionController?
var settings: PrintJobSettings?
var printFormatter: UIPrintFormatter?
var printPageRenderer: UIPrintPageRenderer?
var channelDelegate: PrintJobChannelDelegate?
var state = PrintJobState.created
var creationTime = Int64(Date().timeIntervalSince1970 * 1000)
public init(id: String, job: UIPrintInteractionController? = nil, settings: PrintJobSettings? = nil) {
self.id = id
super.init()
self.job = job
self.settings = settings
self.printFormatter = job?.printFormatter
self.printPageRenderer = job?.printPageRenderer
self.job?.delegate = self
let channel = FlutterMethodChannel(name: PrintJobController.METHOD_CHANNEL_NAME_PREFIX + id,
binaryMessenger: SwiftFlutterPlugin.instance!.registrar!.messenger())
self.channelDelegate = PrintJobChannelDelegate(printJobController: self, channel: channel)
}
public func printInteractionControllerWillStartJob(_ printInteractionController: UIPrintInteractionController) {
state = .started
}
public func present(animated: Bool, completionHandler: UIPrintInteractionController.CompletionHandler? = nil) {
guard let job = job else {
return
}
job.present(animated: animated, completionHandler: { [weak self] (printController, completed, error) in
if !completed {
if let _ = error {
self?.state = .failed
} else {
self?.state = .canceled
}
} else {
self?.state = .completed
}
self?.channelDelegate?.onComplete(completed: completed, error: error)
if let completionHandler = completionHandler {
completionHandler(printController, completed, error)
}
})
}
public func getInfo() -> PrintJobInfo? {
guard let _ = job else {
return nil
}
return PrintJobInfo.init(fromPrintJobController: self)
}
public func disposeNoDismiss() {
channelDelegate?.dispose()
channelDelegate = nil
printFormatter = nil
printPageRenderer = nil
job?.delegate = nil
job = nil
PrintJobManager.jobs[id] = nil
}
public func dispose() {
channelDelegate?.dispose()
channelDelegate = nil
printFormatter = nil
printPageRenderer = nil
job?.delegate = nil
job?.dismiss(animated: false)
job = nil
PrintJobManager.jobs[id] = nil
}
}

View File

@ -0,0 +1,43 @@
//
// PrintJobInfo.swift
// flutter_downloader
//
// Created by Lorenzo Pichilli on 10/05/22.
//
import Foundation
public class PrintJobInfo : NSObject {
var state: PrintJobState
var attributes: PrintAttributes
var creationTime: Int64
var numberOfPages: Int?
var label: String?
var printerId: String?
public init(fromPrintJobController: PrintJobController) {
state = fromPrintJobController.state
creationTime = fromPrintJobController.creationTime
attributes = PrintAttributes.init(fromPrintJobController: fromPrintJobController)
super.init()
if let printPageRenderer = fromPrintJobController.printPageRenderer {
numberOfPages = printPageRenderer.numberOfPages
}
if let job = fromPrintJobController.job, let printInfo = job.printInfo {
label = printInfo.jobName
printerId = printInfo.printerID
}
}
public func toMap () -> [String:Any?] {
return [
"state": state.rawValue,
"attributes": attributes.toMap(),
"copies": nil,
"numberOfPages": numberOfPages,
"creationTime": creationTime,
"label": label,
"printerId": printerId
]
}
}

View File

@ -0,0 +1,28 @@
//
// PrintJobManager.swift
// flutter_inappwebview
//
// Created by Lorenzo Pichilli on 09/05/22.
//
import Foundation
public class PrintJobManager: NSObject, Disposable {
static var jobs: [String: PrintJobController?] = [:]
public override init() {
super.init()
}
public func dispose() {
let jobs = PrintJobManager.jobs.values
jobs.forEach { (job: PrintJobController?) in
job?.dispose()
}
PrintJobManager.jobs.removeAll()
}
deinit {
dispose()
}
}

View File

@ -0,0 +1,154 @@
//
// PrintJobSettings.swift
// flutter_inappwebview
//
// Created by Lorenzo Pichilli on 09/05/22.
//
import Foundation
@objcMembers
public class PrintJobSettings: ISettings<PrintJobController> {
public var handledByClient = false
public var jobName: String?
public var animated = true
public var _orientation: NSNumber?
public var orientation: Int? {
get {
return _orientation?.intValue
}
set {
if let newValue = newValue {
_orientation = NSNumber.init(value: newValue)
} else {
_orientation = nil
}
}
}
public var _numberOfPages: NSNumber?
public var numberOfPages: Int? {
get {
return _numberOfPages?.intValue
}
set {
if let newValue = newValue {
_numberOfPages = NSNumber.init(value: newValue)
} else {
_numberOfPages = nil
}
}
}
public var _forceRenderingQuality: NSNumber?
public var forceRenderingQuality: Int? {
get {
return _forceRenderingQuality?.intValue
}
set {
if let newValue = newValue {
_forceRenderingQuality = NSNumber.init(value: newValue)
} else {
_forceRenderingQuality = nil
}
}
}
public var margins: UIEdgeInsets?
public var _duplexMode: NSNumber?
public var duplexMode: Int? {
get {
return _duplexMode?.intValue
}
set {
if let newValue = newValue {
_duplexMode = NSNumber.init(value: newValue)
} else {
_duplexMode = nil
}
}
}
public var _outputType: NSNumber?
public var outputType: Int? {
get {
return _outputType?.intValue
}
set {
if let newValue = newValue {
_outputType = NSNumber.init(value: newValue)
} else {
_outputType = nil
}
}
}
public var showsNumberOfCopies = true
public var showsPaperSelectionForLoadedPapers = false
public var showsPaperOrientation = true
public var _maximumContentHeight: NSNumber?
public var maximumContentHeight: Double? {
get {
return _maximumContentHeight?.doubleValue
}
set {
if let newValue = newValue {
_maximumContentHeight = NSNumber.init(value: newValue)
} else {
_maximumContentHeight = nil
}
}
}
public var _maximumContentWidth: NSNumber?
public var maximumContentWidth: Double? {
get {
return _maximumContentWidth?.doubleValue
}
set {
if let newValue = newValue {
_maximumContentWidth = NSNumber.init(value: newValue)
} else {
_maximumContentWidth = nil
}
}
}
public var _footerHeight: NSNumber?
public var footerHeight: Double? {
get {
return _footerHeight?.doubleValue
}
set {
if let newValue = newValue {
_footerHeight = NSNumber.init(value: newValue)
} else {
_footerHeight = nil
}
}
}
public var _headerHeight: NSNumber?
public var headerHeight: Double? {
get {
return _headerHeight?.doubleValue
}
set {
if let newValue = newValue {
_headerHeight = NSNumber.init(value: newValue)
} else {
_headerHeight = nil
}
}
}
override init(){
super.init()
}
override func parse(settings: [String: Any?]) -> PrintJobSettings {
let _ = super.parse(settings: settings)
if let marginsMap = settings["margins"] as? [String : Double] {
margins = UIEdgeInsets.fromMap(map: marginsMap)
}
return self
}
override func getRealSettings(obj: PrintJobController?) -> [String: Any?] {
var realOptions: [String: Any?] = toMap()
return realOptions
}
}

View File

@ -35,6 +35,7 @@ public class SwiftFlutterPlugin: NSObject, FlutterPlugin {
var headlessInAppWebViewManager: HeadlessInAppWebViewManager?
var chromeSafariBrowserManager: ChromeSafariBrowserManager?
var webAuthenticationSessionManager: WebAuthenticationSessionManager?
var printJobManager: PrintJobManager?
var webViewControllers: [String: InAppBrowserWebViewController?] = [:]
var safariViewControllers: [String: Any?] = [:]
@ -58,6 +59,7 @@ public class SwiftFlutterPlugin: NSObject, FlutterPlugin {
myWebStorageManager = MyWebStorageManager(registrar: registrar)
}
webAuthenticationSessionManager = WebAuthenticationSessionManager(registrar: registrar)
printJobManager = PrintJobManager()
}
public static func register(with registrar: FlutterPluginRegistrar) {
@ -87,5 +89,7 @@ public class SwiftFlutterPlugin: NSObject, FlutterPlugin {
}
webAuthenticationSessionManager?.dispose()
webAuthenticationSessionManager = nil
printJobManager?.dispose()
printJobManager = nil
}
}

View File

@ -0,0 +1,23 @@
//
// CGRect.swift
// flutter_inappwebview
//
// Created by Lorenzo Pichilli on 10/05/22.
//
import Foundation
extension CGRect {
public static func fromMap(map: [String: Double]) -> CGRect {
return CGRect(x: map["x"]!, y: map["y"]!, width: map["width"]!, height: map["height"]!)
}
public func toMap () -> [String:Any?] {
return [
"x": minX,
"y": minY,
"width": width,
"height": height
]
}
}

View File

@ -0,0 +1,23 @@
//
// UIEdgeInsets.swift
// flutter_inappwebview
//
// Created by Lorenzo Pichilli on 11/05/22.
//
import Foundation
extension UIEdgeInsets {
public static func fromMap(map: [String: Double]) -> UIEdgeInsets {
return UIEdgeInsets.init(top: map["top"]!, left: map["left"]!, bottom: map["bottom"]!, right: map["right"]!)
}
public func toMap () -> [String:Any?] {
return [
"top": top,
"right": self.right,
"bottom": bottom,
"left": self.left
]
}
}

View File

@ -124,7 +124,7 @@ window.flutter_inappwebview = {
} catch (e) {
console.log(e);
}
window.flutter_inappwebview.nativeCommunication('onPrint', viewId, [iframeUrl]);
window.flutter_inappwebview.nativeCommunication('onPrintRequest', viewId, [iframeUrl]);
originalPrint.call(iframe.contentWindow);
};

View File

@ -12,6 +12,7 @@ import '../in_app_webview/in_app_webview_controller.dart';
import '../in_app_webview/in_app_webview_settings.dart';
import '../util.dart';
import '../print_job/main.dart';
import 'in_app_browser_settings.dart';
class InAppBrowserAlreadyOpenedException implements Exception {
@ -724,14 +725,24 @@ class InAppBrowser {
///- iOS
void onUpdateVisitedHistory(Uri? url, bool? isReload) {}
///Use [onPrintRequest] instead
@Deprecated("Use onPrintRequest instead")
void onPrint(Uri? url) {}
///Event fired when `window.print()` is called from JavaScript side.
///Return `true` if you want to handle the print job.
///Otherwise return `false`, so the [PrintJobController] will be handled and disposed automatically by the system.
///
///[url] represents the url on which is called.
///
///[printJobController] represents the controller of the print job created.
///
///**Supported Platforms/Implementations**:
///- Android native WebView
///- iOS
void onPrint(Uri? url) {}
Future<bool?>? onPrintRequest(Uri? url, PrintJobController? printJobController) {
return null;
}
///Event fired when an HTML element of the webview has been clicked and held.
///

View File

@ -7,6 +7,7 @@ import 'package:flutter_inappwebview/src/util.dart';
import '../context_menu.dart';
import '../types/main.dart';
import '../print_job/main.dart';
import 'webview.dart';
import 'in_app_webview_controller.dart';
import 'in_app_webview_settings.dart';
@ -74,7 +75,8 @@ class HeadlessInAppWebView implements WebView {
this.onScrollChanged,
@Deprecated('Use onDownloadStartRequest instead') this.onDownloadStart,
this.onDownloadStartRequest,
@Deprecated('Use onLoadResourceWithCustomScheme instead') this.onLoadResourceCustomScheme,
@Deprecated('Use onLoadResourceWithCustomScheme instead')
this.onLoadResourceCustomScheme,
this.onLoadResourceWithCustomScheme,
this.onCreateWindow,
this.onCloseWindow,
@ -90,7 +92,8 @@ class HeadlessInAppWebView implements WebView {
this.onAjaxProgress,
this.shouldInterceptFetchRequest,
this.onUpdateVisitedHistory,
this.onPrint,
@Deprecated("Use onPrintRequest instead") this.onPrint,
this.onPrintRequest,
this.onLongPressHitTestResult,
this.onEnterFullscreen,
this.onExitFullscreen,
@ -460,7 +463,8 @@ class HeadlessInAppWebView implements WebView {
@override
Future<CustomSchemeResponse?> Function(
InAppWebViewController controller, WebResourceRequest request)? onLoadResourceWithCustomScheme;
InAppWebViewController controller, WebResourceRequest request)?
onLoadResourceWithCustomScheme;
@override
void Function(InAppWebViewController controller, Uri? url)? onLoadStart;
@ -472,9 +476,15 @@ class HeadlessInAppWebView implements WebView {
void Function(InAppWebViewController controller,
InAppWebViewHitTestResult hitTestResult)? onLongPressHitTestResult;
///Use [onPrintRequest] instead
@Deprecated("Use onPrintRequest instead")
@override
void Function(InAppWebViewController controller, Uri? url)? onPrint;
@override
Future<bool?> Function(InAppWebViewController controller, Uri? url,
PrintJobController? printJobController)? onPrintRequest;
@override
void Function(InAppWebViewController controller, int progress)?
onProgressChanged;

View File

@ -13,6 +13,7 @@ import '../web/web_platform_manager.dart';
import '../context_menu.dart';
import '../types/main.dart';
import '../print_job/main.dart';
import 'webview.dart';
import 'in_app_webview_controller.dart';
@ -65,7 +66,8 @@ class InAppWebView extends StatefulWidget implements WebView {
this.onScrollChanged,
@Deprecated('Use onDownloadStartRequest instead') this.onDownloadStart,
this.onDownloadStartRequest,
@Deprecated('Use onLoadResourceWithCustomScheme instead') this.onLoadResourceCustomScheme,
@Deprecated('Use onLoadResourceWithCustomScheme instead')
this.onLoadResourceCustomScheme,
this.onLoadResourceWithCustomScheme,
this.onCreateWindow,
this.onCloseWindow,
@ -81,7 +83,8 @@ class InAppWebView extends StatefulWidget implements WebView {
this.onAjaxProgress,
this.shouldInterceptFetchRequest,
this.onUpdateVisitedHistory,
this.onPrint,
@Deprecated("Use onPrintRequest instead") this.onPrint,
this.onPrintRequest,
this.onLongPressHitTestResult,
this.onEnterFullscreen,
this.onExitFullscreen,
@ -345,7 +348,8 @@ class InAppWebView extends StatefulWidget implements WebView {
@override
final Future<CustomSchemeResponse?> Function(
InAppWebViewController controller, WebResourceRequest request)? onLoadResourceWithCustomScheme;
InAppWebViewController controller, WebResourceRequest request)?
onLoadResourceWithCustomScheme;
@override
final void Function(InAppWebViewController controller, Uri? url)? onLoadStart;
@ -357,9 +361,15 @@ class InAppWebView extends StatefulWidget implements WebView {
final void Function(InAppWebViewController controller,
InAppWebViewHitTestResult hitTestResult)? onLongPressHitTestResult;
///Use [onPrintRequest] instead
@Deprecated("Use onPrintRequest instead")
@override
final void Function(InAppWebViewController controller, Uri? url)? onPrint;
@override
final Future<bool?> Function(InAppWebViewController controller, Uri? url,
PrintJobController? printJobController)? onPrintRequest;
@override
final void Function(InAppWebViewController controller, int progress)?
onProgressChanged;

View File

@ -28,6 +28,8 @@ import 'in_app_webview_settings.dart';
import 'webview.dart';
import '_static_channel.dart';
import '../print_job/main.dart';
///List of forbidden names for JavaScript handlers.
// ignore: non_constant_identifier_names
final _JAVASCRIPT_HANDLER_FORBIDDEN_NAMES = UnmodifiableListView<String>([
@ -36,7 +38,7 @@ final _JAVASCRIPT_HANDLER_FORBIDDEN_NAMES = UnmodifiableListView<String>([
"onAjaxReadyStateChange",
"onAjaxProgress",
"shouldInterceptFetchRequest",
"onPrint",
"onPrintRequest",
"onWindowFocus",
"onWindowBlur",
"callAsyncJavaScript",
@ -1089,15 +1091,31 @@ class InAppWebViewController {
_webview!.onWindowBlur!(this);
else if (_inAppBrowser != null) _inAppBrowser!.onWindowBlur();
break;
case "onPrint":
if ((_webview != null && _webview!.onPrint != null) ||
case "onPrintRequest":
if ((_webview != null &&
(_webview!.onPrintRequest != null ||
// ignore: deprecated_member_use_from_same_package
_webview!.onPrint != null)) ||
_inAppBrowser != null) {
String? url = call.arguments["url"];
String? printJobId = call.arguments["printJobId"];
Uri? uri = url != null ? Uri.parse(url) : null;
if (_webview != null && _webview!.onPrint != null)
_webview!.onPrint!(this, uri);
else
PrintJobController? printJob =
printJobId != null ? PrintJobController(id: printJobId) : null;
if (_webview != null) {
if (_webview!.onPrintRequest != null)
return await _webview!.onPrintRequest!(this, uri, printJob);
else {
// ignore: deprecated_member_use_from_same_package
_webview!.onPrint!(this, uri);
return false;
}
} else {
// ignore: deprecated_member_use_from_same_package
_inAppBrowser!.onPrint(uri);
return await _inAppBrowser!.onPrintRequest(uri, printJob);
}
}
break;
case "onInjectedScriptLoaded":
@ -1235,17 +1253,6 @@ class InAppWebViewController {
await _inAppBrowser!.shouldInterceptFetchRequest(request));
}
return null;
case "onPrint":
if ((_webview != null && _webview!.onPrint != null) ||
_inAppBrowser != null) {
String? url = args[0];
Uri? uri = url != null ? Uri.parse(url) : null;
if (_webview != null && _webview!.onPrint != null)
_webview!.onPrint!(this, uri);
else
_inAppBrowser!.onPrint(uri);
}
return null;
case "onWindowFocus":
if (_webview != null && _webview!.onWindowFocus != null)
_webview!.onWindowFocus!(this);
@ -2273,17 +2280,25 @@ class InAppWebViewController {
///Prints the current page.
///
///**NOTE**: available on Android 21+.
///To obtain the [PrintJobController], use [settings] argument with [PrintJobSettings.handledByClient] to `true`.
///Otherwise this method will return `null` and the [PrintJobController] will be handled and disposed automatically by the system.
///
///**NOTE for Web**: this method will have effect only if the iframe has the same origin.
///**NOTE**: available on Android 19+.
///
///**NOTE for Web**: this method will have effect only if the iframe has the same origin. Also, [PrintJobController] is always `null`.
///
///**Supported Platforms/Implementations**:
///- Android native WebView ([Official API - PrintManager](https://developer.android.com/reference/android/print/PrintManager))
///- iOS ([Official API - UIPrintInteractionController](https://developer.apple.com/documentation/uikit/uiprintinteractioncontroller))
///- Android native WebView ([Official API - PrintManager.print](https://developer.android.com/reference/android/print/PrintManager#print(java.lang.String,%20android.print.PrintDocumentAdapter,%20android.print.PrintAttributes)))
///- iOS ([Official API - UIPrintInteractionController.present](https://developer.apple.com/documentation/uikit/uiprintinteractioncontroller/1618149-present))
///- Web ([Official API - Window.print](https://developer.mozilla.org/en-US/docs/Web/API/Window/print))
Future<void> printCurrentPage() async {
Future<PrintJobController?> printCurrentPage({PrintJobSettings? settings}) async {
Map<String, dynamic> args = <String, dynamic>{};
await _channel.invokeMethod('printCurrentPage', args);
args.putIfAbsent("settings", () => settings?.toMap());
String? jobId = await _channel.invokeMethod('printCurrentPage', args);
if (jobId != null) {
return PrintJobController(id: jobId);
}
return null;
}
///Gets the height of the HTML content.
@ -3205,7 +3220,7 @@ class InAppWebViewController {
///
///[pdfConfiguration] represents the object that specifies the portion of the web view to capture as PDF data.
///
///**NOTE**: available only on iOS 14.0+.
///**NOTE for iOS**: available only on iOS 14.0+.
///
///**Supported Platforms/Implementations**:
///- iOS ([Official API - WKWebView.createPdf](https://developer.apple.com/documentation/webkit/wkwebview/3650490-createpdf))
@ -3223,7 +3238,7 @@ class InAppWebViewController {
///Creates a web archive of the web views current contents asynchronously.
///Returns `null` if a problem occurred.
///
///**NOTE**: available only on iOS 14.0+.
///**NOTE for iOS**: available only on iOS 14.0+.
///
///**Supported Platforms/Implementations**:
///- iOS ([Official API - WKWebView.createWebArchiveData](https://developer.apple.com/documentation/webkit/wkwebview/3650491-createwebarchivedata))
@ -3472,7 +3487,7 @@ class InAppWebViewController {
///
///[urlScheme] represents the URL scheme associated with the resource.
///
///**NOTE**: available only on iOS 11.0+.
///**NOTE for iOS**: available only on iOS 11.0+.
///
///**Supported Platforms/Implementations**:
///- iOS ([Official API - WKWebView.handlesURLScheme](https://developer.apple.com/documentation/webkit/wkwebview/2875370-handlesurlscheme))

View File

@ -4,4 +4,4 @@ export 'in_app_webview_controller.dart';
export 'in_app_webview_settings.dart';
export 'headless_in_app_webview.dart';
export 'android/main.dart';
export 'apple/main.dart';
export 'apple/main.dart';

View File

@ -9,15 +9,20 @@ import '../types/main.dart';
import 'in_app_webview_controller.dart';
import 'in_app_webview_settings.dart';
import 'headless_in_app_webview.dart';
import '../print_job/main.dart';
import '../debug_logging_settings.dart';
///Abstract class that represents a WebView. Used by [InAppWebView], [HeadlessInAppWebView] and the WebView of [InAppBrowser].
abstract class WebView {
///Debug settings used by [InAppWebView], [HeadlessInAppWebView] and [InAppBrowser].
///The default value excludes the [WebView.onScrollChanged] and [WebView.onOverScrolled] events.
///The default value excludes the [WebView.onScrollChanged], [WebView.onOverScrolled] and [WebView.onReceivedIcon] events.
static DebugLoggingSettings debugLoggingSettings = DebugLoggingSettings(
excludeFilter: [RegExp(r"onScrollChanged"), RegExp(r"onOverScrolled")]);
excludeFilter: [
RegExp(r"onScrollChanged"),
RegExp(r"onOverScrolled"),
RegExp(r"onReceivedIcon")
]);
///The window id of a [CreateWindowAction.windowId].
final int? windowId;
@ -184,7 +189,8 @@ abstract class WebView {
///- Android native WebView
///- iOS ([Official API - WKURLSchemeHandler](https://developer.apple.com/documentation/webkit/wkurlschemehandler))
final Future<CustomSchemeResponse?> Function(
InAppWebViewController controller, WebResourceRequest request)? onLoadResourceWithCustomScheme;
InAppWebViewController controller, WebResourceRequest request)?
onLoadResourceWithCustomScheme;
///Event fired when the [WebView] requests the host application to create a new window,
///for example when trying to open a link with `target="_blank"` or when `window.open()` is called by JavaScript side.
@ -433,17 +439,27 @@ abstract class WebView {
InAppWebViewController controller, Uri? url, bool? isReload)?
onUpdateVisitedHistory;
///Use [onPrintRequest] instead
@Deprecated("Use onPrintRequest instead")
final void Function(InAppWebViewController controller, Uri? url)? onPrint;
///Event fired when `window.print()` is called from JavaScript side.
///Return `true` if you want to handle the print job.
///Otherwise return `false`, so the [PrintJobController] will be handled and disposed automatically by the system.
///
///[url] represents the url on which is called.
///
///[printJobController] represents the controller of the print job created.
///**NOTE**: on Web, it is always `null`
///
///**NOTE for Web**: this event will be called only if the iframe has the same origin.
///
///**Supported Platforms/Implementations**:
///- Android native WebView
///- iOS
///- Web
final void Function(InAppWebViewController controller, Uri? url)? onPrint;
final Future<bool?> Function(InAppWebViewController controller, Uri? url,
PrintJobController? printJobController)? onPrintRequest;
///Event fired when an HTML element of the webview has been clicked and held.
///
@ -967,7 +983,8 @@ abstract class WebView {
@Deprecated('Use onDownloadStartRequest instead')
this.onDownloadStart,
this.onDownloadStartRequest,
@Deprecated('Use onLoadResourceWithCustomScheme instead') this.onLoadResourceCustomScheme,
@Deprecated('Use onLoadResourceWithCustomScheme instead')
this.onLoadResourceCustomScheme,
this.onLoadResourceWithCustomScheme,
this.onCreateWindow,
this.onCloseWindow,
@ -983,7 +1000,9 @@ abstract class WebView {
this.onAjaxProgress,
this.shouldInterceptFetchRequest,
this.onUpdateVisitedHistory,
this.onPrint,
@Deprecated("Use onPrintRequest instead")
this.onPrint,
this.onPrintRequest,
this.onLongPressHitTestResult,
this.onEnterFullscreen,
this.onExitFullscreen,

View File

@ -15,4 +15,5 @@ export 'context_menu.dart';
export 'pull_to_refresh/main.dart';
export 'web_message/main.dart';
export 'web_authentication_session/main.dart';
export 'print_job/main.dart';
export 'debug_logging_settings.dart';

View File

@ -0,0 +1,2 @@
export 'print_job_controller.dart';
export 'print_job_settings.dart';

View File

@ -0,0 +1,103 @@
import 'package:flutter/services.dart';
import '../types/print_job_info.dart';
import '../in_app_webview/in_app_webview_controller.dart';
///A completion handler for the [PrintJobController].
typedef PrintJobCompletionHandler = Future<void> Function(bool completed, String? error)?;
///Class representing a print job eventually returned by [InAppWebViewController.printCurrentPage].
class PrintJobController {
///Print job ID.
final String id;
late MethodChannel _channel;
///A completion handler used to handle the conclusion of the print job (for instance, to reset state) and to handle any errors encountered in printing.
///
///**Supported Platforms/Implementations**:
///- iOS ([Official API - UIPrintInteractionController.CompletionHandler](https://developer.apple.com/documentation/uikit/uiprintinteractioncontroller/completionhandler))
PrintJobCompletionHandler onComplete;
PrintJobController(
{required this.id}) {
this._channel = MethodChannel(
'com.pichillilorenzo/flutter_inappwebview_printjobcontroller_$id');
this._channel.setMethodCallHandler(_handleMethod);
}
Future<dynamic> _handleMethod(MethodCall call) async {
switch (call.method) {
case "onComplete":
bool completed = call.arguments["completed"];
String? error = call.arguments["error"];
if (onComplete != null) {
onComplete!(completed, error);
}
break;
default:
throw UnimplementedError("Unimplemented ${call.method} method");
}
}
///Cancels this print job.
///You can request cancellation of a queued, started, blocked, or failed print job.
///
///**Supported Platforms/Implementations**:
///- Android native WebView ([Official API - PrintJob.cancel](https://developer.android.com/reference/android/print/PrintJob#cancel()))
Future<void> cancel() async {
Map<String, dynamic> args = <String, dynamic>{};
await _channel.invokeMethod('cancel', args);
}
///Restarts this print job.
///You can request restart of a failed print job.
///
///**Supported Platforms/Implementations**:
///- Android native WebView ([Official API - PrintJob.restart](https://developer.android.com/reference/android/print/PrintJob#restart()))
Future<void> restart() async {
Map<String, dynamic> args = <String, dynamic>{};
await _channel.invokeMethod('restart', args);
}
///Dismisses the printing-options sheet or popover.
///
///You should dismiss the printing options when they are presented in a sheet or
///animated from a rectangle and the user changes the orientation of the device.
///(This, of course, assumes your application responds to orientation changes.)
///You should then present the printing options again once the new orientation takes effect.
///
///**Supported Platforms/Implementations**:
///- iOS
Future<void> dismiss({bool animated: true}) async {
Map<String, dynamic> args = <String, dynamic>{};
args.putIfAbsent("animated", () => animated);
await _channel.invokeMethod('dismiss', args);
}
///Gets the [PrintJobInfo] that describes this job.
///
///**NOTE**: The returned info object is a snapshot of the
///current print job state. Every call to this method returns a fresh
///info object that reflects the current print job state.
///
///**Supported Platforms/Implementations**:
///- Android native WebView ([Official API - PrintJob.getInfo](https://developer.android.com/reference/android/print/PrintJob#getInfo()))
///- iOS
Future<PrintJobInfo?> getInfo() async {
Map<String, dynamic> args = <String, dynamic>{};
Map<String, dynamic>? infoMap =
(await _channel.invokeMethod('getInfo', args))
?.cast<String, dynamic>();
return PrintJobInfo.fromMap(infoMap);
}
///Disposes the print job.
///
///**Supported Platforms/Implementations**:
///- Android native WebView
///- iOS
Future<void> dispose() async {
Map<String, dynamic> args = <String, dynamic>{};
await _channel.invokeMethod('dispose', args);
}
}

View File

@ -0,0 +1,250 @@
import 'package:flutter/cupertino.dart';
import 'package:flutter_inappwebview/src/types/print_job_media_size.dart';
import '../types/main.dart';
import '../util.dart';
import 'print_job_controller.dart';
///Class that represents the settings of a [PrintJobController].
class PrintJobSettings {
///Set this to `true` to handle the [PrintJobController].
///Otherwise, it will be handled and disposed automatically by the system.
///The default value is `false`.
///
///**Supported Platforms/Implementations**:
///- Android native WebView
///- iOS
bool handledByClient;
///The name of the print job.
///An application should set this property to a name appropriate to the content being printed.
///The default job name is the current webpage title concatenated with the "Document" word at the end.
///
///**Supported Platforms/Implementations**:
///- Android native WebView
///- iOS
String? jobName;
///`true` to animate the display of the sheet, `false` to display the sheet immediately.
///
///**Supported Platforms/Implementations**:
///- iOS
bool animated;
///The orientation of the printed content, portrait or landscape.
///
///**Supported Platforms/Implementations**:
///- Android native WebView
///- iOS
PrintJobOrientation? orientation;
///The number of pages to render.
///
///**Supported Platforms/Implementations**:
///- iOS
int? numberOfPages;
///Force rendering quality.
///
///**NOTE for iOS**: available only on iOS 14.5+.
///
///**Supported Platforms/Implementations**:
///- iOS
PrintJobRenderingQuality? forceRenderingQuality;
///The margins for each printed page.
///Margins define the white space around the content where the left margin defines
///the amount of white space on the left of the content and so on.
///
///**Supported Platforms/Implementations**:
///- iOS
EdgeInsets? margins;
///The media size.
///
///**Supported Platforms/Implementations**:
///- Android native WebView
PrintJobMediaSize? mediaSize;
///The color mode.
///
///**Supported Platforms/Implementations**:
///- Android native WebView
PrintJobColorMode? colorMode;
///The duplex mode to use for the print job.
///
///**NOTE for Android native WebView**: available only on Android 23+.
///
///**Supported Platforms/Implementations**:
///- Android native WebView
///- iOS
PrintJobDuplexMode? duplexMode;
///The kind of printable content.
///
///**Supported Platforms/Implementations**:
///- iOS
PrintJobOutputType? outputType;
///The supported resolution in DPI (dots per inch).
///
///**Supported Platforms/Implementations**:
///- Android native WebView
PrintJobResolution? resolution;
///A Boolean value that determines whether the printing options include the number of copies.
///The default value is `true`.
///
///**Supported Platforms/Implementations**:
///- iOS
bool showsNumberOfCopies;
///A Boolean value that determines whether the paper selection menu displays.
///The default value of this property is `false`.
///Setting the value to `true` enables a paper selection menu on printers that support different types of paper and have more than one paper type loaded.
///On printers where only one paper type is available, no paper selection menu is displayed, regardless of the value of this property.
///
///**Supported Platforms/Implementations**:
///- iOS
bool showsPaperSelectionForLoadedPapers;
///A Boolean value that determines whether the printing options include the paper orientation control when available.
///The default value is `true`.
///
///**NOTE for iOS**: available only on iOS 15.0+.
///
///**Supported Platforms/Implementations**:
///- iOS
bool showsPaperOrientation;
///The height of the page footer.
///
///The footer is measured in points from the bottom of [printableRect] and is below the content area.
///The default footer height is `0.0`.
///
///**Supported Platforms/Implementations**:
///- iOS
double? footerHeight;
///The height of the page header.
///
///The header is measured in points from the top of [printableRect] and is above the content area.
///The default header height is `0.0`.
///
///**Supported Platforms/Implementations**:
///- iOS
double? headerHeight;
///The maximum height of the content area.
///
///The Print Formatter uses this value to determine where the content rectangle begins on the first page.
///It compares the value of this property with the printing rectangles height minus the header and footer heights and
///the top inset value; it uses the lower of the two values.
///The default value of this property is the maximum float value.
///
///**Supported Platforms/Implementations**:
///- iOS
double? maximumContentHeight;
///The maximum width of the content area.
///
///The Print Formatter uses this value to determine the maximum width of the content rectangle.
///It compares the value of this property with the printing rectangles width minus the left and right inset values and uses the lower of the two.
///The default value of this property is the maximum float value.
///
///**Supported Platforms/Implementations**:
///- iOS
double? maximumContentWidth;
PrintJobSettings(
{this.handledByClient = false,
this.jobName,
this.animated = true,
this.orientation,
this.numberOfPages,
this.forceRenderingQuality,
this.margins,
this.mediaSize,
this.colorMode,
this.duplexMode,
this.outputType,
this.resolution,
this.showsNumberOfCopies = true,
this.showsPaperSelectionForLoadedPapers = false,
this.showsPaperOrientation = true,
this.maximumContentHeight,
this.maximumContentWidth,
this.footerHeight,
this.headerHeight});
///Gets a possible [PrintJobSettings] instance from a [Map] value.
static PrintJobSettings fromMap(Map<String, dynamic> map) {
var settings = PrintJobSettings();
settings.handledByClient = map["handledByClient"];
settings.jobName = map["jobName"];
settings.animated = map["animated"];
settings.orientation = PrintJobOrientation.fromValue(map["orientation"]);
settings.numberOfPages = map["numberOfPages"];
settings.forceRenderingQuality =
PrintJobRenderingQuality.fromValue(map["forceRenderingQuality"]);
settings.margins =
MapEdgeInsets.fromMap(map["margins"]?.cast<String, dynamic>());
settings.mediaSize =
PrintJobMediaSize.fromMap(map["mediaSize"]?.cast<String, dynamic>());
settings.colorMode = PrintJobColorMode.fromValue(map["colorMode"]);
settings.duplexMode = PrintJobDuplexMode.fromNativeValue(map["duplexMode"]);
settings.outputType = PrintJobOutputType.fromValue(map["outputType"]);
settings.resolution =
PrintJobResolution.fromMap(map["resolution"]?.cast<String, dynamic>());
settings.showsNumberOfCopies = map["showsNumberOfCopies"];
settings.showsPaperSelectionForLoadedPapers =
map["showsPaperSelectionForLoadedPapers"];
settings.showsPaperOrientation = map["showsPaperOrientation"];
settings.maximumContentHeight = map["maximumContentHeight"];
settings.maximumContentWidth = map["maximumContentWidth"];
settings.footerHeight = map["footerHeight"];
settings.headerHeight = map["headerHeight"];
return settings;
}
///Converts instance to a map.
Map<String, dynamic> toMap() {
return {
"handledByClient": handledByClient,
"jobName": jobName,
"animated": animated,
"orientation": orientation?.toValue(),
"numberOfPages": numberOfPages,
"forceRenderingQuality": forceRenderingQuality?.toValue(),
"margins": margins?.toMap(),
"mediaSize": mediaSize?.toMap(),
"colorMode": colorMode?.toValue(),
"duplexMode": duplexMode?.toNativeValue(),
"outputType": outputType?.toValue(),
"resolution": resolution?.toMap(),
"showsNumberOfCopies": showsNumberOfCopies,
"showsPaperSelectionForLoadedPapers": showsPaperSelectionForLoadedPapers,
"showsPaperOrientation": showsPaperOrientation,
"maximumContentHeight": maximumContentHeight,
"maximumContentWidth": maximumContentWidth,
"footerHeight": footerHeight,
"headerHeight": headerHeight,
};
}
///Gets a copy of the current instance.
PrintJobSettings copy() {
return PrintJobSettings.fromMap(this.toMap());
}
///Converts instance to a map.
Map<String, dynamic> toJson() {
return this.toMap();
}
@override
String toString() {
return toMap().toString();
}
}

View File

@ -20,6 +20,20 @@ class InAppWebViewRect {
assert(this.x >= 0 && this.y >= 0 && this.width >= 0 && this.height >= 0);
}
///Gets a possible [InAppWebViewRect] instance from a [Map] value.
static InAppWebViewRect? fromMap(Map<String, dynamic>? map) {
if (map == null) {
return null;
}
return InAppWebViewRect(
x: map["x"],
y: map["y"],
width: map["width"],
height: map["height"],
);
}
///Converts instance to a map.
Map<String, dynamic> toMap() {
return {"x": x, "y": y, "width": width, "height": height};

View File

@ -149,4 +149,14 @@ export 'proxy_rule.dart';
export 'proxy_scheme_filter.dart';
export 'force_dark_strategy.dart';
export 'url_request_attribution.dart';
export 'web_authentication_session_error.dart';
export 'web_authentication_session_error.dart';
export 'print_job_info.dart';
export 'print_job_state.dart';
export 'print_job_orientation.dart';
export 'print_job_rendering_quality.dart';
export 'print_job_media_size.dart';
export 'print_job_color_mode.dart';
export 'print_job_duplex_mode.dart';
export 'print_job_output_type.dart';
export 'print_job_resolution.dart';
export 'print_job_attributes.dart';

View File

@ -30,7 +30,7 @@ class PermissionResourceType {
return null;
}
///Gets a possible [PermissionResourceType] instance from a value.
///Gets a possible [PermissionResourceType] instance from a native value.
static PermissionResourceType? fromNativeValue(dynamic value) {
if (value != null) {
try {

View File

@ -0,0 +1,173 @@
import 'package:flutter/rendering.dart';
import '../util.dart';
import '../print_job/main.dart';
import '../types/main.dart';
///Class representing the attributes of a [PrintJobController].
///These attributes describe how the printed content should be laid out.
class PrintJobAttributes {
///The color mode.
///
///**Supported Platforms/Implementations**:
///- Android native WebView
int? colorMode;
///The duplex mode to use for the print job.
///
///**NOTE for Android native WebView**: available only on Android 23+.
///
///**Supported Platforms/Implementations**:
///- Android native WebView
///- iOS
PrintJobDuplexMode? duplex;
///The orientation of the printed content, portrait or landscape.
///
///**Supported Platforms/Implementations**:
///- Android native WebView
///- iOS
PrintJobOrientation? orientation;
///The media size.
///
///**Supported Platforms/Implementations**:
///- Android native WebView
PrintJobMediaSize? mediaSize;
///The supported resolution in DPI (dots per inch).
///
///**Supported Platforms/Implementations**:
///- Android native WebView
PrintJobResolution? resolution;
///The margins for each printed page.
///Margins define the white space around the content where the left margin defines
///the amount of white space on the left of the content and so on.
///
///**Supported Platforms/Implementations**:
///- iOS
EdgeInsets? margins;
///The height of the page footer.
///
///The footer is measured in points from the bottom of [printableRect] and is below the content area.
///The default footer height is `0.0`.
///
///**Supported Platforms/Implementations**:
///- iOS
double? footerHeight;
///The height of the page header.
///
///The header is measured in points from the top of [printableRect] and is above the content area.
///The default header height is `0.0`.
///
///**Supported Platforms/Implementations**:
///- iOS
double? headerHeight;
///The area in which printing can occur.
///
///The value of this property is a rectangle that defines the area in which the printer can print content.
///Sometimes this is referred to as the imageable area of the paper.
///
///**Supported Platforms/Implementations**:
///- iOS
InAppWebViewRect? printableRect;
///The size of the paper used for printing.
///
///The value of this property is a rectangle that defines the size of paper chosen for the print job.
///The origin is always (0,0).
///
///**Supported Platforms/Implementations**:
///- iOS
InAppWebViewRect? paperRect;
///The maximum height of the content area.
///
///The Print Formatter uses this value to determine where the content rectangle begins on the first page.
///It compares the value of this property with the printing rectangles height minus the header and footer heights and
///the top inset value; it uses the lower of the two values.
///The default value of this property is the maximum float value.
///
///**Supported Platforms/Implementations**:
///- iOS
double? maximumContentHeight;
///The maximum width of the content area.
///
///The Print Formatter uses this value to determine the maximum width of the content rectangle.
///It compares the value of this property with the printing rectangles width minus the left and right inset values and uses the lower of the two.
///The default value of this property is the maximum float value.
///
///**Supported Platforms/Implementations**:
///- iOS
double? maximumContentWidth;
PrintJobAttributes(
{this.colorMode,
this.duplex,
this.orientation,
this.mediaSize,
this.resolution,
this.margins,
this.maximumContentHeight,
this.maximumContentWidth,
this.footerHeight,
this.headerHeight,
this.paperRect,
this.printableRect});
///Gets a possible [PrintJobAttributes] instance from a [Map] value.
static PrintJobAttributes? fromMap(Map<String, dynamic>? map) {
if (map == null) {
return null;
}
return PrintJobAttributes(
colorMode: map["colorMode"],
duplex: PrintJobDuplexMode.fromNativeValue(map["duplex"]),
orientation: PrintJobOrientation.fromValue(map["orientation"]),
mediaSize: PrintJobMediaSize.fromMap(
map["mediaSize"]?.cast<String, dynamic>()),
resolution: PrintJobResolution.fromMap(
map["resolution"]?.cast<String, dynamic>()),
margins: MapEdgeInsets.fromMap(map["margins"]?.cast<String, dynamic>()),
maximumContentHeight: map["maximumContentHeight"],
maximumContentWidth: map["maximumContentWidth"],
footerHeight: map["footerHeight"],
headerHeight: map["headerHeight"],
paperRect: InAppWebViewRect.fromMap(map["paperRect"]?.cast<String, dynamic>()),
printableRect: InAppWebViewRect.fromMap(map["printableRect"]?.cast<String, dynamic>()));
}
///Converts instance to a map.
Map<String, dynamic> toMap() {
return {
"colorMode": colorMode,
"duplex": duplex?.toNativeValue(),
"orientation": orientation?.toValue(),
"mediaSize": mediaSize?.toMap(),
"resolution": resolution?.toMap(),
"margins": margins?.toMap(),
"maximumContentHeight": maximumContentHeight,
"maximumContentWidth": maximumContentWidth,
"footerHeight": footerHeight,
"headerHeight": headerHeight,
"paperRect": paperRect?.toMap(),
"printableRect": printableRect?.toMap(),
};
}
///Converts instance to a map.
Map<String, dynamic> toJson() {
return this.toMap();
}
@override
String toString() {
return toMap().toString();
}
}

View File

@ -0,0 +1,52 @@
import '../print_job/main.dart';
///Class representing how the printed content of a [PrintJobController] should be laid out.
class PrintJobColorMode {
final int _value;
const PrintJobColorMode._internal(this._value);
///Set of all values of [PrintJobColorMode].
static final Set<PrintJobColorMode> values = [
PrintJobColorMode.MONOCHROME,
PrintJobColorMode.COLOR,
].toSet();
///Gets a possible [PrintJobColorMode] instance from an [int] value.
static PrintJobColorMode? fromValue(int? value) {
if (value != null) {
try {
return PrintJobColorMode.values
.firstWhere((element) => element.toValue() == value);
} catch (e) {
return null;
}
}
return null;
}
///Gets [int] value.
int toValue() => _value;
@override
String toString() {
switch (_value) {
case 1:
return "MONOCHROME";
case 2:
default:
return "COLOR";
}
}
///Monochrome color scheme, for example one color is used.
static const MONOCHROME = const PrintJobColorMode._internal(1);
///Color color scheme, for example many colors are used.
static const COLOR = const PrintJobColorMode._internal(2);
bool operator ==(value) => value == _value;
@override
int get hashCode => _value.hashCode;
}

View File

@ -0,0 +1,90 @@
import 'package:flutter/foundation.dart';
import '../print_job/main.dart';
///Class representing the orientation of a [PrintJobController].
class PrintJobDuplexMode {
final String _value;
final int _nativeValue;
const PrintJobDuplexMode._internal(this._value, this._nativeValue);
///Set of all values of [PrintJobDuplexMode].
static final Set<PrintJobDuplexMode> values = [
PrintJobDuplexMode.NONE,
PrintJobDuplexMode.LONG_EDGE,
PrintJobDuplexMode.SHORT_EDGE,
].toSet();
///Gets a possible [PrintJobDuplexMode] instance from a [String] value.
static PrintJobDuplexMode? fromValue(String? value) {
if (value != null) {
try {
return PrintJobDuplexMode.values
.firstWhere((element) => element.toValue() == value);
} catch (e) {
return null;
}
}
return null;
}
///Gets a possible [PrintJobDuplexMode] instance from an [int] native value.
static PrintJobDuplexMode? fromNativeValue(int? value) {
if (value != null) {
try {
return PrintJobDuplexMode.values
.firstWhere((element) => element.toNativeValue() == value);
} catch (e) {
return null;
}
}
return null;
}
///Gets [String] value.
String toValue() => _value;
///Gets native value.
int toNativeValue() => _nativeValue;
@override
String toString() => _value;
///No double-sided (duplex) printing; single-sided printing only.
static final NONE = PrintJobDuplexMode._internal(
'NONE',
(defaultTargetPlatform == TargetPlatform.android)
? 1
: ((defaultTargetPlatform == TargetPlatform.iOS ||
defaultTargetPlatform == TargetPlatform.macOS)
? 0
: 0));
///Duplex printing that flips the back page along the long edge of the paper.
///Pages are turned sideways along the long edge - like a book.
static final LONG_EDGE = PrintJobDuplexMode._internal(
'LONG_EDGE',
(defaultTargetPlatform == TargetPlatform.android)
? 2
: ((defaultTargetPlatform == TargetPlatform.iOS ||
defaultTargetPlatform == TargetPlatform.macOS)
? 1
: 0));
///Duplex print that flips the back page along the short edge of the paper.
///Pages are turned upwards along the short edge - like a notepad.
static final SHORT_EDGE = PrintJobDuplexMode._internal(
'SHORT_EDGE',
(defaultTargetPlatform == TargetPlatform.android)
? 4
: ((defaultTargetPlatform == TargetPlatform.iOS ||
defaultTargetPlatform == TargetPlatform.macOS)
? 2
: 0));
bool operator ==(value) => value == _value;
@override
int get hashCode => _value.hashCode;
}

View File

@ -0,0 +1,104 @@
import 'print_job_state.dart';
import '../print_job/main.dart';
import '../types/main.dart';
///Class representing the description of a [PrintJobController].
///Note that the print jobs state may change over time and this class represents a snapshot of this state.
class PrintJobInfo {
///The state of the print job.
///
///**Supported Platforms/Implementations**:
///- Android native WebView
///- iOS
PrintJobState? state;
///How many copies to print.
///
///**Supported Platforms/Implementations**:
///- Android native WebView
int? copies;
///The number of pages to print.
///
///**Supported Platforms/Implementations**:
///- Android native WebView
///- iOS
int? numberOfPages;
///The timestamp when the print job was created.
///
///**Supported Platforms/Implementations**:
///- Android native WebView
///- iOS
int? creationTime;
///The human readable print job label.
///
///**Supported Platforms/Implementations**:
///- Android native WebView
///- iOS
String? label;
///The unique id of the printer.
///
///**Supported Platforms/Implementations**:
///- Android native WebView
///- iOS
String? printerId;
///The attributes of a print job.
///
///**Supported Platforms/Implementations**:
///- Android native WebView
///- iOS
PrintJobAttributes? attributes;
PrintJobInfo(
{this.state,
this.copies,
this.numberOfPages,
this.creationTime,
this.label,
this.printerId,
this.attributes});
///Gets a possible [PrintJobInfo] instance from a [Map] value.
static PrintJobInfo? fromMap(Map<String, dynamic>? map) {
if (map == null) {
return null;
}
return PrintJobInfo(
state: PrintJobState.fromValue(map["state"]),
copies: map["copies"],
numberOfPages: map["numberOfPages"],
creationTime: map["creationTime"],
label: map["label"],
printerId: map["printerId"],
attributes: PrintJobAttributes.fromMap(
map["attributes"]?.cast<String, dynamic>()));
}
///Converts instance to a map.
Map<String, dynamic> toMap() {
return {
"state": state?.toValue(),
"copies": copies,
"numberOfPages": numberOfPages,
"creationTime": creationTime,
"label": label,
"printerId": printerId,
"attributes": attributes?.toMap()
};
}
///Converts instance to a map.
Map<String, dynamic> toJson() {
return this.toMap();
}
@override
String toString() {
return toMap().toString();
}
}

View File

@ -0,0 +1,455 @@
import '../print_job/main.dart';
///Class representing the supported media size for a [PrintJobController].
///Media size is the dimension of the media on which the content is printed.
class PrintJobMediaSize {
///The unique media size id.
///
///It is unique amongst other media sizes supported by the printer.
///This id is defined by the client that generated the media size
///instance and should not be interpreted by other parties.
final String id;
///The human readable label.
final String? label;
///The media width in mils (thousandths of an inch).
final int widthMils;
///The media height in mils (thousandths of an inch).
final int heightMils;
const PrintJobMediaSize(
{required this.id,
required this.widthMils,
required this.heightMils,
this.label});
///Gets a possible [PrintJobMediaSize] instance from a [Map] value.
static PrintJobMediaSize? fromMap(Map<String, dynamic>? map) {
if (map == null) {
return null;
}
return PrintJobMediaSize(
id: map["id"],
widthMils: map["widthMils"],
heightMils: map["heightMils"],
label: map["label"],
);
}
///Converts instance to a map.
Map<String, dynamic> toMap() {
return {
"id": id,
"widthMils": widthMils,
"heightMils": heightMils,
"label": label
};
}
///Converts instance to a map.
Map<String, dynamic> toJson() {
return this.toMap();
}
@override
String toString() {
return toMap().toString();
}
///Unknown media size in portrait mode.
///
///**NOTE**: This is for specifying orientation without media size.
///You should not use the dimensions reported by this instance.
static const UNKNOWN_PORTRAIT = const PrintJobMediaSize(
id: "UNKNOWN_PORTRAIT", widthMils: 1, heightMils: 0x7fffffff);
///Unknown media size in portrait mode.
///
///**NOTE**: This is for specifying orientation without media size.
///You should not use the dimensions reported by this instance.
static const UNKNOWN_LANDSCAPE = const PrintJobMediaSize(
id: "UNKNOWN_LANDSCAPE", widthMils: 0x7fffffff, heightMils: 1);
// ISO sizes
///ISO A0 media size: 841mm x 1189mm (33.11" x 46.81")
static const ISO_A0 = const PrintJobMediaSize(
id: "ISO_A0", widthMils: 33110, heightMils: 46810);
///ISO A1 media size: 594mm x 841mm (23.39" x 33.11")
static const ISO_A1 = const PrintJobMediaSize(
id: "ISO_A1", widthMils: 23390, heightMils: 33110);
///ISO A2 media size: 420mm x 594mm (16.54" x 23.39")
static const ISO_A2 = const PrintJobMediaSize(
id: "ISO_A2", widthMils: 16540, heightMils: 23390);
///ISO A3 media size: 297mm x 420mm (11.69" x 16.54")
static const ISO_A3 = const PrintJobMediaSize(
id: "ISO_A3", widthMils: 11690, heightMils: 16540);
///ISO A4 media size: 210mm x 297mm (8.27" x 11.69")
static const ISO_A4 =
const PrintJobMediaSize(id: "ISO_A4", widthMils: 8270, heightMils: 11690);
///ISO A5 media size: 148mm x 210mm (5.83" x 8.27")
static const ISO_A5 =
const PrintJobMediaSize(id: "ISO_A5", widthMils: 5830, heightMils: 8270);
///ISO A6 media size: 105mm x 148mm (4.13" x 5.83")
static const ISO_A6 =
const PrintJobMediaSize(id: "ISO_A6", widthMils: 4130, heightMils: 5830);
///ISO A7 media size: 74mm x 105mm (2.91" x 4.13")
static const ISO_A7 =
const PrintJobMediaSize(id: "ISO_A7", widthMils: 2910, heightMils: 4130);
///ISO A8 media size: 52mm x 74mm (2.05" x 2.91")
static const ISO_A8 =
const PrintJobMediaSize(id: "ISO_A8", widthMils: 2050, heightMils: 2910);
///ISO A9 media size: 37mm x 52mm (1.46" x 2.05")
static const ISO_A9 =
const PrintJobMediaSize(id: "ISO_A9", widthMils: 1460, heightMils: 2050);
///ISO A10 media size: 26mm x 37mm (1.02" x 1.46")
static const ISO_A10 =
const PrintJobMediaSize(id: "ISO_A10", widthMils: 1020, heightMils: 1460);
///ISO B0 media size: 1000mm x 1414mm (39.37" x 55.67")
static const ISO_B0 = const PrintJobMediaSize(
id: "ISO_B0", widthMils: 39370, heightMils: 55670);
///ISO B1 media size: 707mm x 1000mm (27.83" x 39.37")
static const ISO_B1 = const PrintJobMediaSize(
id: "ISO_B1", widthMils: 27830, heightMils: 39370);
///ISO B2 media size: 500mm x 707mm (19.69" x 27.83")
static const ISO_B2 = const PrintJobMediaSize(
id: "ISO_B2", widthMils: 19690, heightMils: 27830);
///ISO B3 media size: 353mm x 500mm (13.90" x 19.69")
static const ISO_B3 = const PrintJobMediaSize(
id: "ISO_B3", widthMils: 13900, heightMils: 19690);
///ISO B4 media size: 250mm x 353mm (9.84" x 13.90")
static const ISO_B4 =
const PrintJobMediaSize(id: "ISO_B4", widthMils: 9840, heightMils: 13900);
///ISO B5 media size: 176mm x 250mm (6.93" x 9.84")
static const ISO_B5 =
const PrintJobMediaSize(id: "ISO_B5", widthMils: 6930, heightMils: 9840);
///ISO B6 media size: 125mm x 176mm (4.92" x 6.93")
static const ISO_B6 =
const PrintJobMediaSize(id: "ISO_B6", widthMils: 4920, heightMils: 6930);
///ISO B7 media size: 88mm x 125mm (3.46" x 4.92")
static const ISO_B7 =
const PrintJobMediaSize(id: "ISO_B7", widthMils: 3460, heightMils: 4920);
///ISO B8 media size: 62mm x 88mm (2.44" x 3.46")
static const ISO_B8 =
const PrintJobMediaSize(id: "ISO_B8", widthMils: 2440, heightMils: 3460);
///ISO B9 media size: 44mm x 62mm (1.73" x 2.44")
static const ISO_B9 =
const PrintJobMediaSize(id: "ISO_B9", widthMils: 1730, heightMils: 2440);
///ISO B10 media size: 31mm x 44mm (1.22" x 1.73")
static const ISO_B10 =
const PrintJobMediaSize(id: "ISO_B10", widthMils: 1220, heightMils: 1730);
///ISO C0 media size: 917mm x 1297mm (36.10" x 51.06")
static const ISO_C0 = const PrintJobMediaSize(
id: "ISO_C0", widthMils: 36100, heightMils: 51060);
///ISO C1 media size: 648mm x 917mm (25.51" x 36.10")
static const ISO_C1 = const PrintJobMediaSize(
id: "ISO_C1", widthMils: 25510, heightMils: 36100);
///ISO C2 media size: 458mm x 648mm (18.03" x 25.51")
static const ISO_C2 = const PrintJobMediaSize(
id: "ISO_C2", widthMils: 18030, heightMils: 25510);
///ISO C3 media size: 324mm x 458mm (12.76" x 18.03")
static const ISO_C3 = const PrintJobMediaSize(
id: "ISO_C3", widthMils: 12760, heightMils: 18030);
///ISO C4 media size: 229mm x 324mm (9.02" x 12.76")
static const ISO_C4 =
const PrintJobMediaSize(id: "ISO_C4", widthMils: 9020, heightMils: 12760);
///ISO C5 media size: 162mm x 229mm (6.38" x 9.02")
static const ISO_C5 =
const PrintJobMediaSize(id: "ISO_C5", widthMils: 6380, heightMils: 9020);
///ISO C6 media size: 114mm x 162mm (4.49" x 6.38")
static const ISO_C6 =
const PrintJobMediaSize(id: "ISO_C6", widthMils: 4490, heightMils: 6380);
///ISO C7 media size: 81mm x 114mm (3.19" x 4.49")
static const ISO_C7 =
const PrintJobMediaSize(id: "ISO_C7", widthMils: 3190, heightMils: 4490);
///ISO C8 media size: 57mm x 81mm (2.24" x 3.19")
static const ISO_C8 =
const PrintJobMediaSize(id: "ISO_C8", widthMils: 2240, heightMils: 3190);
///ISO C9 media size: 40mm x 57mm (1.57" x 2.24")
static const ISO_C9 =
const PrintJobMediaSize(id: "ISO_C9", widthMils: 1570, heightMils: 2240);
///ISO C10 media size: 28mm x 40mm (1.10" x 1.57")
static const ISO_C10 =
const PrintJobMediaSize(id: "ISO_C10", widthMils: 1100, heightMils: 1570);
// North America
///North America Letter media size: 8.5" x 11" (279mm x 216mm)
static const NA_LETTER = const PrintJobMediaSize(
id: "NA_LETTER", widthMils: 8500, heightMils: 11000);
///North America Government-Letter media size: 8.0" x 10.5" (203mm x 267mm)
static const NA_GOVT_LETTER = const PrintJobMediaSize(
id: "NA_GOVT_LETTER", widthMils: 8000, heightMils: 10500);
///North America Legal media size: 8.5" x 14" (216mm x 356mm)
static const NA_LEGAL = const PrintJobMediaSize(
id: "NA_LEGAL", widthMils: 8500, heightMils: 14000);
///North America Junior Legal media size: 8.0" x 5.0" (203mm × 127mm)
static const NA_JUNIOR_LEGAL = const PrintJobMediaSize(
id: "NA_JUNIOR_LEGAL", widthMils: 8000, heightMils: 5000);
///North America Ledger media size: 17" x 11" (432mm × 279mm)
static const NA_LEDGER = const PrintJobMediaSize(
id: "NA_LEDGER", widthMils: 17000, heightMils: 11000);
///North America Tabloid media size: 11" x 17" (279mm × 432mm)
static const NA_TABLOID = const PrintJobMediaSize(
id: "NA_TABLOID", widthMils: 11000, heightMils: 17000);
///North America Index Card 3x5 media size: 3" x 5" (76mm x 127mm)
static const NA_INDEX_3X5 = const PrintJobMediaSize(
id: "NA_INDEX_3X5", widthMils: 3000, heightMils: 5000);
///North America Index Card 4x6 media size: 4" x 6" (102mm x 152mm)
static const NA_INDEX_4X6 = const PrintJobMediaSize(
id: "NA_INDEX_4X6", widthMils: 4000, heightMils: 6000);
///North America Index Card 5x8 media size: 5" x 8" (127mm x 203mm)
static const NA_INDEX_5X8 = const PrintJobMediaSize(
id: "NA_INDEX_5X8", widthMils: 5000, heightMils: 8000);
///North America Monarch media size: 7.25" x 10.5" (184mm x 267mm)
static const NA_MONARCH = const PrintJobMediaSize(
id: "NA_MONARCH", widthMils: 7250, heightMils: 10500);
///North America Quarto media size: 8" x 10" (203mm x 254mm)
static const NA_QUARTO = const PrintJobMediaSize(
id: "NA_QUARTO", widthMils: 8000, heightMils: 10000);
///North America Foolscap media size: 8" x 13" (203mm x 330mm)
static const NA_FOOLSCAP = const PrintJobMediaSize(
id: "NA_FOOLSCAP", widthMils: 8000, heightMils: 13000);
///North America ANSI C media size: 17" x 22" (432mm x 559mm)
static const ANSI_C = const PrintJobMediaSize(
id: "ANSI_C", widthMils: 17000, heightMils: 22000);
///North America ANSI D media size: 22" x 34" (559mm x 864mm)
static const ANSI_D = const PrintJobMediaSize(
id: "ANSI_D", widthMils: 22000, heightMils: 34000);
///North America ANSI E media size: 34" x 44" (864mm x 1118mm)
static const ANSI_E = const PrintJobMediaSize(
id: "ANSI_E", widthMils: 34000, heightMils: 44000);
///North America ANSI F media size: 28" x 40" (711mm x 1016mm)
static const ANSI_F = const PrintJobMediaSize(
id: "ANSI_F", widthMils: 28000, heightMils: 40000);
///North America Arch A media size: 9" x 12" (229mm x 305mm)
static const NA_ARCH_A = const PrintJobMediaSize(
id: "NA_ARCH_A", widthMils: 9000, heightMils: 12000);
///North America Arch B media size: 12" x 18" (305mm x 457mm)
static const NA_ARCH_B = const PrintJobMediaSize(
id: "NA_ARCH_B", widthMils: 12000, heightMils: 18000);
///North America Arch C media size: 18" x 24" (457mm x 610mm)
static const NA_ARCH_C = const PrintJobMediaSize(
id: "NA_ARCH_C", widthMils: 18000, heightMils: 24000);
///North America Arch D media size: 24" x 36" (610mm x 914mm)
static const NA_ARCH_D = const PrintJobMediaSize(
id: "NA_ARCH_D", widthMils: 24000, heightMils: 36000);
///North America Arch E media size: 36" x 48" (914mm x 1219mm)
static const NA_ARCH_E = const PrintJobMediaSize(
id: "NA_ARCH_E", widthMils: 36000, heightMils: 48000);
///North America Arch E1 media size: 30" x 42" (762mm x 1067mm)
static const NA_ARCH_E1 = const PrintJobMediaSize(
id: "NA_ARCH_E1", widthMils: 30000, heightMils: 42000);
///North America Super B media size: 13" x 19" (330mm x 483mm)
static const NA_SUPER_B = const PrintJobMediaSize(
id: "NA_SUPER_B", widthMils: 13000, heightMils: 19000);
// Chinese
///Chinese ROC 8K media size: 270mm x 390mm (10.629" x 15.3543")
static const ROC_8K = const PrintJobMediaSize(
id: "ROC_8K", widthMils: 10629, heightMils: 15354);
///Chinese ROC 16K media size: 195mm x 270mm (7.677" x 10.629")
static const ROC_16K = const PrintJobMediaSize(
id: "ROC_16K", widthMils: 7677, heightMils: 10629);
///Chinese PRC 1 media size: 102mm x 165mm (4.015" x 6.496")
static const PRC_1 =
const PrintJobMediaSize(id: "PRC_1", widthMils: 4015, heightMils: 6496);
///Chinese PRC 2 media size: 102mm x 176mm (4.015" x 6.929")
static const PRC_2 =
const PrintJobMediaSize(id: "PRC_2", widthMils: 4015, heightMils: 6929);
///Chinese PRC 3 media size: 125mm x 176mm (4.921" x 6.929")
static const PRC_3 =
const PrintJobMediaSize(id: "PRC_3", widthMils: 4921, heightMils: 6929);
///Chinese PRC 4 media size: 110mm x 208mm (4.330" x 8.189")
static const PRC_4 =
const PrintJobMediaSize(id: "PRC_4", widthMils: 4330, heightMils: 8189);
///Chinese PRC 5 media size: 110mm x 220mm (4.330" x 8.661")
static const PRC_5 =
const PrintJobMediaSize(id: "PRC_5", widthMils: 4330, heightMils: 8661);
///Chinese PRC 6 media size: 120mm x 320mm (4.724" x 12.599")
static const PRC_6 =
const PrintJobMediaSize(id: "PRC_6", widthMils: 4724, heightMils: 12599);
///Chinese PRC 7 media size: 160mm x 230mm (6.299" x 9.055")
static const PRC_7 =
const PrintJobMediaSize(id: "PRC_7", widthMils: 6299, heightMils: 9055);
///Chinese PRC 8 media size: 120mm x 309mm (4.724" x 12.165")
static const PRC_8 =
const PrintJobMediaSize(id: "PRC_8", widthMils: 4724, heightMils: 12165);
///Chinese PRC 9 media size: 229mm x 324mm (9.016" x 12.756")
static const PRC_9 =
const PrintJobMediaSize(id: "PRC_9", widthMils: 9016, heightMils: 12756);
///Chinese PRC 10 media size: 324mm x 458mm (12.756" x 18.032")
static const PRC_10 = const PrintJobMediaSize(
id: "PRC_10", widthMils: 12756, heightMils: 18032);
///Chinese PRC 16k media size: 146mm x 215mm (5.749" x 8.465")
static const PRC_16K =
const PrintJobMediaSize(id: "PRC_16K", widthMils: 5749, heightMils: 8465);
///Chinese Pa Kai media size: 267mm x 389mm (10.512" x 15.315")
static const OM_PA_KAI = const PrintJobMediaSize(
id: "OM_PA_KAI", widthMils: 10512, heightMils: 15315);
///Chinese Dai Pa Kai media size: 275mm x 395mm (10.827" x 15.551")
static const OM_DAI_PA_KAI = const PrintJobMediaSize(
id: "OM_DAI_PA_KAI", widthMils: 10827, heightMils: 15551);
///Chinese Jurro Ku Kai media size: 198mm x 275mm (7.796" x 10.827")
static const OM_JUURO_KU_KAI = const PrintJobMediaSize(
id: "OM_JUURO_KU_KAI", widthMils: 7796, heightMils: 10827);
// Japanese
///Japanese JIS B10 media size: 32mm x 45mm (1.259" x 1.772")
static const JIS_B10 =
const PrintJobMediaSize(id: "JIS_B10", widthMils: 1259, heightMils: 1772);
///Japanese JIS B9 media size: 45mm x 64mm (1.772" x 2.52")
static const JIS_B9 =
const PrintJobMediaSize(id: "JIS_B9", widthMils: 1772, heightMils: 2520);
///Japanese JIS B8 media size: 64mm x 91mm (2.52" x 3.583")
static const JIS_B8 =
const PrintJobMediaSize(id: "JIS_B8", widthMils: 2520, heightMils: 3583);
///Japanese JIS B7 media size: 91mm x 128mm (3.583" x 5.049")
static const JIS_B7 =
const PrintJobMediaSize(id: "JIS_B7", widthMils: 3583, heightMils: 5049);
///Japanese JIS B6 media size: 128mm x 182mm (5.049" x 7.165")
static const JIS_B6 =
const PrintJobMediaSize(id: "JIS_B6", widthMils: 5049, heightMils: 7165);
///Japanese JIS B5 media size: 182mm x 257mm (7.165" x 10.118")
static const JIS_B5 =
const PrintJobMediaSize(id: "JIS_B5", widthMils: 7165, heightMils: 10118);
///Japanese JIS B4 media size: 257mm x 364mm (10.118" x 14.331")
static const JIS_B4 = const PrintJobMediaSize(
id: "JIS_B4", widthMils: 10118, heightMils: 14331);
///Japanese JIS B3 media size: 364mm x 515mm (14.331" x 20.276")
static const JIS_B3 = const PrintJobMediaSize(
id: "JIS_B3", widthMils: 14331, heightMils: 20276);
///Japanese JIS B2 media size: 515mm x 728mm (20.276" x 28.661")
static const JIS_B2 = const PrintJobMediaSize(
id: "JIS_B2", widthMils: 20276, heightMils: 28661);
///Japanese JIS B1 media size: 728mm x 1030mm (28.661" x 40.551")
static const JIS_B1 = const PrintJobMediaSize(
id: "JIS_B1", widthMils: 28661, heightMils: 40551);
///Japanese JIS B0 media size: 1030mm x 1456mm (40.551" x 57.323")
static const JIS_B0 = const PrintJobMediaSize(
id: "JIS_B0", widthMils: 40551, heightMils: 57323);
///Japanese JIS Exec media size: 216mm x 330mm (8.504" x 12.992")
static const JIS_EXEC = const PrintJobMediaSize(
id: "JIS_EXEC", widthMils: 8504, heightMils: 12992);
///Japanese Chou4 media size: 90mm x 205mm (3.543" x 8.071")
static const JPN_CHOU4 = const PrintJobMediaSize(
id: "JPN_CHOU4", widthMils: 3543, heightMils: 8071);
///Japanese Chou3 media size: 120mm x 235mm (4.724" x 9.252")
static const JPN_CHOU3 = const PrintJobMediaSize(
id: "JPN_CHOU3", widthMils: 4724, heightMils: 9252);
///Japanese Chou2 media size: 111.1mm x 146mm (4.374" x 5.748")
static const JPN_CHOU2 = const PrintJobMediaSize(
id: "JPN_CHOU2", widthMils: 4374, heightMils: 5748);
///Japanese Hagaki media size: 100mm x 148mm (3.937" x 5.827")
static const JPN_HAGAKI = const PrintJobMediaSize(
id: "JPN_HAGAKI", widthMils: 3937, heightMils: 5827);
///Japanese Oufuku media size: 148mm x 200mm (5.827" x 7.874")
static const JPN_OUFUKU = const PrintJobMediaSize(
id: "JPN_OUFUKU", widthMils: 5827, heightMils: 7874);
///Japanese Kahu media size: 240mm x 322.1mm (9.449" x 12.681")
static const JPN_KAHU = const PrintJobMediaSize(
id: "JPN_KAHU", widthMils: 9449, heightMils: 12681);
///Japanese Kaku2 media size: 240mm x 332mm (9.449" x 13.071")
static const JPN_KAKU2 = const PrintJobMediaSize(
id: "JPN_KAKU2", widthMils: 9449, heightMils: 13071);
///Japanese You4 media size: 105mm x 235mm (4.134" x 9.252")
static const JPN_YOU4 = const PrintJobMediaSize(
id: "JPN_YOU4", widthMils: 4134, heightMils: 9252);
///Japanese Photo L media size: 89mm x 127mm (3.5 x 5")
static const JPN_OE_PHOTO_L = const PrintJobMediaSize(
id: "JPN_OE_PHOTO_L", widthMils: 3500, heightMils: 5000);
}

View File

@ -0,0 +1,52 @@
import '../print_job/main.dart';
///Class representing the orientation of a [PrintJobController].
class PrintJobOrientation {
final int _value;
const PrintJobOrientation._internal(this._value);
///Set of all values of [PrintJobOrientation].
static final Set<PrintJobOrientation> values = [
PrintJobOrientation.PORTRAIT,
PrintJobOrientation.LANDSCAPE,
].toSet();
///Gets a possible [PrintJobOrientation] instance from an [int] value.
static PrintJobOrientation? fromValue(int? value) {
if (value != null) {
try {
return PrintJobOrientation.values
.firstWhere((element) => element.toValue() == value);
} catch (e) {
return null;
}
}
return null;
}
///Gets [int] value.
int toValue() => _value;
@override
String toString() {
switch (_value) {
case 1:
return "LANDSCAPE";
case 0:
default:
return "PORTRAIT";
}
}
///Pages are printed in portrait orientation.
static const PORTRAIT = const PrintJobOrientation._internal(0);
///Pages are printed in landscape orientation.
static const LANDSCAPE = const PrintJobOrientation._internal(1);
bool operator ==(value) => value == _value;
@override
int get hashCode => _value.hashCode;
}

View File

@ -0,0 +1,74 @@
import '../print_job/main.dart';
///Class representing the kind of printable content of a [PrintJobController].
class PrintJobOutputType {
final int _value;
const PrintJobOutputType._internal(this._value);
///Set of all values of [PrintJobOutputType].
static final Set<PrintJobOutputType> values = [
PrintJobOutputType.GENERAL,
PrintJobOutputType.PHOTO,
PrintJobOutputType.GRAYSCALE,
PrintJobOutputType.PHOTO_GRAYSCALE
].toSet();
///Gets a possible [PrintJobOutputType] instance from an [int] value.
static PrintJobOutputType? fromValue(int? value) {
if (value != null) {
try {
return PrintJobOutputType.values
.firstWhere((element) => element.toValue() == value);
} catch (e) {
return null;
}
}
return null;
}
///Gets [int] value.
int toValue() => _value;
@override
String toString() {
switch (_value) {
case 1:
return "PHOTO";
case 2:
return "GRAYSCALE";
case 3:
return "PHOTO_GRAYSCALE";
case 0:
default:
return "GENERAL";
}
}
///Specifies that the printed content consists of mixed text, graphics, and images.
///The default paper is Letter, A4, or similar locale-specific designation.
///Output is normal quality, duplex.
static const GENERAL = const PrintJobOutputType._internal(0);
///Specifies that the printed content consists of black-and-white or color images.
///The default paper is 4x6, A6, or similar locale-specific designation.
///Output is high quality, simplex.
static const PHOTO = const PrintJobOutputType._internal(1);
///Specifies that the printed content is grayscale.
///Set the output type to this value when your printable content contains no colorfor example, its black text only.
///The default paper is Letter/A4. Output is grayscale quality, duplex.
///This content type can produce a performance improvement in some cases.
static const GRAYSCALE = const PrintJobOutputType._internal(2);
///Specifies that the printed content is a grayscale image.
///Set the output type to this value when your printable content contains no colorfor example, its black text only.
///The default paper is Letter/A4.
///Output is high quality grayscale, duplex.
static const PHOTO_GRAYSCALE = const PrintJobOutputType._internal(3);
bool operator ==(value) => value == _value;
@override
int get hashCode => _value.hashCode;
}

View File

@ -0,0 +1,53 @@
import '../print_job/main.dart';
///Class representing the rendering quality of a [PrintJobController].
class PrintJobRenderingQuality {
final int _value;
const PrintJobRenderingQuality._internal(this._value);
///Set of all values of [PrintJobRenderingQuality].
static final Set<PrintJobRenderingQuality> values = [
PrintJobRenderingQuality.BEST,
PrintJobRenderingQuality.RESPONSIVE,
].toSet();
///Gets a possible [PrintJobRenderingQuality] instance from an [int] value.
static PrintJobRenderingQuality? fromValue(int? value) {
if (value != null) {
try {
return PrintJobRenderingQuality.values
.firstWhere((element) => element.toValue() == value);
} catch (e) {
return null;
}
}
return null;
}
///Gets [int] value.
int toValue() => _value;
@override
String toString() {
switch (_value) {
case 1:
return "RESPONSIVE";
case 0:
default:
return "BEST";
}
}
///Renders the printing at the best possible quality, regardless of speed.
static const BEST = const PrintJobRenderingQuality._internal(0);
///Sacrifices the least possible amount of rendering quality for speed to maintain a responsive user interface.
///This option should be used only after establishing that best quality rendering does indeed make the user interface unresponsive.
static const RESPONSIVE = const PrintJobRenderingQuality._internal(1);
bool operator ==(value) => value == _value;
@override
int get hashCode => _value.hashCode;
}

View File

@ -0,0 +1,64 @@
import '../print_job/main.dart';
///Class representing the supported resolution in DPI (dots per inch) for a [PrintJobController].
///Resolution defines how many points with different color can be placed
///on one inch in horizontal or vertical direction of the target media.
///For example, a printer with 600 DPI can produce higher quality images
///the one with 300 DPI resolution.
class PrintJobResolution {
///The unique resolution id.
///
///It is unique amongst other resolutions supported by the printer.
///This id is defined by the client that generated the resolution
///instance and should not be interpreted by other parties.
final String id;
///The human readable label.
final String label;
///The vertical resolution in DPI (dots per inch).
final int verticalDpi;
///The horizontal resolution in DPI (dots per inch).
final int horizontalDpi;
const PrintJobResolution(
{required this.id,
required this.label,
required this.verticalDpi,
required this.horizontalDpi});
///Gets a possible [PrintJobResolution] instance from a [Map] value.
static PrintJobResolution? fromMap(Map<String, dynamic>? map) {
if (map == null) {
return null;
}
return PrintJobResolution(
id: map["id"],
label: map["label"],
verticalDpi: map["verticalDpi"],
horizontalDpi: map["horizontalDpi"],
);
}
///Converts instance to a map.
Map<String, dynamic> toMap() {
return {
"id": id,
"label": label,
"verticalDpi": verticalDpi,
"horizontalDpi": horizontalDpi,
};
}
///Converts instance to a map.
Map<String, dynamic> toJson() {
return this.toMap();
}
@override
String toString() {
return toMap().toString();
}
}

View File

@ -0,0 +1,123 @@
import '../print_job/main.dart';
///Class representing the state of a [PrintJobController].
class PrintJobState {
final int _value;
const PrintJobState._internal(this._value);
///Set of all values of [PrintJobState].
static final Set<PrintJobState> values = [
PrintJobState.CREATED,
PrintJobState.QUEUED,
PrintJobState.STARTED,
PrintJobState.BLOCKED,
PrintJobState.COMPLETED,
PrintJobState.FAILED,
PrintJobState.CANCELED,
].toSet();
///Gets a possible [PrintJobState] instance from an [int] value.
static PrintJobState? fromValue(int? value) {
if (value != null) {
try {
return PrintJobState.values
.firstWhere((element) => element.toValue() == value);
} catch (e) {
return null;
}
}
return null;
}
///Gets [int] value.
int toValue() => _value;
@override
String toString() {
switch (_value) {
case 2:
return "QUEUED";
case 3:
return "STARTED";
case 4:
return "BLOCKED";
case 5:
return "COMPLETED";
case 6:
return "FAILED";
case 7:
return "CANCELED";
case 1:
default:
return "CREATED";
}
}
///Print job state: The print job is being created but not yet ready to be printed.
///
///Next valid states: [QUEUED].
///
///**Supported Platforms/Implementations**:
///- Android native WebView ([Official API - PrintJobInfo.STATE_CREATED](https://developer.android.com/reference/android/print/PrintJobInfo#STATE_CREATED))
///- iOS
static const CREATED = const PrintJobState._internal(1);
///Print job state: The print jobs is created, it is ready to be printed and should be processed.
///
///Next valid states: [STARTED], [FAILED], [CANCELED].
///
///**Supported Platforms/Implementations**:
///- Android native WebView ([Official API - PrintJobInfo.STATE_QUEUED](https://developer.android.com/reference/android/print/PrintJobInfo#STATE_QUEUED))
static const QUEUED = const PrintJobState._internal(2);
///Print job state: The print job is being printed.
///
///Next valid states: [COMPLETED], [FAILED], [CANCELED], [BLOCKED].
///
///**Supported Platforms/Implementations**:
///- Android native WebView ([Official API - PrintJobInfo.STATE_STARTED](https://developer.android.com/reference/android/print/PrintJobInfo#STATE_STARTED))
///- iOS
static const STARTED = const PrintJobState._internal(3);
///Print job state: The print job is blocked.
///
///Next valid states: [FAILED], [CANCELED], [STARTED].
///
///**Supported Platforms/Implementations**:
///- Android native WebView ([Official API - PrintJobInfo.STATE_BLOCKED](https://developer.android.com/reference/android/print/PrintJobInfo#STATE_BLOCKED))
static const BLOCKED = const PrintJobState._internal(4);
///Print job state: The print job is successfully printed. This is a terminal state.
///
///Next valid states: None.
///
///**Supported Platforms/Implementations**:
///- Android native WebView ([Official API - PrintJobInfo.STATE_COMPLETED](https://developer.android.com/reference/android/print/PrintJobInfo#STATE_COMPLETED))
///- iOS
static const COMPLETED = const PrintJobState._internal(5);
///Print job state: The print job was printing but printing failed.
///
///Next valid states: None.
///
///**Supported Platforms/Implementations**:
///- Android native WebView ([Official API - PrintJobInfo.STATE_FAILED](https://developer.android.com/reference/android/print/PrintJobInfo#STATE_FAILED))
///- iOS
static const FAILED = const PrintJobState._internal(6);
///Print job state: The print job is canceled. This is a terminal state.
///
///Next valid states: None.
///
///**Supported Platforms/Implementations**:
///- Android native WebView ([Official API - PrintJobInfo.STATE_CANCELED](https://developer.android.com/reference/android/print/PrintJobInfo#STATE_CANCELED))
///- iOS
static const CANCELED = const PrintJobState._internal(7);
bool operator ==(value) => value == _value;
@override
int get hashCode => _value.hashCode;
}

View File

@ -38,7 +38,7 @@ class SslErrorType {
return null;
}
///Gets a possible [SslErrorType] instance from a value.
///Gets a possible [SslErrorType] instance from a native value.
static SslErrorType? fromNativeValue(int? value) {
if (value != null) {
try {

View File

@ -79,7 +79,7 @@ class WebResourceErrorType {
return null;
}
///Gets a possible [WebResourceErrorType] instance from an [int] value.
///Gets a possible [WebResourceErrorType] instance from an [int] native value.
static WebResourceErrorType? fromNativeValue(int? value) {
if (value != null) {
try {

View File

@ -502,3 +502,21 @@ extension MapSize on Size {
return {'width': width, 'height': height};
}
}
extension MapEdgeInsets on EdgeInsets {
static EdgeInsets? fromMap(Map<String, dynamic>? map) {
if (map == null) {
return null;
}
return EdgeInsets.fromLTRB(
map['left'], map['top'], map['right'], map['bottom']);
}
Map<String, double> toJson() {
return toMap();
}
Map<String, double> toMap() {
return {'top': top, 'right': right, 'bottom': bottom, 'left': left};
}
}

View File

@ -157,6 +157,7 @@ class InAppWebViewWebElement {
'flutter_inappwebview for web doesn\'t implement \'${call.method}\'',
);
}
return null;
}
void prepare() {
@ -502,10 +503,13 @@ class InAppWebViewWebElement {
await _channel?.invokeMethod("onWindowBlur");
}
void onPrint(String? url) async {
var obj = {"url": url};
void onPrintRequest(String? url) async {
var obj = {
"url": url,
"printJobId": null
};
await _channel?.invokeMethod("onPrint", obj);
await _channel?.invokeMethod("onPrintRequest", obj);
}
void onEnterFullscreen() async {

View File

@ -82,9 +82,9 @@ Future<dynamic> _dartNativeCommunication(String method, dynamic viewId,
case 'onWindowBlur':
webViewHtmlElement.onWindowBlur();
break;
case 'onPrint':
case 'onPrintRequest':
String? url = args![0];
webViewHtmlElement.onPrint(url);
webViewHtmlElement.onPrintRequest(url);
break;
case 'onEnterFullscreen':
webViewHtmlElement.onEnterFullscreen();