diff --git a/android/app/build.gradle b/android/app/build.gradle index c4b6630..d542bb1 100644 --- a/android/app/build.gradle +++ b/android/app/build.gradle @@ -59,7 +59,10 @@ android { signingConfigs { release { - storeFile file(System.getenv("AVM_KEYSTORE_FILE")) + def keystorePath = System.getenv("AVM_KEYSTORE_FILE") + if (keystorePath != null) { + storeFile file(keystorePath) + } storePassword System.getenv("AVM_KEYSTORE_PASSWORD") keyAlias System.getenv("AVM_KEY_ALIAS") keyPassword System.getenv("AVM_KEY_PASSWORD") @@ -70,6 +73,10 @@ android { release { signingConfig signingConfigs.release } + + debug { + // no signing config for debug builds + } } } diff --git a/devtools_options.yaml b/devtools_options.yaml new file mode 100644 index 0000000..7e7e7f6 --- /dev/null +++ b/devtools_options.yaml @@ -0,0 +1 @@ +extensions: diff --git a/lib/bloc/present_signed_document_cubit.dart b/lib/bloc/present_signed_document_cubit.dart index 7e8ee3c..06d4160 100644 --- a/lib/bloc/present_signed_document_cubit.dart +++ b/lib/bloc/present_signed_document_cubit.dart @@ -11,6 +11,7 @@ import 'package:path/path.dart' as p; import 'package:path_provider/path_provider.dart'; import '../app_service.dart'; +import '../data/document_signing_type.dart'; import '../file_extensions.dart'; import '../file_system_entity_extensions.dart'; import '../ui/screens/present_signed_document_screen.dart'; @@ -31,11 +32,17 @@ class PresentSignedDocumentCubit extends Cubit { final SignDocumentResponseBody signedDocument; - PresentSignedDocumentCubit({ - required AppService appService, - @factoryParam required this.signedDocument, - }) : _appService = appService, - super(const PresentSignedDocumentInitialState()); + PresentSignedDocumentCubit( + {required AppService appService, + @factoryParam required this.signedDocument, + @factoryParam required DocumentSigningType signingType}) + : _appService = appService, + super( + signingType == DocumentSigningType.local + ? const PresentSignedDocumentInitialState() + // Remote documents are not saved locally, so we go directly to success state + : const PresentSignedRemoteDocumentSuccessState(), + ); /// Saves [signedDocument] into public directory. Future saveDocument() async { @@ -67,7 +74,7 @@ class PresentSignedDocumentCubit extends Cubit { Future getShareableFile() async { final state = this.state; - if (state is PresentSignedDocumentSuccessState) { + if (state is PresentSignedLocalDocumentSuccessState) { final file = state.file; if (await file.exists()) { diff --git a/lib/bloc/present_signed_document_state.dart b/lib/bloc/present_signed_document_state.dart index cbe5684..b3cdfc7 100644 --- a/lib/bloc/present_signed_document_state.dart +++ b/lib/bloc/present_signed_document_state.dart @@ -15,8 +15,8 @@ sealed class PresentSignedDocumentState { return PresentSignedDocumentErrorState(error); } - PresentSignedDocumentSuccessState toSuccess(File file) { - return PresentSignedDocumentSuccessState(file); + PresentSignedLocalDocumentSuccessState toSuccess(File file) { + return PresentSignedLocalDocumentSuccessState(file); } @override @@ -44,13 +44,24 @@ class PresentSignedDocumentErrorState extends PresentSignedDocumentState { } } -class PresentSignedDocumentSuccessState extends PresentSignedDocumentState { +class PresentSignedLocalDocumentSuccessState + extends PresentSignedDocumentState { final File file; - const PresentSignedDocumentSuccessState(this.file); + const PresentSignedLocalDocumentSuccessState(this.file); @override String toString() { return "$runtimeType(file: $file)"; } } + +class PresentSignedRemoteDocumentSuccessState + extends PresentSignedDocumentState { + const PresentSignedRemoteDocumentSuccessState(); + + @override + String toString() { + return "$runtimeType()"; + } +} diff --git a/lib/di.config.dart b/lib/di.config.dart index 2fc7be1..d5821fd 100644 --- a/lib/di.config.dart +++ b/lib/di.config.dart @@ -8,28 +8,29 @@ // coverage:ignore-file // ignore_for_file: no_leading_underscores_for_library_prefixes -import 'dart:async' as _i16; -import 'dart:io' as _i17; +import 'dart:async' as _i17; +import 'dart:io' as _i18; import 'package:autogram_sign/autogram_sign.dart' as _i7; import 'package:eidmsdk/eidmsdk.dart' as _i4; -import 'package:eidmsdk/types.dart' as _i13; -import 'package:flutter/foundation.dart' as _i12; +import 'package:eidmsdk/types.dart' as _i14; +import 'package:flutter/foundation.dart' as _i13; import 'package:get_it/get_it.dart' as _i1; import 'package:injectable/injectable.dart' as _i2; import 'app_service.dart' as _i3; -import 'bloc/create_document_cubit.dart' as _i15; -import 'bloc/get_document_signature_type_cubit.dart' as _i20; +import 'bloc/create_document_cubit.dart' as _i16; +import 'bloc/get_document_signature_type_cubit.dart' as _i21; import 'bloc/paired_device_list_cubit.dart' as _i8; import 'bloc/present_signed_document_cubit.dart' as _i9; -import 'bloc/preview_document_cubit.dart' as _i10; -import 'bloc/select_signing_certificate_cubit.dart' as _i11; -import 'bloc/sign_document_cubit.dart' as _i14; -import 'data/pdf_signing_option.dart' as _i18; -import 'di.dart' as _i21; +import 'bloc/preview_document_cubit.dart' as _i11; +import 'bloc/select_signing_certificate_cubit.dart' as _i12; +import 'bloc/sign_document_cubit.dart' as _i15; +import 'data/document_signing_type.dart' as _i10; +import 'data/pdf_signing_option.dart' as _i19; +import 'di.dart' as _i22; import 'services/encryption_key_registry.dart' as _i5; -import 'use_case/get_document_signature_type_use_case.dart' as _i19; +import 'use_case/get_document_signature_type_use_case.dart' as _i20; import 'use_case/get_document_version_use_case.dart' as _i6; extension GetItInjectableX on _i1.GetIt { @@ -54,62 +55,63 @@ extension GetItInjectableX on _i1.GetIt { gh.factory<_i8.PairedDeviceListCubit>( () => _i8.PairedDeviceListCubit(service: gh<_i7.IAutogramService>())); gh.factoryParam<_i9.PresentSignedDocumentCubit, - _i7.SignDocumentResponseBody, dynamic>(( + _i7.SignDocumentResponseBody, _i10.DocumentSigningType>(( signedDocument, - _, + signingType, ) => _i9.PresentSignedDocumentCubit( appService: gh<_i3.AppService>(), signedDocument: signedDocument, + signingType: signingType, )); - gh.factoryParam<_i10.PreviewDocumentCubit, String, dynamic>(( + gh.factoryParam<_i11.PreviewDocumentCubit, String, dynamic>(( documentId, _, ) => - _i10.PreviewDocumentCubit( + _i11.PreviewDocumentCubit( service: gh<_i7.IAutogramService>(), documentId: documentId, )); - gh.factoryParam<_i11.SelectSigningCertificateCubit, - _i12.ValueNotifier<_i13.Certificate?>, dynamic>(( + gh.factoryParam<_i12.SelectSigningCertificateCubit, + _i13.ValueNotifier<_i14.Certificate?>, dynamic>(( signingCertificate, _, ) => - _i11.SelectSigningCertificateCubit( + _i12.SelectSigningCertificateCubit( eidmsdk: gh<_i4.Eidmsdk>(), signingCertificate: signingCertificate, )); - gh.factoryParam<_i14.SignDocumentCubit, String, _i13.Certificate>(( + gh.factoryParam<_i15.SignDocumentCubit, String, _i14.Certificate>(( documentId, certificate, ) => - _i14.SignDocumentCubit( + _i15.SignDocumentCubit( service: gh<_i7.IAutogramService>(), eidmsdk: gh<_i4.Eidmsdk>(), documentId: documentId, certificate: certificate, )); - gh.factoryParam<_i15.CreateDocumentCubit, _i16.FutureOr<_i17.File>, - _i18.PdfSigningOption>(( + gh.factoryParam<_i16.CreateDocumentCubit, _i17.FutureOr<_i18.File>, + _i19.PdfSigningOption>(( file, pdfSigningOption, ) => - _i15.CreateDocumentCubit( + _i16.CreateDocumentCubit( service: gh<_i7.IAutogramService>(), file: file, pdfSigningOption: pdfSigningOption, )); - gh.lazySingleton<_i19.GetDocumentSignatureTypeUseCase>( - () => _i19.GetDocumentSignatureTypeUseCase(gh<_i7.IAutogramService>())); - gh.factory<_i20.GetDocumentSignatureTypeCubit>(() => - _i20.GetDocumentSignatureTypeCubit( + gh.lazySingleton<_i20.GetDocumentSignatureTypeUseCase>( + () => _i20.GetDocumentSignatureTypeUseCase(gh<_i7.IAutogramService>())); + gh.factory<_i21.GetDocumentSignatureTypeCubit>(() => + _i21.GetDocumentSignatureTypeCubit( getDocumentSignatureType: - gh<_i19.GetDocumentSignatureTypeUseCase>())); + gh<_i20.GetDocumentSignatureTypeUseCase>())); return this; } } -class _$ExtrernalModule extends _i21.ExtrernalModule { +class _$ExtrernalModule extends _i22.ExtrernalModule { @override _i4.Eidmsdk get eidmsdk => _i4.Eidmsdk(); } diff --git a/lib/l10n/app_localizations.dart b/lib/l10n/app_localizations.dart index 158bab4..cc4e2f6 100644 --- a/lib/l10n/app_localizations.dart +++ b/lib/l10n/app_localizations.dart @@ -217,6 +217,48 @@ abstract class AppLocalizations { /// **'Podpísať iným certifikátom'** String get buttonSignWithDifferentCertificateLabel; + /// No description provided for @buttonMenuLabelSemantics. + /// + /// In sk, this message translates to: + /// **'Otvoriť menu'** + String get buttonMenuLabelSemantics; + + /// No description provided for @qrCodeScannerTorchOnSemantics. + /// + /// In sk, this message translates to: + /// **'Zapnúť blesk'** + String get qrCodeScannerTorchOnSemantics; + + /// No description provided for @qrCodeScannerTorchOffSemantics. + /// + /// In sk, this message translates to: + /// **'Vypnúť blesk'** + String get qrCodeScannerTorchOffSemantics; + + /// No description provided for @qrCodeScannerBackSemantics. + /// + /// In sk, this message translates to: + /// **'Späť'** + String get qrCodeScannerBackSemantics; + + /// No description provided for @qrCodeScannerOpenSemantics. + /// + /// In sk, this message translates to: + /// **'Podpísať vzdialený dokument pomocou QR kódu'** + String get qrCodeScannerOpenSemantics; + + /// No description provided for @shareDocumentPreviewSemantics. + /// + /// In sk, this message translates to: + /// **'Zdieľať náhľad dokumentu'** + String get shareDocumentPreviewSemantics; + + /// No description provided for @previewDocumentSemantics. + /// + /// In sk, this message translates to: + /// **'Náhľad dokumentu'** + String get previewDocumentSemantics; + /// No description provided for @deepLinkParseErrorMessage. /// /// In sk, this message translates to: diff --git a/lib/l10n/app_localizations_sk.dart b/lib/l10n/app_localizations_sk.dart index 83267b3..8e2428f 100644 --- a/lib/l10n/app_localizations_sk.dart +++ b/lib/l10n/app_localizations_sk.dart @@ -71,6 +71,27 @@ class AppLocalizationsSk extends AppLocalizations { @override String get buttonSignWithDifferentCertificateLabel => 'Podpísať iným certifikátom'; + @override + String get buttonMenuLabelSemantics => 'Otvoriť menu'; + + @override + String get qrCodeScannerTorchOnSemantics => 'Zapnúť blesk'; + + @override + String get qrCodeScannerTorchOffSemantics => 'Vypnúť blesk'; + + @override + String get qrCodeScannerBackSemantics => 'Späť'; + + @override + String get qrCodeScannerOpenSemantics => 'Podpísať vzdialený dokument pomocou QR kódu'; + + @override + String get shareDocumentPreviewSemantics => 'Zdieľať náhľad dokumentu'; + + @override + String get previewDocumentSemantics => 'Náhľad dokumentu'; + @override String deepLinkParseErrorMessage(Object error) { return 'Nepodporovaný alebo nesprávny odkaz:\n$error'; diff --git a/lib/l10n/app_sk.arb b/lib/l10n/app_sk.arb index 466dc62..f94f41c 100644 --- a/lib/l10n/app_sk.arb +++ b/lib/l10n/app_sk.arb @@ -22,6 +22,13 @@ "buttonSelectCertificateLabel": "Vybrať certifikát", "buttonSignWithCertificateLabel": "Podpísať ako {subject}", "buttonSignWithDifferentCertificateLabel": "Podpísať iným certifikátom", + "buttonMenuLabelSemantics": "Otvoriť menu", + "qrCodeScannerTorchOnSemantics": "Zapnúť blesk", + "qrCodeScannerTorchOffSemantics": "Vypnúť blesk", + "qrCodeScannerBackSemantics": "Späť", + "qrCodeScannerOpenSemantics": "Podpísať vzdialený dokument pomocou QR kódu", + "shareDocumentPreviewSemantics": "Zdieľať náhľad dokumentu", + "previewDocumentSemantics": "Náhľad dokumentu", "deepLinkParseErrorMessage": "Nepodporovaný alebo nesprávny odkaz:\n{error}", "stepIndicatorText": "Krok {stepNumber} z {totalSteps}", diff --git a/lib/ui/screens/about_screen.dart b/lib/ui/screens/about_screen.dart index a39a645..f34cebc 100644 --- a/lib/ui/screens/about_screen.dart +++ b/lib/ui/screens/about_screen.dart @@ -40,10 +40,13 @@ class _Body extends StatelessWidget { final child = Column( children: [ - Text( - strings.appName, - textAlign: TextAlign.center, - style: Theme.of(context).textTheme.headlineLarge, + Semantics( + header: true, + child: Text( + strings.appName, + textAlign: TextAlign.center, + style: Theme.of(context).textTheme.headlineLarge, + ), ), const SizedBox(height: 16), const AppVersionText(), diff --git a/lib/ui/screens/main_menu_screen.dart b/lib/ui/screens/main_menu_screen.dart index b49bb8b..c425627 100644 --- a/lib/ui/screens/main_menu_screen.dart +++ b/lib/ui/screens/main_menu_screen.dart @@ -29,12 +29,15 @@ class MainMenuScreen extends StatelessWidget { const Spacer(flex: 1), Padding( padding: const EdgeInsets.only(bottom: 20), - child: Text( - strings.menuTitle, - style: const TextStyle( - fontSize: 20, - fontWeight: FontWeight.w700, - letterSpacing: 0.15, + child: Semantics( + header: true, + child: Text( + strings.menuTitle, + style: const TextStyle( + fontSize: 20, + fontWeight: FontWeight.w700, + letterSpacing: 0.15, + ), ), ), ), @@ -157,11 +160,14 @@ class _MenuItem extends StatelessWidget { return Padding( padding: const EdgeInsets.symmetric(vertical: 4), - child: InkWell( - onTap: onPressed, - child: Padding( - padding: const EdgeInsets.symmetric(vertical: 8), - child: text, + child: Semantics( + button: true, + child: InkWell( + onTap: onPressed, + child: Padding( + padding: const EdgeInsets.symmetric(vertical: 8), + child: text, + ), ), ), ); diff --git a/lib/ui/screens/main_screen.dart b/lib/ui/screens/main_screen.dart index 1c85709..77895f5 100644 --- a/lib/ui/screens/main_screen.dart +++ b/lib/ui/screens/main_screen.dart @@ -233,21 +233,31 @@ AppBar _MainAppBar({ return AppBar( foregroundColor: kMainAppBarForegroundColor, backgroundColor: kMainAppBarBackgroundColor, - leading: IconButton( - icon: SvgPicture.asset( - 'assets/icons/menu.svg', - colorFilter: colorFilter, + leading: Semantics( + button: true, + excludeSemantics: true, + label: context.strings.buttonMenuLabelSemantics, + child: IconButton( + icon: SvgPicture.asset( + 'assets/icons/menu.svg', + colorFilter: colorFilter, + ), + onPressed: onMenuPressed, ), - onPressed: onMenuPressed, ), actions: [ if (showQrCodeScannerIcon) - IconButton( - icon: SvgPicture.asset( - 'assets/icons/qr_code_scanner.svg', - colorFilter: colorFilter, + Semantics( + label: context.strings.qrCodeScannerOpenSemantics, + button: true, + excludeSemantics: true, + child: IconButton( + icon: SvgPicture.asset( + 'assets/icons/qr_code_scanner.svg', + colorFilter: colorFilter, + ), + onPressed: onQrCodeScannerPressed, ), - onPressed: onQrCodeScannerPressed, ), ], title: Builder(builder: (context) { diff --git a/lib/ui/screens/present_signed_document_screen.dart b/lib/ui/screens/present_signed_document_screen.dart index 9aae052..ceb6dc7 100644 --- a/lib/ui/screens/present_signed_document_screen.dart +++ b/lib/ui/screens/present_signed_document_screen.dart @@ -42,6 +42,7 @@ class PresentSignedDocumentScreen extends StatelessWidget { create: (context) { final cubit = getIt.get( param1: signedDocument, + param2: signingType, ); if (signingType == DocumentSigningType.local) { @@ -163,11 +164,16 @@ class _Body extends StatelessWidget { onShareFileRequested: onShareFileRequested, onCloseRequested: onCloseRequested, ), - PresentSignedDocumentSuccessState state => _SuccessContent( + PresentSignedLocalDocumentSuccessState state => _SuccessContent( file: state.file, onShareFileRequested: onShareFileRequested, onCloseRequested: onCloseRequested, ), + PresentSignedRemoteDocumentSuccessState() => _SuccessContent( + file: null, + onShareFileRequested: null, + onCloseRequested: onCloseRequested, + ), }; } } @@ -347,7 +353,7 @@ Widget previewSuccessPresentSignedDocumentScreen(BuildContext context) { final file = File(path); return _Body( - state: PresentSignedDocumentSuccessState(file), + state: PresentSignedLocalDocumentSuccessState(file), signingType: signingType, onShareFileRequested: () { developer.log('onShareFileRequested'); diff --git a/lib/ui/screens/preview_document_screen.dart b/lib/ui/screens/preview_document_screen.dart index df411fc..4d1b9dd 100644 --- a/lib/ui/screens/preview_document_screen.dart +++ b/lib/ui/screens/preview_document_screen.dart @@ -63,10 +63,15 @@ class PreviewDocumentScreen extends StatelessWidget { title: Text(context.strings.previewDocumentTitle), actions: [ if (file != null) - IconButton( - onPressed: () => _onShareRequested(context), - icon: const Icon(Icons.share_outlined), - color: Theme.of(context).colorScheme.primary, + Semantics( + label: context.strings.shareDocumentPreviewSemantics, + excludeSemantics: true, + button: true, + child: IconButton( + onPressed: () => _onShareRequested(context), + icon: const Icon(Icons.share_outlined), + color: Theme.of(context).colorScheme.primary, + ), ), ], ), @@ -143,20 +148,27 @@ class _SuccessContent extends StatelessWidget { @override Widget build(BuildContext context) { final dashColor = Theme.of(context).colorScheme.primary; - + print(" mime ${visualization.filename}" ?? ""); return Column( children: [ // Document preview Expanded( child: Padding( padding: const EdgeInsets.only(bottom: 16), - child: DottedBorder( - color: dashColor, - strokeWidth: 4, - dashPattern: const [16, 16], - padding: const EdgeInsets.all(2), - child: DocumentVisualization( - visualization: visualization, + child: Semantics( + image: ["jpeg", "jpg", "png", "gif", "bmp", "webp", "svg", "heic"] + .any((element) => + visualization.filename?.endsWith(element) ?? false), + label: context.strings.previewDocumentSemantics, + excludeSemantics: true, + child: DottedBorder( + color: dashColor, + strokeWidth: 4, + dashPattern: const [16, 16], + padding: const EdgeInsets.all(2), + child: DocumentVisualization( + visualization: visualization, + ), ), ), ), diff --git a/lib/ui/screens/qr_code_scanner_screen.dart b/lib/ui/screens/qr_code_scanner_screen.dart index e860c17..ef2e39f 100644 --- a/lib/ui/screens/qr_code_scanner_screen.dart +++ b/lib/ui/screens/qr_code_scanner_screen.dart @@ -58,13 +58,18 @@ class _QRCodeScannerScreenState extends State { padding: kScreenMargin.copyWith( top: MediaQuery.of(context).padding.top, ), - child: SquareButton( - onPressed: () { - Navigator.maybePop(context); - }, - child: Icon( - Icons.arrow_back, - color: Theme.of(context).colorScheme.onBackground, + child: Semantics( + label: context.strings.qrCodeScannerBackSemantics, + button: true, + excludeSemantics: true, + child: SquareButton( + onPressed: () { + Navigator.maybePop(context); + }, + child: Icon( + Icons.arrow_back, + color: Theme.of(context).colorScheme.onBackground, + ), ), ), ), @@ -93,9 +98,20 @@ class _QRCodeScannerScreenState extends State { TorchState.on => Icons.flashlight_off, }; - return Icon( - icon, - color: Theme.of(context).colorScheme.onBackground, + final semanticsLabel = switch (torchState) { + TorchState.off => + context.strings.qrCodeScannerTorchOnSemantics, + TorchState.on => + context.strings.qrCodeScannerTorchOffSemantics, + }; + + return Semantics( + button: true, + label: semanticsLabel, + child: Icon( + icon, + color: Theme.of(context).colorScheme.onBackground, + ), ); }, ), diff --git a/lib/ui/screens/settings_screen.dart b/lib/ui/screens/settings_screen.dart index a1922c8..bd72eee 100644 --- a/lib/ui/screens/settings_screen.dart +++ b/lib/ui/screens/settings_screen.dart @@ -191,12 +191,15 @@ class _ValueListenableBoundTile extends StatelessWidget { builder: (context, value, _) { final summary = summaryGetter(setting.value); - return PreferenceTile( - title: title, - summary: summary, - onPressed: () { - _onEditItemRequested(context, value); - }, + return Semantics( + button: true, + child: PreferenceTile( + title: title, + summary: summary, + onPressed: () { + _onEditItemRequested(context, value); + }, + ), ); }, ); @@ -242,7 +245,7 @@ class _ValueListenableBoundTile extends StatelessWidget { ); return AlertDialog( - title: Text(title), + title: Semantics(header: true, child: Text(title)), content: content, ); }, diff --git a/lib/ui/widgets/option_picker.dart b/lib/ui/widgets/option_picker.dart index 4a20cd0..130a64a 100644 --- a/lib/ui/widgets/option_picker.dart +++ b/lib/ui/widgets/option_picker.dart @@ -55,18 +55,22 @@ class OptionPicker extends StatelessWidget { ), ); - return Material( - child: InkWell( - onTap: () { - onValueChanged(value); - }, - child: Row( - mainAxisAlignment: MainAxisAlignment.start, - children: [ - radio, - const SizedBox(width: 8), - label, - ], + return Semantics( + checked: value == selectedValue, + inMutuallyExclusiveGroup: true, + child: Material( + child: InkWell( + onTap: () { + onValueChanged(value); + }, + child: Row( + mainAxisAlignment: MainAxisAlignment.start, + children: [ + ExcludeSemantics(child: radio), + const SizedBox(width: 8), + label, + ], + ), ), ), ); diff --git a/lib/ui/widgets/signature_type_picker.dart b/lib/ui/widgets/signature_type_picker.dart index fc48990..224a4f5 100644 --- a/lib/ui/widgets/signature_type_picker.dart +++ b/lib/ui/widgets/signature_type_picker.dart @@ -88,18 +88,24 @@ class _ListItem extends StatelessWidget { ); // NOT using RadioListTile because need to scale-up and style Radio - return ListTile( - onTap: (canSelect ? onSelected : null), - enabled: enabled, - leading: Transform.scale( - scale: kRadioScale, - child: radio, + return Semantics( + checked: value == selectedValue, + inMutuallyExclusiveGroup: true, + excludeSemantics: true, + label: "$titleText, $subtitleText", + child: ListTile( + onTap: (canSelect ? onSelected : null), + enabled: enabled, + leading: Transform.scale( + scale: kRadioScale, + child: radio, + ), + title: Text( + titleText, + style: const TextStyle(fontWeight: FontWeight.bold), + ), + subtitle: Text(subtitleText), ), - title: Text( - titleText, - style: const TextStyle(fontWeight: FontWeight.bold), - ), - subtitle: Text(subtitleText), ); } }