ajustes de captura de tela ios e android
This commit is contained in:
parent
bebed14863
commit
d9893fa59e
|
|
@ -31,12 +31,17 @@ if (flutterVersionName == null) {
|
|||
|
||||
android {
|
||||
namespace = "com.myrobot.MakerBook"
|
||||
compileSdk = 34 // flutter.compileSdkVersion
|
||||
compileSdk = 36 // flutter.compileSdkVersion
|
||||
ndkVersion = flutter.ndkVersion
|
||||
|
||||
compileOptions {
|
||||
sourceCompatibility = JavaVersion.VERSION_1_8
|
||||
targetCompatibility = JavaVersion.VERSION_1_8
|
||||
sourceCompatibility = JavaVersion.VERSION_17
|
||||
targetCompatibility = JavaVersion.VERSION_17
|
||||
coreLibraryDesugaringEnabled true
|
||||
}
|
||||
|
||||
kotlinOptions {
|
||||
jvmTarget = "17"
|
||||
}
|
||||
|
||||
defaultConfig {
|
||||
|
|
@ -45,7 +50,7 @@ android {
|
|||
// You can update the following values to match your application needs.
|
||||
// For more information, see: https://docs.flutter.dev/deployment/android#reviewing-the-gradle-build-configuration.
|
||||
minSdk = flutter.minSdkVersion
|
||||
targetSdk = 34 //flutter.targetSdkVersion
|
||||
targetSdk = 36 //flutter.targetSdkVersion
|
||||
versionCode = flutterVersionCode.toInteger()
|
||||
versionName = flutterVersionName
|
||||
}
|
||||
|
|
@ -74,6 +79,10 @@ android {
|
|||
|
||||
}
|
||||
|
||||
dependencies {
|
||||
coreLibraryDesugaring "com.android.tools:desugar_jdk_libs:2.0.4"
|
||||
}
|
||||
|
||||
flutter {
|
||||
source = "../.."
|
||||
}
|
||||
|
|
|
|||
|
|
@ -3,16 +3,43 @@ allprojects {
|
|||
google()
|
||||
mavenCentral()
|
||||
}
|
||||
|
||||
tasks.withType(JavaCompile).configureEach {
|
||||
sourceCompatibility = JavaVersion.VERSION_17.toString()
|
||||
targetCompatibility = JavaVersion.VERSION_17.toString()
|
||||
}
|
||||
|
||||
tasks.withType(org.jetbrains.kotlin.gradle.tasks.KotlinCompile).configureEach {
|
||||
kotlinOptions {
|
||||
jvmTarget = "17"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
rootProject.buildDir = "../build"
|
||||
subprojects {
|
||||
project.buildDir = "${rootProject.buildDir}/${project.name}"
|
||||
}
|
||||
subprojects {
|
||||
project.evaluationDependsOn(":app")
|
||||
}
|
||||
|
||||
subprojects { p ->
|
||||
// Força especificamente o plugin qr_code_scanner
|
||||
if (p.name == "qr_code_scanner") {
|
||||
p.tasks.withType(JavaCompile).configureEach {
|
||||
sourceCompatibility = JavaVersion.VERSION_17.toString()
|
||||
targetCompatibility = JavaVersion.VERSION_17.toString()
|
||||
}
|
||||
p.tasks.withType(org.jetbrains.kotlin.gradle.tasks.KotlinCompile).configureEach {
|
||||
kotlinOptions { jvmTarget = "17" }
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
tasks.register("clean", Delete) {
|
||||
delete rootProject.buildDir
|
||||
}
|
||||
|
||||
buildscript {
|
||||
ext.kotlin_version = '2.0.20'
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,3 +1,4 @@
|
|||
org.gradle.jvmargs=-Xmx4G -XX:+HeapDumpOnOutOfMemoryError
|
||||
android.useAndroidX=true
|
||||
android.enableJetifier=true
|
||||
org.gradle.jvmargs=-Xmx4096m
|
||||
kotlin.jvm.target=17
|
||||
|
|
@ -2,4 +2,4 @@ distributionBase=GRADLE_USER_HOME
|
|||
distributionPath=wrapper/dists
|
||||
zipStoreBase=GRADLE_USER_HOME
|
||||
zipStorePath=wrapper/dists
|
||||
distributionUrl=https\://services.gradle.org/distributions/gradle-7.6.3-all.zip
|
||||
distributionUrl=https\://services.gradle.org/distributions/gradle-8.10.2-all.zip
|
||||
|
|
|
|||
|
|
@ -18,8 +18,9 @@ pluginManagement {
|
|||
|
||||
plugins {
|
||||
id "dev.flutter.flutter-plugin-loader" version "1.0.0"
|
||||
id "com.android.application" version "7.3.0" apply false
|
||||
id "org.jetbrains.kotlin.android" version "1.7.10" apply false
|
||||
id "com.android.application" version "8.6.0" apply false
|
||||
id "com.android.library" version "8.6.0" apply false
|
||||
id "org.jetbrains.kotlin.android" version "2.1.0" apply false
|
||||
}
|
||||
|
||||
include ":app"
|
||||
|
|
|
|||
|
|
@ -1,5 +1,5 @@
|
|||
# Uncomment this line to define a global platform for your project
|
||||
# platform :ios, '13.0'
|
||||
platform :ios, '15.5'
|
||||
|
||||
# CocoaPods analytics sends network stats synchronously affecting flutter build latency.
|
||||
ENV['COCOAPODS_DISABLE_STATS'] = 'true'
|
||||
|
|
|
|||
|
|
@ -198,6 +198,7 @@
|
|||
9705A1C41CF9048500538489 /* Embed Frameworks */,
|
||||
3B06AD1E1E4923F5004D2608 /* Thin Binary */,
|
||||
AE54976BBDDFFE7ECF874480 /* [CP] Embed Pods Frameworks */,
|
||||
851D672B5FE59B27299B483A /* [CP] Copy Pods Resources */,
|
||||
);
|
||||
buildRules = (
|
||||
);
|
||||
|
|
@ -329,6 +330,23 @@
|
|||
shellScript = "diff \"${PODS_PODFILE_DIR_PATH}/Podfile.lock\" \"${PODS_ROOT}/Manifest.lock\" > /dev/null\nif [ $? != 0 ] ; then\n # print error to STDERR\n echo \"error: The sandbox is not in sync with the Podfile.lock. Run 'pod install' or update your CocoaPods installation.\" >&2\n exit 1\nfi\n# This output is used by Xcode 'outputs' to avoid re-running this script phase.\necho \"SUCCESS\" > \"${SCRIPT_OUTPUT_FILE_0}\"\n";
|
||||
showEnvVarsInLog = 0;
|
||||
};
|
||||
851D672B5FE59B27299B483A /* [CP] Copy Pods Resources */ = {
|
||||
isa = PBXShellScriptBuildPhase;
|
||||
buildActionMask = 2147483647;
|
||||
files = (
|
||||
);
|
||||
inputFileListPaths = (
|
||||
"${PODS_ROOT}/Target Support Files/Pods-Runner/Pods-Runner-resources-${CONFIGURATION}-input-files.xcfilelist",
|
||||
);
|
||||
name = "[CP] Copy Pods Resources";
|
||||
outputFileListPaths = (
|
||||
"${PODS_ROOT}/Target Support Files/Pods-Runner/Pods-Runner-resources-${CONFIGURATION}-output-files.xcfilelist",
|
||||
);
|
||||
runOnlyForDeploymentPostprocessing = 0;
|
||||
shellPath = /bin/sh;
|
||||
shellScript = "\"${PODS_ROOT}/Target Support Files/Pods-Runner/Pods-Runner-resources.sh\"\n";
|
||||
showEnvVarsInLog = 0;
|
||||
};
|
||||
9740EEB61CF901F6004384FC /* Run Script */ = {
|
||||
isa = PBXShellScriptBuildPhase;
|
||||
alwaysOutOfDate = 1;
|
||||
|
|
|
|||
|
|
@ -3,11 +3,91 @@ import UIKit
|
|||
|
||||
@main
|
||||
@objc class AppDelegate: FlutterAppDelegate {
|
||||
|
||||
private var secureField: UITextField?
|
||||
private var secureContainer: UIView?
|
||||
|
||||
override func application(
|
||||
_ application: UIApplication,
|
||||
didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?
|
||||
) -> Bool {
|
||||
let controller = window?.rootViewController as! FlutterViewController
|
||||
let channel = FlutterMethodChannel(name: "ios_secure_screen", binaryMessenger: controller.binaryMessenger)
|
||||
|
||||
channel.setMethodCallHandler { [weak self] call, result in
|
||||
guard let self = self else { return }
|
||||
switch call.method {
|
||||
case "enable":
|
||||
self.enableSecureMode()
|
||||
result(nil)
|
||||
case "disable":
|
||||
self.disableSecureMode()
|
||||
result(nil)
|
||||
default:
|
||||
result(FlutterMethodNotImplemented)
|
||||
}
|
||||
}
|
||||
|
||||
GeneratedPluginRegistrant.register(with: self)
|
||||
return super.application(application, didFinishLaunchingWithOptions: launchOptions)
|
||||
}
|
||||
|
||||
private func enableSecureMode() {
|
||||
guard let window = self.window else { return }
|
||||
guard let flutterVC = window.rootViewController else { return }
|
||||
let flutterView = flutterVC.view!
|
||||
|
||||
if secureContainer != nil { return }
|
||||
|
||||
let field = UITextField(frame: window.bounds)
|
||||
field.isSecureTextEntry = true
|
||||
|
||||
// IMPORTANTE: precisa ficar interativo, senão mata os toques do FlutterView
|
||||
field.isUserInteractionEnabled = true
|
||||
field.isEnabled = true
|
||||
field.textColor = .clear
|
||||
field.tintColor = .clear
|
||||
field.backgroundColor = .clear
|
||||
field.borderStyle = .none
|
||||
field.autocorrectionType = .no
|
||||
field.spellCheckingType = .no
|
||||
|
||||
guard let container = field.subviews.first else { return }
|
||||
container.frame = window.bounds
|
||||
container.isUserInteractionEnabled = true
|
||||
|
||||
flutterView.removeFromSuperview()
|
||||
container.addSubview(flutterView)
|
||||
flutterView.frame = container.bounds
|
||||
flutterView.autoresizingMask = [.flexibleWidth, .flexibleHeight]
|
||||
|
||||
window.addSubview(field)
|
||||
field.frame = window.bounds
|
||||
field.autoresizingMask = [.flexibleWidth, .flexibleHeight]
|
||||
|
||||
self.secureField = field
|
||||
self.secureContainer = container
|
||||
}
|
||||
|
||||
private func disableSecureMode() {
|
||||
guard let window = self.window else { return }
|
||||
guard let flutterVC = window.rootViewController else { return }
|
||||
let flutterView = flutterVC.view!
|
||||
|
||||
// Se não está habilitado, sai
|
||||
guard let field = secureField else { return }
|
||||
|
||||
// Remove flutter do container e volta pra window
|
||||
flutterView.removeFromSuperview()
|
||||
window.insertSubview(flutterView, belowSubview: field)
|
||||
flutterView.frame = window.bounds
|
||||
flutterView.autoresizingMask = [.flexibleWidth, .flexibleHeight]
|
||||
|
||||
// Remove o campo seguro
|
||||
field.removeFromSuperview()
|
||||
|
||||
self.secureField = nil
|
||||
self.secureContainer = nil
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,8 +1,10 @@
|
|||
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
|
||||
<document type="com.apple.InterfaceBuilder3.CocoaTouch.Storyboard.XIB" version="3.0" toolsVersion="10117" systemVersion="15F34" targetRuntime="iOS.CocoaTouch" propertyAccessControl="none" useAutolayout="YES" useTraitCollections="YES" initialViewController="BYZ-38-t0r">
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<document type="com.apple.InterfaceBuilder3.CocoaTouch.Storyboard.XIB" version="3.0" toolsVersion="23727" targetRuntime="iOS.CocoaTouch" propertyAccessControl="none" useAutolayout="YES" useTraitCollections="YES" colorMatched="YES" initialViewController="BYZ-38-t0r">
|
||||
<device id="retina6_12" orientation="portrait" appearance="light"/>
|
||||
<dependencies>
|
||||
<deployment identifier="iOS"/>
|
||||
<plugIn identifier="com.apple.InterfaceBuilder.IBCocoaTouchPlugin" version="10085"/>
|
||||
<plugIn identifier="com.apple.InterfaceBuilder.IBCocoaTouchPlugin" version="23721"/>
|
||||
<capability name="documents saved in the Xcode 8 format" minToolsVersion="8.0"/>
|
||||
</dependencies>
|
||||
<scenes>
|
||||
<!--Flutter View Controller-->
|
||||
|
|
@ -14,13 +16,14 @@
|
|||
<viewControllerLayoutGuide type="bottom" id="wfy-db-euE"/>
|
||||
</layoutGuides>
|
||||
<view key="view" contentMode="scaleToFill" id="8bC-Xf-vdC">
|
||||
<rect key="frame" x="0.0" y="0.0" width="600" height="600"/>
|
||||
<rect key="frame" x="0.0" y="0.0" width="393" height="852"/>
|
||||
<autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/>
|
||||
<color key="backgroundColor" white="1" alpha="1" colorSpace="custom" customColorSpace="calibratedWhite"/>
|
||||
<color key="backgroundColor" red="1" green="1" blue="1" alpha="1" colorSpace="custom" customColorSpace="sRGB"/>
|
||||
</view>
|
||||
</viewController>
|
||||
<placeholder placeholderIdentifier="IBFirstResponder" id="dkx-z0-nzr" sceneMemberID="firstResponder"/>
|
||||
</objects>
|
||||
<point key="canvasLocation" x="-16" y="-40"/>
|
||||
</scene>
|
||||
</scenes>
|
||||
</document>
|
||||
|
|
|
|||
|
|
@ -47,5 +47,7 @@
|
|||
<true/>
|
||||
<key>NSCameraUsageDescription</key>
|
||||
<string>Usamos a câmera para ler QR Code e permitir o login seguro no Maker Book.</string>
|
||||
<key>NSMicrophoneUsageDescription</key>
|
||||
<string>O Maker Book não grava áudio. Esta permissão pode ser exibida pelo iOS por causa de bibliotecas de reprodução de áudio usadas no app.</string>
|
||||
</dict>
|
||||
</plist>
|
||||
|
|
|
|||
|
|
@ -1,22 +1,54 @@
|
|||
import 'dart:io';
|
||||
//import 'package:MakerBook/secure_screen/screenshot_guard.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter_windowmanager/flutter_windowmanager.dart';
|
||||
import 'package:screen_protector/screen_protector.dart';
|
||||
import 'pages/home_page.dart';
|
||||
|
||||
Future<void> main() async {
|
||||
WidgetsFlutterBinding.ensureInitialized();
|
||||
|
||||
// Só Android tem esse plugin
|
||||
if (Platform.isAndroid) {
|
||||
await FlutterWindowManager.addFlags(FlutterWindowManager.FLAG_SECURE);
|
||||
}
|
||||
// Android → FLAG_SECURE
|
||||
// iOS → blur no app switcher + bloqueio de screen recording
|
||||
await ScreenProtector.preventScreenshotOn();
|
||||
await ScreenProtector.protectDataLeakageOn();
|
||||
|
||||
runApp(const MyApp());
|
||||
}
|
||||
|
||||
class MyApp extends StatelessWidget {
|
||||
class MyApp extends StatefulWidget {
|
||||
const MyApp({super.key});
|
||||
|
||||
@override
|
||||
State<MyApp> createState() => _MyAppState();
|
||||
}
|
||||
|
||||
class _MyAppState extends State<MyApp> with WidgetsBindingObserver {
|
||||
@override
|
||||
void initState() {
|
||||
super.initState();
|
||||
WidgetsBinding.instance.addObserver(this);
|
||||
|
||||
}
|
||||
|
||||
@override
|
||||
void dispose() {
|
||||
WidgetsBinding.instance.removeObserver(this);
|
||||
super.dispose();
|
||||
}
|
||||
|
||||
@override
|
||||
void didChangeAppLifecycleState(AppLifecycleState state) async {
|
||||
// iOS: protege quando vai para background / app switcher
|
||||
if (state == AppLifecycleState.paused ||
|
||||
state == AppLifecycleState.inactive) {
|
||||
await ScreenProtector.protectDataLeakageOn();
|
||||
}
|
||||
|
||||
// remove blur ao voltar
|
||||
if (state == AppLifecycleState.resumed) {
|
||||
await ScreenProtector.protectDataLeakageOff();
|
||||
}
|
||||
}
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return MaterialApp(
|
||||
|
|
|
|||
|
|
@ -2,10 +2,11 @@
|
|||
import 'dart:convert';
|
||||
import 'dart:io';
|
||||
import 'dart:typed_data';
|
||||
import 'package:audioplayers/audioplayers.dart';
|
||||
//import 'package:audioplayers/audioplayers.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:google_fonts/google_fonts.dart';
|
||||
import 'package:http/http.dart' as http;
|
||||
import 'package:just_audio/just_audio.dart';
|
||||
import 'package:path_provider/path_provider.dart';
|
||||
import 'package:MakerBook/pages/epub_page.dart';
|
||||
import 'package:MakerBook/pages/qr_code_scanner.dart';
|
||||
|
|
@ -54,8 +55,11 @@ class _LoginPageState extends State<LoginPage> {
|
|||
|
||||
await baixarOuAtualizarArquivo(book_id, bookPath, ext, modifiedAt);
|
||||
|
||||
//final player = AudioPlayer();
|
||||
//await player.play(AssetSource('sounds/open_book.mp3'));
|
||||
final player = AudioPlayer();
|
||||
await player.play(AssetSource('sounds/open_book.mp3'));
|
||||
await player.setAsset('assets/sounds/open_book.mp3');
|
||||
await player.play();
|
||||
|
||||
Navigator.push(
|
||||
context,
|
||||
|
|
|
|||
|
|
@ -1,4 +1,3 @@
|
|||
import 'dart:io';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:webview_flutter/webview_flutter.dart';
|
||||
|
||||
|
|
@ -10,17 +9,27 @@ class PrivacyPage extends StatefulWidget {
|
|||
}
|
||||
|
||||
class _PrivacyPageState extends State<PrivacyPage> {
|
||||
late final WebViewController _controller;
|
||||
bool _loading = true;
|
||||
//late WebViewController _controller;
|
||||
|
||||
@override
|
||||
void initState() {
|
||||
super.initState();
|
||||
|
||||
// Necessário para Android quando usando WebView em SDKs antigos
|
||||
if (Platform.isAndroid) {
|
||||
WebView.platform = SurfaceAndroidWebView();
|
||||
}
|
||||
_controller = WebViewController()
|
||||
..setJavaScriptMode(JavaScriptMode.unrestricted)
|
||||
..setNavigationDelegate(
|
||||
NavigationDelegate(
|
||||
onPageFinished: (_) {
|
||||
setState(() => _loading = false);
|
||||
},
|
||||
onWebResourceError: (error) {
|
||||
// Opcional: se quiser mostrar algo quando falhar
|
||||
// print('WebView error: ${error.description}');
|
||||
},
|
||||
),
|
||||
)
|
||||
..loadRequest(Uri.parse('https://makereducacional.com.br/privacy.html'));
|
||||
}
|
||||
|
||||
@override
|
||||
|
|
@ -34,22 +43,9 @@ class _PrivacyPageState extends State<PrivacyPage> {
|
|||
),
|
||||
body: Stack(
|
||||
children: [
|
||||
WebView(
|
||||
initialUrl: 'https://makereducacional.com.br/privacy.html',
|
||||
javascriptMode: JavascriptMode.unrestricted,
|
||||
onWebViewCreated: (controller) {
|
||||
//_controller = controller;
|
||||
},
|
||||
onPageFinished: (_) {
|
||||
setState(() {
|
||||
_loading = false;
|
||||
});
|
||||
},
|
||||
),
|
||||
WebViewWidget(controller: _controller),
|
||||
if (_loading)
|
||||
const Center(
|
||||
child: CircularProgressIndicator(),
|
||||
),
|
||||
const Center(child: CircularProgressIndicator()),
|
||||
],
|
||||
),
|
||||
);
|
||||
|
|
|
|||
|
|
@ -1,62 +1,53 @@
|
|||
import 'package:flutter/material.dart';
|
||||
import 'package:qr_code_scanner/qr_code_scanner.dart';
|
||||
import 'package:mobile_scanner/mobile_scanner.dart';
|
||||
|
||||
class QRCodeScannerPage extends StatefulWidget {
|
||||
const QRCodeScannerPage({Key? key}) : super(key: key);
|
||||
|
||||
@override
|
||||
_QRCodeScannerPageState createState() => _QRCodeScannerPageState();
|
||||
State<QRCodeScannerPage> createState() => _QRCodeScannerPageState();
|
||||
}
|
||||
|
||||
class _QRCodeScannerPageState extends State<QRCodeScannerPage> {
|
||||
final GlobalKey qrKey = GlobalKey(debugLabel: 'QR');
|
||||
QRViewController? controller;
|
||||
|
||||
bool lido = false;
|
||||
|
||||
@override
|
||||
void dispose() {
|
||||
controller?.dispose();
|
||||
super.dispose();
|
||||
}
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return Scaffold(
|
||||
appBar: AppBar(title: const Text('Leia o QR Code')),
|
||||
body: Stack(
|
||||
children: [
|
||||
// Câmera
|
||||
QRView(
|
||||
key: qrKey,
|
||||
onQRViewCreated: _onQRViewCreated,
|
||||
// 📷 Câmera
|
||||
MobileScanner(
|
||||
fit: BoxFit.cover,
|
||||
onDetect: (capture) {
|
||||
if (lido) return;
|
||||
|
||||
final barcode = capture.barcodes.firstOrNull;
|
||||
final value = barcode?.rawValue;
|
||||
|
||||
if (value == null) return;
|
||||
|
||||
lido = true;
|
||||
debugPrint('[LOG] Leitura QRCode: $value');
|
||||
|
||||
Navigator.pop(context, value);
|
||||
},
|
||||
),
|
||||
|
||||
// Escurecimento com buraco no centro
|
||||
Positioned.fill(
|
||||
// 🕶️ Overlay com janela central
|
||||
const Positioned.fill(
|
||||
child: ScannerOverlay(),
|
||||
),
|
||||
],
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
|
||||
|
||||
void _onQRViewCreated(QRViewController controller) {
|
||||
print("[LOG] QRCode criado");
|
||||
this.controller = controller;
|
||||
controller.scannedDataStream.listen((scanData) {
|
||||
if (!lido) {
|
||||
lido = true;
|
||||
print("[LOG] Leitura QRCode: ${scanData.code}");
|
||||
Navigator.pop(context, scanData.code); // Retorna o código escaneado
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
class ScannerOverlay extends StatelessWidget {
|
||||
const ScannerOverlay({super.key});
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return LayoutBuilder(builder: (context, constraints) {
|
||||
|
|
@ -68,10 +59,10 @@ class ScannerOverlay extends StatelessWidget {
|
|||
|
||||
return Stack(
|
||||
children: [
|
||||
// Camada escura semi-transparente
|
||||
// Camada escura
|
||||
Container(color: Colors.black.withOpacity(0.5)),
|
||||
|
||||
// Janela recortada no centro
|
||||
// Janela central
|
||||
Positioned(
|
||||
left: left,
|
||||
top: top,
|
||||
|
|
|
|||
|
|
@ -0,0 +1,16 @@
|
|||
import 'dart:io';
|
||||
import 'package:flutter/services.dart';
|
||||
|
||||
class IosSecureScreen {
|
||||
static const MethodChannel _ch = MethodChannel('ios_secure_screen');
|
||||
|
||||
static Future<void> enable() async {
|
||||
if (!Platform.isIOS) return;
|
||||
await _ch.invokeMethod('enable');
|
||||
}
|
||||
|
||||
static Future<void> disable() async {
|
||||
if (!Platform.isIOS) return;
|
||||
await _ch.invokeMethod('disable');
|
||||
}
|
||||
}
|
||||
|
|
@ -1,10 +1,10 @@
|
|||
import 'dart:io';
|
||||
import 'package:flutter_windowmanager/flutter_windowmanager.dart';
|
||||
//import 'package:flutter_windowmanager/flutter_windowmanager.dart';
|
||||
|
||||
class SecureScreen {
|
||||
static Future<void> enable() async {
|
||||
if (Platform.isAndroid) {
|
||||
await FlutterWindowManager.addFlags(FlutterWindowManager.FLAG_SECURE);
|
||||
//await FlutterWindowManager.addFlags(FlutterWindowManager.FLAG_SECURE);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -7,7 +7,7 @@ import 'package:MakerBook/utils/global.dart';
|
|||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter/services.dart';
|
||||
import 'package:path_provider/path_provider.dart';
|
||||
import 'package:vocsy_epub_viewer/epub_viewer.dart';
|
||||
//import 'package:vocsy_epub_viewer/epub_viewer.dart';
|
||||
|
||||
class EpubViewerPage extends StatefulWidget {
|
||||
final int book_id;
|
||||
|
|
@ -28,14 +28,14 @@ class _EpubViewerPageState extends State<EpubViewerPage> {
|
|||
_prepareTempEpub();
|
||||
|
||||
// Registra um listener para capturar o lastLocation quando o leitor informar
|
||||
VocsyEpub.locatorStream.listen((locator) async {
|
||||
/*VocsyEpub.locatorStream.listen((locator) async {
|
||||
print('Localização atual: $locator');
|
||||
// Armazena o locator como um Map<String, dynamic> em vez de string diretamente
|
||||
lastLocation = locator;
|
||||
|
||||
await saveLastLocation(widget.book_id, lastLocation);
|
||||
Navigator.pop(context);
|
||||
});
|
||||
});*/
|
||||
|
||||
//openLocalEpub();
|
||||
}
|
||||
|
|
@ -49,7 +49,7 @@ class _EpubViewerPageState extends State<EpubViewerPage> {
|
|||
await file.writeAsBytes(data.buffer.asUint8List());
|
||||
|
||||
// Config obrigatória
|
||||
VocsyEpub.setConfig(
|
||||
/*VocsyEpub.setConfig(
|
||||
themeColor: Colors.blue,
|
||||
identifier: 'test-book',
|
||||
scrollDirection: EpubScrollDirection.VERTICAL,
|
||||
|
|
@ -57,7 +57,7 @@ class _EpubViewerPageState extends State<EpubViewerPage> {
|
|||
enableTts: false,
|
||||
);
|
||||
|
||||
VocsyEpub.open(path);
|
||||
VocsyEpub.open(path);*/
|
||||
}
|
||||
|
||||
@override
|
||||
|
|
@ -84,19 +84,19 @@ class _EpubViewerPageState extends State<EpubViewerPage> {
|
|||
lastLocation = await getLastLocation(widget.book_id);
|
||||
|
||||
// Configuração do leitor
|
||||
VocsyEpub.setConfig(
|
||||
/*VocsyEpub.setConfig(
|
||||
themeColor: Theme.of(context).primaryColor,
|
||||
identifier: widget.book_id.toString(),
|
||||
scrollDirection: EpubScrollDirection.ALLDIRECTIONS,
|
||||
allowSharing: false,
|
||||
enableTts: false,
|
||||
);
|
||||
);*/
|
||||
|
||||
// Abrindo o arquivo EPUB e usando o lastLocation se estiver disponível
|
||||
VocsyEpub.open(
|
||||
/*VocsyEpub.open(
|
||||
tempFile.path,
|
||||
lastLocation: lastLocation != null ? EpubLocator.fromJson(jsonDecode(lastLocation!)) : null,
|
||||
);
|
||||
);*/
|
||||
}
|
||||
|
||||
|
||||
|
|
|
|||
|
|
@ -1,120 +0,0 @@
|
|||
import 'dart:convert';
|
||||
import 'dart:io';
|
||||
|
||||
import 'package:MakerBook/utils/encrypt.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:path_provider/path_provider.dart';
|
||||
import 'package:vocsy_epub_viewer/epub_viewer.dart';
|
||||
|
||||
class EpubViewer2Page extends StatefulWidget {
|
||||
final int book_id;
|
||||
|
||||
const EpubViewer2Page({super.key, required this.book_id});
|
||||
|
||||
@override
|
||||
_EpubViewer2PageState createState() => _EpubViewer2PageState();
|
||||
}
|
||||
|
||||
class _EpubViewer2PageState extends State<EpubViewer2Page> {
|
||||
|
||||
String? lastLocation;
|
||||
String? epubPath;
|
||||
|
||||
@override
|
||||
void initState() {
|
||||
super.initState();
|
||||
_prepareTempEpub();
|
||||
|
||||
//_initializeEpubController();
|
||||
|
||||
// Registra um listener para capturar o lastLocation quando o leitor informar
|
||||
VocsyEpub.locatorStream.listen((locator) async {
|
||||
print('Localização atual: $locator');
|
||||
// Armazena o locator como um Map<String, dynamic> em vez de string diretamente
|
||||
lastLocation = locator;
|
||||
|
||||
await _saveLastLocation();
|
||||
});
|
||||
}
|
||||
|
||||
@override
|
||||
void dispose() {
|
||||
super.dispose();
|
||||
}
|
||||
|
||||
// Função para salvar a última posição de leitura
|
||||
Future<void> _saveLastLocation() async {
|
||||
if (lastLocation == null) return;
|
||||
|
||||
final directory = await getApplicationDocumentsDirectory();
|
||||
final file = File('${directory.path}/${widget.book_id}.epub');
|
||||
|
||||
// Recarrega o conteúdo protegido atual, atualiza o lastLocation, e salva novamente
|
||||
String protectedContent = await file.readAsString();
|
||||
Map<String, dynamic> fileData = unprotectFile(protectedContent);
|
||||
String updatedProtectedContent = await protectFile(fileData['epubBytes'], lastLocation!);
|
||||
await file.writeAsString(updatedProtectedContent);
|
||||
|
||||
Navigator.pop(context);
|
||||
}
|
||||
|
||||
// Prepara o arquivo temporário para o leitor EPUB
|
||||
Future<void> _prepareTempEpub_bkp() async {
|
||||
final directoryReal = await getApplicationDocumentsDirectory();
|
||||
final file = File('${directoryReal.path}/${widget.book_id}.epub');
|
||||
|
||||
final directory = await getTemporaryDirectory();
|
||||
final tempFile = File('${directory.path}/${widget.book_id}_temp.epub');
|
||||
|
||||
// Lê o conteúdo protegido e remove a proteção
|
||||
String protectedContent = await file.readAsString();
|
||||
Map<String, dynamic> fileData = unprotectFile(protectedContent);
|
||||
List<int> unprotectedBytes = fileData['epubBytes'];
|
||||
lastLocation = fileData['lastLocation']; // Recupera o lastLocation
|
||||
|
||||
// Salva o arquivo descriptografado no diretório temporário
|
||||
await tempFile.writeAsBytes(unprotectedBytes);
|
||||
|
||||
// Configuração do leitor
|
||||
VocsyEpub.setConfig(
|
||||
themeColor: Theme.of(context).primaryColor,
|
||||
identifier: widget.book_id.toString(),
|
||||
scrollDirection: EpubScrollDirection.ALLDIRECTIONS,
|
||||
allowSharing: false,
|
||||
enableTts: false,
|
||||
);
|
||||
|
||||
// Abrindo o arquivo EPUB e usando o lastLocation se estiver disponível
|
||||
VocsyEpub.open(
|
||||
tempFile.path,
|
||||
lastLocation: lastLocation != null ? EpubLocator.fromJson(jsonDecode(lastLocation!)) : null,
|
||||
);
|
||||
}
|
||||
|
||||
// Prepara o arquivo temporário para o leitor EPUB
|
||||
Future<void> _prepareTempEpub() async {
|
||||
final directoryReal = await getApplicationDocumentsDirectory();
|
||||
final file = File('${directoryReal.path}/${widget.book_id}.epub');
|
||||
|
||||
// Configuração do leitor
|
||||
VocsyEpub.setConfig(
|
||||
themeColor: Theme.of(context).primaryColor,
|
||||
identifier: widget.book_id.toString(),
|
||||
scrollDirection: EpubScrollDirection.ALLDIRECTIONS,
|
||||
allowSharing: false,
|
||||
enableTts: false,
|
||||
);
|
||||
|
||||
// Abrindo o arquivo EPUB e usando o lastLocation se estiver disponível
|
||||
VocsyEpub.open(
|
||||
file.path,
|
||||
lastLocation: lastLocation != null ? EpubLocator.fromJson(jsonDecode(lastLocation!)) : null,
|
||||
);
|
||||
}
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return const Center(child: CircularProgressIndicator());
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -1,6 +1,7 @@
|
|||
// widgets/pdf_viewer.dart
|
||||
import 'dart:convert';
|
||||
|
||||
import 'package:MakerBook/secure_screen/ios_secure_screen.dart';
|
||||
import 'package:MakerBook/utils/global.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter_pdfview/flutter_pdfview.dart';
|
||||
|
|
@ -34,6 +35,7 @@ class _PdfViewerState extends State<PdfViewer> {
|
|||
@override
|
||||
void initState() {
|
||||
super.initState();
|
||||
IosSecureScreen.enable(); // <- liga a “tela segura” no iOS
|
||||
_loadPdf();
|
||||
_verificaSeMostraTutorial();
|
||||
}
|
||||
|
|
@ -41,6 +43,7 @@ class _PdfViewerState extends State<PdfViewer> {
|
|||
@override
|
||||
void dispose() {
|
||||
_saveLastPage();
|
||||
IosSecureScreen.enable(); // <- liga a “tela segura” no iOS
|
||||
super.dispose();
|
||||
}
|
||||
|
||||
|
|
@ -380,7 +383,8 @@ class _PdfViewerState extends State<PdfViewer> {
|
|||
|
||||
void _verificaSeMostraTutorial() async {
|
||||
final prefs = await SharedPreferences.getInstance();
|
||||
bool jaViu = true; // prefs.getBool('tutorialPdfVisto') ?? false;
|
||||
bool jaViu = prefs.getBool('tutorialPdfVisto') ?? false;
|
||||
//bool jaViu = false;
|
||||
|
||||
setState(() {
|
||||
setaTutorial = !jaViu;
|
||||
|
|
|
|||
|
|
@ -8,28 +8,40 @@ class WebViewer extends StatefulWidget {
|
|||
const WebViewer({super.key, required this.book_id});
|
||||
|
||||
@override
|
||||
_WebViewerState createState() => _WebViewerState();
|
||||
State<WebViewer> createState() => _WebViewerState();
|
||||
}
|
||||
|
||||
class _WebViewerState extends State<WebViewer> {
|
||||
late WebViewController _controller;
|
||||
late final WebViewController _controller;
|
||||
bool _loading = true;
|
||||
|
||||
final String baseUrl = "http://192.168.99.115:8080/#fmt=EPUB&mode=read_book&book_id=";
|
||||
final String baseUrl =
|
||||
"http://192.168.99.115:8080/#fmt=EPUB&mode=read_book&book_id=";
|
||||
|
||||
@override
|
||||
void initState() {
|
||||
super.initState();
|
||||
|
||||
if (Platform.isAndroid) {
|
||||
WebView.platform = SurfaceAndroidWebView();
|
||||
}
|
||||
final fullUrl = baseUrl + widget.book_id;
|
||||
|
||||
// Controller novo (API 4.x)
|
||||
_controller = WebViewController()
|
||||
..setJavaScriptMode(JavaScriptMode.unrestricted)
|
||||
..setUserAgent("Mozilla/5.0 (Mobile; Flutter)")
|
||||
..setNavigationDelegate(
|
||||
NavigationDelegate(
|
||||
onPageFinished: (_) {
|
||||
setState(() {
|
||||
_loading = false;
|
||||
});
|
||||
},
|
||||
),
|
||||
)
|
||||
..loadRequest(Uri.parse(fullUrl));
|
||||
}
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
final fullUrl = baseUrl + widget.book_id;
|
||||
|
||||
return Scaffold(
|
||||
appBar: AppBar(
|
||||
title: const Text("Leitor Online"),
|
||||
|
|
@ -38,21 +50,11 @@ class _WebViewerState extends State<WebViewer> {
|
|||
),
|
||||
body: Stack(
|
||||
children: [
|
||||
WebView(
|
||||
initialUrl: fullUrl,
|
||||
javascriptMode: JavascriptMode.unrestricted,
|
||||
userAgent: "Mozilla/5.0 (Mobile; Flutter)",
|
||||
onWebViewCreated: (controller) {
|
||||
_controller = controller;
|
||||
},
|
||||
onPageFinished: (_) {
|
||||
setState(() {
|
||||
_loading = false;
|
||||
});
|
||||
},
|
||||
),
|
||||
WebViewWidget(controller: _controller),
|
||||
if (_loading)
|
||||
const Center(child: CircularProgressIndicator()),
|
||||
const Center(
|
||||
child: CircularProgressIndicator(),
|
||||
),
|
||||
],
|
||||
),
|
||||
);
|
||||
|
|
|
|||
|
|
@ -6,10 +6,6 @@
|
|||
|
||||
#include "generated_plugin_registrant.h"
|
||||
|
||||
#include <audioplayers_linux/audioplayers_linux_plugin.h>
|
||||
|
||||
void fl_register_plugins(FlPluginRegistry* registry) {
|
||||
g_autoptr(FlPluginRegistrar) audioplayers_linux_registrar =
|
||||
fl_plugin_registry_get_registrar_for_plugin(registry, "AudioplayersLinuxPlugin");
|
||||
audioplayers_linux_plugin_register_with_registrar(audioplayers_linux_registrar);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -3,7 +3,6 @@
|
|||
#
|
||||
|
||||
list(APPEND FLUTTER_PLUGIN_LIST
|
||||
audioplayers_linux
|
||||
)
|
||||
|
||||
list(APPEND FLUTTER_FFI_PLUGIN_LIST
|
||||
|
|
|
|||
|
|
@ -5,12 +5,18 @@
|
|||
import FlutterMacOS
|
||||
import Foundation
|
||||
|
||||
import audioplayers_darwin
|
||||
import audio_session
|
||||
import just_audio
|
||||
import mobile_scanner
|
||||
import path_provider_foundation
|
||||
import shared_preferences_foundation
|
||||
import webview_flutter_wkwebview
|
||||
|
||||
func RegisterGeneratedPlugins(registry: FlutterPluginRegistry) {
|
||||
AudioplayersDarwinPlugin.register(with: registry.registrar(forPlugin: "AudioplayersDarwinPlugin"))
|
||||
AudioSessionPlugin.register(with: registry.registrar(forPlugin: "AudioSessionPlugin"))
|
||||
JustAudioPlugin.register(with: registry.registrar(forPlugin: "JustAudioPlugin"))
|
||||
MobileScannerPlugin.register(with: registry.registrar(forPlugin: "MobileScannerPlugin"))
|
||||
PathProviderPlugin.register(with: registry.registrar(forPlugin: "PathProviderPlugin"))
|
||||
SharedPreferencesPlugin.register(with: registry.registrar(forPlugin: "SharedPreferencesPlugin"))
|
||||
WebViewFlutterPlugin.register(with: registry.registrar(forPlugin: "WebViewFlutterPlugin"))
|
||||
}
|
||||
|
|
|
|||
|
|
@ -16,7 +16,7 @@ publish_to: 'none' # Remove this line if you wish to publish to pub.dev
|
|||
# https://developer.apple.com/library/archive/documentation/General/Reference/InfoPlistKeyReference/Articles/CoreFoundationKeys.html
|
||||
# In Windows, build-name is used as the major, minor, and patch parts
|
||||
# of the product and file versions while build-number is used as the build suffix.
|
||||
version: 1.0.3+14
|
||||
version: 1.0.4+18
|
||||
|
||||
environment:
|
||||
sdk: '>=3.4.3 <4.0.0'
|
||||
|
|
@ -32,18 +32,22 @@ dependencies:
|
|||
sdk: flutter
|
||||
flutter_pdfview: ^1.0.4
|
||||
http: ^1.4.0
|
||||
flutter_windowmanager: ^0.2.0
|
||||
qr_code_scanner: ^1.0.0
|
||||
vocsy_epub_viewer: ^3.0.0
|
||||
#flutter_windowmanager: ^0.2.0
|
||||
#qr_code_scanner: ^1.0.1
|
||||
mobile_scanner: ^6.0.2
|
||||
#vocsy_epub_viewer: ^3.0.0
|
||||
encrypt: ^5.0.0
|
||||
epub_view: ^3.2.0
|
||||
webview_flutter: ^3.0.4
|
||||
flutter_svg: ^1.1.6
|
||||
webview_flutter: ^4.13.0
|
||||
flutter_svg: ^2.2.3
|
||||
flutter_launcher_icons: ^0.11.0
|
||||
google_fonts: ^4.0.4
|
||||
audioplayers: ^5.2.1
|
||||
google_fonts: ^6.3.3
|
||||
#audioplayers: ^6.5.1
|
||||
just_audio: ^0.9.38
|
||||
tutorial_coach_mark: ^1.3.2
|
||||
shared_preferences: ^2.2.0
|
||||
screen_protector: ^1.4.2
|
||||
#screenshot_callback: ^3.0.0
|
||||
|
||||
|
||||
# The following adds the Cupertino Icons font to your application.
|
||||
|
|
@ -66,7 +70,7 @@ dev_dependencies:
|
|||
# activated in the `analysis_options.yaml` file located at the root of your
|
||||
# package. See that file for information about deactivating specific lint
|
||||
# rules and activating additional ones.
|
||||
flutter_lints: ^3.0.0
|
||||
flutter_lints: ^6.0.0
|
||||
|
||||
# For information on the generic Dart part of this file, see the
|
||||
# following page: https://dart.dev/tools/pub/pubspec
|
||||
|
|
|
|||
|
|
@ -6,9 +6,6 @@
|
|||
|
||||
#include "generated_plugin_registrant.h"
|
||||
|
||||
#include <audioplayers_windows/audioplayers_windows_plugin.h>
|
||||
|
||||
void RegisterPlugins(flutter::PluginRegistry* registry) {
|
||||
AudioplayersWindowsPluginRegisterWithRegistrar(
|
||||
registry->GetRegistrarForPlugin("AudioplayersWindowsPlugin"));
|
||||
}
|
||||
|
|
|
|||
|
|
@ -3,7 +3,6 @@
|
|||
#
|
||||
|
||||
list(APPEND FLUTTER_PLUGIN_LIST
|
||||
audioplayers_windows
|
||||
)
|
||||
|
||||
list(APPEND FLUTTER_FFI_PLUGIN_LIST
|
||||
|
|
|
|||
Loading…
Reference in New Issue