Support linux version of MIHpt2

This commit is contained in:
2026-02-24 16:30:06 +02:00
parent 6ad6b6ccbd
commit 07d4ba4afa
7 changed files with 97 additions and 45 deletions

View File

@@ -1,3 +1,5 @@
import 'dart:io';
import 'package:country_code_picker/country_code_picker.dart'; import 'package:country_code_picker/country_code_picker.dart';
import 'package:file_picker/file_picker.dart'; import 'package:file_picker/file_picker.dart';
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
@@ -451,7 +453,7 @@ class _PackageToolOneState extends State<PackageToolOne> {
), ),
], ],
), ),
MihBannerAd(), if (Platform.isAndroid || Platform.isIOS) MihBannerAd(),
const SizedBox(height: 10), const SizedBox(height: 10),
Divider( Divider(
color: MihColors.getSecondaryColor( color: MihColors.getSecondaryColor(

View File

@@ -1,3 +1,5 @@
import 'dart:io';
import 'package:app_settings/app_settings.dart'; import 'package:app_settings/app_settings.dart';
import 'package:flutter/foundation.dart'; import 'package:flutter/foundation.dart';
import 'package:local_auth/local_auth.dart'; import 'package:local_auth/local_auth.dart';
@@ -155,7 +157,8 @@ class _MihPackageTileState extends State<MihPackageTile> {
Future<void> authenticateUser() async { Future<void> authenticateUser() async {
if (widget.authenticateUser != null && if (widget.authenticateUser != null &&
widget.authenticateUser! && widget.authenticateUser! &&
!kIsWeb) { !kIsWeb &&
!Platform.isLinux) {
if (await isUserAuthenticated()) { if (await isUserAuthenticated()) {
widget.onTap(); widget.onTap();
} }

View File

@@ -1,3 +1,5 @@
import 'dart:io';
import 'package:flutter/gestures.dart'; import 'package:flutter/gestures.dart';
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:mzansi_innovation_hub/main.dart'; import 'package:mzansi_innovation_hub/main.dart';
@@ -157,7 +159,11 @@ class _CurrencyExchangeRateState extends State<CurrencyExchangeRate> {
), ),
SizedBox(height: 10), SizedBox(height: 10),
Consumer(builder: (context, bannerAdDisplay, child) { Consumer(builder: (context, bannerAdDisplay, child) {
return MihBannerAd(); if (Platform.isAndroid || Platform.isIOS) {
return MihBannerAd();
} else {
return const SizedBox(height: 0);
}
}), }),
], ],
), ),

View File

@@ -1,3 +1,5 @@
import 'dart:io';
import 'package:mzansi_innovation_hub/main.dart'; 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_package_components/mih_banner_ad.dart';
import 'package:mzansi_innovation_hub/mih_config/mih_colors.dart'; import 'package:mzansi_innovation_hub/mih_config/mih_colors.dart';
@@ -229,7 +231,11 @@ class _TipCalcState extends State<TipCalc> {
), ),
SizedBox(height: 10), SizedBox(height: 10),
Consumer(builder: (context, bannerAdDisplay, child) { 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(), // if (splitBillController.text == "Yes") const Divider(),
], ],

View File

@@ -1,4 +1,5 @@
import 'dart:async'; import 'dart:async';
import 'dart:io';
import 'dart:math'; import 'dart:math';
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:flutter_speed_dial/flutter_speed_dial.dart'; import 'package:flutter_speed_dial/flutter_speed_dial.dart';
@@ -851,7 +852,9 @@ class _MineSweeperGameState extends State<MineSweeperGame> {
], ],
), ),
), ),
_timer != null ? MihBannerAd() : SizedBox(), _timer != null && (Platform.isAndroid || Platform.isIOS)
? MihBannerAd()
: SizedBox(),
SizedBox(height: 15), SizedBox(height: 15),
], ],
); );

View File

@@ -1,4 +1,5 @@
import 'dart:convert'; import 'dart:convert';
import 'dart:io';
import 'package:flutter/foundation.dart'; import 'package:flutter/foundation.dart';
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
@@ -112,15 +113,35 @@ class _MihAiChatState extends State<MihAiChat> with WidgetsBindingObserver {
void speakLastMessage(MzansiAiProvider aiProvider) { void speakLastMessage(MzansiAiProvider aiProvider) {
final history = aiProvider.ollamaProvider.history; final history = aiProvider.ollamaProvider.history;
if (history.isNotEmpty) { if (history.isEmpty) return;
final historyList = history.toList();
for (int i = historyList.length - 1; i >= 0; i--) { final historyList = history.toList();
if (historyList[i].origin == MessageOrigin.llm && String? textToSpeak;
historyList[i].text != null &&
historyList[i].text!.isNotEmpty) { // Find the last LLM message
_flutterTts.speak(historyList[i].text!); for (int i = historyList.length - 1; i >= 0; i--) {
return; 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<MihAiChat> with WidgetsBindingObserver {
// } // }
void stopTTS(MzansiAiProvider aiProvider) { 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); aiProvider.setTTSstate(false);
} }
Future<void> initTts(MzansiAiProvider aiProvider) async { Future<void> initTts(MzansiAiProvider aiProvider) async {
if (Platform.isLinux) return;
try { try {
await _flutterTts.setSpeechRate(!kIsWeb ? 0.55 : 1); await _flutterTts.setSpeechRate(!kIsWeb ? 0.55 : 1);
// await _flutterTts.setLanguage("en-US"); // await _flutterTts.setLanguage("en-US");
@@ -258,7 +284,9 @@ class _MihAiChatState extends State<MihAiChat> with WidgetsBindingObserver {
@override @override
void dispose() { void dispose() {
_flutterTts.stop(); if (!Platform.isLinux) {
_flutterTts.stop();
}
WidgetsBinding.instance.removeObserver(this); WidgetsBinding.instance.removeObserver(this);
super.dispose(); super.dispose();
} }

View File

@@ -1,3 +1,5 @@
import 'dart:io';
import 'package:flutter/foundation.dart'; import 'package:flutter/foundation.dart';
import 'package:google_mobile_ads/google_mobile_ads.dart'; import 'package:google_mobile_ads/google_mobile_ads.dart';
import 'package:mzansi_innovation_hub/mih_config/mih_env.dart'; import 'package:mzansi_innovation_hub/mih_config/mih_env.dart';
@@ -28,35 +30,37 @@ class MihBannerAdProvider extends ChangeNotifier {
} }
void loadBannerAd() { void loadBannerAd() {
if (bannerAd != null) { if (Platform.isAndroid || Platform.isIOS) {
bannerAd!.dispose(); if (bannerAd != null) {
bannerAd = null; bannerAd!.dispose();
isBannerAdLoaded = false; 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();
} }
} }