diff --git a/mih_ui/lib/mih_package_components/Example/package_tools/package_tool_one.dart b/mih_ui/lib/mih_package_components/Example/package_tools/package_tool_one.dart index 47364ef8..95de0348 100644 --- a/mih_ui/lib/mih_package_components/Example/package_tools/package_tool_one.dart +++ b/mih_ui/lib/mih_package_components/Example/package_tools/package_tool_one.dart @@ -1,3 +1,5 @@ +import 'dart:io'; + import 'package:country_code_picker/country_code_picker.dart'; import 'package:file_picker/file_picker.dart'; import 'package:flutter/material.dart'; @@ -451,7 +453,7 @@ class _PackageToolOneState extends State { ), ], ), - MihBannerAd(), + if (Platform.isAndroid || Platform.isIOS) MihBannerAd(), const SizedBox(height: 10), Divider( color: MihColors.getSecondaryColor( diff --git a/mih_ui/lib/mih_package_components/mih_package_tile.dart b/mih_ui/lib/mih_package_components/mih_package_tile.dart index 5496841d..e5d25f35 100644 --- a/mih_ui/lib/mih_package_components/mih_package_tile.dart +++ b/mih_ui/lib/mih_package_components/mih_package_tile.dart @@ -1,3 +1,5 @@ +import 'dart:io'; + import 'package:app_settings/app_settings.dart'; import 'package:flutter/foundation.dart'; import 'package:local_auth/local_auth.dart'; @@ -155,7 +157,8 @@ class _MihPackageTileState extends State { Future authenticateUser() async { if (widget.authenticateUser != null && widget.authenticateUser! && - !kIsWeb) { + !kIsWeb && + !Platform.isLinux) { if (await isUserAuthenticated()) { widget.onTap(); } diff --git a/mih_ui/lib/mih_packages/calculator/package_tools/currency_exchange_rate.dart b/mih_ui/lib/mih_packages/calculator/package_tools/currency_exchange_rate.dart index c9ed4c87..794765ff 100644 --- a/mih_ui/lib/mih_packages/calculator/package_tools/currency_exchange_rate.dart +++ b/mih_ui/lib/mih_packages/calculator/package_tools/currency_exchange_rate.dart @@ -1,3 +1,5 @@ +import 'dart:io'; + import 'package:flutter/gestures.dart'; import 'package:flutter/material.dart'; import 'package:mzansi_innovation_hub/main.dart'; @@ -157,7 +159,11 @@ class _CurrencyExchangeRateState extends State { ), SizedBox(height: 10), Consumer(builder: (context, bannerAdDisplay, child) { - return MihBannerAd(); + if (Platform.isAndroid || Platform.isIOS) { + return MihBannerAd(); + } else { + return const SizedBox(height: 0); + } }), ], ), diff --git a/mih_ui/lib/mih_packages/calculator/package_tools/tip_calc.dart b/mih_ui/lib/mih_packages/calculator/package_tools/tip_calc.dart index a8d9a572..72ed50a6 100644 --- a/mih_ui/lib/mih_packages/calculator/package_tools/tip_calc.dart +++ b/mih_ui/lib/mih_packages/calculator/package_tools/tip_calc.dart @@ -1,3 +1,5 @@ +import 'dart:io'; + import 'package:mzansi_innovation_hub/main.dart'; import 'package:mzansi_innovation_hub/mih_package_components/mih_banner_ad.dart'; import 'package:mzansi_innovation_hub/mih_config/mih_colors.dart'; @@ -229,7 +231,11 @@ class _TipCalcState extends State { ), SizedBox(height: 10), Consumer(builder: (context, bannerAdDisplay, child) { - return MihBannerAd(); + if (Platform.isAndroid || Platform.isIOS) { + return MihBannerAd(); + } else { + return const SizedBox(height: 0); + } }), // if (splitBillController.text == "Yes") const Divider(), ], diff --git a/mih_ui/lib/mih_packages/mine_sweeper/package_tools/mine_sweeper_game.dart b/mih_ui/lib/mih_packages/mine_sweeper/package_tools/mine_sweeper_game.dart index 2fcdd04e..f400c98b 100644 --- a/mih_ui/lib/mih_packages/mine_sweeper/package_tools/mine_sweeper_game.dart +++ b/mih_ui/lib/mih_packages/mine_sweeper/package_tools/mine_sweeper_game.dart @@ -1,4 +1,5 @@ import 'dart:async'; +import 'dart:io'; import 'dart:math'; import 'package:flutter/material.dart'; import 'package:flutter_speed_dial/flutter_speed_dial.dart'; @@ -851,7 +852,9 @@ class _MineSweeperGameState extends State { ], ), ), - _timer != null ? MihBannerAd() : SizedBox(), + _timer != null && (Platform.isAndroid || Platform.isIOS) + ? MihBannerAd() + : SizedBox(), SizedBox(height: 15), ], ); diff --git a/mih_ui/lib/mih_packages/mzansi_ai/package_tools/mih_ai_chat.dart b/mih_ui/lib/mih_packages/mzansi_ai/package_tools/mih_ai_chat.dart index f4da1b6c..2ec0671e 100644 --- a/mih_ui/lib/mih_packages/mzansi_ai/package_tools/mih_ai_chat.dart +++ b/mih_ui/lib/mih_packages/mzansi_ai/package_tools/mih_ai_chat.dart @@ -1,4 +1,5 @@ import 'dart:convert'; +import 'dart:io'; import 'package:flutter/foundation.dart'; import 'package:flutter/material.dart'; @@ -112,15 +113,35 @@ class _MihAiChatState extends State with WidgetsBindingObserver { void speakLastMessage(MzansiAiProvider aiProvider) { final history = aiProvider.ollamaProvider.history; - if (history.isNotEmpty) { - final historyList = history.toList(); - for (int i = historyList.length - 1; i >= 0; i--) { - if (historyList[i].origin == MessageOrigin.llm && - historyList[i].text != null && - historyList[i].text!.isNotEmpty) { - _flutterTts.speak(historyList[i].text!); - return; - } + if (history.isEmpty) return; + + final historyList = history.toList(); + String? textToSpeak; + + // Find the last LLM message + for (int i = historyList.length - 1; i >= 0; i--) { + if (historyList[i].origin == MessageOrigin.llm && + historyList[i].text != null && + historyList[i].text!.isNotEmpty) { + textToSpeak = historyList[i].text!; + break; + } + } + + if (textToSpeak != null) { + if (Platform.isLinux) { + // Linux Workaround: Use Speech Dispatcher (standard on most distros) + // '-t female1' is optional for voice variety + Process.run('spd-say', [textToSpeak]); + + // Since spd-say doesn't have an easy "completion handler" via CLI, + // we manually toggle the UI state or just leave it off. + aiProvider.setTTSstate(true); + Future.delayed( + Duration(seconds: 5), () => aiProvider.setTTSstate(false)); + } else { + // Your existing mobile/web logic + _flutterTts.speak(textToSpeak); } } } @@ -183,11 +204,16 @@ class _MihAiChatState extends State with WidgetsBindingObserver { // } void stopTTS(MzansiAiProvider aiProvider) { - _flutterTts.stop(); + if (Platform.isLinux) { + Process.run('spd-say', ['-S']); // The -S flag stops current speech + } else { + _flutterTts.stop(); + } aiProvider.setTTSstate(false); } Future initTts(MzansiAiProvider aiProvider) async { + if (Platform.isLinux) return; try { await _flutterTts.setSpeechRate(!kIsWeb ? 0.55 : 1); // await _flutterTts.setLanguage("en-US"); @@ -258,7 +284,9 @@ class _MihAiChatState extends State with WidgetsBindingObserver { @override void dispose() { - _flutterTts.stop(); + if (!Platform.isLinux) { + _flutterTts.stop(); + } WidgetsBinding.instance.removeObserver(this); super.dispose(); } diff --git a/mih_ui/lib/mih_providers/mih_banner_ad_provider.dart b/mih_ui/lib/mih_providers/mih_banner_ad_provider.dart index 394bcbb4..e37594db 100644 --- a/mih_ui/lib/mih_providers/mih_banner_ad_provider.dart +++ b/mih_ui/lib/mih_providers/mih_banner_ad_provider.dart @@ -1,3 +1,5 @@ +import 'dart:io'; + import 'package:flutter/foundation.dart'; import 'package:google_mobile_ads/google_mobile_ads.dart'; import 'package:mzansi_innovation_hub/mih_config/mih_env.dart'; @@ -28,35 +30,37 @@ class MihBannerAdProvider extends ChangeNotifier { } void loadBannerAd() { - if (bannerAd != null) { - bannerAd!.dispose(); - bannerAd = null; - isBannerAdLoaded = false; + if (Platform.isAndroid || Platform.isIOS) { + if (bannerAd != null) { + bannerAd!.dispose(); + bannerAd = null; + isBannerAdLoaded = false; + } + bannerAd = BannerAd( + adUnitId: adUnitId, + request: const AdRequest(), + size: AdSize.banner, + listener: BannerAdListener( + onAdLoaded: (ad) { + debugPrint('$ad loaded.'); + isBannerAdLoaded = true; + notifyListeners(); + }, + onAdFailedToLoad: (ad, err) { + debugPrint('BannerAd failed to load: $err'); + errorMessage = + 'Failed to load ad- Message: ${err.message} Code :${err.code}'; + ad.dispose(); // Dispose the ad to free resources + isBannerAdLoaded = false; // ⬅️ Explicitly set to false + bannerAd = null; // ⬅️ Explicitly set to null + notifyListeners(); + }, + onAdOpened: (Ad ad) => debugPrint('$ad opened.'), + onAdClosed: (Ad ad) => debugPrint('$ad closed.'), + onAdImpression: (Ad ad) => debugPrint('$ad impression.'), + ), + ); + bannerAd!.load(); } - bannerAd = BannerAd( - adUnitId: adUnitId, - request: const AdRequest(), - size: AdSize.banner, - listener: BannerAdListener( - onAdLoaded: (ad) { - debugPrint('$ad loaded.'); - isBannerAdLoaded = true; - notifyListeners(); - }, - onAdFailedToLoad: (ad, err) { - debugPrint('BannerAd failed to load: $err'); - errorMessage = - 'Failed to load ad- Message: ${err.message} Code :${err.code}'; - ad.dispose(); // Dispose the ad to free resources - isBannerAdLoaded = false; // ⬅️ Explicitly set to false - bannerAd = null; // ⬅️ Explicitly set to null - notifyListeners(); - }, - onAdOpened: (Ad ad) => debugPrint('$ad opened.'), - onAdClosed: (Ad ad) => debugPrint('$ad closed.'), - onAdImpression: (Ad ad) => debugPrint('$ad impression.'), - ), - ); - bannerAd!.load(); } }