diff --git a/.github/actions/ios/action.yml b/.github/actions/ios/action.yml index 4ce96c960..f61d37d0f 100644 --- a/.github/actions/ios/action.yml +++ b/.github/actions/ios/action.yml @@ -18,10 +18,27 @@ runs: with: cache: true - - name: Build iOS IPA + - name: Update Podfile + shell: bash + run: | + cd ./iOS + flutter pub get + pod install --repo-update + + - name: Build iOS IPA (No Code Signing for PRs) + if: ${{ github.event_name == 'pull_request' }} shell: bash env: - VERSION_NAME: ${{inputs.VERSION_NAME}} - VERSION_CODE: ${{inputs.VERSION_CODE}} + VERSION_NAME: ${{ inputs.VERSION_NAME }} + VERSION_CODE: ${{ inputs.VERSION_CODE }} run: | flutter build ipa --no-codesign --build-name $VERSION_NAME --build-number $VERSION_CODE + + - name: Build iOS IPA (With Code Signing) + if: ${{ github.event_name != 'pull_request' }} + shell: bash + env: + VERSION_NAME: ${{ inputs.VERSION_NAME }} + VERSION_CODE: ${{ inputs.VERSION_CODE }} + run: | + flutter build ipa --build-name $VERSION_NAME --build-number $VERSION_CODE diff --git a/.github/workflows/push.yml b/.github/workflows/push.yml index 2d648a7c0..94fc822f9 100644 --- a/.github/workflows/push.yml +++ b/.github/workflows/push.yml @@ -64,7 +64,7 @@ jobs: - name: Pre Checks run: | - bash scripts/check-screenshots.sh + bash scripts/check-android-screenshots.sh - name: Prepare Build Keys if: ${{ github.repository == 'fossasia/badgemagic-app' }} @@ -72,7 +72,7 @@ jobs: ENCRYPTED_F10B5E0E5262_IV: ${{ secrets.ENCRYPTED_F10B5E0E5262_IV }} ENCRYPTED_F10B5E0E5262_KEY: ${{ secrets.ENCRYPTED_F10B5E0E5262_KEY }} run: | - bash scripts/prep-key.sh + bash scripts/prep-android-key.sh - name: Android Workflow uses: ./.github/actions/android @@ -136,23 +136,11 @@ jobs: git branch -m apk git push --force origin apk - - name: Setup Ruby - if: ${{ github.repository == 'fossasia/badgemagic-app' }} - uses: ruby/setup-ruby@v1 - with: - ruby-version: '3.3' - bundler-cache: true - - - name: Prepare Bundler - if: ${{ github.repository == 'fossasia/badgemagic-app' }} - run: | - bundle config path vendor/bundle - bundle install --jobs 4 --retry 3 - - name: Push app in open testing track if: ${{ github.repository == 'fossasia/badgemagic-app' }} run: | - bundle exec fastlane uploadToOpenTesting + cd ./android + fastlane uploadToOpenTesting if [[ $? -ne 0 ]]; then exit 1 fi @@ -164,11 +152,40 @@ jobs: steps: - uses: actions/checkout@v4 + - name: Prepare Build Keys + if: ${{ github.repository == 'fossasia/badgemagic-app' }} + env: + ENCRYPTED_IOS_IV: ${{ secrets.ENCRYPTED_IOS_IV }} + ENCRYPTED_IOS_KEY: ${{ secrets.ENCRYPTED_IOS_KEY }} + run: | + bash scripts/prep-ios-key.sh + + - name: Setup Certs + if: ${{ github.repository == 'fossasia/badgemagic-app' }} + env: + MATCH_PASSWORD: ${{ secrets.MATCH_PASSWORD }} + MATCH_GIT_BASIC_AUTHORIZATION: ${{ secrets.MATCH_GIT_BASIC_AUTHORIZATION }} + run: | + cd ./iOS + fastlane setupCertificates + if [[ $? -ne 0 ]]; then + exit 1 + fi + - name: iOS Workflow uses: ./.github/actions/ios with: VERSION_NAME: ${{needs.common.outputs.VERSION_NAME}} VERSION_CODE: ${{needs.common.outputs.VERSION_CODE}} + + - name: Push app to testflight + if: ${{ github.repository == 'fossasia/badgemagic-app' }} + run: | + cd ./iOS + fastlane uploadToBeta + if [[ $? -ne 0 ]]; then + exit 1 + fi update-release: name: Update Draft Release diff --git a/android/.gitignore b/android/.gitignore index 6f568019d..dd935f0da 100644 --- a/android/.gitignore +++ b/android/.gitignore @@ -11,3 +11,4 @@ GeneratedPluginRegistrant.java key.properties **/*.keystore **/*.jks +fastlane.json diff --git a/android/app/src/main/AndroidManifest.xml b/android/app/src/main/AndroidManifest.xml index d1ce6942c..d5995a0bb 100644 --- a/android/app/src/main/AndroidManifest.xml +++ b/android/app/src/main/AndroidManifest.xml @@ -1,6 +1,6 @@ NSBluetoothAlwaysUsageDescription - This app needs Bluetooth to function + App needs Bluetooth access to control BLE Badge + NSBluetoothPeripheralUsageDescription + App needs Bluetooth Peripheral access to control BLE Badge + NSPhotoLibraryUsageDescription + App needs Photos access to help convert photos to badge images CFBundleDevelopmentRegion $(DEVELOPMENT_LANGUAGE) CFBundleDisplayName @@ -54,5 +58,7 @@ UIApplicationSupportsIndirectInputEvents + ITSAppUsesNonExemptEncryption + \ No newline at end of file diff --git a/iOS/fastlane/Appfile b/iOS/fastlane/Appfile new file mode 100644 index 000000000..d8694fb52 --- /dev/null +++ b/iOS/fastlane/Appfile @@ -0,0 +1,6 @@ +app_identifier("org.fossasia.badgemagic.ios") # The bundle identifier of your app +# apple_id("[[APPLE_ID]]") # Your Apple Developer Portal username + + +# For more information about the Appfile, see: +# https://docs.fastlane.tools/advanced/#appfile diff --git a/iOS/fastlane/Deliverfile b/iOS/fastlane/Deliverfile new file mode 100644 index 000000000..74739f740 --- /dev/null +++ b/iOS/fastlane/Deliverfile @@ -0,0 +1,3 @@ +# The Deliverfile allows you to store various App Store Connect metadata +# For more information, check out the docs +# https://docs.fastlane.tools/actions/deliver/ diff --git a/iOS/fastlane/Fastfile b/iOS/fastlane/Fastfile new file mode 100644 index 000000000..47a2cb00e --- /dev/null +++ b/iOS/fastlane/Fastfile @@ -0,0 +1,57 @@ +# This file contains the fastlane.tools configuration +# You can find the documentation at https://docs.fastlane.tools +# +# For a list of all available actions, check out +# +# https://docs.fastlane.tools/actions +# +# For a list of all available plugins, check out +# +# https://docs.fastlane.tools/plugins/available-plugins +# + +# Uncomment the line if you want fastlane to automatically update itself +# update_fastlane + +default_platform(:ios) + +platform :ios do + before_all do + setup_ci + end + + lane :setupCertificates do + bundle_id = CredentialsManager::AppfileConfig.try_fetch_value(:app_identifier) + runner_temp = ENV['RUNNER_TEMP'] || '/tmp' + keychain_path = File.join(runner_temp, 'app-signing.keychain-db') + keychain_password = ENV['KEYCHAIN_PASSWORD'] + + sync_code_signing( + api_key_path: './fastlane.json', + type: "appstore", + readonly: true, + ) + + update_code_signing_settings( + use_automatic_signing: false, + path: "Runner.xcodeproj", + bundle_identifier: bundle_id, + code_sign_identity: "iPhone Distribution", + profile_name: Actions.lane_context[SharedValues::MATCH_PROVISIONING_PROFILE_MAPPING][bundle_id] + ) + end + + desc "Push a new beta build to TestFlight" + lane :uploadToBeta do + puts "Building and uploading to TestFlight" + build_app( + skip_build_archive: true, + archive_path: "../build/ios/archive/Runner.xcarchive", + ) + upload_to_testflight( + api_key_path: './fastlane.json', + skip_waiting_for_build_processing: true, + groups: 'External Testing Group' + ) + end +end diff --git a/iOS/fastlane/Matchfile b/iOS/fastlane/Matchfile new file mode 100644 index 000000000..93940047d --- /dev/null +++ b/iOS/fastlane/Matchfile @@ -0,0 +1,13 @@ +git_url("https://github.com/fossasia/deployment-certs.git") + +storage_mode("git") + +type("appstore") # The default type, can be: appstore, adhoc, enterprise or development +app_identifier(["org.fossasia.badgemagic.ios"]) + +# username("user@fastlane.tools") # Your Apple Developer Portal username + +# For all available options run `fastlane match --help` +# Remove the # in the beginning of the line to enable the other options + +# The docs are available on https://docs.fastlane.tools/actions/match diff --git a/iOS/fastlane/metadata/copyright.txt b/iOS/fastlane/metadata/copyright.txt new file mode 100644 index 000000000..8b1378917 --- /dev/null +++ b/iOS/fastlane/metadata/copyright.txt @@ -0,0 +1 @@ + diff --git a/iOS/fastlane/metadata/en-US/apple_tv_privacy_policy.txt b/iOS/fastlane/metadata/en-US/apple_tv_privacy_policy.txt new file mode 100644 index 000000000..8b1378917 --- /dev/null +++ b/iOS/fastlane/metadata/en-US/apple_tv_privacy_policy.txt @@ -0,0 +1 @@ + diff --git a/iOS/fastlane/metadata/en-US/description.txt b/iOS/fastlane/metadata/en-US/description.txt new file mode 100644 index 000000000..8b1378917 --- /dev/null +++ b/iOS/fastlane/metadata/en-US/description.txt @@ -0,0 +1 @@ + diff --git a/iOS/fastlane/metadata/en-US/keywords.txt b/iOS/fastlane/metadata/en-US/keywords.txt new file mode 100644 index 000000000..8b1378917 --- /dev/null +++ b/iOS/fastlane/metadata/en-US/keywords.txt @@ -0,0 +1 @@ + diff --git a/iOS/fastlane/metadata/en-US/marketing_url.txt b/iOS/fastlane/metadata/en-US/marketing_url.txt new file mode 100644 index 000000000..8b1378917 --- /dev/null +++ b/iOS/fastlane/metadata/en-US/marketing_url.txt @@ -0,0 +1 @@ + diff --git a/iOS/fastlane/metadata/en-US/name.txt b/iOS/fastlane/metadata/en-US/name.txt new file mode 100644 index 000000000..eaab7c3f8 --- /dev/null +++ b/iOS/fastlane/metadata/en-US/name.txt @@ -0,0 +1 @@ +Badge Magic iOS diff --git a/iOS/fastlane/metadata/en-US/privacy_url.txt b/iOS/fastlane/metadata/en-US/privacy_url.txt new file mode 100644 index 000000000..013491898 --- /dev/null +++ b/iOS/fastlane/metadata/en-US/privacy_url.txt @@ -0,0 +1 @@ +https://badgemagic.fossasia.org/privacy/ diff --git a/iOS/fastlane/metadata/en-US/promotional_text.txt b/iOS/fastlane/metadata/en-US/promotional_text.txt new file mode 100644 index 000000000..8b1378917 --- /dev/null +++ b/iOS/fastlane/metadata/en-US/promotional_text.txt @@ -0,0 +1 @@ + diff --git a/iOS/fastlane/metadata/en-US/release_notes.txt b/iOS/fastlane/metadata/en-US/release_notes.txt new file mode 100644 index 000000000..8b1378917 --- /dev/null +++ b/iOS/fastlane/metadata/en-US/release_notes.txt @@ -0,0 +1 @@ + diff --git a/iOS/fastlane/metadata/en-US/subtitle.txt b/iOS/fastlane/metadata/en-US/subtitle.txt new file mode 100644 index 000000000..2b5264419 --- /dev/null +++ b/iOS/fastlane/metadata/en-US/subtitle.txt @@ -0,0 +1 @@ +Magically Create Symbols, Text diff --git a/iOS/fastlane/metadata/en-US/support_url.txt b/iOS/fastlane/metadata/en-US/support_url.txt new file mode 100644 index 000000000..8b1378917 --- /dev/null +++ b/iOS/fastlane/metadata/en-US/support_url.txt @@ -0,0 +1 @@ + diff --git a/iOS/fastlane/metadata/primary_category.txt b/iOS/fastlane/metadata/primary_category.txt new file mode 100644 index 000000000..41f44c008 --- /dev/null +++ b/iOS/fastlane/metadata/primary_category.txt @@ -0,0 +1 @@ +UTILITIES diff --git a/iOS/fastlane/metadata/primary_first_sub_category.txt b/iOS/fastlane/metadata/primary_first_sub_category.txt new file mode 100644 index 000000000..8b1378917 --- /dev/null +++ b/iOS/fastlane/metadata/primary_first_sub_category.txt @@ -0,0 +1 @@ + diff --git a/iOS/fastlane/metadata/primary_second_sub_category.txt b/iOS/fastlane/metadata/primary_second_sub_category.txt new file mode 100644 index 000000000..8b1378917 --- /dev/null +++ b/iOS/fastlane/metadata/primary_second_sub_category.txt @@ -0,0 +1 @@ + diff --git a/iOS/fastlane/metadata/review_information/demo_password.txt b/iOS/fastlane/metadata/review_information/demo_password.txt new file mode 100644 index 000000000..8b1378917 --- /dev/null +++ b/iOS/fastlane/metadata/review_information/demo_password.txt @@ -0,0 +1 @@ + diff --git a/iOS/fastlane/metadata/review_information/demo_user.txt b/iOS/fastlane/metadata/review_information/demo_user.txt new file mode 100644 index 000000000..8b1378917 --- /dev/null +++ b/iOS/fastlane/metadata/review_information/demo_user.txt @@ -0,0 +1 @@ + diff --git a/iOS/fastlane/metadata/review_information/email_address.txt b/iOS/fastlane/metadata/review_information/email_address.txt new file mode 100644 index 000000000..8b1378917 --- /dev/null +++ b/iOS/fastlane/metadata/review_information/email_address.txt @@ -0,0 +1 @@ + diff --git a/iOS/fastlane/metadata/review_information/first_name.txt b/iOS/fastlane/metadata/review_information/first_name.txt new file mode 100644 index 000000000..8b1378917 --- /dev/null +++ b/iOS/fastlane/metadata/review_information/first_name.txt @@ -0,0 +1 @@ + diff --git a/iOS/fastlane/metadata/review_information/last_name.txt b/iOS/fastlane/metadata/review_information/last_name.txt new file mode 100644 index 000000000..8b1378917 --- /dev/null +++ b/iOS/fastlane/metadata/review_information/last_name.txt @@ -0,0 +1 @@ + diff --git a/iOS/fastlane/metadata/review_information/notes.txt b/iOS/fastlane/metadata/review_information/notes.txt new file mode 100644 index 000000000..8b1378917 --- /dev/null +++ b/iOS/fastlane/metadata/review_information/notes.txt @@ -0,0 +1 @@ + diff --git a/iOS/fastlane/metadata/review_information/phone_number.txt b/iOS/fastlane/metadata/review_information/phone_number.txt new file mode 100644 index 000000000..8b1378917 --- /dev/null +++ b/iOS/fastlane/metadata/review_information/phone_number.txt @@ -0,0 +1 @@ + diff --git a/iOS/fastlane/metadata/secondary_category.txt b/iOS/fastlane/metadata/secondary_category.txt new file mode 100644 index 000000000..cd65e7934 --- /dev/null +++ b/iOS/fastlane/metadata/secondary_category.txt @@ -0,0 +1 @@ +PRODUCTIVITY diff --git a/iOS/fastlane/metadata/secondary_first_sub_category.txt b/iOS/fastlane/metadata/secondary_first_sub_category.txt new file mode 100644 index 000000000..8b1378917 --- /dev/null +++ b/iOS/fastlane/metadata/secondary_first_sub_category.txt @@ -0,0 +1 @@ + diff --git a/iOS/fastlane/metadata/secondary_second_sub_category.txt b/iOS/fastlane/metadata/secondary_second_sub_category.txt new file mode 100644 index 000000000..8b1378917 --- /dev/null +++ b/iOS/fastlane/metadata/secondary_second_sub_category.txt @@ -0,0 +1 @@ + diff --git a/lib/providers/speed_dial_provider.dart b/lib/providers/speed_dial_provider.dart index 227bf323c..6f949658b 100644 --- a/lib/providers/speed_dial_provider.dart +++ b/lib/providers/speed_dial_provider.dart @@ -1,4 +1,3 @@ -import 'package:badgemagic/bademagic_module/utils/byte_array_utils.dart'; import 'package:badgemagic/providers/animation_badge_provider.dart'; import 'package:flutter/material.dart'; @@ -17,6 +16,5 @@ class SpeedDialProvider extends ChangeNotifier { SpeedDialProvider(AnimationBadgeProvider provider) { badgeProvider = provider; notifyListeners(); - logger.i("Badge Provider set for SpeedDialProvider"); } } diff --git a/lib/view/about_us_screen.dart b/lib/view/about_us_screen.dart index b19e7f63c..bdb0cbf34 100644 --- a/lib/view/about_us_screen.dart +++ b/lib/view/about_us_screen.dart @@ -1,5 +1,4 @@ import 'package:badgemagic/constants.dart'; -import 'package:badgemagic/view/widgets/about_us_daialog.dart'; import 'package:badgemagic/view/widgets/common_scaffold_widget.dart'; import 'package:flutter/material.dart'; import 'package:flutter/services.dart'; @@ -62,8 +61,7 @@ class _AboutUsScreenState extends State { ), const SizedBox(height: 30), Text( - 'Badge Magic is an Android app to control LED name badges. ' - 'The goal is to provide options to portray names, graphics, and simple animations on LED badges. ' + 'Badge Magic is an app to control LED name badges. ' 'The goal is to provide options to portray names, graphics, and simple animations on LED badges. ' 'For the data transfer from the smartphone to the LED badge we use Bluetooth. The project is based on the work of Nihlcem.', textAlign: TextAlign.justify, @@ -88,7 +86,7 @@ class _AboutUsScreenState extends State { ), GestureDetector( onTap: () => openUrl( - 'https://github.com/fossasia/badge-magic-android'), + 'https://github.com/fossasia/badgemagic-app/graphs/contributors'), child: Text( 'FOSSASIA contributors', style: GoogleFonts.sora( @@ -145,8 +143,8 @@ class _AboutUsScreenState extends State { fontWeight: FontWeight.w500, color: Colors.grey), ), - onTap: () => openUrl( - 'https://github.com/fossasia/badge-magic-android'), + onTap: () => + openUrl('https://github.com/fossasia/badgemagic-app'), ), ], ), @@ -198,24 +196,24 @@ class _AboutUsScreenState extends State { onTap: () => openUrl( 'https://github.com/fossasia/badgemagic-app/blob/development/LICENSE'), ), - ListTile( - leading: Image.asset('assets/icons/book.png', height: 40), - title: Text( - 'Library Licenses', - style: GoogleFonts.sora( - fontSize: 16, - fontWeight: FontWeight.w500, - color: Colors.black), - ), - subtitle: Text( - 'Check third-party libs used on Badge Magic.', - style: GoogleFonts.sora( - fontSize: 12, - fontWeight: FontWeight.w500, - color: Colors.grey), - ), - onTap: () => showLicenseDialog(context), - ), + // ListTile( + // leading: Image.asset('assets/icons/book.png', height: 40), + // title: Text( + // 'Library Licenses', + // style: GoogleFonts.sora( + // fontSize: 16, + // fontWeight: FontWeight.w500, + // color: Colors.black), + // ), + // subtitle: Text( + // 'Check third-party libs used on Badge Magic.', + // style: GoogleFonts.sora( + // fontSize: 12, + // fontWeight: FontWeight.w500, + // color: Colors.grey), + // ), + // onTap: () => showLicenseDialog(context), + // ), ], ), ), diff --git a/lib/view/widgets/about_us_daialog.dart b/lib/view/widgets/about_us_daialog.dart index afc980015..a480f3d98 100644 --- a/lib/view/widgets/about_us_daialog.dart +++ b/lib/view/widgets/about_us_daialog.dart @@ -185,7 +185,7 @@ void showLicenseDialog(BuildContext context) { LicenseDialogContainer( title: '• Android BLE Scanner Compat library', url: - 'https://github.com/NordicSemiconductor/Andr oid-Scanner-Compat-Library', + 'https://github.com/NordicSemiconductor/Android-Scanner-Compat-Library', content: ''''Copyright 2015, Nordic Semiconductor Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at diff --git a/lib/view/widgets/navigation_drawer.dart b/lib/view/widgets/navigation_drawer.dart index 88ac06789..be72c3d1e 100644 --- a/lib/view/widgets/navigation_drawer.dart +++ b/lib/view/widgets/navigation_drawer.dart @@ -115,7 +115,7 @@ class _BMDrawerState extends State { title: 'Share', routeName: '/share', shareText: - 'Badge Magic is an Android app to control LED name badges. This app provides features to portray names, graphics and simple animations on LED badges. You can also download it from below link https://play.google.com/store/apps/details?id=org.fossasia.badgemagic', + 'Badge Magic is an app to control LED name badges. This app provides features to portray names, graphics and simple animations on LED badges.You can also download it from below link https://play.google.com/store/apps/details?id=org.fossasia.badgemagic', ), _buildListTile( index: 8, diff --git a/scripts/secrets.tar.enc b/scripts/android-secrets.tar.enc similarity index 100% rename from scripts/secrets.tar.enc rename to scripts/android-secrets.tar.enc diff --git a/scripts/check-screenshots.sh b/scripts/check-android-screenshots.sh similarity index 58% rename from scripts/check-screenshots.sh rename to scripts/check-android-screenshots.sh index 7df044b97..c744643d6 100644 --- a/scripts/check-screenshots.sh +++ b/scripts/check-android-screenshots.sh @@ -1,6 +1,6 @@ #!/bin/sh -FILES_DIFF=$(diff <(find docs/images -type f -name "screen-?.jpg" -exec md5sum {} + | sort -k 2 | sed 's/ .*\// /') <(find fastlane/metadata/android/en-US/images/phoneScreenshots -type f -exec md5sum {} + | sort -k 2 | sed 's/ .*\// /')) +FILES_DIFF=$(diff <(find docs/images -type f -name "screen-?.jpg" -exec md5sum {} + | sort -k 2 | sed 's/ .*\// /') <(find android/fastlane/metadata/android/en-US/images/phoneScreenshots -type f -exec md5sum {} + | sort -k 2 | sed 's/ .*\// /')) if [[ $FILES_DIFF ]]; then echo -e "\033[0;31mScreenshots in docs/images and fastlane/metadata/android/en-US/images/phoneScreenshots are not same\033[0m" >&2 exit 1; diff --git a/scripts/ios-secrets.tar.enc b/scripts/ios-secrets.tar.enc new file mode 100644 index 000000000..e288469e8 Binary files /dev/null and b/scripts/ios-secrets.tar.enc differ diff --git a/scripts/prep-android-key.sh b/scripts/prep-android-key.sh new file mode 100644 index 000000000..6124603cb --- /dev/null +++ b/scripts/prep-android-key.sh @@ -0,0 +1,5 @@ +#!/bin/sh +set -e + +openssl aes-256-cbc -K $ENCRYPTED_F10B5E0E5262_KEY -iv $ENCRYPTED_F10B5E0E5262_IV -in ./scripts/android-secrets.tar.enc -out ./scripts/android-secrets.tar -d +tar xvf ./scripts/android-secrets.tar -C android/ \ No newline at end of file diff --git a/scripts/prep-ios-key.sh b/scripts/prep-ios-key.sh new file mode 100644 index 000000000..9f5a4cfef --- /dev/null +++ b/scripts/prep-ios-key.sh @@ -0,0 +1,5 @@ +#!/bin/sh +set -e + +openssl aes-256-cbc -K $ENCRYPTED_IOS_KEY -iv $ENCRYPTED_IOS_IV -in ./scripts/ios-secrets.tar.enc -out ./scripts/ios-secrets.tar -d +tar xvf ./scripts/ios-secrets.tar -C iOS/ \ No newline at end of file diff --git a/scripts/prep-key.sh b/scripts/prep-key.sh deleted file mode 100644 index b35e78168..000000000 --- a/scripts/prep-key.sh +++ /dev/null @@ -1,5 +0,0 @@ -#!/bin/sh -set -e - -openssl aes-256-cbc -K $ENCRYPTED_F10B5E0E5262_KEY -iv $ENCRYPTED_F10B5E0E5262_IV -in ./scripts/secrets.tar.enc -out ./scripts/secrets.tar -d -tar xvf ./scripts/secrets.tar -C android/ \ No newline at end of file