Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -85,6 +85,12 @@ public class SecureApplicationPlugin: FlutterPlugin, MethodCallHandler, Activity
} else if (call.method == "open") {
activity?.window?.clearFlags(LayoutParams.FLAG_SECURE)
result.success(true)
} else if (call.method == "useLaunchImage") {
// This is currently not possible on Android, as far as I am aware
result.success(true)
} else if (call.method == "backgroundColor") {
// This is currently not possible on Android, as far as I am aware
result.success(true)
} else {
result.success(true)
}
Expand Down
153 changes: 113 additions & 40 deletions ios/Classes/SwiftSecureApplicationPlugin.swift
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,9 @@ import UIKit
public class SwiftSecureApplicationPlugin: NSObject, FlutterPlugin {
var secured = false;
var opacity: CGFloat = 0.2;

var useLaunchImage: Bool = false;
var backgroundColor: UIColor = UIColor.white;

var backgroundTask: UIBackgroundTaskIdentifier!

internal let registrar: FlutterPluginRegistrar
Expand All @@ -28,29 +30,51 @@ public class SwiftSecureApplicationPlugin: NSObject, FlutterPlugin {
if let window = UIApplication.shared.windows.filter({ (w) -> Bool in
return w.isHidden == false
}).first {
if let existingView = window.viewWithTag(99699), let existingBlurrView = window.viewWithTag(99698) {
window.bringSubviewToFront(existingView)
window.bringSubviewToFront(existingBlurrView)
return
if (useLaunchImage) {
if let existingView = window.viewWithTag(99697) {
window.bringSubviewToFront(existingView)
return
} else {
let imageView = UIImageView.init(frame: window.bounds)
imageView.tag = 99697
imageView.backgroundColor = backgroundColor
imageView.clipsToBounds = true
imageView.contentMode = .center
imageView.image = UIImage(named: "LaunchImage")
imageView.isMultipleTouchEnabled = true
imageView.translatesAutoresizingMaskIntoConstraints = false

window.addSubview(imageView)
window.bringSubviewToFront(imageView)

window.snapshotView(afterScreenUpdates: true)
RunLoop.current.run(until: Date(timeIntervalSinceNow:0.5))
}
} else {
let colorView = UIView(frame: window.bounds);
colorView.tag = 99699
colorView.autoresizingMask = [.flexibleWidth, .flexibleHeight]
colorView.backgroundColor = UIColor(white: 1, alpha: opacity)
window.addSubview(colorView)
window.bringSubviewToFront(colorView)

let blurEffect = UIBlurEffect(style: UIBlurEffect.Style.extraLight)
let blurEffectView = UIVisualEffectView(effect: blurEffect)
blurEffectView.frame = window.bounds
blurEffectView.autoresizingMask = [.flexibleWidth, .flexibleHeight]

blurEffectView.tag = 99698

window.addSubview(blurEffectView)
window.bringSubviewToFront(blurEffectView)
window.snapshotView(afterScreenUpdates: true)
RunLoop.current.run(until: Date(timeIntervalSinceNow:0.5))
if let existingView = window.viewWithTag(99699), let existingBlurrView = window.viewWithTag(99698) {
window.bringSubviewToFront(existingView)
window.bringSubviewToFront(existingBlurrView)
return
} else {
let colorView = UIView(frame: window.bounds);
colorView.tag = 99699
colorView.autoresizingMask = [.flexibleWidth, .flexibleHeight]
colorView.backgroundColor = backgroundColor.withAlphaComponent(opacity)
window.addSubview(colorView)
window.bringSubviewToFront(colorView)

let blurEffect = UIBlurEffect(style: UIBlurEffect.Style.extraLight)
let blurEffectView = UIVisualEffectView(effect: blurEffect)
blurEffectView.frame = window.bounds
blurEffectView.autoresizingMask = [.flexibleWidth, .flexibleHeight]

blurEffectView.tag = 99698

window.addSubview(blurEffectView)
window.bringSubviewToFront(blurEffectView)
window.snapshotView(afterScreenUpdates: true)
RunLoop.current.run(until: Date(timeIntervalSinceNow:0.5))
}
}
}
self.endBackgroundTask()
Expand All @@ -72,30 +96,79 @@ public class SwiftSecureApplicationPlugin: NSObject, FlutterPlugin {
public func handle(_ call: FlutterMethodCall, result: @escaping FlutterResult) {
if (call.method == "secure") {
secured = true;
if let args = call.arguments as? Dictionary<String, Any>,
let opacity = args["opacity"] as? NSNumber {
self.opacity = opacity as! CGFloat
if let args = call.arguments as? Dictionary<String, Any> {
if let opacity = args["opacity"] as? NSNumber {
self.opacity = opacity as! CGFloat
}

if let useLaunchImage = args["useLaunchImage"] as? Bool {
self.useLaunchImage = useLaunchImage
}

if let backgroundColor = args["backgroundColor"] as? String {
self.backgroundColor = hexStringToUIColor(hex: backgroundColor)
}
}
} else if (call.method == "open") {
secured = false;
} else if (call.method == "opacity") {
if let args = call.arguments as? Dictionary<String, Any>,
let opacity = args["opacity"] as? NSNumber {
self.opacity = opacity as! CGFloat
}
} else if (call.method == "opacity") {
if let args = call.arguments as? Dictionary<String, Any>,
let opacity = args["opacity"] as? NSNumber {
self.opacity = opacity as! CGFloat
}
} else if (call.method == "backgroundColor") {
if let args = call.arguments as? Dictionary<String, Any>,
let backgroundColor = args["backgroundColor"] as? String {
self.backgroundColor = hexStringToUIColor(hex: backgroundColor)
}
} else if (call.method == "useLaunchImage") {
if let args = call.arguments as? Dictionary<String, Any>,
let useLaunchImage = args["useLaunchImage"] as? Bool {
self.useLaunchImage = useLaunchImage
}
} else if (call.method == "unlock") {
if let window = UIApplication.shared.windows.filter({ (w) -> Bool in
return w.isHidden == false
}).first, let view = window.viewWithTag(99699), let blurrView = window.viewWithTag(99698) {
UIView.animate(withDuration: 0.5, animations: {
view.alpha = 0.0
blurrView.alpha = 0.0
}, completion: { finished in
view.removeFromSuperview()
blurrView.removeFromSuperview()

})
}).first {
if let colorView = window.viewWithTag(99699), let blurrView = window.viewWithTag(99698) {
UIView.animate(withDuration: 0.5, animations: {
colorView.alpha = 0.0
}, completion: { finished in
colorView.removeFromSuperview()
blurrView.removeFromSuperview()
})
}

if let imageView = window.viewWithTag(99697) {
UIView.animate(withDuration: 0.3, animations: {
imageView.alpha = 0.0
}, completion: { finished in
imageView.removeFromSuperview()
})
}
}
}
}

func hexStringToUIColor (hex:String) -> UIColor {
var cString:String = hex.trimmingCharacters(in: .whitespacesAndNewlines).uppercased()

if (cString.hasPrefix("#")) {
cString.remove(at: cString.startIndex)
}

if ((cString.count) != 6) {
return UIColor.gray
}

var rgbValue:UInt64 = 0
Scanner(string: cString).scanHexInt64(&rgbValue)

return UIColor(
red: CGFloat((rgbValue & 0xFF0000) >> 16) / 255.0,
green: CGFloat((rgbValue & 0x00FF00) >> 8) / 255.0,
blue: CGFloat(rgbValue & 0x0000FF) / 255.0,
alpha: CGFloat(1.0)
)
}
}
14 changes: 13 additions & 1 deletion lib/secure_application_native.dart
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import 'dart:async';
import 'dart:ui';

import 'package:flutter/foundation.dart';
import 'package:flutter/services.dart';

class SecureApplicationNative {
Expand Down Expand Up @@ -45,4 +45,16 @@ class SecureApplicationNative {
static Future opacity(double opacity) {
return _channel.invokeMethod('opacity', {"opacity": opacity});
}

static Future useLaunchImageIOS(bool useLaunchImage) {
return _channel.invokeMethod('useLaunchImage', {
'useLaunchImage': useLaunchImage,
});
}

static Future backgroundColor(Color color) {
return _channel.invokeMethod('backgroundColor', {
'backgroundColor': '#${color.value.toRadixString(16).substring(2, 8)}',
});
}
}
32 changes: 30 additions & 2 deletions lib/secure_gate.dart
Original file line number Diff line number Diff line change
@@ -1,9 +1,9 @@
import 'dart:ui';

import 'package:flutter/material.dart';
import 'package:secure_application/secure_application_controller.dart';
import 'package:secure_application/secure_application_native.dart';
import 'package:secure_application/secure_application_provider.dart';
import 'package:secure_application/secure_application_controller.dart';

/// it will display a blurr over your content if locked
///
Expand All @@ -27,13 +27,30 @@ class SecureGate extends StatefulWidget {
/// default to 0.6
final double opacity;

/// Whether to use the application's LaunchImage in the app switcher.
///
/// For this to work, you MUST have a LaunchImage ImageSet in your iOS folder,
/// just like a newly generated flutter application (at ios/Runner/Assets.xcassets/LaunchImage.imageset)
/// More info here: https://docs.flutter.dev/development/ui/advanced/splash-screen#ios-launch-screen
///
/// If this is true, [opacity] and [blurr] are ignored (iOS only).
///
/// Only available on iOS. It is not possible on Android, as far as I'm aware
final bool useLaunchImageIOS;

/// The background color in the app switcher.
final Color? backgroundColor;

const SecureGate({
Key? key,
required this.child,
this.blurr = 20,
this.opacity = 0.6,
this.lockedBuilder,
this.useLaunchImageIOS = false,
this.backgroundColor,
}) : super(key: key);

@override
_SecureGateState createState() => _SecureGateState();
}
Expand All @@ -50,6 +67,10 @@ class _SecureGateState extends State<SecureGate>
AnimationController(vsync: this, duration: kThemeAnimationDuration * 2)
..addListener(_handleChange);
SecureApplicationNative.opacity(widget.opacity);
SecureApplicationNative.useLaunchImageIOS(widget.useLaunchImageIOS);
if (widget.backgroundColor != null) {
SecureApplicationNative.backgroundColor(widget.backgroundColor!);
}

super.initState();
}
Expand All @@ -70,6 +91,13 @@ class _SecureGateState extends State<SecureGate>
if (oldWidget.opacity != widget.opacity) {
SecureApplicationNative.opacity(widget.opacity);
}
if (oldWidget.useLaunchImageIOS != widget.useLaunchImageIOS) {
SecureApplicationNative.useLaunchImageIOS(widget.useLaunchImageIOS);
}
if (oldWidget.backgroundColor != widget.backgroundColor &&
widget.backgroundColor != null) {
SecureApplicationNative.backgroundColor(widget.backgroundColor!);
}
}

void _sercureNotified() {
Expand Down Expand Up @@ -108,7 +136,7 @@ class _SecureGateState extends State<SecureGate>
sigmaY: widget.blurr * _gateVisibility.value),
child: Container(
decoration: BoxDecoration(
color: Colors.grey.shade200
color: (widget.backgroundColor ?? Colors.grey.shade200)
.withOpacity(widget.opacity * _gateVisibility.value)),
),
),
Expand Down