Added ads
This commit is contained in:
parent
eed999d2ab
commit
7c47298cd7
|
|
@ -1,3 +1,4 @@
|
|||
{
|
||||
"cmake.ignoreCMakeListsMissing": true
|
||||
"cmake.ignoreCMakeListsMissing": true,
|
||||
"java.configuration.updateBuildConfiguration": "disabled"
|
||||
}
|
||||
|
|
@ -1,7 +1,8 @@
|
|||
<manifest xmlns:android="http://schemas.android.com/apk/res/android">
|
||||
<uses-permission android:name="android.permission.INTERNET" />
|
||||
<uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />
|
||||
|
||||
<uses-permission android:name="android.permission.USE_BIOMETRIC"/>
|
||||
|
||||
<application
|
||||
android:label="QuickSSH"
|
||||
android:name="${applicationName}"
|
||||
|
|
@ -33,6 +34,9 @@
|
|||
<meta-data
|
||||
android:name="flutterEmbedding"
|
||||
android:value="2" />
|
||||
<meta-data
|
||||
android:name="com.google.android.gms.ads.APPLICATION_ID"
|
||||
android:value="ca-app-pub-2626773788355001~4397436505"/>
|
||||
</application>
|
||||
<!-- Required to query activities that can process text, see:
|
||||
https://developer.android.com/training/package-visibility and
|
||||
|
|
|
|||
|
|
@ -1,5 +1,6 @@
|
|||
package com.example.quick_ssh
|
||||
|
||||
import io.flutter.embedding.android.FlutterActivity
|
||||
import io.flutter.embedding.android.FlutterFragmentActivity
|
||||
|
||||
class MainActivity : FlutterActivity()
|
||||
class MainActivity : FlutterFragmentActivity(){
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,18 +1,10 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<resources>
|
||||
<!-- Theme applied to the Android Window while the process is starting when the OS's Dark Mode setting is off -->
|
||||
<style name="LaunchTheme" parent="@android:style/Theme.Light.NoTitleBar">
|
||||
<!-- Show a splash screen on the activity. Automatically removed when
|
||||
the Flutter engine draws its first frame -->
|
||||
<style name="LaunchTheme" parent="Theme.AppCompat.Light.NoActionBar">
|
||||
<item name="android:windowBackground">@drawable/launch_background</item>
|
||||
</style>
|
||||
<!-- Theme applied to the Android Window as soon as the process has started.
|
||||
This theme determines the color of the Android Window while your
|
||||
Flutter UI initializes, as well as behind your Flutter UI while its
|
||||
running.
|
||||
|
||||
This Theme is only used starting with V2 of Flutter's Android embedding. -->
|
||||
<style name="NormalTheme" parent="@android:style/Theme.Light.NoTitleBar">
|
||||
<style name="NormalTheme" parent="Theme.AppCompat.Light.NoActionBar">
|
||||
<item name="android:windowBackground">?android:colorBackground</item>
|
||||
</style>
|
||||
</resources>
|
||||
</resources>
|
||||
|
|
@ -45,5 +45,8 @@
|
|||
<true/>
|
||||
<key>UIApplicationSupportsIndirectInputEvents</key>
|
||||
<true/>
|
||||
<key>NSFaceIDUsageDescription</key>
|
||||
<string>QuickSSH needs FaceID to protect your terminal access.</string>
|
||||
</dict>
|
||||
|
||||
</plist>
|
||||
|
|
|
|||
|
|
@ -7,7 +7,13 @@ import 'package:QuickSSH/services/theme_controller.dart';
|
|||
final themeController = ThemeController();
|
||||
final settingsController = SettingsController();
|
||||
|
||||
void main() {
|
||||
final GlobalKey<NavigatorState> navigatorKey = GlobalKey<NavigatorState>();
|
||||
|
||||
void main() async {
|
||||
WidgetsFlutterBinding.ensureInitialized();
|
||||
|
||||
await settingsController.initializationFuture;
|
||||
|
||||
runApp(MyApp());
|
||||
}
|
||||
|
||||
|
|
@ -22,6 +28,8 @@ class MyApp extends StatelessWidget {
|
|||
return MaterialApp(
|
||||
title: 'Quick SSH',
|
||||
|
||||
navigatorKey: navigatorKey,
|
||||
|
||||
theme: MyThemes.lightTheme,
|
||||
darkTheme: MyThemes.darkTheme,
|
||||
themeMode: themeController.themeMode,
|
||||
|
|
|
|||
|
|
@ -5,6 +5,9 @@ import 'add_client.dart';
|
|||
import 'package:QuickSSH/classes/ServerCommand.dart';
|
||||
import 'package:QuickSSH/services/storage_service.dart';
|
||||
import 'package:QuickSSH/screens/settings.dart';
|
||||
import 'package:flutter/services.dart';
|
||||
import 'package:QuickSSH/main.dart';
|
||||
import 'package:QuickSSH/widgets/ad_banner.dart';
|
||||
|
||||
class HomeScreen extends StatefulWidget {
|
||||
const HomeScreen({super.key});
|
||||
|
|
@ -20,6 +23,13 @@ class _HomeScreenState extends State<HomeScreen> {
|
|||
void initState() {
|
||||
super.initState();
|
||||
_loadData();
|
||||
WidgetsBinding.instance.addPostFrameCallback((_) async {
|
||||
bool allowed = await settingsController.checkSecurity();
|
||||
|
||||
if (!allowed) {
|
||||
SystemNavigator.pop();
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
void _loadData() async {
|
||||
|
|
@ -106,6 +116,9 @@ class _HomeScreenState extends State<HomeScreen> {
|
|||
);
|
||||
},
|
||||
),
|
||||
bottomNavigationBar: SafeArea(
|
||||
child: MyBannerAd(isEnabled: settingsController.adsEnabled),
|
||||
),
|
||||
backgroundColor: theme.surface,
|
||||
);
|
||||
}
|
||||
|
|
@ -151,7 +164,9 @@ class _HomeScreenState extends State<HomeScreen> {
|
|||
_openSettings() {
|
||||
Navigator.push(
|
||||
context,
|
||||
MaterialPageRoute(builder: (context) => const Settings()),
|
||||
);
|
||||
MaterialPageRoute(builder: (context) => Settings()),
|
||||
).then((_) {
|
||||
setState(() {});
|
||||
});
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -115,6 +115,64 @@ class _SettingsState extends State<Settings> {
|
|||
),
|
||||
],
|
||||
),
|
||||
SizedBox(height: 20),
|
||||
Row(
|
||||
children: [
|
||||
Expanded(
|
||||
child: Text(
|
||||
"Biometric Unlock",
|
||||
style: TextStyle(
|
||||
fontWeight: FontWeight.bold,
|
||||
fontSize: 20,
|
||||
color: theme.onSurface,
|
||||
),
|
||||
),
|
||||
),
|
||||
Expanded(
|
||||
child: Switch(
|
||||
activeTrackColor: theme.primary,
|
||||
activeThumbColor: theme.onPrimary,
|
||||
inactiveThumbColor: theme.onSurface,
|
||||
inactiveTrackColor: theme.surface,
|
||||
value: settingsController.biometricEnabled,
|
||||
onChanged: (bool value) {
|
||||
settingsController.toggleBiometric(value);
|
||||
if (value) HapticFeedback.mediumImpact();
|
||||
setState(() {});
|
||||
},
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
SizedBox(height: 20),
|
||||
Row(
|
||||
children: [
|
||||
Expanded(
|
||||
child: Text(
|
||||
"Enable Ads",
|
||||
style: TextStyle(
|
||||
fontWeight: FontWeight.bold,
|
||||
fontSize: 20,
|
||||
color: theme.onSurface,
|
||||
),
|
||||
),
|
||||
),
|
||||
Expanded(
|
||||
child: Switch(
|
||||
activeTrackColor: theme.primary,
|
||||
activeThumbColor: theme.onPrimary,
|
||||
inactiveThumbColor: theme.onSurface,
|
||||
inactiveTrackColor: theme.surface,
|
||||
value: settingsController.adsEnabled,
|
||||
onChanged: (bool value) {
|
||||
settingsController.toggleAds(value);
|
||||
if (value) HapticFeedback.mediumImpact();
|
||||
setState(() {});
|
||||
},
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
|
|
|
|||
|
|
@ -1,25 +1,71 @@
|
|||
import 'package:flutter/material.dart';
|
||||
import 'package:shared_preferences/shared_preferences.dart';
|
||||
import 'package:local_auth/local_auth.dart';
|
||||
|
||||
class SettingsController extends ChangeNotifier {
|
||||
bool _vibrationEnabled = true;
|
||||
bool get vibrationEnabled => _vibrationEnabled;
|
||||
|
||||
bool _biometricEnabled = false;
|
||||
bool get biometricEnabled => _biometricEnabled;
|
||||
|
||||
bool _adsEnabled = true;
|
||||
bool get adsEnabled => _adsEnabled;
|
||||
|
||||
final LocalAuthentication auth = LocalAuthentication();
|
||||
|
||||
late Future<void> initializationFuture;
|
||||
|
||||
SettingsController() {
|
||||
_loadSettings();
|
||||
initializationFuture = _loadSettings();
|
||||
}
|
||||
|
||||
Future<void> _loadSettings() async {
|
||||
final prefs = await SharedPreferences.getInstance();
|
||||
_vibrationEnabled = prefs.getBool('vibration_enabled') ?? true;
|
||||
_biometricEnabled = prefs.getBool('biometric_enabled') ?? false;
|
||||
_adsEnabled = prefs.getBool('ads_enabled') ?? true;
|
||||
notifyListeners();
|
||||
}
|
||||
|
||||
Future<void> toggleVibration(bool value) async {
|
||||
print("Toggling vibration to: $value");
|
||||
_vibrationEnabled = value;
|
||||
final prefs = await SharedPreferences.getInstance();
|
||||
await prefs.setBool('vibration_enabled', value);
|
||||
notifyListeners();
|
||||
}
|
||||
|
||||
Future<void> toggleBiometric(bool value) async {
|
||||
_biometricEnabled = value;
|
||||
final prefs = await SharedPreferences.getInstance();
|
||||
await prefs.setBool('biometric_enabled', value);
|
||||
notifyListeners();
|
||||
}
|
||||
|
||||
Future<void> toggleAds(bool value) async {
|
||||
_adsEnabled = value;
|
||||
final prefs = await SharedPreferences.getInstance();
|
||||
await prefs.setBool('ads_enabled', value);
|
||||
notifyListeners();
|
||||
}
|
||||
|
||||
Future<bool> checkSecurity() async {
|
||||
await initializationFuture;
|
||||
|
||||
if (!_biometricEnabled) return true;
|
||||
|
||||
bool canCheck = await auth.canCheckBiometrics;
|
||||
bool isSupported = await auth.isDeviceSupported();
|
||||
|
||||
if (!canCheck && !isSupported) return true;
|
||||
|
||||
try {
|
||||
return await auth.authenticate(
|
||||
localizedReason: 'Unlock QuickSSH',
|
||||
biometricOnly: false,
|
||||
);
|
||||
} catch (e) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -0,0 +1,58 @@
|
|||
import 'package:google_mobile_ads/google_mobile_ads.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
|
||||
class MyBannerAd extends StatefulWidget {
|
||||
final bool isEnabled;
|
||||
const MyBannerAd({super.key, required this.isEnabled});
|
||||
|
||||
@override
|
||||
State<MyBannerAd> createState() => _MyBannerAdState();
|
||||
}
|
||||
|
||||
class _MyBannerAdState extends State<MyBannerAd> {
|
||||
BannerAd? _bannerAd;
|
||||
bool _isLoaded = false;
|
||||
bool _isInitializing = false;
|
||||
|
||||
@override
|
||||
void initState() {
|
||||
super.initState();
|
||||
if (widget.isEnabled) {
|
||||
_loadAd();
|
||||
}
|
||||
}
|
||||
|
||||
void _loadAd() async {
|
||||
_bannerAd = BannerAd(
|
||||
adUnitId: 'ca-app-pub-2626773788355001/7557216229',
|
||||
request: const AdRequest(),
|
||||
size: AdSize.banner,
|
||||
listener: BannerAdListener(
|
||||
onAdLoaded: (ad) => setState(() => _isLoaded = true),
|
||||
onAdFailedToLoad: (ad, error) {
|
||||
ad.dispose();
|
||||
print('Ad failed to load: $error');
|
||||
},
|
||||
),
|
||||
)..load();
|
||||
}
|
||||
|
||||
@override
|
||||
void dispose() {
|
||||
_bannerAd?.dispose();
|
||||
super.dispose();
|
||||
}
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
if (!widget.isEnabled || !_isLoaded || _bannerAd == null) {
|
||||
return const SizedBox.shrink();
|
||||
}
|
||||
return Container(
|
||||
alignment: Alignment.center,
|
||||
width: double.infinity,
|
||||
height: _bannerAd!.size.height.toDouble(),
|
||||
child: AdWidget(ad: _bannerAd!),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
|
@ -9,10 +9,12 @@ import flutter_secure_storage_macos
|
|||
import local_auth_darwin
|
||||
import path_provider_foundation
|
||||
import shared_preferences_foundation
|
||||
import webview_flutter_wkwebview
|
||||
|
||||
func RegisterGeneratedPlugins(registry: FlutterPluginRegistry) {
|
||||
FlutterSecureStoragePlugin.register(with: registry.registrar(forPlugin: "FlutterSecureStoragePlugin"))
|
||||
LocalAuthPlugin.register(with: registry.registrar(forPlugin: "LocalAuthPlugin"))
|
||||
PathProviderPlugin.register(with: registry.registrar(forPlugin: "PathProviderPlugin"))
|
||||
SharedPreferencesPlugin.register(with: registry.registrar(forPlugin: "SharedPreferencesPlugin"))
|
||||
WebViewFlutterPlugin.register(with: registry.registrar(forPlugin: "WebViewFlutterPlugin"))
|
||||
}
|
||||
|
|
|
|||
56
pubspec.lock
56
pubspec.lock
|
|
@ -224,6 +224,14 @@ packages:
|
|||
description: flutter
|
||||
source: sdk
|
||||
version: "0.0.0"
|
||||
google_mobile_ads:
|
||||
dependency: "direct main"
|
||||
description:
|
||||
name: google_mobile_ads
|
||||
sha256: "0d4a3744b5e8ed1b8be6a1b452d309f811688855a497c6113fc4400f922db603"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "5.3.1"
|
||||
image:
|
||||
dependency: transitive
|
||||
description:
|
||||
|
|
@ -292,26 +300,26 @@ packages:
|
|||
dependency: "direct main"
|
||||
description:
|
||||
name: local_auth
|
||||
sha256: "434d854cf478f17f12ab29a76a02b3067f86a63a6d6c4eb8fbfdcfe4879c1b7b"
|
||||
sha256: a4f1bf57f0236a4aeb5e8f0ec180e197f4b112a3456baa6c1e73b546630b0422
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "2.3.0"
|
||||
version: "3.0.0"
|
||||
local_auth_android:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: local_auth_android
|
||||
sha256: a0bdfcc0607050a26ef5b31d6b4b254581c3d3ce3c1816ab4d4f4a9173e84467
|
||||
sha256: "162b8e177fd9978c4620da2a8002a5c6bed4d20f0c6daf5137e72e9a8b767d2e"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "1.0.56"
|
||||
version: "2.0.4"
|
||||
local_auth_darwin:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: local_auth_darwin
|
||||
sha256: "699873970067a40ef2f2c09b4c72eb1cfef64224ef041b3df9fdc5c4c1f91f49"
|
||||
sha256: "668ea65edaab17380956e9713f57e34f78ede505ca0cfd8d39db34e2f260bfee"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "1.6.1"
|
||||
version: "2.0.1"
|
||||
local_auth_platform_interface:
|
||||
dependency: transitive
|
||||
description:
|
||||
|
|
@ -324,10 +332,10 @@ packages:
|
|||
dependency: transitive
|
||||
description:
|
||||
name: local_auth_windows
|
||||
sha256: bc4e66a29b0fdf751aafbec923b5bed7ad6ed3614875d8151afe2578520b2ab5
|
||||
sha256: be12c5b8ba5e64896983123655c5f67d2484ecfcc95e367952ad6e3bff94cb16
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "1.0.11"
|
||||
version: "2.0.1"
|
||||
matcher:
|
||||
dependency: transitive
|
||||
description:
|
||||
|
|
@ -597,6 +605,38 @@ packages:
|
|||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "1.1.1"
|
||||
webview_flutter:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: webview_flutter
|
||||
sha256: a3da219916aba44947d3a5478b1927876a09781174b5a2b67fa5be0555154bf9
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "4.13.1"
|
||||
webview_flutter_android:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: webview_flutter_android
|
||||
sha256: eeeb3fcd5f0ff9f8446c9f4bbc18a99b809e40297528a3395597d03aafb9f510
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "4.10.11"
|
||||
webview_flutter_platform_interface:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: webview_flutter_platform_interface
|
||||
sha256: "63d26ee3aca7256a83ccb576a50272edd7cfc80573a4305caa98985feb493ee0"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "2.14.0"
|
||||
webview_flutter_wkwebview:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: webview_flutter_wkwebview
|
||||
sha256: "0412b657a2828fb301e73509909e6ec02b77cd2b441ae9f77125d482b3ddf0e7"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "3.23.6"
|
||||
win32:
|
||||
dependency: transitive
|
||||
description:
|
||||
|
|
|
|||
|
|
@ -33,7 +33,8 @@ dependencies:
|
|||
shared_preferences: ^2.5.0
|
||||
dartssh2: ^2.0.0
|
||||
flutter_secure_storage: ^9.2.2
|
||||
local_auth: ^2.3.0
|
||||
local_auth: ^3.0.0
|
||||
google_mobile_ads: ^5.1.0
|
||||
|
||||
# The following adds the Cupertino Icons font to your application.
|
||||
# Use with the CupertinoIcons class for iOS style icons.
|
||||
|
|
|
|||
Loading…
Reference in New Issue